diff options
Diffstat (limited to 'test/support')
31 files changed, 3655 insertions, 281 deletions
diff --git a/test/support/Counter.h b/test/support/Counter.h index 2bc3642f50594..eb6e04e72ef62 100644 --- a/test/support/Counter.h +++ b/test/support/Counter.h @@ -13,7 +13,7 @@ #include <functional> // for std::hash struct Counter_base { static int gConstructed; }; - + template <typename T> class Counter : public Counter_base { @@ -27,7 +27,7 @@ public: Counter& operator=(Counter&& rhs) { ++gConstructed; data_ = std::move(rhs.data_); return *this; } #endif ~Counter() { --gConstructed; } - + const T& get() const {return data_;} bool operator==(const Counter& x) const {return data_ == x.data_;} diff --git a/test/support/MoveOnly.h b/test/support/MoveOnly.h index ee6ae7c7e5f9e..a3e9cca89ee50 100644 --- a/test/support/MoveOnly.h +++ b/test/support/MoveOnly.h @@ -10,6 +10,8 @@ #ifndef MOVEONLY_H #define MOVEONLY_H +#include "test_macros.h" + #ifndef _LIBCPP_HAS_NO_RVALUE_REFERENCES #include <cstddef> diff --git a/test/support/allocators.h b/test/support/allocators.h index 7c2103ff27216..4aa467f824aa6 100644 --- a/test/support/allocators.h +++ b/test/support/allocators.h @@ -101,7 +101,7 @@ public: A2& operator=(const A2& a) TEST_NOEXCEPT { id_ = a.id(); copy_called = true; return *this;} A2& operator=(A2&& a) TEST_NOEXCEPT { id_ = a.id(); move_called = true; return *this;} - T* allocate(std::size_t n, const void* hint) + T* allocate(std::size_t, const void* hint) { allocate_called = true; return (T*)hint; diff --git a/test/support/asan_testing.h b/test/support/asan_testing.h index 45ad04b1bb2ca..678f12a91b65a 100644 --- a/test/support/asan_testing.h +++ b/test/support/asan_testing.h @@ -10,12 +10,12 @@ #ifndef ASAN_TESTING_H #define ASAN_TESTING_H -#include <__config> +#include "test_macros.h" -#ifndef _LIBCPP_HAS_NO_ASAN +#if TEST_HAS_FEATURE(address_sanitizer) extern "C" int __sanitizer_verify_contiguous_container ( const void *beg, const void *mid, const void *end ); - + template <typename T, typename Alloc> bool is_contiguous_container_asan_correct ( const std::vector<T, Alloc> &c ) { @@ -27,11 +27,11 @@ bool is_contiguous_container_asan_correct ( const std::vector<T, Alloc> &c ) #else template <typename T, typename Alloc> -bool is_contiguous_container_asan_correct ( const std::vector<T, Alloc> &c ) +bool is_contiguous_container_asan_correct ( const std::vector<T, Alloc> &) { return true; } #endif - + #endif // ASAN_TESTING_H diff --git a/test/support/assert_checkpoint.h b/test/support/assert_checkpoint.h new file mode 100644 index 0000000000000..6627b35eb30d1 --- /dev/null +++ b/test/support/assert_checkpoint.h @@ -0,0 +1,73 @@ +#ifndef SUPPORT_ASSERT_CHECKPOINT_H +#define SUPPORT_ASSERT_CHECKPOINT_H + +#include <csignal> +#include <iostream> +#include <cstdlib> + +struct Checkpoint { + const char* file; + const char* func; + int line; + const char* msg; + + Checkpoint() : file(nullptr), func(nullptr), line(-1), msg(nullptr) {} + Checkpoint(const char* xfile, const char* xfunc, int xline, const char* xmsg) + : file(xfile), func(xfunc), line(xline), msg(xmsg) + {} + + template <class Stream> + void print(Stream& s) const { + if (!file) { + s << "NO CHECKPOINT\n"; + return; + } + s << file << ":" << line << " " << func << ": Checkpoint"; + if (msg) + s << " '" << msg << "'"; + s << std::endl; + } +}; + +inline Checkpoint& globalCheckpoint() { + static Checkpoint C; + return C; +} + +inline void clearCheckpoint() { + globalCheckpoint() = Checkpoint(); +} + +#if defined(__GNUC__) +#define CHECKPOINT_FUNCTION_NAME __PRETTY_FUNCTION__ +#else +#define CHECKPOINT_FUNCTION_NAME __func__ +#endif + +#define CHECKPOINT(msg) globalCheckpoint() = Checkpoint(__FILE__, CHECKPOINT_FUNCTION_NAME, __LINE__, msg); + +inline void checkpointSignalHandler(int signal) { + if (signal == SIGABRT) { + globalCheckpoint().print(std::cerr); + } else { + std::cerr << "Unexpected signal " << signal << " received\n"; + } + std::_Exit(EXIT_FAILURE); +} + +inline bool initCheckpointHandler() { + typedef void(*HandlerT)(int); + static bool isInit = false; + if (isInit) return true; + HandlerT prev_h = std::signal(SIGABRT, checkpointSignalHandler); + if (prev_h == SIG_ERR) { + std::cerr << "Setup failed.\n"; + std::_Exit(EXIT_FAILURE); + } + isInit = true; + return false; +} + +static bool initDummy = initCheckpointHandler(); + +#endif diff --git a/test/support/constexpr_char_traits.hpp b/test/support/constexpr_char_traits.hpp index b069c90076a2c..0a73d3ad1eadd 100644 --- a/test/support/constexpr_char_traits.hpp +++ b/test/support/constexpr_char_traits.hpp @@ -11,9 +11,9 @@ #ifndef _CONSTEXPR_CHAR_TRAITS #define _CONSTEXPR_CHAR_TRAITS -#include <__config> #include <string> +#include "test_macros.h" template <class _CharT> struct constexpr_char_traits @@ -24,41 +24,41 @@ struct constexpr_char_traits typedef std::streampos pos_type; typedef std::mbstate_t state_type; - static _LIBCPP_CONSTEXPR_AFTER_CXX11 void assign(char_type& __c1, const char_type& __c2) _NOEXCEPT + static TEST_CONSTEXPR_CXX14 void assign(char_type& __c1, const char_type& __c2) TEST_NOEXCEPT {__c1 = __c2;} - static _LIBCPP_CONSTEXPR bool eq(char_type __c1, char_type __c2) _NOEXCEPT + static TEST_CONSTEXPR bool eq(char_type __c1, char_type __c2) TEST_NOEXCEPT {return __c1 == __c2;} - static _LIBCPP_CONSTEXPR bool lt(char_type __c1, char_type __c2) _NOEXCEPT + static TEST_CONSTEXPR bool lt(char_type __c1, char_type __c2) TEST_NOEXCEPT {return __c1 < __c2;} - static _LIBCPP_CONSTEXPR_AFTER_CXX11 int compare(const char_type* __s1, const char_type* __s2, size_t __n); - static _LIBCPP_CONSTEXPR_AFTER_CXX11 size_t length(const char_type* __s); - static _LIBCPP_CONSTEXPR_AFTER_CXX11 const char_type* find(const char_type* __s, size_t __n, const char_type& __a); - static _LIBCPP_CONSTEXPR_AFTER_CXX11 char_type* move(char_type* __s1, const char_type* __s2, size_t __n); - static _LIBCPP_CONSTEXPR_AFTER_CXX11 char_type* copy(char_type* __s1, const char_type* __s2, size_t __n); - static _LIBCPP_CONSTEXPR_AFTER_CXX11 char_type* assign(char_type* __s, size_t __n, char_type __a); + static TEST_CONSTEXPR_CXX14 int compare(const char_type* __s1, const char_type* __s2, size_t __n); + static TEST_CONSTEXPR_CXX14 size_t length(const char_type* __s); + static TEST_CONSTEXPR_CXX14 const char_type* find(const char_type* __s, size_t __n, const char_type& __a); + static TEST_CONSTEXPR_CXX14 char_type* move(char_type* __s1, const char_type* __s2, size_t __n); + static TEST_CONSTEXPR_CXX14 char_type* copy(char_type* __s1, const char_type* __s2, size_t __n); + static TEST_CONSTEXPR_CXX14 char_type* assign(char_type* __s, size_t __n, char_type __a); - static _LIBCPP_CONSTEXPR int_type not_eof(int_type __c) _NOEXCEPT + static TEST_CONSTEXPR int_type not_eof(int_type __c) TEST_NOEXCEPT {return eq_int_type(__c, eof()) ? ~eof() : __c;} - static _LIBCPP_CONSTEXPR char_type to_char_type(int_type __c) _NOEXCEPT + static TEST_CONSTEXPR char_type to_char_type(int_type __c) TEST_NOEXCEPT {return char_type(__c);} - static _LIBCPP_CONSTEXPR int_type to_int_type(char_type __c) _NOEXCEPT + static TEST_CONSTEXPR int_type to_int_type(char_type __c) TEST_NOEXCEPT {return int_type(__c);} - static _LIBCPP_CONSTEXPR bool eq_int_type(int_type __c1, int_type __c2) _NOEXCEPT + static TEST_CONSTEXPR bool eq_int_type(int_type __c1, int_type __c2) TEST_NOEXCEPT {return __c1 == __c2;} - static _LIBCPP_CONSTEXPR int_type eof() _NOEXCEPT + static TEST_CONSTEXPR int_type eof() TEST_NOEXCEPT {return int_type(EOF);} }; template <class _CharT> -_LIBCPP_CONSTEXPR_AFTER_CXX11 int +TEST_CONSTEXPR_CXX14 int constexpr_char_traits<_CharT>::compare(const char_type* __s1, const char_type* __s2, size_t __n) { for (; __n; --__n, ++__s1, ++__s2) @@ -72,7 +72,7 @@ constexpr_char_traits<_CharT>::compare(const char_type* __s1, const char_type* _ } template <class _CharT> -_LIBCPP_CONSTEXPR_AFTER_CXX11 size_t +TEST_CONSTEXPR_CXX14 size_t constexpr_char_traits<_CharT>::length(const char_type* __s) { size_t __len = 0; @@ -82,7 +82,7 @@ constexpr_char_traits<_CharT>::length(const char_type* __s) } template <class _CharT> -_LIBCPP_CONSTEXPR_AFTER_CXX11 const _CharT* +TEST_CONSTEXPR_CXX14 const _CharT* constexpr_char_traits<_CharT>::find(const char_type* __s, size_t __n, const char_type& __a) { for (; __n; --__n) @@ -95,7 +95,7 @@ constexpr_char_traits<_CharT>::find(const char_type* __s, size_t __n, const char } template <class _CharT> -_LIBCPP_CONSTEXPR_AFTER_CXX11 _CharT* +TEST_CONSTEXPR_CXX14 _CharT* constexpr_char_traits<_CharT>::move(char_type* __s1, const char_type* __s2, size_t __n) { char_type* __r = __s1; @@ -115,7 +115,7 @@ constexpr_char_traits<_CharT>::move(char_type* __s1, const char_type* __s2, size } template <class _CharT> -_LIBCPP_CONSTEXPR_AFTER_CXX11 _CharT* +TEST_CONSTEXPR_CXX14 _CharT* constexpr_char_traits<_CharT>::copy(char_type* __s1, const char_type* __s2, size_t __n) { _LIBCPP_ASSERT(__s2 < __s1 || __s2 >= __s1+__n, "char_traits::copy overlapped range"); @@ -126,7 +126,7 @@ constexpr_char_traits<_CharT>::copy(char_type* __s1, const char_type* __s2, size } template <class _CharT> -_LIBCPP_CONSTEXPR_AFTER_CXX11 _CharT* +TEST_CONSTEXPR_CXX14 _CharT* constexpr_char_traits<_CharT>::assign(char_type* __s, size_t __n, char_type __a) { char_type* __r = __s; diff --git a/test/support/container_test_types.h b/test/support/container_test_types.h new file mode 100644 index 0000000000000..0b97f2e94e75a --- /dev/null +++ b/test/support/container_test_types.h @@ -0,0 +1,492 @@ +//===----------------------------------------------------------------------===// +// +// The LLVM Compiler Infrastructure +// +// This file is dual licensed under the MIT and the University of Illinois Open +// Source Licenses. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// +#ifndef SUPPORT_CONTAINER_TEST_TYPES_H +#define SUPPORT_CONTAINER_TEST_TYPES_H + +// container_test_types.h - A set of types used for testing STL containers. +// The types container within this header are used to test the requirements in +// [container.requirements.general]. The header is made up of 3 main components: +// +// * test-types: 'CopyInsertable', 'MoveInsertable' and 'EmplaceConstructible' - +// These test types are used to test the container requirements of the same +// name. These test types use the global 'AllocatorConstructController' to +// assert that they are only constructed by the containers allocator. +// +// * test-allocator: 'ContainerTestAllocator' - This test allocator is used to +// test the portions of [container.requirements.general] that pertain to the +// containers allocator. The three primary jobs of the test allocator are: +// 1. Enforce that 'a.construct(...)' and 'a.destroy(...)' are only ever +// instantiated for 'Container::value_type'. +// 2. Provide a mechanism of checking calls to 'a.construct(Args...)'. +// Including controlling when and with what types 'a.construct(...)' +// may be called with. +// 3. Support the test types internals by controlling the global +// 'AllocatorConstructController' object. +// +// * 'AllocatorConstructController' - This type defines an interface for testing +// the construction of types using an allocator. This type is used to communicate +// between the test author, the containers allocator, and the types +// being constructed by the container. +// The controllers primary functions are: +// 1. Allow calls to 'a.construct(p, args...)' to be checked by a test. +// The test uses 'cc->expect<Args...>()' to specify that the allocator +// should expect one call to 'a.construct' with the specified argument +// types. +// 2. Controlling the value of 'cc->isInAllocatorConstruct()' within the +// 'construct' method. The test-types use this value to assert that +// they are being constructed by the allocator. +// +// 'AllocatorConstructController' enforces the Singleton pattern since the +// test-types, test-allocator and test need to share the same controller +// object. A pointer to the global controller is returned by +// 'getConstructController()'. +// +//---------------------------------------------------------------------------- +/* + * Usage: The following example checks that 'unordered_map::emplace(Args&&...)' + * with 'Args = [CopyInsertable<1> const&, CopyInsertible<2>&&]' + * calls 'alloc.construct(value_type*, Args&&...)' with the same types. + * + * // Typedefs for container + * using Key = CopyInsertible<1>; + * using Value = CopyInsertible<2>; + * using ValueTp = std::pair<const Key, Value>; + * using Alloc = ContainerTestAllocator<ValueTp, ValueTp>; + * using Map = std::unordered_map<Key, Value, std::hash<Key>, std::equal_to<Key>, Alloc>; + * + * // Get the global controller, reset it, and construct an allocator with + * // the controller. + * ConstructController* cc = getConstructController(); + * cc->reset(); + * + * // Create a Map and a Key and Value to insert. Note that the test-allocator + * // does not need to be given 'cc'. + * Map m; + * const Key k(1); + * Value v(1); + * + * // Tell the controller to expect a construction from the specified types. + * cc->expect<Key const&, Value&&>(); + * + * // Emplace the objects into the container. 'Alloc.construct(p, UArgs...)' + * // will assert 'cc->check<UArgs&&>()' is true which will consume + * // the call to 'cc->expect<...>()'. + * m.emplace(k, std::move(v)); + * + * // Assert that the "expect" was consumed by a matching "check" call within + * // Alloc. + * assert(!cc->unexpected()); + * + */ + +#include <functional> +#include <cassert> + +#include "test_macros.h" + +#if TEST_STD_VER < 11 +#error This header requires C++11 or greater +#endif + +namespace detail { +// TypeID - Represent a unique identifier for a type. TypeID allows equality +// comparisons between different types. +struct TypeID { + friend bool operator==(TypeID const& LHS, TypeID const& RHS) + {return LHS.m_id == RHS.m_id; } + friend bool operator!=(TypeID const& LHS, TypeID const& RHS) + {return LHS.m_id != RHS.m_id; } +private: + explicit constexpr TypeID(const int* xid) : m_id(xid) {} + const int* const m_id; + template <class T> friend class TypeInfo; +}; + +// TypeInfo - Represent information for the specified type 'T', including a +// unique TypeID. +template <class T> +class TypeInfo { +public: + typedef T value_type; + typedef TypeID ID; + static ID const& GetID() { static ID id(&dummy_addr); return id; } + +private: + static const int dummy_addr; +}; + +template <class L, class R> +inline bool operator==(TypeInfo<L> const&, TypeInfo<R> const&) +{ return std::is_same<L, R>::value; } + +template <class L, class R> +inline bool operator!=(TypeInfo<L> const& lhs, TypeInfo<R> const& rhs) +{ return !(lhs == rhs); } + +template <class T> +const int TypeInfo<T>::dummy_addr = 42; + +// makeTypeID - Return the TypeID for the specified type 'T'. +template <class T> +inline constexpr TypeID const& makeTypeID() { return TypeInfo<T>::GetID(); } + +template <class ...Args> +struct ArgumentListID {}; + +// makeArgumentID - Create and return a unique identifier for a given set +// of arguments. +template <class ...Args> +inline constexpr TypeID const& makeArgumentID() { + return makeTypeID<ArgumentListID<Args...>>(); +} + +} // namespace detail + +//===----------------------------------------------------------------------===// +// AllocatorConstructController +//===----------------------------------------------------------------------===// + +struct AllocatorConstructController { + const detail::TypeID* m_expected_args; + bool m_allow_constructions; + bool m_allow_unchecked; + int m_expected_count; + + void clear() { + m_expected_args = nullptr; + m_expected_count = -1; + } + + // Check for and consume an expected construction added by 'expect'. + // Return true if the construction was expected and false otherwise. + // This should only be called by 'Allocator.construct'. + bool check(detail::TypeID const& tid) { + if (!m_expected_args) + assert(m_allow_unchecked); + bool res = *m_expected_args == tid; + if (m_expected_count == -1 || --m_expected_count == -1) + m_expected_args = nullptr; + return res; + } + + // Return true iff there is an unchecked construction expression. + bool unchecked() { + return m_expected_args != nullptr; + } + + // Expect a call to Allocator::construct with Args that match 'tid'. + void expect(detail::TypeID const& tid) { + assert(!unchecked()); + m_expected_args = &tid; + } + + template <class ...Args> + void expect(int times = 1) { + assert(!unchecked()); + assert(times > 0); + m_expected_count = times - 1; + m_expected_args = &detail::makeArgumentID<Args...>(); + } + template <class ...Args> + bool check() { + return check(detail::makeArgumentID<Args...>()); + } + + + // Return true iff the program is currently within a call to "Allocator::construct" + bool isInAllocatorConstruct() const { + return m_allow_constructions; + } + + void inAllocatorConstruct(bool value = true) { + m_allow_constructions = value; + } + + void allowUnchecked(bool value = true) { + m_allow_unchecked = value; + } + + void reset() { + m_allow_constructions = false; + m_expected_args = nullptr; + m_allow_unchecked = false; + m_expected_count = -1; + } + +private: + friend AllocatorConstructController* getConstructController(); + AllocatorConstructController() { reset(); } + AllocatorConstructController(AllocatorConstructController const&); + AllocatorConstructController& operator=(AllocatorConstructController const&); +}; + +typedef AllocatorConstructController ConstructController; + +// getConstructController - Return the global allocator construction controller. +inline ConstructController* getConstructController() { + static ConstructController c; + return &c; +} + +//===----------------------------------------------------------------------===// +// ContainerTestAllocator +//===----------------------------------------------------------------------===// + +// ContainerTestAllocator - A STL allocator type that only allows 'construct' +// and 'destroy' to be called for 'AllowConstructT' types. ContainerTestAllocator +// uses the 'AllocatorConstructionController' interface. +template <class T, class AllowConstructT> +class ContainerTestAllocator +{ + struct InAllocatorConstructGuard { + ConstructController *m_cc; + bool m_old; + InAllocatorConstructGuard(ConstructController* cc) : m_cc(cc) { + if (m_cc) { + m_old = m_cc->isInAllocatorConstruct(); + m_cc->inAllocatorConstruct(true); + } + } + ~InAllocatorConstructGuard() { + if (m_cc) m_cc->inAllocatorConstruct(m_old); + } + private: + InAllocatorConstructGuard(InAllocatorConstructGuard const&); + InAllocatorConstructGuard& operator=(InAllocatorConstructGuard const&); + }; + +public: + typedef T value_type; + + int construct_called; + int destroy_called; + ConstructController* controller; + + ContainerTestAllocator() TEST_NOEXCEPT + : controller(getConstructController()) {} + + explicit ContainerTestAllocator(ConstructController* c) + : controller(c) + {} + + template <class U> + ContainerTestAllocator(ContainerTestAllocator<U, AllowConstructT> other) TEST_NOEXCEPT + : controller(other.controller) + {} + + T* allocate(std::size_t n) + { + return static_cast<T*>(::operator new(n*sizeof(T))); + } + + void deallocate(T* p, std::size_t) + { + return ::operator delete(static_cast<void*>(p)); + } + + template <class Up, class ...Args> + void construct(Up* p, Args&&... args) { + static_assert((std::is_same<Up, AllowConstructT>::value), + "Only allowed to construct Up"); + assert(controller->check<Args&&...>()); + { + InAllocatorConstructGuard g(controller); + ::new ((void*)p) Up(std::forward<Args>(args)...); + } + } + + template <class Up> + void destroy(Up* p) { + static_assert((std::is_same<Up, AllowConstructT>::value), + "Only allowed to destroy Up"); + { + InAllocatorConstructGuard g(controller); + p->~Up(); + } + } + + friend bool operator==(ContainerTestAllocator, ContainerTestAllocator) {return true;} + friend bool operator!=(ContainerTestAllocator x, ContainerTestAllocator y) {return !(x == y);} +}; + + +namespace test_detail { +typedef ContainerTestAllocator<int, int> A1; +typedef std::allocator_traits<A1> A1T; +typedef ContainerTestAllocator<float, int> A2; +typedef std::allocator_traits<A2> A2T; + +static_assert(std::is_same<A1T::rebind_traits<float>, A2T>::value, ""); +static_assert(std::is_same<A2T::rebind_traits<int>, A1T>::value, ""); +} // end namespace test_detail + +//===----------------------------------------------------------------------===// +// 'CopyInsertable', 'MoveInsertable' and 'EmplaceConstructible' test types +//===----------------------------------------------------------------------===// + +template <int Dummy = 0> +struct CopyInsertable { + int data; + mutable bool copied_once; + bool constructed_under_allocator; + + explicit CopyInsertable(int val) : data(val), copied_once(false), + constructed_under_allocator(false) { + if (getConstructController()->isInAllocatorConstruct()) { + copied_once = true; + constructed_under_allocator = true; + } + } + + CopyInsertable() : data(0), copied_once(false), constructed_under_allocator(true) + { + assert(getConstructController()->isInAllocatorConstruct()); + } + + CopyInsertable(CopyInsertable const& other) : data(other.data), + copied_once(true), + constructed_under_allocator(true) { + assert(getConstructController()->isInAllocatorConstruct()); + assert(other.copied_once == false); + other.copied_once = true; + } + + CopyInsertable(CopyInsertable& other) : data(other.data), copied_once(true), + constructed_under_allocator(true) { + assert(getConstructController()->isInAllocatorConstruct()); + assert(other.copied_once == false); + other.copied_once = true; + } + + CopyInsertable(CopyInsertable&& other) : CopyInsertable(other) {} + + // Forgive pair for not downcasting this to an lvalue it its constructors. + CopyInsertable(CopyInsertable const && other) : CopyInsertable(other) {} + + + template <class ...Args> + CopyInsertable(Args&&... args) { + assert(false); + } + + ~CopyInsertable() { + assert(constructed_under_allocator == getConstructController()->isInAllocatorConstruct()); + } + + void reset(int value) { + data = value; + copied_once = false; + constructed_under_allocator = false; + } +}; + +template <int ID> +bool operator==(CopyInsertable<ID> const& L, CopyInsertable<ID> const& R) { + return L.data == R.data; +} + + +template <int ID> +bool operator!=(CopyInsertable<ID> const& L, CopyInsertable<ID> const& R) { + return L.data != R.data; +} + +template <int ID> +bool operator <(CopyInsertable<ID> const& L, CopyInsertable<ID> const& R) { + return L.data < R.data; +} + + +#ifdef _LIBCPP_BEGIN_NAMESPACE_STD +_LIBCPP_BEGIN_NAMESPACE_STD +#else +namespace std { +#endif + template <int ID> + struct hash< ::CopyInsertable<ID> > { + typedef ::CopyInsertable<ID> argument_type; + typedef size_t result_type; + + size_t operator()(argument_type const& arg) const { + return arg.data; + } + }; + + template <class _Key, class _Value, class _Less, class _Alloc> + class map; + template <class _Key, class _Value, class _Less, class _Alloc> + class multimap; + template <class _Value, class _Less, class _Alloc> + class set; + template <class _Value, class _Less, class _Alloc> + class multiset; + template <class _Key, class _Value, class _Hash, class _Equals, class _Alloc> + class unordered_map; + template <class _Key, class _Value, class _Hash, class _Equals, class _Alloc> + class unordered_multimap; + template <class _Value, class _Hash, class _Equals, class _Alloc> + class unordered_set; + template <class _Value, class _Hash, class _Equals, class _Alloc> + class unordered_multiset; + +#ifdef _LIBCPP_END_NAMESPACE_STD +_LIBCPP_END_NAMESPACE_STD +#else +} // end namespace std +#endif + +// TCT - Test container type +namespace TCT { + +template <class Key = CopyInsertable<1>, class Value = CopyInsertable<2>, + class ValueTp = std::pair<const Key, Value> > +using unordered_map = + std::unordered_map<Key, Value, std::hash<Key>, std::equal_to<Key>, + ContainerTestAllocator<ValueTp, ValueTp> >; + +template <class Key = CopyInsertable<1>, class Value = CopyInsertable<2>, + class ValueTp = std::pair<const Key, Value> > +using map = + std::map<Key, Value, std::less<Key>, + ContainerTestAllocator<ValueTp, ValueTp> >; + +template <class Key = CopyInsertable<1>, class Value = CopyInsertable<2>, + class ValueTp = std::pair<const Key, Value> > +using unordered_multimap = + std::unordered_multimap<Key, Value, std::hash<Key>, std::equal_to<Key>, + ContainerTestAllocator<ValueTp, ValueTp> >; + +template <class Key = CopyInsertable<1>, class Value = CopyInsertable<2>, + class ValueTp = std::pair<const Key, Value> > +using multimap = + std::multimap<Key, Value, std::less<Key>, + ContainerTestAllocator<ValueTp, ValueTp> >; + +template <class Value = CopyInsertable<1> > +using unordered_set = + std::unordered_set<Value, std::hash<Value>, std::equal_to<Value>, + ContainerTestAllocator<Value, Value> >; + +template <class Value = CopyInsertable<1> > +using set = + std::set<Value, std::less<Value>, ContainerTestAllocator<Value, Value> >; + +template <class Value = CopyInsertable<1> > +using unordered_multiset = + std::unordered_multiset<Value, std::hash<Value>, std::equal_to<Value>, + ContainerTestAllocator<Value, Value> >; + +template <class Value = CopyInsertable<1> > +using multiset = + std::multiset<Value, std::less<Value>, ContainerTestAllocator<Value, Value> >; + +} // end namespace TCT + + +#endif // SUPPORT_CONTAINER_TEST_TYPES_H diff --git a/test/support/count_new.hpp b/test/support/count_new.hpp index e8968a93de980..923e495134871 100644 --- a/test/support/count_new.hpp +++ b/test/support/count_new.hpp @@ -14,16 +14,24 @@ # include <cassert> # include <new> -#ifndef __has_feature -# define __has_feature(x) 0 -#endif +#include "test_macros.h" -#if __has_feature(address_sanitizer) \ - || __has_feature(memory_sanitizer) \ - || __has_feature(thread_sanitizer) +#if defined(TEST_HAS_SANITIZERS) #define DISABLE_NEW_COUNT #endif +namespace detail +{ + TEST_NORETURN + inline void throw_bad_alloc_helper() { +#ifndef TEST_HAS_NO_EXCEPTIONS + throw std::bad_alloc(); +#else + std::abort(); +#endif + } +} + class MemCounter { public: @@ -43,6 +51,11 @@ public: // code doesn't perform any allocations. bool disable_allocations; + // number of allocations to throw after. Default (unsigned)-1. If + // throw_after has the default value it will never be decremented. + static const unsigned never_throw_value = static_cast<unsigned>(-1); + unsigned throw_after; + int outstanding_new; int new_called; int delete_called; @@ -58,6 +71,12 @@ public: { assert(disable_allocations == false); assert(s); + if (throw_after == 0) { + throw_after = never_throw_value; + detail::throw_bad_alloc_helper(); + } else if (throw_after != never_throw_value) { + --throw_after; + } ++new_called; ++outstanding_new; last_new_size = s; @@ -74,6 +93,12 @@ public: { assert(disable_allocations == false); assert(s); + if (throw_after == 0) { + throw_after = never_throw_value; + detail::throw_bad_alloc_helper(); + } else { + // don't decrement throw_after here. newCalled will end up doing that. + } ++outstanding_array_new; ++new_array_called; last_new_array_size = s; @@ -96,9 +121,11 @@ public: disable_allocations = false; } + void reset() { disable_allocations = false; + throw_after = never_throw_value; outstanding_new = 0; new_called = 0; @@ -132,6 +159,11 @@ public: return disable_checking || n != new_called; } + bool checkNewCalledGreaterThan(int n) const + { + return disable_checking || new_called > n; + } + bool checkDeleteCalledEq(int n) const { return disable_checking || n == delete_called; @@ -205,7 +237,10 @@ MemCounter globalMemCounter((MemCounter::MemCounterCtorArg_())); void* operator new(std::size_t s) throw(std::bad_alloc) { globalMemCounter.newCalled(s); - return std::malloc(s); + void* ret = std::malloc(s); + if (ret == nullptr) + detail::throw_bad_alloc_helper(); + return ret; } void operator delete(void* p) throw() @@ -255,4 +290,33 @@ private: DisableAllocationGuard& operator=(DisableAllocationGuard const&); }; + +struct RequireAllocationGuard { + explicit RequireAllocationGuard(std::size_t RequireAtLeast = 1) + : m_req_alloc(RequireAtLeast), + m_new_count_on_init(globalMemCounter.new_called), + m_outstanding_new_on_init(globalMemCounter.outstanding_new), + m_exactly(false) + { + } + + void requireAtLeast(std::size_t N) { m_req_alloc = N; m_exactly = false; } + void requireExactly(std::size_t N) { m_req_alloc = N; m_exactly = true; } + + ~RequireAllocationGuard() { + assert(globalMemCounter.checkOutstandingNewEq(m_outstanding_new_on_init)); + std::size_t Expect = m_new_count_on_init + m_req_alloc; + assert(globalMemCounter.checkNewCalledEq(Expect) || + (!m_exactly && globalMemCounter.checkNewCalledGreaterThan(Expect))); + } + +private: + std::size_t m_req_alloc; + const std::size_t m_new_count_on_init; + const std::size_t m_outstanding_new_on_init; + bool m_exactly; + RequireAllocationGuard(RequireAllocationGuard const&); + RequireAllocationGuard& operator=(RequireAllocationGuard const&); +}; + #endif /* COUNT_NEW_HPP */ diff --git a/test/support/counting_predicates.hpp b/test/support/counting_predicates.hpp index aab6a9785772c..2bd1426934e51 100644 --- a/test/support/counting_predicates.hpp +++ b/test/support/counting_predicates.hpp @@ -16,11 +16,11 @@ struct unary_counting_predicate : public std::unary_function<Arg, bool> { public: unary_counting_predicate(Predicate p) : p_(p), count_(0) {} ~unary_counting_predicate() {} - + bool operator () (const Arg &a) const { ++count_; return p_(a); } size_t count() const { return count_; } void reset() { count_ = 0; } - + private: Predicate p_; mutable size_t count_; @@ -33,7 +33,7 @@ public: binary_counting_predicate ( Predicate p ) : p_(p), count_(0) {} ~binary_counting_predicate() {} - + bool operator () (const Arg1 &a1, const Arg2 &a2) const { ++count_; return p_(a1, a2); } size_t count() const { return count_; } void reset() { count_ = 0; } diff --git a/test/support/disable_missing_braces_warning.h b/test/support/disable_missing_braces_warning.h index 97fd8a89a51a3..c53eef8a951d1 100644 --- a/test/support/disable_missing_braces_warning.h +++ b/test/support/disable_missing_braces_warning.h @@ -11,6 +11,8 @@ // std::array is explicitly allowed to be initialized with A a = { init-list };. // Disable the missing braces warning for this reason. +#if defined(__GNUC__) #pragma GCC diagnostic ignored "-Wmissing-braces" +#endif #endif // SUPPORT_DISABLE_MISSING_BRACES_WARNING_H diff --git a/test/support/filesystem_dynamic_test_helper.py b/test/support/filesystem_dynamic_test_helper.py new file mode 100644 index 0000000000000..1f48c95279829 --- /dev/null +++ b/test/support/filesystem_dynamic_test_helper.py @@ -0,0 +1,85 @@ +import sys +import os +import stat + +# Ensure that this is being run on a specific platform +assert sys.platform.startswith('linux') or sys.platform.startswith('darwin') \ + or sys.platform.startswith('cygwin') or sys.platform.startswith('freebsd') + +def env_path(): + ep = os.environ.get('LIBCXX_FILESYSTEM_DYNAMIC_TEST_ROOT') + assert ep is not None + ep = os.path.realpath(ep) + assert os.path.isdir(ep) + return ep + +env_path_global = env_path() + +# Make sure we don't try and write outside of env_path. +# All paths used should be sanitized +def sanitize(p): + p = os.path.realpath(p) + if os.path.commonprefix([env_path_global, p]): + return p + assert False + +""" +Some of the tests restrict permissions to induce failures. +Before we delete the test enviroment, we have to walk it and re-raise the +permissions. +""" +def clean_recursive(root_p): + if not os.path.islink(root_p): + os.chmod(root_p, 0o777) + for ent in os.listdir(root_p): + p = os.path.join(root_p, ent) + if os.path.islink(p) or not os.path.isdir(p): + os.remove(p) + else: + assert os.path.isdir(p) + clean_recursive(p) + os.rmdir(p) + + +def init_test_directory(root_p): + root_p = sanitize(root_p) + assert not os.path.exists(root_p) + os.makedirs(root_p) + + +def destroy_test_directory(root_p): + root_p = sanitize(root_p) + clean_recursive(root_p) + os.rmdir(root_p) + + +def create_file(fname, size): + with open(sanitize(fname), 'w') as f: + f.write('c' * size) + + +def create_dir(dname): + os.mkdir(sanitize(dname)) + + +def create_symlink(source, link): + os.symlink(sanitize(source), sanitize(link)) + + +def create_hardlink(source, link): + os.link(sanitize(source), sanitize(link)) + + +def create_fifo(source): + os.mkfifo(sanitize(source)) + + +def create_socket(source): + mode = 0600|stat.S_IFSOCK + os.mknod(sanitize(source), mode) + + +if __name__ == '__main__': + command = " ".join(sys.argv[1:]) + eval(command) + sys.exit(0) diff --git a/test/support/filesystem_test_helper.hpp b/test/support/filesystem_test_helper.hpp new file mode 100644 index 0000000000000..7150f79739b61 --- /dev/null +++ b/test/support/filesystem_test_helper.hpp @@ -0,0 +1,403 @@ +#ifndef FILESYSTEM_TEST_HELPER_HPP +#define FILESYSTEM_TEST_HELPER_HPP + +#include <experimental/filesystem> +#include <cassert> +#include <cstdio> // for printf +#include <string> +#include <fstream> +#include <random> +#include <chrono> + +namespace fs = std::experimental::filesystem; + +// static test helpers + +#ifndef LIBCXX_FILESYSTEM_STATIC_TEST_ROOT +#warning "STATIC TESTS DISABLED" +#else // LIBCXX_FILESYSTEM_STATIC_TEST_ROOT + +namespace StaticEnv { + +inline fs::path makePath(fs::path const& p) { + // env_path is expected not to contain symlinks. + static const fs::path env_path = LIBCXX_FILESYSTEM_STATIC_TEST_ROOT; + return env_path / p; +} + +static const fs::path Root = LIBCXX_FILESYSTEM_STATIC_TEST_ROOT; + +static const fs::path TestFileList[] = { + makePath("empty_file"), + makePath("non_empty_file"), + makePath("dir1/file1"), + makePath("dir1/file2") +}; +const std::size_t TestFileListSize = sizeof(TestFileList) / sizeof(fs::path); + +static const fs::path TestDirList[] = { + makePath("dir1"), + makePath("dir1/dir2"), + makePath("dir1/dir2/dir3") +}; +const std::size_t TestDirListSize = sizeof(TestDirList) / sizeof(fs::path); + +static const fs::path File = TestFileList[0]; +static const fs::path Dir = TestDirList[0]; +static const fs::path Dir2 = TestDirList[1]; +static const fs::path Dir3 = TestDirList[2]; +static const fs::path SymlinkToFile = makePath("symlink_to_empty_file"); +static const fs::path SymlinkToDir = makePath("symlink_to_dir"); +static const fs::path BadSymlink = makePath("bad_symlink"); +static const fs::path DNE = makePath("DNE"); +static const fs::path EmptyFile = TestFileList[0]; +static const fs::path NonEmptyFile = TestFileList[1]; +static const fs::path CharFile = "/dev/null"; // Hopefully this exists + +static const fs::path DirIterationList[] = { + makePath("dir1/dir2"), + makePath("dir1/file1"), + makePath("dir1/file2") +}; +const std::size_t DirIterationListSize = sizeof(DirIterationList) + / sizeof(fs::path); + +static const fs::path DirIterationListDepth1[] = { + makePath("dir1/dir2/afile3"), + makePath("dir1/dir2/dir3"), + makePath("dir1/dir2/symlink_to_dir3"), + makePath("dir1/dir2/file4"), +}; + +static const fs::path RecDirIterationList[] = { + makePath("dir1/dir2"), + makePath("dir1/file1"), + makePath("dir1/file2"), + makePath("dir1/dir2/afile3"), + makePath("dir1/dir2/dir3"), + makePath("dir1/dir2/symlink_to_dir3"), + makePath("dir1/dir2/file4"), + makePath("dir1/dir2/dir3/file5") +}; + +static const fs::path RecDirFollowSymlinksIterationList[] = { + makePath("dir1/dir2"), + makePath("dir1/file1"), + makePath("dir1/file2"), + makePath("dir1/dir2/afile3"), + makePath("dir1/dir2/dir3"), + makePath("dir1/dir2/file4"), + makePath("dir1/dir2/dir3/file5"), + makePath("dir1/dir2/symlink_to_dir3"), + makePath("dir1/dir2/symlink_to_dir3/file5"), +}; + +} // namespace StaticEnv + +#endif // LIBCXX_FILESYSTEM_STATIC_TEST_ROOT + +#ifndef LIBCXX_FILESYSTEM_DYNAMIC_TEST_ROOT +#warning LIBCXX_FILESYSTEM_DYNAMIC_TEST_ROOT must be defined +#else // LIBCXX_FILESYSTEM_DYNAMIC_TEST_ROOT + +#ifndef LIBCXX_FILESYSTEM_DYNAMIC_TEST_HELPER +#error LIBCXX_FILESYSTEM_DYNAMIC_TEST_HELPER must be defined +#endif + +struct scoped_test_env +{ + scoped_test_env() : test_root(random_env_path()) + { fs_helper_run(fs_make_cmd("init_test_directory", test_root)); } + + ~scoped_test_env() + { fs_helper_run(fs_make_cmd("destroy_test_directory", test_root)); } + + scoped_test_env(scoped_test_env const &) = delete; + scoped_test_env & operator=(scoped_test_env const &) = delete; + + fs::path make_env_path(std::string p) { return sanitize_path(p); } + + std::string sanitize_path(std::string raw) { + assert(raw.find("..") == std::string::npos); + std::string const& root = test_root.native(); + if (root.compare(0, root.size(), raw, 0, root.size()) != 0) { + assert(raw.front() != '\\'); + fs::path tmp(test_root); + tmp /= raw; + return std::move(const_cast<std::string&>(tmp.native())); + } + return raw; + } + + std::string create_file(std::string filename, std::size_t size = 0) { + filename = sanitize_path(std::move(filename)); + std::string out_str(size, 'a'); + { + std::ofstream out(filename.c_str()); + out << out_str; + } + return filename; + } + + std::string create_dir(std::string filename) { + filename = sanitize_path(std::move(filename)); + fs_helper_run(fs_make_cmd("create_dir", filename)); + return filename; + } + + std::string create_symlink(std::string source, std::string to) { + source = sanitize_path(std::move(source)); + to = sanitize_path(std::move(to)); + fs_helper_run(fs_make_cmd("create_symlink", source, to)); + return to; + } + + std::string create_hardlink(std::string source, std::string to) { + source = sanitize_path(std::move(source)); + to = sanitize_path(std::move(to)); + fs_helper_run(fs_make_cmd("create_hardlink", source, to)); + return to; + } + + std::string create_fifo(std::string file) { + file = sanitize_path(std::move(file)); + fs_helper_run(fs_make_cmd("create_fifo", file)); + return file; + } + + // OS X and FreeBSD doesn't support socket files so we shouldn't even + // allow tests to call this unguarded. +#if !defined(__FreeBSD__) && !defined(__APPLE__) + std::string create_socket(std::string file) { + file = sanitize_path(std::move(file)); + fs_helper_run(fs_make_cmd("create_socket", file)); + return file; + } +#endif + + fs::path const test_root; + +private: + static char to_hex(int ch) { + return ch < 10 ? static_cast<char>('0' + ch) + : static_cast<char>('a' + (ch - 10)); + } + + static char random_hex_char() { + static std::mt19937 rd { std::random_device{}() }; + static std::uniform_int_distribution<int> mrand{0, 15}; + return to_hex( mrand(rd) ); + } + + static std::string unique_path_suffix() { + std::string model = "test.%%%%%%"; + for (auto & ch : model) { + if (ch == '%') ch = random_hex_char(); + } + return model; + } + + // This could potentially introduce a filesystem race with other tests + // running at the same time, but oh well, it's just test code. + static inline fs::path random_env_path() { + static const char* env_path = LIBCXX_FILESYSTEM_DYNAMIC_TEST_ROOT; + fs::path p = fs::path(env_path) / unique_path_suffix(); + assert(p.parent_path() == env_path); + return p; + } + + static inline std::string make_arg(std::string const& arg) { + return "'" + arg + "'"; + } + + static inline std::string make_arg(std::size_t arg) { + return std::to_string(arg); + } + + template <class T> + static inline std::string + fs_make_cmd(std::string const& cmd_name, T const& arg) { + return cmd_name + "(" + make_arg(arg) + ")"; + } + + template <class T, class U> + static inline std::string + fs_make_cmd(std::string const& cmd_name, T const& arg1, U const& arg2) { + return cmd_name + "(" + make_arg(arg1) + ", " + make_arg(arg2) + ")"; + } + + static inline void fs_helper_run(std::string const& raw_cmd) { + // check that the fs test root in the enviroment matches what we were + // compiled with. + static bool checked = checkDynamicTestRoot(); + std::string cmd = LIBCXX_FILESYSTEM_DYNAMIC_TEST_HELPER; + cmd += " \"" + raw_cmd + "\""; + int ret = std::system(cmd.c_str()); + assert(ret == 0); + } + + static bool checkDynamicTestRoot() { + // LIBCXX_FILESYSTEM_DYNAMIC_TEST_ROOT is expected not to contain symlinks. + char* fs_root = std::getenv("LIBCXX_FILESYSTEM_DYNAMIC_TEST_ROOT"); + if (!fs_root) { + std::printf("ERROR: LIBCXX_FILESYSTEM_DYNAMIC_TEST_ROOT must be a defined " + "environment variable when running the test.\n"); + std::abort(); + } + if (std::string(fs_root) != LIBCXX_FILESYSTEM_DYNAMIC_TEST_ROOT) { + std::printf("ERROR: LIBCXX_FILESYSTEM_DYNAMIC_TEST_ROOT enviroment variable" + " must have the same value as when the test was compiled.\n"); + std::printf(" Current Value: '%s'\n", fs_root); + std::printf(" Expected Value: '%s'\n", LIBCXX_FILESYSTEM_DYNAMIC_TEST_ROOT); + std::abort(); + } + return true; + } + +}; + +#endif // LIBCXX_FILESYSTEM_DYNAMIC_TEST_ROOT + +// Misc test types + +#define CONCAT2(LHS, RHS) LHS##RHS +#define CONCAT(LHS, RHS) CONCAT2(LHS, RHS) +#define MKSTR(Str) {Str, CONCAT(L, Str), CONCAT(u, Str), CONCAT(U, Str)} + +struct MultiStringType { + const char* s; + const wchar_t* w; + const char16_t* u16; + const char32_t* u32; + + operator const char* () const { return s; } + operator const wchar_t* () const { return w; } + operator const char16_t* () const { return u16; } + operator const char32_t* () const { return u32; } +}; + +const MultiStringType PathList[] = { + MKSTR(""), + MKSTR(" "), + MKSTR("//"), + MKSTR("."), + MKSTR(".."), + MKSTR("foo"), + MKSTR("/"), + MKSTR("/foo"), + MKSTR("foo/"), + MKSTR("/foo/"), + MKSTR("foo/bar"), + MKSTR("/foo/bar"), + MKSTR("//net"), + MKSTR("//net/foo"), + MKSTR("///foo///"), + MKSTR("///foo///bar"), + MKSTR("/."), + MKSTR("./"), + MKSTR("/.."), + MKSTR("../"), + MKSTR("foo/."), + MKSTR("foo/.."), + MKSTR("foo/./"), + MKSTR("foo/./bar"), + MKSTR("foo/../"), + MKSTR("foo/../bar"), + MKSTR("c:"), + MKSTR("c:/"), + MKSTR("c:foo"), + MKSTR("c:/foo"), + MKSTR("c:foo/"), + MKSTR("c:/foo/"), + MKSTR("c:/foo/bar"), + MKSTR("prn:"), + MKSTR("c:\\"), + MKSTR("c:\\foo"), + MKSTR("c:foo\\"), + MKSTR("c:\\foo\\"), + MKSTR("c:\\foo/"), + MKSTR("c:/foo\\bar"), + MKSTR("//"), + MKSTR("/finally/we/need/one/really/really/really/really/really/really/really/long/string") +}; +const unsigned PathListSize = sizeof(PathList) / sizeof(MultiStringType); + +template <class Iter> +Iter IterEnd(Iter B) { + using VT = typename std::iterator_traits<Iter>::value_type; + for (; *B != VT{}; ++B) + ; + return B; +} + +template <class CharT> +const CharT* StrEnd(CharT const* P) { + return IterEnd(P); +} + +template <class CharT> +std::size_t StrLen(CharT const* P) { + return StrEnd(P) - P; +} + +// Testing the allocation behavior of the code_cvt functions requires +// *knowing* that the allocation was not done by "path::__str_". +// This hack forces path to allocate enough memory. +inline void PathReserve(fs::path& p, std::size_t N) { + auto const& native_ref = p.native(); + const_cast<std::string&>(native_ref).reserve(N); +} + +template <class Iter1, class Iter2> +bool checkCollectionsEqual( + Iter1 start1, Iter1 const end1 + , Iter2 start2, Iter2 const end2 + ) +{ + while (start1 != end1 && start2 != end2) { + if (*start1 != *start2) { + return false; + } + ++start1; ++start2; + } + return (start1 == end1 && start2 == end2); +} + + +template <class Iter1, class Iter2> +bool checkCollectionsEqualBackwards( + Iter1 const start1, Iter1 end1 + , Iter2 const start2, Iter2 end2 + ) +{ + while (start1 != end1 && start2 != end2) { + --end1; --end2; + if (*end1 != *end2) { + return false; + } + } + return (start1 == end1 && start2 == end2); +} + +// We often need to test that the error_code was cleared if no error occurs +// this function returns a error_code which is set to an error that will +// never be returned by the filesystem functions. +inline std::error_code GetTestEC() { + return std::make_error_code(std::errc::address_family_not_supported); +} + +// Provide our own Sleep routine since std::this_thread::sleep_for is not +// available in single-threaded mode. +void SleepFor(std::chrono::seconds dur) { + using namespace std::chrono; +#if defined(_LIBCPP_HAS_NO_MONOTONIC_CLOCK) + using Clock = system_clock; +#else + using Clock = steady_clock; +#endif + const auto wake_time = Clock::now() + dur; + while (Clock::now() < wake_time) + ; +} + +#endif /* FILESYSTEM_TEST_HELPER_HPP */ diff --git a/test/support/hexfloat.h b/test/support/hexfloat.h index 7ef093714fed0..19008d1d54abb 100644 --- a/test/support/hexfloat.h +++ b/test/support/hexfloat.h @@ -15,7 +15,6 @@ #ifndef HEXFLOAT_H #define HEXFLOAT_H -#include <algorithm> #include <cmath> #include <climits> @@ -23,13 +22,26 @@ template <class T> class hexfloat { T value_; + + static int CountLeadingZeros(unsigned long long n) { + const std::size_t Digits = sizeof(unsigned long long) * CHAR_BIT; + const unsigned long long TopBit = 1ull << (Digits - 1); + if (n == 0) return Digits; + int LeadingZeros = 0; + while ((n & TopBit) == 0) { + ++LeadingZeros; + n <<= 1; + } + return LeadingZeros; + } + public: hexfloat(long long m1, unsigned long long m0, int exp) { - const std::size_t n = sizeof(unsigned long long) * CHAR_BIT; + const std::size_t Digits = sizeof(unsigned long long) * CHAR_BIT; int s = m1 < 0 ? -1 : 1; - value_ = std::ldexp(m1 + s * std::ldexp(T(m0), -static_cast<int>(n - - std::__clz(m0)/4*4)), exp); + int exp2 = -static_cast<int>(Digits - CountLeadingZeros(m0)/4*4); + value_ = std::ldexp(m1 + s * std::ldexp(T(m0), exp2), exp); } operator T() const {return value_;} diff --git a/test/support/is_transparent.h b/test/support/is_transparent.h index 58255248abc9c..d76b00536b526 100644 --- a/test/support/is_transparent.h +++ b/test/support/is_transparent.h @@ -10,7 +10,7 @@ #ifndef TRANSPARENT_H #define TRANSPARENT_H -// testing transparent +// testing transparent #if _LIBCPP_STD_VER > 11 struct transparent_less @@ -63,7 +63,7 @@ struct C2Int { // comparable to int private: int i_; }; - + bool operator <(int rhs, const C2Int& lhs) { return rhs < lhs.get(); } bool operator <(const C2Int& rhs, const C2Int& lhs) { return rhs.get() < lhs.get(); } bool operator <(const C2Int& rhs, int lhs) { return rhs.get() < lhs; } diff --git a/test/support/min_allocator.h b/test/support/min_allocator.h index 5e3ae5d2a136b..701159e0481a2 100644 --- a/test/support/min_allocator.h +++ b/test/support/min_allocator.h @@ -11,6 +11,9 @@ #define MIN_ALLOCATOR_H #include <cstddef> +#include <cstdlib> +#include <cstddef> +#include <cassert> #include "test_macros.h" @@ -39,8 +42,59 @@ public: friend bool operator!=(bare_allocator x, bare_allocator y) {return !(x == y);} }; +struct malloc_allocator_base { + static size_t alloc_count; + static size_t dealloc_count; + static bool disable_default_constructor; + + static size_t outstanding_alloc() { + assert(alloc_count >= dealloc_count); + return (alloc_count - dealloc_count); + } + + static void reset() { + assert(outstanding_alloc() == 0); + disable_default_constructor = false; + alloc_count = 0; + dealloc_count = 0; + } +}; + + +size_t malloc_allocator_base::alloc_count = 0; +size_t malloc_allocator_base::dealloc_count = 0; +bool malloc_allocator_base::disable_default_constructor = false; + + +template <class T> +class malloc_allocator : public malloc_allocator_base +{ +public: + typedef T value_type; + + malloc_allocator() TEST_NOEXCEPT { assert(!disable_default_constructor); } + + template <class U> + malloc_allocator(malloc_allocator<U>) TEST_NOEXCEPT {} + + T* allocate(std::size_t n) + { + ++alloc_count; + return static_cast<T*>(std::malloc(n*sizeof(T))); + } + + void deallocate(T* p, std::size_t) + { + ++dealloc_count; + std::free(static_cast<void*>(p)); + } + + friend bool operator==(malloc_allocator, malloc_allocator) {return true;} + friend bool operator!=(malloc_allocator x, malloc_allocator y) {return !(x == y);} +}; + -#if __cplusplus >= 201103L +#if TEST_STD_VER >= 11 #include <memory> @@ -286,6 +340,6 @@ public: friend bool operator!=(min_allocator x, min_allocator y) {return !(x == y);} }; -#endif // __cplusplus >= 201103L +#endif // TEST_STD_VER >= 11 #endif // MIN_ALLOCATOR_H diff --git a/test/support/nasty_containers.hpp b/test/support/nasty_containers.hpp index 5a2e19505424b..b571469227d41 100644 --- a/test/support/nasty_containers.hpp +++ b/test/support/nasty_containers.hpp @@ -7,9 +7,10 @@ // //===----------------------------------------------------------------------===// -#ifndef NASTY_VECTOR_H -#define NASTY_VECTOR_H +#ifndef NASTY_CONTAINERS_H +#define NASTY_CONTAINERS_H +#include <cassert> #include <vector> #include <list> @@ -99,7 +100,7 @@ public: { return v_.emplace(pos, std::forward<Args>(args)...); } #endif #endif - + iterator insert(const_iterator pos, const value_type& x) { return v_.insert(pos, x); } #ifndef _LIBCPP_HAS_NO_RVALUE_REFERENCES iterator insert(const_iterator pos, value_type&& x) { return v_.insert(pos, std::forward<value_type>(x)); } @@ -123,10 +124,10 @@ public: void swap(nasty_vector &nv) _NOEXCEPT_(std::__is_nothrow_swappable<nested_container>::value) { v_.swap(nv.v_); } - - nasty_vector *operator &() { return nullptr; } // nasty - const nasty_vector *operator &() const { return nullptr; } // nasty - + + nasty_vector *operator &() { assert(false); return nullptr; } // nasty + const nasty_vector *operator &() const { assert(false); return nullptr; } // nasty + nested_container v_; }; @@ -220,7 +221,7 @@ public: { return l_.emplace(pos, std::forward<Args>(args)...); } #endif #endif - + iterator insert(const_iterator pos, const value_type& x) { return l_.insert(pos, x); } #ifndef _LIBCPP_HAS_NO_RVALUE_REFERENCES iterator insert(const_iterator pos, value_type&& x) { return l_.insert(pos, std::forward<value_type>(x)); } @@ -242,7 +243,7 @@ public: void swap(nasty_list &nl) _NOEXCEPT_(std::__is_nothrow_swappable<nested_container>::value) { l_.swap(nl.l_); } - + void clear() _NOEXCEPT { l_.clear(); } // void splice(const_iterator position, list& x); @@ -253,7 +254,7 @@ public: // const_iterator last); // void splice(const_iterator position, list&& x, const_iterator first, // const_iterator last); -// +// // void remove(const value_type& value); // template <class Pred> void remove_if(Pred pred); // void unique(); @@ -270,8 +271,8 @@ public: // void sort(Compare comp); // void reverse() noexcept; - nasty_list *operator &() { return nullptr; } // nasty - const nasty_list *operator &() const { return nullptr; } // nasty + nasty_list *operator &() { assert(false); return nullptr; } // nasty + const nasty_list *operator &() const { assert(false); return nullptr; } // nasty nested_container l_; }; @@ -279,4 +280,30 @@ public: template <class T> bool operator==(const nasty_list<T>& x, const nasty_list<T>& y) { return x.l_ == y.l_; } +// Not really a mutex, but can play one in tests +class nasty_mutex +{ +public: + nasty_mutex() _NOEXCEPT {} + ~nasty_mutex() {} + + nasty_mutex *operator& () { assert(false); return nullptr; } + template <typename T> + void operator, (const T &) { assert(false); } + +private: + nasty_mutex(const nasty_mutex&) { assert(false); } + nasty_mutex& operator=(const nasty_mutex&) { assert(false); return *this; } + +public: + void lock() {} + bool try_lock() _NOEXCEPT { return true; } + void unlock() _NOEXCEPT {} + + // Shared ownership + void lock_shared() {} + bool try_lock_shared() { return true; } + void unlock_shared() {} +}; + #endif diff --git a/test/support/platform_support.h b/test/support/platform_support.h index 233e721ff5155..f4c2247e0dfbc 100644 --- a/test/support/platform_support.h +++ b/test/support/platform_support.h @@ -15,8 +15,6 @@ #ifndef PLATFORM_SUPPORT_H #define PLATFORM_SUPPORT_H -#include <__config> - // locale names #ifdef _WIN32 // WARNING: Windows does not support UTF-8 codepages. @@ -54,8 +52,8 @@ #include <stdio.h> #include <stdlib.h> #include <string> -#if defined(_LIBCPP_MSVCRT) || defined(__MINGW32__) -#include <io.h> // _mktemp +#if defined(_WIN32) || defined(__MINGW32__) +#include <io.h> // _mktemp_s #else #include <unistd.h> // close #endif @@ -67,17 +65,20 @@ extern "C" { } #endif -#ifndef _LIBCPP_HAS_NO_GLOBAL_FILESYSTEM_NAMESPACE +#ifndef __CloudABI__ inline -std::string -get_temp_file_name() +std::string get_temp_file_name() { -#if defined(_LIBCPP_MSVCRT) || defined(__MINGW32__) - char Path[MAX_PATH+1]; - char FN[MAX_PATH+1]; +#if defined(__MINGW32__) + char Path[MAX_PATH + 1]; + char FN[MAX_PATH + 1]; do { } while (0 == GetTempPath(MAX_PATH+1, Path)); do { } while (0 == GetTempFileName(Path, "libcxx", 0, FN)); return FN; +#elif defined(_WIN32) + char Name[] = "libcxx.XXXXXX"; + if (_mktemp_s(Name, sizeof(Name)) != 0) abort(); + return Name; #else std::string Name; int FD = -1; @@ -93,6 +94,6 @@ get_temp_file_name() return Name; #endif } -#endif // _LIBCPP_HAS_NO_GLOBAL_FILESYSTEM_NAMESPACE +#endif // __CloudABI__ #endif // PLATFORM_SUPPORT_H diff --git a/test/support/propagate_const_helpers.h b/test/support/propagate_const_helpers.h new file mode 100644 index 0000000000000..581565a342708 --- /dev/null +++ b/test/support/propagate_const_helpers.h @@ -0,0 +1,119 @@ + // A lightweight class, with pointer-like methods, that contains an int +struct X +{ + int i_; + + constexpr const int &operator*() const { return i_; } + constexpr int &operator*() { return i_; } + constexpr const int *get() const { return &i_; } + constexpr int *get() { return &i_; } + constexpr const int *operator->() const { return &i_; } + constexpr int *operator->() { return &i_; } + + constexpr X(int i) : i_(i) {} +}; + +struct XWithImplicitIntStarConversion +{ + int i_; + + constexpr const int &operator*() const { return i_; } + constexpr int &operator*() { return i_; } + constexpr const int *get() const { return &i_; } + constexpr int *get() { return &i_; } + constexpr const int *operator->() const { return &i_; } + constexpr int *operator->() { return &i_; } + constexpr operator int* () { return &i_; } + + constexpr XWithImplicitIntStarConversion(int i) : i_(i) {} +}; + +struct XWithImplicitConstIntStarConversion +{ + int i_; + + constexpr const int &operator*() const { return i_; } + constexpr int &operator*() { return i_; } + constexpr const int *get() const { return &i_; } + constexpr int *get() { return &i_; } + constexpr const int *operator->() const { return &i_; } + constexpr int *operator->() { return &i_; } + constexpr operator const int* () const { return &i_; } + + constexpr XWithImplicitConstIntStarConversion(int i) : i_(i) {} +}; + +struct ExplicitX +{ + int i_; + + constexpr const int &operator*() const { return i_; } + constexpr int &operator*() { return i_; } + constexpr const int *get() const { return &i_; } + constexpr int *get() { return &i_; } + constexpr const int *operator->() const { return &i_; } + constexpr int *operator->() { return &i_; } + + constexpr explicit ExplicitX(int i) : i_(i) {} +}; + +struct MoveConstructibleFromX +{ + int i_; + + constexpr const int &operator*() const { return i_; } + constexpr int &operator*() { return i_; } + constexpr const int *get() const { return &i_; } + constexpr int *get() { return &i_; } + constexpr const int *operator->() const { return &i_; } + constexpr int *operator->() { return &i_; } + + constexpr MoveConstructibleFromX(int i) : i_(i) {} + constexpr MoveConstructibleFromX(X&& x) : i_(x.i_) {} +}; + +struct ExplicitMoveConstructibleFromX +{ + int i_; + + constexpr const int &operator*() const { return i_; } + constexpr int &operator*() { return i_; } + constexpr const int *get() const { return &i_; } + constexpr int *get() { return &i_; } + constexpr const int *operator->() const { return &i_; } + constexpr int *operator->() { return &i_; } + + constexpr ExplicitMoveConstructibleFromX(int i) : i_(i) {} + constexpr explicit ExplicitMoveConstructibleFromX(X&& x) : i_(x.i_) {} +}; + +struct CopyConstructibleFromX +{ + int i_; + + constexpr const int &operator*() const { return i_; } + constexpr int &operator*() { return i_; } + constexpr const int *get() const { return &i_; } + constexpr int *get() { return &i_; } + constexpr const int *operator->() const { return &i_; } + constexpr int *operator->() { return &i_; } + + constexpr CopyConstructibleFromX(int i) : i_(i) {} + constexpr CopyConstructibleFromX(const X& x) : i_(x.i_) {} +}; + +struct ExplicitCopyConstructibleFromX +{ + int i_; + + constexpr const int &operator*() const { return i_; } + constexpr int &operator*() { return i_; } + constexpr const int *get() const { return &i_; } + constexpr int *get() { return &i_; } + constexpr const int *operator->() const { return &i_; } + constexpr int *operator->() { return &i_; } + + constexpr ExplicitCopyConstructibleFromX(int i) : i_(i) {} + constexpr explicit ExplicitCopyConstructibleFromX(const X& x) : i_(x.i_) {} +}; + diff --git a/test/support/rapid-cxx-test.hpp b/test/support/rapid-cxx-test.hpp new file mode 100644 index 0000000000000..a25bda53109cf --- /dev/null +++ b/test/support/rapid-cxx-test.hpp @@ -0,0 +1,847 @@ +#ifndef RAPID_CXX_TEST_HPP +#define RAPID_CXX_TEST_HPP + +# include <cstddef> +# include <cstdlib> +# include <cstdio> +# include <cstring> +# include <cassert> + +#include "test_macros.h" + +#if !defined(RAPID_CXX_TEST_NO_SYSTEM_HEADER) || !defined(__GNUC__) +#pragma GCC system_header +#endif + +# define RAPID_CXX_TEST_PP_CAT(x, y) RAPID_CXX_TEST_PP_CAT_2(x, y) +# define RAPID_CXX_TEST_PP_CAT_2(x, y) x##y + +# define RAPID_CXX_TEST_PP_STR(...) RAPID_CXX_TEST_PP_STR_2(__VA_ARGS__) +# define RAPID_CXX_TEST_PP_STR_2(...) #__VA_ARGS__ + +# if defined(__GNUC__) +# define TEST_FUNC_NAME() __PRETTY_FUNCTION__ +# define RAPID_CXX_TEST_UNUSED __attribute__((unused)) +# else +# define TEST_FUNC_NAME() __func__ +# define RAPID_CXX_TEST_UNUSED +# endif + +//////////////////////////////////////////////////////////////////////////////// +// TEST_SUITE +//////////////////////////////////////////////////////////////////////////////// +# define TEST_SUITE(Name) \ +namespace Name \ +{ \ + inline ::rapid_cxx_test::test_suite & get_test_suite() \ + { \ + static ::rapid_cxx_test::test_suite m_suite(#Name); \ + return m_suite; \ + } \ + \ + inline int unit_test_main(int, char**) \ + { \ + ::rapid_cxx_test::test_runner runner(get_test_suite()); \ + return runner.run(); \ + } \ +} \ +int main(int argc, char **argv) \ +{ \ + return Name::unit_test_main(argc, argv); \ +} \ +namespace Name \ +{ /* namespace closed in TEST_SUITE_END */ +# + +//////////////////////////////////////////////////////////////////////////////// +// TEST_SUITE_END +//////////////////////////////////////////////////////////////////////////////// +# define TEST_SUITE_END() \ +} /* namespace opened in TEST_SUITE(...) */ +# + +//////////////////////////////////////////////////////////////////////////////// +// TEST_CASE +//////////////////////////////////////////////////////////////////////////////// + +# if !defined(__clang__) +# +# define TEST_CASE(Name) \ + void Name(); \ + static void RAPID_CXX_TEST_PP_CAT(Name, _invoker)() \ + { \ + Name(); \ + } \ + static ::rapid_cxx_test::registrar \ + RAPID_CXX_TEST_PP_CAT(rapid_cxx_test_registrar_, Name)( \ + get_test_suite() \ + , ::rapid_cxx_test::test_case(__FILE__, #Name, __LINE__, & RAPID_CXX_TEST_PP_CAT(Name, _invoker)) \ + ); \ + void Name() +# +# else /* __clang__ */ +# +# define TEST_CASE(Name) \ + void Name(); \ + static void RAPID_CXX_TEST_PP_CAT(Name, _invoker)() \ + { \ + Name(); \ + } \ + _Pragma("clang diagnostic push") \ + _Pragma("clang diagnostic ignored \"-Wglobal-constructors\"") \ + static ::rapid_cxx_test::registrar \ + RAPID_CXX_TEST_PP_CAT(rapid_cxx_test_registrar_, Name)( \ + get_test_suite() \ + , ::rapid_cxx_test::test_case(__FILE__, #Name, __LINE__, & RAPID_CXX_TEST_PP_CAT(Name, _invoker)) \ + ); \ + _Pragma("clang diagnostic pop") \ + void Name() +# +# endif /* !defined(__clang__) */ + + +# define TEST_SET_CHECKPOINT() ::rapid_cxx_test::set_checkpoint(__FILE__, TEST_FUNC_NAME(), __LINE__) + +#define RAPID_CXX_TEST_OUTCOME() + +//////////////////////////////////////////////////////////////////////////////// +// TEST_UNSUPPORTED +//////////////////////////////////////////////////////////////////////////////// +# define TEST_UNSUPPORTED() \ + do { \ + TEST_SET_CHECKPOINT(); \ + ::rapid_cxx_test::test_outcome m_f( \ + ::rapid_cxx_test::failure_type::unsupported, __FILE__, TEST_FUNC_NAME(), __LINE__ \ + , "", "" \ + ); \ + ::rapid_cxx_test::get_reporter().report(m_f); \ + return; \ + } while (false) +# + + +//////////////////////////////////////////////////////////////////////////////// +// BASIC ASSERTIONS +//////////////////////////////////////////////////////////////////////////////// +# define TEST_WARN(...) \ + do { \ + TEST_SET_CHECKPOINT(); \ + ::rapid_cxx_test::test_outcome m_f( \ + ::rapid_cxx_test::failure_type::none, __FILE__, TEST_FUNC_NAME(), __LINE__ \ + , "TEST_WARN(" #__VA_ARGS__ ")", "" \ + ); \ + if (not (__VA_ARGS__)) { \ + m_f.type = ::rapid_cxx_test::failure_type::warn; \ + } \ + ::rapid_cxx_test::get_reporter().report(m_f); \ + } while (false) +# + +# define TEST_CHECK(...) \ + do { \ + TEST_SET_CHECKPOINT(); \ + ::rapid_cxx_test::test_outcome m_f( \ + ::rapid_cxx_test::failure_type::none, __FILE__, TEST_FUNC_NAME(), __LINE__ \ + , "TEST_CHECK(" #__VA_ARGS__ ")", "" \ + ); \ + if (not (__VA_ARGS__)) { \ + m_f.type = ::rapid_cxx_test::failure_type::check; \ + } \ + ::rapid_cxx_test::get_reporter().report(m_f); \ + } while (false) +# + +# define TEST_REQUIRE(...) \ + do { \ + TEST_SET_CHECKPOINT(); \ + ::rapid_cxx_test::test_outcome m_f( \ + ::rapid_cxx_test::failure_type::none, __FILE__, TEST_FUNC_NAME(), __LINE__ \ + , "TEST_REQUIRE(" #__VA_ARGS__ ")", "" \ + ); \ + if (not (__VA_ARGS__)) { \ + m_f.type = ::rapid_cxx_test::failure_type::require; \ + } \ + ::rapid_cxx_test::get_reporter().report(m_f); \ + if (m_f.type != ::rapid_cxx_test::failure_type::none) { \ + return; \ + } \ + } while (false) +# + +# define TEST_ASSERT(...) \ + do { \ + TEST_SET_CHECKPOINT(); \ + ::rapid_cxx_test::test_outcome m_f( \ + ::rapid_cxx_test::failure_type::none, __FILE__, TEST_FUNC_NAME(), __LINE__ \ + , "TEST_ASSERT(" #__VA_ARGS__ ")", "" \ + ); \ + if (not (__VA_ARGS__)) { \ + m_f.type = ::rapid_cxx_test::failure_type::assert; \ + } \ + ::rapid_cxx_test::get_reporter().report(m_f); \ + if (m_f.type != ::rapid_cxx_test::failure_type::none) { \ + std::abort(); \ + } \ + } while (false) +# + +//////////////////////////////////////////////////////////////////////////////// +// TEST_CHECK_NO_THROW / TEST_CHECK_THROW +//////////////////////////////////////////////////////////////////////////////// +#ifndef TEST_HAS_NO_EXCEPTIONS + +# define TEST_CHECK_NO_THROW(...) \ + do { \ + TEST_SET_CHECKPOINT(); \ + ::rapid_cxx_test::test_outcome m_f( \ + ::rapid_cxx_test::failure_type::none, __FILE__, TEST_FUNC_NAME(), __LINE__ \ + , "TEST_CHECK_NO_THROW(" #__VA_ARGS__ ")", "" \ + ); \ + try { \ + (static_cast<void>(__VA_ARGS__)); \ + } catch (...) { \ + m_f.type = ::rapid_cxx_test::failure_type::check; \ + } \ + ::rapid_cxx_test::get_reporter().report(m_f); \ + } while (false) +# + +# define TEST_CHECK_THROW(Except, ...) \ + do { \ + TEST_SET_CHECKPOINT(); \ + ::rapid_cxx_test::test_outcome m_f( \ + ::rapid_cxx_test::failure_type::none, __FILE__, TEST_FUNC_NAME(), __LINE__ \ + , "TEST_CHECK_THROW(" #Except "," #__VA_ARGS__ ")", "" \ + ); \ + try { \ + (static_cast<void>(__VA_ARGS__)); \ + m_f.type = ::rapid_cxx_test::failure_type::check; \ + } catch (Except const &) {} \ + ::rapid_cxx_test::get_reporter().report(m_f); \ + } while (false) +# + +#else // TEST_HAS_NO_EXCEPTIONS + +# define TEST_CHECK_NO_THROW(...) \ + do { \ + TEST_SET_CHECKPOINT(); \ + ::rapid_cxx_test::test_outcome m_f( \ + ::rapid_cxx_test::failure_type::none, __FILE__, TEST_FUNC_NAME(), __LINE__ \ + , "TEST_CHECK_NO_THROW(" #__VA_ARGS__ ")", "" \ + ); \ + (static_cast<void>(__VA_ARGS__)); \ + ::rapid_cxx_test::get_reporter().report(m_f); \ + } while (false) +# + +#define TEST_CHECK_THROW(Except, ...) ((void)0) + +#endif // TEST_HAS_NO_EXCEPTIONS + + +//////////////////////////////////////////////////////////////////////////////// +// TEST_REQUIRE_NO_THROW / TEST_REQUIRE_THROWs +//////////////////////////////////////////////////////////////////////////////// +#ifndef TEST_HAS_NO_EXCEPTIONS + +# define TEST_REQUIRE_NO_THROW(...) \ + do { \ + TEST_SET_CHECKPOINT(); \ + ::rapid_cxx_test::test_outcome m_f( \ + ::rapid_cxx_test::failure_type::none, __FILE__, TEST_FUNC_NAME(), __LINE__ \ + , "TEST_REQUIRE_NO_THROW(" #__VA_ARGS__ ")", "" \ + ); \ + try { \ + (static_cast<void>(__VA_ARGS__)); \ + } catch (...) { \ + m_f.type = ::rapid_cxx_test::failure_type::require; \ + } \ + ::rapid_cxx_test::get_reporter().report(m_f); \ + if (m_f.type != ::rapid_cxx_test::failure_type::none) { \ + return; \ + } \ + } while (false) +# + +# define TEST_REQUIRE_THROW(Except, ...) \ + do { \ + TEST_SET_CHECKPOINT(); \ + ::rapid_cxx_test::test_outcome m_f( \ + ::rapid_cxx_test::failure_type::none, __FILE__, TEST_FUNC_NAME(), __LINE__ \ + , "TEST_REQUIRE_THROW(" #Except "," #__VA_ARGS__ ")", "" \ + ); \ + try { \ + (static_cast<void>(__VA_ARGS__)); \ + m_f.type = ::rapid_cxx_test::failure_type::require; \ + } catch (Except const &) {} \ + ::rapid_cxx_test::get_reporter().report(m_f); \ + if (m_f.type != ::rapid_cxx_test::failure_type::none) { \ + return; \ + } \ + } while (false) +# + +#else // TEST_HAS_NO_EXCEPTIONS + +# define TEST_REQUIRE_NO_THROW(...) \ + do { \ + TEST_SET_CHECKPOINT(); \ + ::rapid_cxx_test::test_outcome m_f( \ + ::rapid_cxx_test::failure_type::none, __FILE__, TEST_FUNC_NAME(), __LINE__ \ + , "TEST_REQUIRE_NO_THROW(" #__VA_ARGS__ ")", "" \ + ); \ + (static_cast<void>(__VA_ARGS__)); \ + ::rapid_cxx_test::get_reporter().report(m_f); \ + } while (false) +# + +#define TEST_REQUIRE_THROW(Except, ...) ((void)0) + +#endif // TEST_HAS_NO_EXCEPTIONS + +//////////////////////////////////////////////////////////////////////////////// +// TEST_ASSERT_NO_THROW / TEST_ASSERT_THROW +//////////////////////////////////////////////////////////////////////////////// +#ifndef TEST_HAS_NO_EXCEPTIONS + +# define TEST_ASSERT_NO_THROW(...) \ + do { \ + TEST_SET_CHECKPOINT(); \ + ::rapid_cxx_test::test_outcome m_f( \ + ::rapid_cxx_test::failure_type::none, __FILE__, TEST_FUNC_NAME(), __LINE__ \ + , "TEST_ASSERT_NO_THROW(" #__VA_ARGS__ ")", "" \ + ); \ + try { \ + (static_cast<void>(__VA_ARGS__)); \ + } catch (...) { \ + m_f.type = ::rapid_cxx_test::failure_type::assert; \ + } \ + ::rapid_cxx_test::get_reporter().report(m_f); \ + if (m_f.type != ::rapid_cxx_test::failure_type::none) { \ + std::abort(); \ + } \ + } while (false) +# + +# define TEST_ASSERT_THROW(Except, ...) \ + do { \ + TEST_SET_CHECKPOINT(); \ + ::rapid_cxx_test::test_outcome m_f( \ + ::rapid_cxx_test::failure_type::none, __FILE__, TEST_FUNC_NAME(), __LINE__ \ + , "TEST_ASSERT_THROW(" #Except "," #__VA_ARGS__ ")", "" \ + ); \ + try { \ + (static_cast<void>(__VA_ARGS__)); \ + m_f.type = ::rapid_cxx_test::failure_type::assert; \ + } catch (Except const &) {} \ + ::rapid_cxx_test::get_reporter().report(m_f); \ + if (m_f.type != ::rapid_cxx_test::failure_type::none) { \ + std::abort(); \ + } \ + } while (false) +# + +#else // TEST_HAS_NO_EXCEPTIONS + +# define TEST_ASSERT_NO_THROW(...) \ + do { \ + TEST_SET_CHECKPOINT(); \ + ::rapid_cxx_test::test_outcome m_f( \ + ::rapid_cxx_test::failure_type::none, __FILE__, TEST_FUNC_NAME(), __LINE__ \ + , "TEST_ASSERT_NO_THROW(" #__VA_ARGS__ ")", "" \ + ); \ + (static_cast<void>(__VA_ARGS__)); \ + ::rapid_cxx_test::get_reporter().report(m_f); \ + } while (false) +# + +#define TEST_ASSERT_THROW(Except, ...) ((void)0) + +#endif // TEST_HAS_NO_EXCEPTIONS + +//////////////////////////////////////////////////////////////////////////////// +// +//////////////////////////////////////////////////////////////////////////////// + +# define TEST_WARN_EQUAL_COLLECTIONS(...) \ + do { \ + TEST_SET_CHECKPOINT(); \ + ::rapid_cxx_test::test_outcome m_f( \ + ::rapid_cxx_test::failure_type::none, __FILE__, TEST_FUNC_NAME(), __LINE__ \ + , "TEST_WARN_EQUAL_COLLECTIONS(" #__VA_ARGS__ ")", "" \ + ); \ + if (not ::rapid_cxx_test::detail::check_equal_collections_impl(__VA_ARGS__)) { \ + m_f.type = ::rapid_cxx_test::failure_type::warn; \ + } \ + ::rapid_cxx_test::get_reporter().report(m_f); \ + } while (false) +# + +# define TEST_CHECK_EQUAL_COLLECTIONS(...) \ + do { \ + TEST_SET_CHECKPOINT(); \ + ::rapid_cxx_test::test_outcome m_f( \ + ::rapid_cxx_test::failure_type::none, __FILE__, TEST_FUNC_NAME(), __LINE__ \ + , "TEST_CHECK_EQUAL_COLLECTIONS(" #__VA_ARGS__ ")", "" \ + ); \ + if (not ::rapid_cxx_test::detail::check_equal_collections_impl(__VA_ARGS__)) { \ + m_f.type = ::rapid_cxx_test::failure_type::check; \ + } \ + ::rapid_cxx_test::get_reporter().report(m_f); \ + } while (false) +# + +# define TEST_REQUIRE_EQUAL_COLLECTIONS(...) \ + do { \ + TEST_SET_CHECKPOINT(); \ + ::rapid_cxx_test::test_outcome m_f( \ + ::rapid_cxx_test::failure_type::none, __FILE__, TEST_FUNC_NAME(), __LINE__ \ + , "TEST_REQUIRE_EQUAL_COLLECTIONS(" #__VA_ARGS__ ")", "" \ + ); \ + if (not ::rapid_cxx_test::detail::check_equal_collections_impl(__VA_ARGS__)) { \ + m_f.type = ::rapid_cxx_test::failure_type::require; \ + } \ + ::rapid_cxx_test::get_reporter().report(m_f); \ + if (m_f.type != ::rapid_cxx_test::failure_type::none) { \ + return; \ + } \ + } while (false) +# + +# define TEST_ASSERT_EQUAL_COLLECTIONS(...) \ + do { \ + TEST_SET_CHECKPOINT(); \ + ::rapid_cxx_test::test_outcome m_f( \ + ::rapid_cxx_test::failure_type::none, __FILE__, TEST_FUNC_NAME(), __LINE__ \ + , "TEST_ASSERT_EQUAL_COLLECTIONS(" #__VA_ARGS__ ")", "" \ + ); \ + if (not ::rapid_cxx_test::detail::check_equal_collections_impl(__VA_ARGS__)) { \ + m_f.type = ::rapid_cxx_test::failure_type::assert; \ + } \ + ::rapid_cxx_test::get_reporter().report(m_f); \ + if (m_f.type != ::rapid_cxx_test::failure_type::none) { \ + ::std::abort(); \ + } \ + } while (false) +# + +namespace rapid_cxx_test +{ + typedef void (*invoker_t)(); + + //////////////////////////////////////////////////////////////////////////// + struct test_case + { + test_case() + : file(""), func(""), line(0), invoke(NULL) + {} + + test_case(const char* file1, const char* func1, std::size_t line1, + invoker_t invoke1) + : file(file1), func(func1), line(line1), invoke(invoke1) + {} + + const char *file; + const char *func; + std::size_t line; + invoker_t invoke; + }; + + //////////////////////////////////////////////////////////////////////////// + struct failure_type + { + enum enum_type { + none, + unsupported, + warn, + check, + require, + assert, + uncaught_exception + }; + }; + + typedef failure_type::enum_type failure_type_t; + + //////////////////////////////////////////////////////////////////////////// + struct test_outcome + { + test_outcome() + : type(failure_type::none), + file(""), func(""), line(0), + expression(""), message("") + {} + + test_outcome(failure_type_t type1, const char* file1, const char* func1, + std::size_t line1, const char* expression1, + const char* message1) + : type(type1), file(file1), func(func1), line(line1), + expression(expression1), message(message1) + { + trim_func_string(); + } + + failure_type_t type; + const char *file; + const char *func; + std::size_t line; + const char *expression; + const char *message; + + private: + void trim_file_string() { + const char* f_start = file; + const char* prev_start = f_start; + const char* last_start = f_start; + char last; + while ((last = *f_start) != '\0') { + ++f_start; + if (last == '/' && *f_start) { + prev_start = last_start; + last_start = f_start; + } + } + file = prev_start; + } + void trim_func_string() { + const char* void_loc = ::strstr(func, "void "); + if (void_loc == func) { + func += strlen("void "); + } + const char* namespace_loc = ::strstr(func, "::"); + if (namespace_loc) { + func = namespace_loc + 2; + } + } + }; + + //////////////////////////////////////////////////////////////////////////// + struct checkpoint + { + const char *file; + const char *func; + std::size_t line; + }; + + namespace detail + { + inline checkpoint & global_checkpoint() + { + static checkpoint cp = {"", "", 0}; + return cp; + } + } + + //////////////////////////////////////////////////////////////////////////// + inline void set_checkpoint(const char* file, const char* func, std::size_t line) + { + checkpoint& cp = detail::global_checkpoint(); + cp.file = file; + cp.func = func; + cp.line = line; + } + + //////////////////////////////////////////////////////////////////////////// + inline checkpoint const & get_checkpoint() + { + return detail::global_checkpoint(); + } + + //////////////////////////////////////////////////////////////////////////// + class test_suite + { + public: + typedef test_case const* iterator; + typedef iterator const_iterator; + + public: + test_suite(const char *xname) + : m_name(xname), m_tests(), m_size(0) + { + assert(xname); + } + + public: + const char *name() const { return m_name; } + + std::size_t size() const { return m_size; } + + test_case const & operator[](std::size_t i) const + { + assert(i < m_size); + return m_tests[i]; + } + + const_iterator begin() const + { return m_tests; } + + const_iterator end() const + { + return m_tests + m_size; + } + + public: + std::size_t register_test(test_case tc) + { + static std::size_t test_case_max = sizeof(m_tests) / sizeof(test_case); + assert(m_size < test_case_max); + m_tests[m_size] = tc; + return m_size++; + } + + private: + test_suite(test_suite const &); + test_suite & operator=(test_suite const &); + + private: + const char* m_name; + // Since fast compile times in a priority, we use simple containers + // with hard limits. + test_case m_tests[256]; + std::size_t m_size; + }; + + //////////////////////////////////////////////////////////////////////////// + class registrar + { + public: + registrar(test_suite & st, test_case tc) + { + st.register_test(tc); + } + }; + + //////////////////////////////////////////////////////////////////////////// + class test_reporter + { + public: + test_reporter() + : m_testcases(0), m_testcase_failures(0), m_unsupported(0), + m_assertions(0), m_warning_failures(0), m_check_failures(0), + m_require_failures(0), m_uncaught_exceptions(0), m_failure() + { + } + + void test_case_begin() + { + ++m_testcases; + clear_failure(); + } + + void test_case_end() + { + if (m_failure.type != failure_type::none + && m_failure.type != failure_type::unsupported) { + ++m_testcase_failures; + } + } + +# if defined(__GNUC__) +# pragma GCC diagnostic push +# pragma GCC diagnostic ignored "-Wswitch-default" +# endif + // Each assertion and failure is reported through this function. + void report(test_outcome o) + { + ++m_assertions; + switch (o.type) + { + case failure_type::none: + break; + case failure_type::unsupported: + ++m_unsupported; + m_failure = o; + break; + case failure_type::warn: + ++m_warning_failures; + report_error(o); + break; + case failure_type::check: + ++m_check_failures; + report_error(o); + m_failure = o; + break; + case failure_type::require: + ++m_require_failures; + report_error(o); + m_failure = o; + break; + case failure_type::assert: + report_error(o); + break; + case failure_type::uncaught_exception: + ++m_uncaught_exceptions; + std::fprintf(stderr + , "Test case FAILED with uncaught exception:\n" + " last checkpoint near %s::%lu %s\n\n" + , o.file, o.line, o.func + ); + m_failure = o; + break; + } + } +# if defined(__GNUC__) +# pragma GCC diagnostic pop +# endif + + test_outcome current_failure() const + { + return m_failure; + } + + void clear_failure() + { + m_failure.type = failure_type::none; + m_failure.file = ""; + m_failure.func = ""; + m_failure.line = 0; + m_failure.expression = ""; + m_failure.message = ""; + } + + std::size_t test_case_count() const + { return m_testcases; } + + std::size_t test_case_failure_count() const + { return m_testcase_failures; } + + std::size_t unsupported_count() const + { return m_unsupported; } + + std::size_t assertion_count() const + { return m_assertions; } + + std::size_t warning_failure_count() const + { return m_warning_failures; } + + std::size_t check_failure_count() const + { return m_check_failures; } + + std::size_t require_failure_count() const + { return m_require_failures; } + + std::size_t failure_count() const + { return m_check_failures + m_require_failures + m_uncaught_exceptions; } + + // Print a summary of what was run and the outcome. + void print_summary(const char* suitename) const + { + FILE* out = failure_count() ? stderr : stdout; + std::size_t testcases_run = m_testcases - m_unsupported; + std::fprintf(out, "Summary for testsuite %s:\n", suitename); + std::fprintf(out, " %lu of %lu test cases passed.\n", testcases_run - m_testcase_failures, testcases_run); + std::fprintf(out, " %lu of %lu assertions passed.\n", m_assertions - (m_warning_failures + m_check_failures + m_require_failures), m_assertions); + std::fprintf(out, " %lu unsupported test case%s.\n", m_unsupported, (m_unsupported != 1 ? "s" : "")); + } + + private: + test_reporter(test_reporter const &); + test_reporter const & operator=(test_reporter const &); + + void report_error(test_outcome o) const + { + std::fprintf(stderr, "In %s:%lu Assertion %s failed.\n in file: %s\n %s\n" + , o.func, o.line, o.expression, o.file, o.message ? o.message : "" + ); + } + + private: + // counts of testcases, failed testcases, and unsupported testcases. + std::size_t m_testcases; + std::size_t m_testcase_failures; + std::size_t m_unsupported; + + // counts of assertions and assertion failures. + std::size_t m_assertions; + std::size_t m_warning_failures; + std::size_t m_check_failures; + std::size_t m_require_failures; + std::size_t m_uncaught_exceptions; + + // The last failure. This is cleared between testcases. + test_outcome m_failure; + }; + + //////////////////////////////////////////////////////////////////////////// + inline test_reporter & get_reporter() + { + static test_reporter o; + return o; + } + + //////////////////////////////////////////////////////////////////////////// + class test_runner + { + public: + test_runner(test_suite & ts) + : m_ts(ts) + {} + + public: + int run() + { + // for each testcase + for (test_suite::const_iterator b = m_ts.begin(), e = m_ts.end(); + b != e; ++b) + { + test_case const& tc = *b; + set_checkpoint(tc.file, tc.func, tc.line); + get_reporter().test_case_begin(); +#ifndef TEST_HAS_NO_EXCEPTIONS + try { +#endif + tc.invoke(); +#ifndef TEST_HAS_NO_EXCEPTIONS + } catch (...) { + test_outcome o; + o.type = failure_type::uncaught_exception; + o.file = get_checkpoint().file; + o.func = get_checkpoint().func; + o.line = get_checkpoint().line; + o.expression = ""; + o.message = ""; + get_reporter().report(o); + } +#endif + get_reporter().test_case_end(); + } + auto exit_code = get_reporter().failure_count() ? EXIT_FAILURE : EXIT_SUCCESS; + if (exit_code == EXIT_FAILURE) + get_reporter().print_summary(m_ts.name()); + return exit_code; + } + + private: + test_runner(test_runner const &); + test_runner operator=(test_runner const &); + + test_suite & m_ts; + }; + + namespace detail + { + template <class Iter1, class Iter2> + bool check_equal_collections_impl( + Iter1 start1, Iter1 const end1 + , Iter2 start2, Iter2 const end2 + ) + { + while (start1 != end1 && start2 != end2) { + if (*start1 != *start2) { + return false; + } + ++start1; ++start2; + } + return (start1 == end1 && start2 == end2); + } + } // namespace detail + +} // namespace rapid_cxx_test + + +# if defined(__GNUC__) +# pragma GCC diagnostic pop +# endif + +#endif /* RAPID_CXX_TEST_HPP */ diff --git a/test/support/test.support/test_convertible_header.pass.cpp b/test/support/test.support/test_convertible_header.pass.cpp new file mode 100644 index 0000000000000..a56b84b4739c5 --- /dev/null +++ b/test/support/test.support/test_convertible_header.pass.cpp @@ -0,0 +1,68 @@ +//===----------------------------------------------------------------------===// +// +// The LLVM Compiler Infrastructure +// +// This file is dual licensed under the MIT and the University of Illinois Open +// Source Licenses. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +// UNSUPPORTED: c++98, c++03 + +// "support/test_convertible.hpp" + +#include "test_convertible.hpp" + +struct ImplicitDefault { + ImplicitDefault() {} +}; +static_assert(test_convertible<ImplicitDefault>(), "Must be convertible"); + +struct ExplicitDefault { + explicit ExplicitDefault() {} +}; +static_assert(!test_convertible<ExplicitDefault>(), "Must not be convertible"); + +struct ImplicitInt { + ImplicitInt(int) {} +}; +static_assert(test_convertible<ImplicitInt, int>(), "Must be convertible"); + +struct ExplicitInt { + explicit ExplicitInt(int) {} +}; +static_assert(!test_convertible<ExplicitInt, int>(), "Must not be convertible"); + +struct ImplicitCopy { + ImplicitCopy(ImplicitCopy const&) {} +}; +static_assert(test_convertible<ImplicitCopy, ImplicitCopy>(), "Must be convertible"); + +struct ExplicitCopy { + explicit ExplicitCopy(ExplicitCopy const&) {} +}; +static_assert(!test_convertible<ExplicitCopy, ExplicitCopy>(), "Must not be convertible"); + +struct ImplicitMove { + ImplicitMove(ImplicitMove&&) {} +}; +static_assert(test_convertible<ImplicitMove, ImplicitMove>(), "Must be convertible"); + +struct ExplicitMove { + explicit ExplicitMove(ExplicitMove&&) {} +}; +static_assert(!test_convertible<ExplicitMove, ExplicitMove>(), "Must not be convertible"); + +struct ImplicitArgs { + ImplicitArgs(int, int, int) {} +}; +static_assert(test_convertible<ImplicitArgs, int, int, int>(), "Must be convertible"); + +struct ExplicitArgs { + explicit ExplicitArgs(int, int, int) {} +}; +static_assert(!test_convertible<ExplicitArgs, int, int, int>(), "Must not be convertible"); + +int main() { + // Nothing to do +} diff --git a/test/support/test.support/test_macros_header_exceptions.fail.cpp b/test/support/test.support/test_macros_header_exceptions.fail.cpp new file mode 100644 index 0000000000000..ade2cd98fe08c --- /dev/null +++ b/test/support/test.support/test_macros_header_exceptions.fail.cpp @@ -0,0 +1,24 @@ +//===----------------------------------------------------------------------===// +// +// The LLVM Compiler Infrastructure +// +// This file is dual licensed under the MIT and the University of Illinois Open +// Source Licenses. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +// "support/test_macros.hpp" + +// #define TEST_HAS_NO_EXCEPTIONS + +#include "test_macros.h" + +int main() { +#if defined(TEST_HAS_NO_EXCEPTIONS) + try { ((void)0); } catch (...) {} // expected-error {{exceptions disabled}} +#else + try { ((void)0); } catch (...) {} +#error exceptions enabled +// expected-error@-1 {{exceptions enabled}} +#endif +} diff --git a/test/support/test.support/test_macros_header_exceptions.pass.cpp b/test/support/test.support/test_macros_header_exceptions.pass.cpp new file mode 100644 index 0000000000000..589e148a00320 --- /dev/null +++ b/test/support/test.support/test_macros_header_exceptions.pass.cpp @@ -0,0 +1,24 @@ +//===----------------------------------------------------------------------===// +// +// The LLVM Compiler Infrastructure +// +// This file is dual licensed under the MIT and the University of Illinois Open +// Source Licenses. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +// UNSUPPORTED: libcpp-no-exceptions + +// "support/test_macros.hpp" + +// #define TEST_HAS_NO_EXCEPTIONS + +#include "test_macros.h" + +#if defined(TEST_HAS_NO_EXCEPTIONS) +#error macro defined unexpectedly +#endif + +int main() { + try { ((void)0); } catch (...) {} +} diff --git a/test/support/test.support/test_macros_header_rtti.fail.cpp b/test/support/test.support/test_macros_header_rtti.fail.cpp new file mode 100644 index 0000000000000..851a6c6014164 --- /dev/null +++ b/test/support/test.support/test_macros_header_rtti.fail.cpp @@ -0,0 +1,29 @@ +//===----------------------------------------------------------------------===// +// +// The LLVM Compiler Infrastructure +// +// This file is dual licensed under the MIT and the University of Illinois Open +// Source Licenses. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +// "support/test_macros.hpp" + +// #define TEST_HAS_NO_RTTI + +#include "test_macros.h" + +struct A { virtual ~A() {} }; +struct B : A {}; + +int main() { +#if defined(TEST_HAS_NO_RTTI) + A* ptr = new B; + (void)dynamic_cast<B*>(ptr); // expected-error{{cannot use dynamic_cast}} +#else + A* ptr = new B; + (void)dynamic_cast<B*>(ptr); +#error RTTI enabled +// expected-error@-1{{RTTI enabled}} +#endif +} diff --git a/test/support/test.support/test_macros_header_rtti.pass.cpp b/test/support/test.support/test_macros_header_rtti.pass.cpp new file mode 100644 index 0000000000000..d189a0efc2fcb --- /dev/null +++ b/test/support/test.support/test_macros_header_rtti.pass.cpp @@ -0,0 +1,28 @@ +//===----------------------------------------------------------------------===// +// +// The LLVM Compiler Infrastructure +// +// This file is dual licensed under the MIT and the University of Illinois Open +// Source Licenses. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +// UNSUPPORTED: libcpp-no-rtti + +// "support/test_macros.hpp" + +// #define TEST_HAS_NO_RTTI + +#include "test_macros.h" + +#if defined(TEST_HAS_NO_RTTI) +#error Macro defined unexpectedly +#endif + +struct A { virtual ~A() {} }; +struct B : A {}; + +int main() { + A* ptr = new B; + (void)dynamic_cast<B*>(ptr); +} diff --git a/test/support/test_allocator.h b/test/support/test_allocator.h index 3ca672f89a9be..466c7fabc66d7 100644 --- a/test/support/test_allocator.h +++ b/test/support/test_allocator.h @@ -76,18 +76,22 @@ public: ++alloc_count; return (pointer)::operator new(n * sizeof(T)); } - void deallocate(pointer p, size_type n) + void deallocate(pointer p, size_type) {assert(data_ >= 0); --alloc_count; ::operator delete((void*)p);} size_type max_size() const throw() {return UINT_MAX / sizeof(T);} +#if TEST_STD_VER < 11 void construct(pointer p, const T& val) - {::new(p) T(val);} -#ifndef _LIBCPP_HAS_NO_RVALUE_REFERENCES - void construct(pointer p, T&& val) - {::new(p) T(std::move(val));} -#endif // _LIBCPP_HAS_NO_RVALUE_REFERENCES - void destroy(pointer p) {p->~T();} - + {::new(static_cast<void*>(p)) T(val);} +#else + template <class U> void construct(pointer p, U&& val) + {::new(static_cast<void*>(p)) T(std::forward<U>(val));} +#endif + void destroy(pointer p) + { + p->~T(); + ((void)p); // Prevent MSVC's spurious unused warning + } friend bool operator==(const test_allocator& x, const test_allocator& y) {return x.data_ == y.data_;} friend bool operator!=(const test_allocator& x, const test_allocator& y) @@ -136,16 +140,17 @@ public: ++alloc_count; return (pointer)::operator new (n * sizeof(T)); } - void deallocate(pointer p, size_type n) + void deallocate(pointer p, size_type) {assert(data_ >= 0); --alloc_count; ::operator delete((void*)p); } size_type max_size() const throw() {return UINT_MAX / sizeof(T);} +#if TEST_STD_VER < 11 void construct(pointer p, const T& val) - {::new(p) T(val);} -#ifndef _LIBCPP_HAS_NO_RVALUE_REFERENCES - void construct(pointer p, T&& val) - {::new(p) T(std::move(val));} -#endif // _LIBCPP_HAS_NO_RVALUE_REFERENCES + {::new(static_cast<void*>(p)) T(val);} +#else + template <class U> void construct(pointer p, U&& val) + {::new(static_cast<void*>(p)) T(std::forward<U>(val));} +#endif void destroy(pointer p) {p->~T();} friend bool operator==(const non_default_test_allocator& x, const non_default_test_allocator& y) @@ -201,7 +206,7 @@ public: : data_(a.data_) {} T* allocate(std::size_t n) {return (T*)::operator new(n * sizeof(T));} - void deallocate(T* p, std::size_t n) + void deallocate(T* p, std::size_t) {::operator delete((void*)p);} other_allocator select_on_container_copy_construction() const @@ -223,4 +228,82 @@ public: }; +#if TEST_STD_VER >= 11 + +struct Ctor_Tag {}; + +template <typename T> class TaggingAllocator; + +struct Tag_X { + // All constructors must be passed the Tag type. + + // DefaultInsertable into vector<X, TaggingAllocator<X>>, + Tag_X(Ctor_Tag) {} + // CopyInsertable into vector<X, TaggingAllocator<X>>, + Tag_X(Ctor_Tag, const Tag_X&) {} + // MoveInsertable into vector<X, TaggingAllocator<X>>, and + Tag_X(Ctor_Tag, Tag_X&&) {} + + // EmplaceConstructible into vector<X, TaggingAllocator<X>> from args. + template<typename... Args> + Tag_X(Ctor_Tag, Args&&...) { } + + // not DefaultConstructible, CopyConstructible or MoveConstructible. + Tag_X() = delete; + Tag_X(const Tag_X&) = delete; + Tag_X(Tag_X&&) = delete; + + // CopyAssignable. + Tag_X& operator=(const Tag_X&) { return *this; } + + // MoveAssignable. + Tag_X& operator=(Tag_X&&) { return *this; } + +private: + // Not Destructible. + ~Tag_X() { } + + // Erasable from vector<X, TaggingAllocator<X>>. + friend class TaggingAllocator<Tag_X>; +}; + + +template<typename T> +class TaggingAllocator { +public: + using value_type = T; + TaggingAllocator() = default; + + template<typename U> + TaggingAllocator(const TaggingAllocator<U>&) { } + + T* allocate(std::size_t n) { return std::allocator<T>{}.allocate(n); } + + void deallocate(T* p, std::size_t n) { std::allocator<T>{}.deallocate(p, n); } + + template<typename... Args> + void construct(Tag_X* p, Args&&... args) + { ::new((void*)p) Tag_X(Ctor_Tag{}, std::forward<Args>(args)...); } + + template<typename U, typename... Args> + void construct(U* p, Args&&... args) + { ::new((void*)p) U(std::forward<Args>(args)...); } + + template<typename U, typename... Args> + void destroy(U* p) + { p->~U(); } +}; + +template<typename T, typename U> +bool +operator==(const TaggingAllocator<T>&, const TaggingAllocator<U>&) +{ return true; } + +template<typename T, typename U> +bool +operator!=(const TaggingAllocator<T>&, const TaggingAllocator<U>&) +{ return false; } +#endif + + #endif // TEST_ALLOCATOR_H diff --git a/test/support/test_convertible.hpp b/test/support/test_convertible.hpp new file mode 100644 index 0000000000000..787cef6568f28 --- /dev/null +++ b/test/support/test_convertible.hpp @@ -0,0 +1,42 @@ +//===----------------------------------------------------------------------===// +// +// The LLVM Compiler Infrastructure +// +// This file is dual licensed under the MIT and the University of Illinois Open +// Source Licenses. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#ifndef SUPPORT_TEST_CONVERTIBLE_HPP +#define SUPPORT_TEST_CONVERTIBLE_HPP + +// "test_convertible<Tp, Args...>()" is a metafunction used to check if 'Tp' +// is implicitly convertible from 'Args...' for any number of arguments, +// Unlike 'std::is_convertible' which only allows checking for single argument +// conversions. + +#include <type_traits> + +#include "test_macros.h" + +#if TEST_STD_VER < 11 +#error test_convertible.hpp requires C++11 or newer +#endif + +namespace detail { + template <class Tp> void eat_type(Tp); + + template <class Tp, class ...Args> + constexpr auto test_convertible_imp(int) + -> decltype(eat_type<Tp>({std::declval<Args>()...}), true) + { return true; } + + template <class Tp, class ...Args> + constexpr auto test_convertible_imp(long) -> bool { return false; } +} + +template <class Tp, class ...Args> +constexpr bool test_convertible() +{ return detail::test_convertible_imp<Tp, Args...>(0); } + +#endif // SUPPORT_TEST_CONVERTIBLE_HPP
\ No newline at end of file diff --git a/test/support/test_iterators.h b/test/support/test_iterators.h index d814879fa2727..d4a079971b15a 100644 --- a/test/support/test_iterators.h +++ b/test/support/test_iterators.h @@ -12,6 +12,7 @@ #include <iterator> #include <stdexcept> +#include <cstddef> #include <cassert> #include "test_macros.h" @@ -335,116 +336,137 @@ struct ThrowingIterator { typedef const T * pointer; typedef const T & reference; - enum ThrowingAction { TAIncrement, TADecrement, TADereference, TAAssignment, TAComparison }; - -// Constructors - ThrowingIterator () - : begin_(nullptr), end_(nullptr), current_(nullptr), action_(TADereference), index_(0) {} - ThrowingIterator (const T *first, const T *last, size_t index = 0, ThrowingAction action = TADereference) - : begin_(first), end_(last), current_(first), action_(action), index_(index) {} - ThrowingIterator (const ThrowingIterator &rhs) - : begin_(rhs.begin_), end_(rhs.end_), current_(rhs.current_), action_(rhs.action_), index_(rhs.index_) {} - ThrowingIterator & operator= (const ThrowingIterator &rhs) - { - if (action_ == TAAssignment) - { - if (index_ == 0) - throw std::runtime_error ("throw from iterator assignment"); - else - --index_; - } - begin_ = rhs.begin_; - end_ = rhs.end_; - current_ = rhs.current_; - action_ = rhs.action_; - index_ = rhs.index_; - return *this; - } - -// iterator operations - reference operator*() const - { - if (action_ == TADereference) - { - if (index_ == 0) - throw std::runtime_error ("throw from iterator dereference"); - else - --index_; - } - return *current_; - } - - ThrowingIterator & operator++() - { - if (action_ == TAIncrement) - { - if (index_ == 0) - throw std::runtime_error ("throw from iterator increment"); - else - --index_; - } - ++current_; - return *this; - } - - ThrowingIterator operator++(int) - { - ThrowingIterator temp = *this; - ++(*this); - return temp; - } - - ThrowingIterator & operator--() - { - if (action_ == TADecrement) - { - if (index_ == 0) - throw std::runtime_error ("throw from iterator decrement"); - else - --index_; - } - --current_; - return *this; - } - - ThrowingIterator operator--(int) { - ThrowingIterator temp = *this; - --(*this); - return temp; - } - - bool operator== (const ThrowingIterator &rhs) const - { - if (action_ == TAComparison) - { - if (index_ == 0) - throw std::runtime_error ("throw from iterator comparison"); - else - --index_; - } - bool atEndL = current_ == end_; - bool atEndR = rhs.current_ == rhs.end_; - if (atEndL != atEndR) return false; // one is at the end (or empty), the other is not. - if (atEndL) return true; // both are at the end (or empty) - return current_ == rhs.current_; - } + enum ThrowingAction { TAIncrement, TADecrement, TADereference, TAAssignment, TAComparison }; + +// Constructors + ThrowingIterator () + : begin_(nullptr), end_(nullptr), current_(nullptr), action_(TADereference), index_(0) {} + ThrowingIterator (const T *first, const T *last, size_t index = 0, ThrowingAction action = TADereference) + : begin_(first), end_(last), current_(first), action_(action), index_(index) {} + ThrowingIterator (const ThrowingIterator &rhs) + : begin_(rhs.begin_), end_(rhs.end_), current_(rhs.current_), action_(rhs.action_), index_(rhs.index_) {} + ThrowingIterator & operator= (const ThrowingIterator &rhs) + { + if (action_ == TAAssignment) + { + if (index_ == 0) +#ifndef TEST_HAS_NO_EXCEPTIONS + throw std::runtime_error ("throw from iterator assignment"); +#else + assert(false); +#endif + + else + --index_; + } + begin_ = rhs.begin_; + end_ = rhs.end_; + current_ = rhs.current_; + action_ = rhs.action_; + index_ = rhs.index_; + return *this; + } + +// iterator operations + reference operator*() const + { + if (action_ == TADereference) + { + if (index_ == 0) +#ifndef TEST_HAS_NO_EXCEPTIONS + throw std::runtime_error ("throw from iterator dereference"); +#else + assert(false); +#endif + else + --index_; + } + return *current_; + } + + ThrowingIterator & operator++() + { + if (action_ == TAIncrement) + { + if (index_ == 0) +#ifndef TEST_HAS_NO_EXCEPTIONS + throw std::runtime_error ("throw from iterator increment"); +#else + assert(false); +#endif + else + --index_; + } + ++current_; + return *this; + } + + ThrowingIterator operator++(int) + { + ThrowingIterator temp = *this; + ++(*this); + return temp; + } + + ThrowingIterator & operator--() + { + if (action_ == TADecrement) + { + if (index_ == 0) +#ifndef TEST_HAS_NO_EXCEPTIONS + throw std::runtime_error ("throw from iterator decrement"); +#else + assert(false); +#endif + else + --index_; + } + --current_; + return *this; + } + + ThrowingIterator operator--(int) { + ThrowingIterator temp = *this; + --(*this); + return temp; + } + + bool operator== (const ThrowingIterator &rhs) const + { + if (action_ == TAComparison) + { + if (index_ == 0) +#ifndef TEST_HAS_NO_EXCEPTIONS + throw std::runtime_error ("throw from iterator comparison"); +#else + assert(false); +#endif + else + --index_; + } + bool atEndL = current_ == end_; + bool atEndR = rhs.current_ == rhs.end_; + if (atEndL != atEndR) return false; // one is at the end (or empty), the other is not. + if (atEndL) return true; // both are at the end (or empty) + return current_ == rhs.current_; + } private: - const T* begin_; - const T* end_; - const T* current_; - ThrowingAction action_; - mutable size_t index_; + const T* begin_; + const T* end_; + const T* current_; + ThrowingAction action_; + mutable size_t index_; }; template <typename T> bool operator== (const ThrowingIterator<T>& a, const ThrowingIterator<T>& b) -{ return a.operator==(b); } +{ return a.operator==(b); } template <typename T> bool operator!= (const ThrowingIterator<T>& a, const ThrowingIterator<T>& b) -{ return !a.operator==(b); } - +{ return !a.operator==(b); } + template <typename T> struct NonThrowingIterator { typedef std::bidirectional_iterator_tag iterator_category; @@ -453,75 +475,75 @@ struct NonThrowingIterator { typedef const T * pointer; typedef const T & reference; -// Constructors - NonThrowingIterator () - : begin_(nullptr), end_(nullptr), current_(nullptr) {} - NonThrowingIterator (const T *first, const T* last) - : begin_(first), end_(last), current_(first) {} - NonThrowingIterator (const NonThrowingIterator &rhs) - : begin_(rhs.begin_), end_(rhs.end_), current_(rhs.current_) {} - NonThrowingIterator & operator= (const NonThrowingIterator &rhs) TEST_NOEXCEPT - { - begin_ = rhs.begin_; - end_ = rhs.end_; - current_ = rhs.current_; - return *this; - } - -// iterator operations - reference operator*() const TEST_NOEXCEPT - { - return *current_; - } - - NonThrowingIterator & operator++() TEST_NOEXCEPT - { - ++current_; - return *this; - } - - NonThrowingIterator operator++(int) TEST_NOEXCEPT - { - NonThrowingIterator temp = *this; - ++(*this); - return temp; - } - - NonThrowingIterator & operator--() TEST_NOEXCEPT - { - --current_; - return *this; - } - - NonThrowingIterator operator--(int) TEST_NOEXCEPT - { - NonThrowingIterator temp = *this; - --(*this); - return temp; - } - - bool operator== (const NonThrowingIterator &rhs) const TEST_NOEXCEPT - { - bool atEndL = current_ == end_; - bool atEndR = rhs.current_ == rhs.end_; - if (atEndL != atEndR) return false; // one is at the end (or empty), the other is not. - if (atEndL) return true; // both are at the end (or empty) - return current_ == rhs.current_; - } +// Constructors + NonThrowingIterator () + : begin_(nullptr), end_(nullptr), current_(nullptr) {} + NonThrowingIterator (const T *first, const T* last) + : begin_(first), end_(last), current_(first) {} + NonThrowingIterator (const NonThrowingIterator &rhs) + : begin_(rhs.begin_), end_(rhs.end_), current_(rhs.current_) {} + NonThrowingIterator & operator= (const NonThrowingIterator &rhs) TEST_NOEXCEPT + { + begin_ = rhs.begin_; + end_ = rhs.end_; + current_ = rhs.current_; + return *this; + } + +// iterator operations + reference operator*() const TEST_NOEXCEPT + { + return *current_; + } + + NonThrowingIterator & operator++() TEST_NOEXCEPT + { + ++current_; + return *this; + } + + NonThrowingIterator operator++(int) TEST_NOEXCEPT + { + NonThrowingIterator temp = *this; + ++(*this); + return temp; + } + + NonThrowingIterator & operator--() TEST_NOEXCEPT + { + --current_; + return *this; + } + + NonThrowingIterator operator--(int) TEST_NOEXCEPT + { + NonThrowingIterator temp = *this; + --(*this); + return temp; + } + + bool operator== (const NonThrowingIterator &rhs) const TEST_NOEXCEPT + { + bool atEndL = current_ == end_; + bool atEndR = rhs.current_ == rhs.end_; + if (atEndL != atEndR) return false; // one is at the end (or empty), the other is not. + if (atEndL) return true; // both are at the end (or empty) + return current_ == rhs.current_; + } private: - const T* begin_; - const T* end_; - const T* current_; + const T* begin_; + const T* end_; + const T* current_; }; template <typename T> bool operator== (const NonThrowingIterator<T>& a, const NonThrowingIterator<T>& b) TEST_NOEXCEPT -{ return a.operator==(b); } +{ return a.operator==(b); } template <typename T> bool operator!= (const NonThrowingIterator<T>& a, const NonThrowingIterator<T>& b) TEST_NOEXCEPT -{ return !a.operator==(b); } +{ return !a.operator==(b); } #undef DELETE_FUNCTION diff --git a/test/support/test_macros.h b/test/support/test_macros.h index c34e8cf7ef14f..a072e31b77b70 100644 --- a/test/support/test_macros.h +++ b/test/support/test_macros.h @@ -11,6 +11,8 @@ #ifndef SUPPORT_TEST_MACROS_HPP #define SUPPORT_TEST_MACROS_HPP +#include <ciso646> // Get STL specific macros like _LIBCPP_VERSION + #define TEST_CONCAT1(X, Y) X##Y #define TEST_CONCAT(X, Y) TEST_CONCAT1(X, Y) @@ -33,6 +35,7 @@ #endif /* Make a nice name for the standard version */ +#ifndef TEST_STD_VER #if __cplusplus <= 199711L # define TEST_STD_VER 3 #elif __cplusplus <= 201103L @@ -40,15 +43,8 @@ #elif __cplusplus <= 201402L # define TEST_STD_VER 14 #else -# define TEST_STD_VER 99 // greater than current standard +# define TEST_STD_VER 16 // current year; greater than current standard #endif - -/* Features that were introduced in C++11 */ -#if TEST_STD_VER >= 11 -#define TEST_HAS_RVALUE_REFERENCES -#define TEST_HAS_VARIADIC_TEMPLATES -#define TEST_HAS_INITIALIZER_LISTS -#define TEST_HAS_BASIC_CONSTEXPR #endif /* Features that were introduced in C++14 */ @@ -61,44 +57,27 @@ #if TEST_STD_VER > 14 #endif -#if TEST_HAS_EXTENSION(cxx_decltype) || TEST_STD_VER >= 11 -#define TEST_DECLTYPE(T) decltype(T) -#else -#define TEST_DECLTYPE(T) __typeof__(T) -#endif - #if TEST_STD_VER >= 11 #define TEST_CONSTEXPR constexpr #define TEST_NOEXCEPT noexcept +# if TEST_STD_VER >= 14 +# define TEST_CONSTEXPR_CXX14 constexpr +# else +# define TEST_CONSTEXPR_CXX14 +# endif #else #define TEST_CONSTEXPR +#define TEST_CONSTEXPR_CXX14 #define TEST_NOEXCEPT #endif -#if TEST_HAS_EXTENSION(cxx_static_assert) || TEST_STD_VER >= 11 -# define TEST_STATIC_ASSERT(Expr, Msg) static_assert(Expr, Msg) -#else -# define TEST_STATIC_ASSERT(Expr, Msg) \ - typedef ::test_detail::static_assert_check<sizeof( \ - ::test_detail::static_assert_incomplete_test<(Expr)>)> \ - TEST_CONCAT(test_assert, __LINE__) -# -#endif - -namespace test_detail { - -template <bool> struct static_assert_incomplete_test; -template <> struct static_assert_incomplete_test<true> {}; -template <unsigned> struct static_assert_check {}; - -} // end namespace test_detail - - -#if !TEST_HAS_FEATURE(cxx_rtti) && !defined(__cxx_rtti) +#if !TEST_HAS_FEATURE(cxx_rtti) && !defined(__cpp_rtti) \ + && !defined(__GXX_RTTI) #define TEST_HAS_NO_RTTI #endif -#if !TEST_HAS_FEATURE(cxx_exceptions) && !defined(__cxx_exceptions) +#if !TEST_HAS_FEATURE(cxx_exceptions) && !defined(__cpp_exceptions) \ + && !defined(__EXCEPTIONS) #define TEST_HAS_NO_EXCEPTIONS #endif @@ -107,4 +86,36 @@ template <unsigned> struct static_assert_check {}; #define TEST_HAS_SANITIZERS #endif +#if defined(_LIBCPP_NORETURN) +#define TEST_NORETURN _LIBCPP_NORETURN +#else +#define TEST_NORETURN [[noreturn]] +#endif + +/* Macros for testing libc++ specific behavior and extensions */ +#if defined(_LIBCPP_VERSION) +#define LIBCPP_ASSERT(...) assert(__VA_ARGS__) +#define LIBCPP_STATIC_ASSERT(...) static_assert(__VA_ARGS__) +#else +#define LIBCPP_ASSERT(...) ((void)0) +#define LIBCPP_STATIC_ASSERT(...) ((void)0) +#endif + +#define ASSERT_NOEXCEPT(...) \ + static_assert(noexcept(__VA_ARGS__), "Operation must be noexcept") + +#define ASSERT_NOT_NOEXCEPT(...) \ + static_assert(!noexcept(__VA_ARGS__), "Operation must NOT be noexcept") + +namespace test_macros_detail { +template <class T, class U> +struct is_same { enum { value = 0};} ; +template <class T> +struct is_same<T, T> { enum {value = 1}; }; +} // namespace test_macros_detail + +#define ASSERT_SAME_TYPE(...) \ + static_assert(test_macros_detail::is_same<__VA_ARGS__>::value, \ + "Types differ uexpectedly") + #endif // SUPPORT_TEST_MACROS_HPP diff --git a/test/support/test_memory_resource.hpp b/test/support/test_memory_resource.hpp new file mode 100644 index 0000000000000..e7b067cf631d5 --- /dev/null +++ b/test/support/test_memory_resource.hpp @@ -0,0 +1,507 @@ +//===----------------------------------------------------------------------===// +// +// The LLVM Compiler Infrastructure +// +// This file is dual licensed under the MIT and the University of Illinois Open +// Source Licenses. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#ifndef SUPPORT_TEST_MEMORY_RESOURCE_HPP +#define SUPPORT_TEST_MEMORY_RESOURCE_HPP + +#include <experimental/memory_resource> +#include <memory> +#include <type_traits> +#include <cstddef> +#include <cstdlib> +#include <cstring> +#include <cstdint> +#include <cassert> +#include "test_macros.h" + +struct AllocController; + // 'AllocController' is a concrete type that instruments and controls the + // behavior of of test allocators. + +template <class T> +class CountingAllocator; + // 'CountingAllocator' is an basic implementation of the 'Allocator' + // requirements that use the 'AllocController' interface. + +template <class T> +class MinAlignAllocator; + // 'MinAlignAllocator' is an instrumented test type which implements the + // 'Allocator' requirements. 'MinAlignAllocator' ensures that it *never* + // returns a pointer to over-aligned storage. For example + // 'MinAlignPointer<char>{}.allocate(...)' will never a 2-byte aligned + // pointer. + +template <class T> +class NullAllocator; + // 'NullAllocator' is an instrumented test type which implements the + // 'Allocator' requirements except that 'allocator' and 'deallocate' are + // nops. + + +#define DISALLOW_COPY(Type) \ + Type(Type const&) = delete; \ + Type& operator=(Type const&) = delete + +constexpr std::size_t MaxAlignV = alignof(std::max_align_t); + +struct TestException {}; + +struct AllocController { + int copy_constructed = 0; + int move_constructed = 0; + + int alive = 0; + int alloc_count = 0; + int dealloc_count = 0; + int is_equal_count = 0; + + std::size_t alive_size; + std::size_t allocated_size; + std::size_t deallocated_size; + + std::size_t last_size = 0; + std::size_t last_align = 0; + void * last_pointer = 0; + + std::size_t last_alloc_size = 0; + std::size_t last_alloc_align = 0; + void * last_alloc_pointer = nullptr; + + std::size_t last_dealloc_size = 0; + std::size_t last_dealloc_align = 0; + void * last_dealloc_pointer = nullptr; + + bool throw_on_alloc = false; + + AllocController() = default; + + void countAlloc(void* p, size_t s, size_t a) { + ++alive; + ++alloc_count; + alive_size += s; + allocated_size += s; + last_pointer = last_alloc_pointer = p; + last_size = last_alloc_size = s; + last_align = last_alloc_align = a; + } + + void countDealloc(void* p, size_t s, size_t a) { + --alive; + ++dealloc_count; + alive_size -= s; + deallocated_size += s; + last_pointer = last_dealloc_pointer = p; + last_size = last_dealloc_size = s; + last_align = last_dealloc_align = a; + } + + void reset() { std::memset(this, 0, sizeof(*this)); } + +public: + bool checkAlloc(void* p, size_t s, size_t a) const { + return p == last_alloc_pointer && + s == last_alloc_size && + a == last_alloc_align; + } + + bool checkAlloc(void* p, size_t s) const { + return p == last_alloc_pointer && + s == last_alloc_size; + } + + bool checkAllocAtLeast(void* p, size_t s, size_t a) const { + return p == last_alloc_pointer && + s <= last_alloc_size && + a <= last_alloc_align; + } + + bool checkAllocAtLeast(void* p, size_t s) const { + return p == last_alloc_pointer && + s <= last_alloc_size; + } + + bool checkDealloc(void* p, size_t s, size_t a) const { + return p == last_dealloc_pointer && + s == last_dealloc_size && + a == last_dealloc_align; + } + + bool checkDealloc(void* p, size_t s) const { + return p == last_dealloc_pointer && + s == last_dealloc_size; + } + + bool checkDeallocMatchesAlloc() const { + return last_dealloc_pointer == last_alloc_pointer && + last_dealloc_size == last_alloc_size && + last_dealloc_align == last_alloc_align; + } + + void countIsEqual() { + ++is_equal_count; + } + + bool checkIsEqualCalledEq(int n) const { + return is_equal_count == n; + } +private: + DISALLOW_COPY(AllocController); +}; + +template <class T> +class CountingAllocator +{ +public: + typedef T value_type; + typedef T* pointer; + CountingAllocator() = delete; + explicit CountingAllocator(AllocController& PP) : P(&PP) {} + + CountingAllocator(CountingAllocator const& other) : P(other.P) { + P->copy_constructed += 1; + } + + CountingAllocator(CountingAllocator&& other) : P(other.P) { + P->move_constructed += 1; + } + + template <class U> + CountingAllocator(CountingAllocator<U> const& other) TEST_NOEXCEPT : P(other.P) { + P->copy_constructed += 1; + } + + template <class U> + CountingAllocator(CountingAllocator<U>&& other) TEST_NOEXCEPT : P(other.P) { + P->move_constructed += 1; + } + + T* allocate(std::size_t n) + { + void* ret = ::operator new(n*sizeof(T)); + P->countAlloc(ret, n*sizeof(T), alignof(T)); + return static_cast<T*>(ret); + } + + void deallocate(T* p, std::size_t n) + { + void* vp = static_cast<void*>(p); + P->countDealloc(vp, n*sizeof(T), alignof(T)); + ::operator delete(vp); + } + + AllocController& getController() const { return *P; } + +private: + template <class Tp> friend class CountingAllocator; + AllocController *P; +}; + +template <class T, class U> +inline bool operator==(CountingAllocator<T> const& x, + CountingAllocator<U> const& y) { + return &x.getController() == &y.getController(); +} + +template <class T, class U> +inline bool operator!=(CountingAllocator<T> const& x, + CountingAllocator<U> const& y) { + return !(x == y); +} + +template <class T> +class MinAlignedAllocator +{ +public: + typedef T value_type; + typedef T* pointer; + + MinAlignedAllocator() = delete; + + explicit MinAlignedAllocator(AllocController& R) : P(&R) {} + + MinAlignedAllocator(MinAlignedAllocator const& other) : P(other.P) { + P->copy_constructed += 1; + } + + MinAlignedAllocator(MinAlignedAllocator&& other) : P(other.P) { + P->move_constructed += 1; + } + + template <class U> + MinAlignedAllocator(MinAlignedAllocator<U> const& other) TEST_NOEXCEPT : P(other.P) { + P->copy_constructed += 1; + } + + template <class U> + MinAlignedAllocator(MinAlignedAllocator<U>&& other) TEST_NOEXCEPT : P(other.P) { + P->move_constructed += 1; + } + + T* allocate(std::size_t n) { + char* aligned_ptr = (char*)::operator new(alloc_size(n*sizeof(T))); + assert(is_max_aligned(aligned_ptr)); + + char* unaligned_ptr = aligned_ptr + alignof(T); + assert(is_min_aligned(unaligned_ptr)); + + P->countAlloc(unaligned_ptr, n * sizeof(T), alignof(T)); + + return ((T*)unaligned_ptr); + } + + void deallocate(T* p, std::size_t n) { + assert(is_min_aligned(p)); + + char* aligned_ptr = ((char*)p) - alignof(T); + assert(is_max_aligned(aligned_ptr)); + + P->countDealloc(p, n*sizeof(T), alignof(T)); + + return ::operator delete(static_cast<void*>(aligned_ptr)); + } + + AllocController& getController() const { return *P; } + +private: + static const std::size_t BlockSize = alignof(std::max_align_t); + + static std::size_t alloc_size(std::size_t s) { + std::size_t bytes = (s + BlockSize - 1) & ~(BlockSize - 1); + bytes += BlockSize; + assert(bytes % BlockSize == 0); + return bytes; + } + + static bool is_max_aligned(void* p) { + return reinterpret_cast<std::uintptr_t>(p) % BlockSize == 0; + } + + static bool is_min_aligned(void* p) { + if (alignof(T) == BlockSize) { + return is_max_aligned(p); + } else { + return reinterpret_cast<std::uintptr_t>(p) % BlockSize == alignof(T); + } + } + + template <class Tp> friend class MinAlignedAllocator; + mutable AllocController *P; +}; + + +template <class T, class U> +inline bool operator==(MinAlignedAllocator<T> const& x, + MinAlignedAllocator<U> const& y) { + return &x.getController() == &y.getController(); +} + +template <class T, class U> +inline bool operator!=(MinAlignedAllocator<T> const& x, + MinAlignedAllocator<U> const& y) { + return !(x == y); +} + +template <class T> +class NullAllocator +{ +public: + typedef T value_type; + typedef T* pointer; + NullAllocator() = delete; + explicit NullAllocator(AllocController& PP) : P(&PP) {} + + NullAllocator(NullAllocator const& other) : P(other.P) { + P->copy_constructed += 1; + } + + NullAllocator(NullAllocator&& other) : P(other.P) { + P->move_constructed += 1; + } + + template <class U> + NullAllocator(NullAllocator<U> const& other) TEST_NOEXCEPT : P(other.P) { + P->copy_constructed += 1; + } + + template <class U> + NullAllocator(NullAllocator<U>&& other) TEST_NOEXCEPT : P(other.P) { + P->move_constructed += 1; + } + + T* allocate(std::size_t n) + { + P->countAlloc(nullptr, n*sizeof(T), alignof(T)); + return nullptr; + } + + void deallocate(T* p, std::size_t n) + { + void* vp = static_cast<void*>(p); + P->countDealloc(vp, n*sizeof(T), alignof(T)); + } + + AllocController& getController() const { return *P; } + +private: + template <class Tp> friend class NullAllocator; + AllocController *P; +}; + +template <class T, class U> +inline bool operator==(NullAllocator<T> const& x, + NullAllocator<U> const& y) { + return &x.getController() == &y.getController(); +} + +template <class T, class U> +inline bool operator!=(NullAllocator<T> const& x, + NullAllocator<U> const& y) { + return !(x == y); +} + + + +template <class ProviderT, int = 0> +class TestResourceImp : public std::experimental::pmr::memory_resource +{ +public: + static int resource_alive; + static int resource_constructed; + static int resource_destructed; + + static void resetStatics() { + assert(resource_alive == 0); + resource_alive = 0; + resource_constructed = 0; + resource_destructed = 0; + } + + using memory_resource = std::experimental::pmr::memory_resource; + using Provider = ProviderT; + + int value; + + explicit TestResourceImp(int val = 0) : value(val) { + ++resource_alive; + ++resource_constructed; + } + + ~TestResourceImp() noexcept { + --resource_alive; + ++resource_destructed; + } + + void reset() { C.reset(); P.reset(); } + AllocController& getController() { return C; } + + bool checkAlloc(void* p, std::size_t s, std::size_t a) const + { return C.checkAlloc(p, s, a); } + + bool checkDealloc(void* p, std::size_t s, std::size_t a) const + { return C.checkDealloc(p, s, a); } + + bool checkIsEqualCalledEq(int n) const { return C.checkIsEqualCalledEq(n); } + +protected: + virtual void * do_allocate(std::size_t s, std::size_t a) { + if (C.throw_on_alloc) { +#ifndef TEST_HAS_NO_EXCEPTIONS + throw TestException{}; +#else + assert(false); +#endif + } + void* ret = P.allocate(s, a); + C.countAlloc(ret, s, a); + return ret; + } + + virtual void do_deallocate(void * p, std::size_t s, std::size_t a) { + C.countDealloc(p, s, a); + P.deallocate(p, s, a); + } + + virtual bool do_is_equal(memory_resource const & other) const noexcept { + C.countIsEqual(); + TestResourceImp const * o = dynamic_cast<TestResourceImp const *>(&other); + return o && o->value == value; + } +private: + mutable AllocController C; + mutable Provider P; + DISALLOW_COPY(TestResourceImp); +}; + +template <class Provider, int N> +int TestResourceImp<Provider, N>::resource_alive = 0; + +template <class Provider, int N> +int TestResourceImp<Provider, N>::resource_constructed = 0; + +template <class Provider, int N> +int TestResourceImp<Provider, N>::resource_destructed = 0; + + +struct NullProvider { + NullProvider() {} + void* allocate(size_t, size_t) { return nullptr; } + void deallocate(void*, size_t, size_t) {} + void reset() {} +private: + DISALLOW_COPY(NullProvider); +}; + +struct NewDeleteProvider { + NewDeleteProvider() {} + void* allocate(size_t s, size_t) { return ::operator new(s); } + void deallocate(void* p, size_t, size_t) { ::operator delete(p); } + void reset() {} +private: + DISALLOW_COPY(NewDeleteProvider); +}; + +template <size_t Size = 4096 * 10> // 10 pages worth of memory. +struct BufferProvider { + char buffer[Size]; + void* next = &buffer; + size_t space = Size; + + BufferProvider() {} + + void* allocate(size_t s, size_t a) { + void* ret = std::align(s, a, next, space); + if (ret == nullptr) { +#ifndef TEST_HAS_NO_EXCEPTIONS + throw std::bad_alloc(); +#else + assert(false); +#endif + } + + return ret; + } + + void deallocate(void*, size_t, size_t) {} + + void reset() { + next = &buffer; + space = Size; + } +private: + DISALLOW_COPY(BufferProvider); +}; + +using NullResource = TestResourceImp<NullProvider, 0>; +using NewDeleteResource = TestResourceImp<NewDeleteProvider, 0>; +using TestResource = TestResourceImp<BufferProvider<>, 0>; +using TestResource1 = TestResourceImp<BufferProvider<>, 1>; +using TestResource2 = TestResourceImp<BufferProvider<>, 2>; + + +#endif /* SUPPORT_TEST_MEMORY_RESOURCE_HPP */ diff --git a/test/support/type_id.h b/test/support/type_id.h new file mode 100644 index 0000000000000..309f0884e4a7b --- /dev/null +++ b/test/support/type_id.h @@ -0,0 +1,57 @@ +//===----------------------------------------------------------------------===// +// +// The LLVM Compiler Infrastructure +// +// This file is dual licensed under the MIT and the University of Illinois Open +// Source Licenses. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// +#ifndef SUPPORT_TYPE_ID_H +#define SUPPORT_TYPE_ID_H + +#include <functional> +#include <cassert> + +#include "test_macros.h" + +#if TEST_STD_VER < 11 +#error This header requires C++11 or greater +#endif + +// TypeID - Represent a unique identifier for a type. TypeID allows equality +// comparisons between different types. +struct TypeID { + friend bool operator==(TypeID const& LHS, TypeID const& RHS) + {return LHS.m_id == RHS.m_id; } + friend bool operator!=(TypeID const& LHS, TypeID const& RHS) + {return LHS.m_id != RHS.m_id; } +private: + explicit constexpr TypeID(const int* xid) : m_id(xid) {} + + TypeID(const TypeID&) = delete; + TypeID& operator=(TypeID const&) = delete; + + const int* const m_id; + template <class T> friend TypeID const& makeTypeID(); + +}; + +// makeTypeID - Return the TypeID for the specified type 'T'. +template <class T> +inline TypeID const& makeTypeID() { + static int dummy; + static const TypeID id(&dummy); + return id; +} + +template <class ...Args> +struct ArgumentListID {}; + +// makeArgumentID - Create and return a unique identifier for a given set +// of arguments. +template <class ...Args> +inline TypeID const& makeArgumentID() { + return makeTypeID<ArgumentListID<Args...>>(); +} + +#endif // SUPPORT_TYPE_ID_H diff --git a/test/support/uses_alloc_types.hpp b/test/support/uses_alloc_types.hpp new file mode 100644 index 0000000000000..834e57729a7ae --- /dev/null +++ b/test/support/uses_alloc_types.hpp @@ -0,0 +1,298 @@ +//===----------------------------------------------------------------------===// +// +// The LLVM Compiler Infrastructure +// +// This file is dual licensed under the MIT and the University of Illinois Open +// Source Licenses. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#ifndef USES_ALLOC_TYPES_HPP +#define USES_ALLOC_TYPES_HPP + +# include <experimental/memory_resource> +# include <experimental/utility> +# include <memory> +# include <cassert> + +#include "test_memory_resource.hpp" +#include "type_id.h" + +// There are two forms of uses-allocator construction: +// (1) UA_AllocArg: 'T(allocator_arg_t, Alloc const&, Args&&...)' +// (2) UA_AllocLast: 'T(Args&&..., Alloc const&)' +// 'UA_None' represents non-uses allocator construction. +enum class UsesAllocatorType { + UA_None = 0, + UA_AllocArg = 2, + UA_AllocLast = 4 +}; +constexpr UsesAllocatorType UA_None = UsesAllocatorType::UA_None; +constexpr UsesAllocatorType UA_AllocArg = UsesAllocatorType::UA_AllocArg; +constexpr UsesAllocatorType UA_AllocLast = UsesAllocatorType::UA_AllocLast; + +template <class Alloc, std::size_t N> +class UsesAllocatorV1; + // Implements form (1) of uses-allocator construction from the specified + // 'Alloc' type and exactly 'N' additional arguments. It also provides + // non-uses allocator construction from 'N' arguments. This test type + // blows up when form (2) of uses-allocator is even considered. + +template <class Alloc, std::size_t N> +class UsesAllocatorV2; + // Implements form (2) of uses-allocator construction from the specified + // 'Alloc' type and exactly 'N' additional arguments. It also provides + // non-uses allocator construction from 'N' arguments. + +template <class Alloc, std::size_t N> +class UsesAllocatorV3; + // Implements both form (1) and (2) of uses-allocator construction from + // the specified 'Alloc' type and exactly 'N' additional arguments. It also + // provides non-uses allocator construction from 'N' arguments. + +template <class Alloc, std::size_t> +class NotUsesAllocator; + // Implements both form (1) and (2) of uses-allocator construction from + // the specified 'Alloc' type and exactly 'N' additional arguments. It also + // provides non-uses allocator construction from 'N' arguments. However + // 'NotUsesAllocator' never provides a 'allocator_type' typedef so it is + // never automatically uses-allocator constructed. + + +template <class ...ArgTypes, class TestType> +bool checkConstruct(TestType& value, UsesAllocatorType form, + typename TestType::CtorAlloc const& alloc) + // Check that 'value' was constructed using the specified 'form' of + // construction and with the specified 'ArgTypes...'. Additionally + // check that 'value' was constructed using the specified 'alloc'. +{ + if (form == UA_None) { + return value.template checkConstruct<ArgTypes&&...>(form); + } else { + return value.template checkConstruct<ArgTypes&&...>(form, alloc); + } +} + + +template <class ...ArgTypes, class TestType> +bool checkConstruct(TestType& value, UsesAllocatorType form) { + return value.template checkConstruct<ArgTypes&&...>(form); +} + +template <class TestType> +bool checkConstructionEquiv(TestType& T, TestType& U) + // check that 'T' and 'U' where initialized in the exact same manner. +{ + return T.checkConstructEquiv(U); +} + +//////////////////////////////////////////////////////////////////////////////// +namespace detail { + +template <bool IsZero, size_t N, class ArgList, class ...Args> +struct TakeNImp; + +template <class ArgList, class ...Args> +struct TakeNImp<true, 0, ArgList, Args...> { + typedef ArgList type; +}; + +template <size_t N, class ...A1, class F, class ...R> +struct TakeNImp<false, N, ArgumentListID<A1...>, F, R...> + : TakeNImp<N-1 == 0, N - 1, ArgumentListID<A1..., F>, R...> {}; + +template <size_t N, class ...Args> +struct TakeNArgs : TakeNImp<N == 0, N, ArgumentListID<>, Args...> {}; + +template <class T> +struct Identity { typedef T type; }; + +template <class T> +using IdentityT = typename Identity<T>::type; + +template <bool Value> +using EnableIfB = typename std::enable_if<Value, bool>::type; + +} // end namespace detail + +using detail::EnableIfB; + +struct AllocLastTag {}; + +template <class Alloc> +struct UsesAllocatorTestBase { +public: + using CtorAlloc = typename std::conditional< + std::is_same<Alloc, std::experimental::erased_type>::value, + std::experimental::pmr::memory_resource*, + Alloc + >::type; + + template <class ...ArgTypes> + bool checkConstruct(UsesAllocatorType expectType) const { + return expectType == constructor_called && + makeArgumentID<ArgTypes...>() == *args_id; + } + + template <class ...ArgTypes> + bool checkConstruct(UsesAllocatorType expectType, + CtorAlloc const& expectAlloc) const { + return expectType == constructor_called && + makeArgumentID<ArgTypes...>() == *args_id && + expectAlloc == allocator; + } + + bool checkConstructEquiv(UsesAllocatorTestBase& O) const { + return constructor_called == O.constructor_called + && *args_id == *O.args_id + && allocator == O.allocator; + } + +protected: + explicit UsesAllocatorTestBase(const TypeID* aid) + : args_id(aid), constructor_called(UA_None), allocator() + {} + + template <class ...Args> + UsesAllocatorTestBase(std::allocator_arg_t, CtorAlloc const& a, Args&&...) + : args_id(&makeArgumentID<Args&&...>()), + constructor_called(UA_AllocArg), + allocator(a) + {} + + template <class ...Args, class ArgsIDL = detail::TakeNArgs<sizeof...(Args) - 1, Args&&...>> + UsesAllocatorTestBase(AllocLastTag, Args&&... args) + : args_id(&makeTypeID<typename ArgsIDL::type>()), + constructor_called(UA_AllocLast), + allocator(getAllocatorFromPack( + typename ArgsIDL::type{}, + std::forward<Args>(args)...)) + { + } + +private: + template <class ...LArgs, class ...Args> + static CtorAlloc getAllocatorFromPack(ArgumentListID<LArgs...>, Args&&... args) { + return getAllocatorFromPackImp<LArgs const&...>(args...); + } + + template <class ...LArgs> + static CtorAlloc getAllocatorFromPackImp( + typename detail::Identity<LArgs>::type..., CtorAlloc const& alloc) { + return alloc; + } + + const TypeID* args_id; + UsesAllocatorType constructor_called = UA_None; + CtorAlloc allocator; +}; + +template <class Alloc, size_t Arity> +class UsesAllocatorV1 : public UsesAllocatorTestBase<Alloc> +{ +public: + typedef Alloc allocator_type; + + using Base = UsesAllocatorTestBase<Alloc>; + using CtorAlloc = typename Base::CtorAlloc; + + UsesAllocatorV1() : Base(&makeArgumentID<>()) {} + + // Non-Uses Allocator Ctor + template <class ...Args, EnableIfB<sizeof...(Args) == Arity> = false> + UsesAllocatorV1(Args&&... args) : Base(&makeArgumentID<Args&&...>()) {}; + + // Uses Allocator Arg Ctor + template <class ...Args> + UsesAllocatorV1(std::allocator_arg_t tag, CtorAlloc const & a, Args&&... args) + : Base(tag, a, std::forward<Args>(args)...) + { } + + // BLOWS UP: Uses Allocator Last Ctor + template <class _First, class ...Args, EnableIfB<sizeof...(Args) == Arity> _Dummy = false> + constexpr UsesAllocatorV1(_First&& __first, Args&&... args) + { + static_assert(!std::is_same<_First, _First>::value, ""); + } +}; + + +template <class Alloc, size_t Arity> +class UsesAllocatorV2 : public UsesAllocatorTestBase<Alloc> +{ +public: + typedef Alloc allocator_type; + + using Base = UsesAllocatorTestBase<Alloc>; + using CtorAlloc = typename Base::CtorAlloc; + + UsesAllocatorV2() : Base(&makeArgumentID<>()) {} + + // Non-Uses Allocator Ctor + template <class ...Args, EnableIfB<sizeof...(Args) == Arity> = false> + UsesAllocatorV2(Args&&... args) : Base(&makeArgumentID<Args&&...>()) {}; + + // Uses Allocator Last Ctor + template <class ...Args, EnableIfB<sizeof...(Args) == Arity + 1> = false> + UsesAllocatorV2(Args&&... args) + : Base(AllocLastTag{}, std::forward<Args>(args)...) + {} +}; + +template <class Alloc, size_t Arity> +class UsesAllocatorV3 : public UsesAllocatorTestBase<Alloc> +{ +public: + typedef Alloc allocator_type; + + using Base = UsesAllocatorTestBase<Alloc>; + using CtorAlloc = typename Base::CtorAlloc; + + UsesAllocatorV3() : Base(&makeArgumentID<>()) {} + + // Non-Uses Allocator Ctor + template <class ...Args, EnableIfB<sizeof...(Args) == Arity> = false> + UsesAllocatorV3(Args&&... args) : Base(&makeArgumentID<Args&&...>()) {}; + + // Uses Allocator Arg Ctor + template <class ...Args> + UsesAllocatorV3(std::allocator_arg_t tag, CtorAlloc const& alloc, Args&&... args) + : Base(tag, alloc, std::forward<Args>(args)...) + {} + + // Uses Allocator Last Ctor + template <class ...Args, EnableIfB<sizeof...(Args) == Arity + 1> = false> + UsesAllocatorV3(Args&&... args) + : Base(AllocLastTag{}, std::forward<Args>(args)...) + {} +}; + +template <class Alloc, size_t Arity> +class NotUsesAllocator : public UsesAllocatorTestBase<Alloc> +{ +public: + // no allocator_type typedef provided + + using Base = UsesAllocatorTestBase<Alloc>; + using CtorAlloc = typename Base::CtorAlloc; + + NotUsesAllocator() : Base(&makeArgumentID<>()) {} + + // Non-Uses Allocator Ctor + template <class ...Args, EnableIfB<sizeof...(Args) == Arity> = false> + NotUsesAllocator(Args&&... args) : Base(&makeArgumentID<Args&&...>()) {}; + + // Uses Allocator Arg Ctor + template <class ...Args> + NotUsesAllocator(std::allocator_arg_t tag, CtorAlloc const& alloc, Args&&... args) + : Base(tag, alloc, std::forward<Args>(args)...) + {} + + // Uses Allocator Last Ctor + template <class ...Args, EnableIfB<sizeof...(Args) == Arity + 1> = false> + NotUsesAllocator(Args&&... args) + : Base(AllocLastTag{}, std::forward<Args>(args)...) + {} +}; + +#endif /* USES_ALLOC_TYPES_HPP */ |