diff options
author | Stefan <29021710+Saalvage@users.noreply.github.com> | 2022-04-28 03:47:24 +0300 |
---|---|---|
committer | GitHub <noreply@github.com> | 2022-04-28 03:47:24 +0300 |
commit | e8ba771d4b4cf90cb99e011ef01059dd6533d39c (patch) | |
tree | f035dbd7d8e6f6e918a4da3182f5cb44f2890ac7 | |
parent | eb8b04a2b1db23ea5665cd8b5cbe859beb0ef80d (diff) |
Refactor stringification (#585)
* matcher-like nan check
* Remove superfluous extern template declarations
* Add explicit template parameters
* Correct template instantiation
* Fix test includes
* class -> struct
* Correctly instantiate
* Oops
* Try fix interface
* Add MinGW exception
* Add info regarding interface decl and def
* Adjust docs
* Remove accidental paste in comment
* First draft
* operator<< => StringStream (for now)
* Forward declare cstr output operator
* Remove unnecessary String constructor
* Port more stuff to streams
* Remove std::string stringification (it was broken anyways)
* Remove anonymous namespace for the time being
* Revert "Remove anonymous namespace for the time being"
This reverts commit ec2819c44bdb647546108d29b135720083ded48c.
* Move toStream to prevent disabling
* Restore customization points
* Remove superfluous const char* catcher
* Merge branch 'dev' into fix-string
* Better IsNaN stringification
* Reset doctest
* We're getting somewhere!
* size_t -> unsigned long
* Fix nullptr handling
* Why is it selecting the template over the overload??
* Reduce template count
* Forward declare cstr output operator (again)
* Fix pointer stringification
* Add flag that forces custom stringification methods to be provided (#595)
* Add flag that forces custom stringification methods to be provided
* Add docs
* Add IsNaN operator! (#603)
* Add IsNaN operator!
* Docs
* More concise impl
* Optimized floating point stringification
* Remove float stringification override
* unsigned long -> size_t where appropriate
* Automatic type stringification with optional overrides
* Fix type stringification
* Add manual short override to fix tests
* Add tests
* insertion fix?
* Make operator<< static
* Clean up fake type traits
* Try fix stl warnings
* Reintroduce deferred_false
* Work around dumb VS15 shit
* Oops
* Yet another MSVS2015 workaround
* Fix #618
* Doing ungodly things to make MSVS2015 work
* Oops
* rerun tests
* Rerun tests
* Fix #618 by removing string_view
* Remove incorrect restrictions on <string> inclusion
* Add String::EMPTY
* Replace String::EMPTY with static EMPTY_STRING in order to avoid SIOF
* Revert "Add String::EMPTY"
This reverts commit 8856a220596398f27e11a031cedda352f067cbf8.
Revert "Replace String::EMPTY with static EMPTY_STRING in order to avoid SIOF"
This reverts commit 83d3c4f45dde09038d13e77379ea3b40843ce37f.
23 files changed, 1030 insertions, 1003 deletions
diff --git a/doc/markdown/assertions.md b/doc/markdown/assertions.md index ef908fb8..41d90a46 100644 --- a/doc/markdown/assertions.md +++ b/doc/markdown/assertions.md @@ -176,6 +176,8 @@ CHECK(std::isnan(performComputation()); // does not capture the result of the ca CHECK(doctest::IsNaN(performComputation()); // captures the result! ``` +`IsNaN` is able to capture the value, even if negated via `!`. + -------- - Check out the [**example**](../../examples/all_features/assertion_macros.cpp) which shows many of these macros diff --git a/doc/markdown/configuration.md b/doc/markdown/configuration.md index de1c4b3e..6b340f2c 100644 --- a/doc/markdown/configuration.md +++ b/doc/markdown/configuration.md @@ -12,6 +12,7 @@ Defining something ```globally``` means for every source file of the binary (exe - [**```DOCTEST_CONFIG_IMPLEMENTATION_IN_DLL```**](#doctest_config_implementation_in_dll) - [**```DOCTEST_CONFIG_NO_SHORT_MACRO_NAMES```**](#doctest_config_no_short_macro_names) - [**```DOCTEST_CONFIG_TREAT_CHAR_STAR_AS_STRING```**](#doctest_config_treat_char_star_as_string) +- [**```DOCTEST_CONFIG_REQUIRE_STRINGIFICATION_FOR_ALL_USED_TYPES```**](#doctest_config_require_stringification_for_all_used_types) - [**```DOCTEST_CONFIG_SUPER_FAST_ASSERTS```**](#doctest_config_super_fast_asserts) - [**```DOCTEST_CONFIG_USE_STD_HEADERS```**](#doctest_config_use_std_headers) - [**```DOCTEST_CONFIG_VOID_CAST_EXPRESSIONS```**](#doctest_config_void_cast_expressions) @@ -82,6 +83,12 @@ By default ```char*``` is being treated as a pointer. With this option comparing This should be defined globally. +### **```DOCTEST_CONFIG_REQUIRE_STRINGIFICATION_FOR_ALL_USED_TYPES```** + +By default if stringification is not available for a type, it is simply printed as `{?}`. By enabling this flag, whenever a type is used in an assert that does not provide stringification, the compilation is stopped. + +This can be defined both globally and in specific source files only. + ### **```DOCTEST_CONFIG_SUPER_FAST_ASSERTS```** This config option makes the assert macros (except for those dealing with exceptions) compile [**much faster**](benchmarks.md#cost-of-an-assertion-macro)! (31-91% - depending on the type - [**normal**](assertions.md#expression-decomposing-asserts) or [**binary**](assertions.md#binary-and-unary-asserts)) diff --git a/doctest/doctest.h b/doctest/doctest.h index efe5877f..7a756876 100644 --- a/doctest/doctest.h +++ b/doctest/doctest.h @@ -68,6 +68,12 @@ // ideas for the version stuff are taken from here: https://github.com/cxxstuff/cxx_detect +#ifdef _MSC_VER +#define DOCTEST_CPLUSPLUS _MSVC_LANG +#else +#define DOCTEST_CPLUSPLUS __cplusplus +#endif + #define DOCTEST_COMPILER(MAJOR, MINOR, PATCH) ((MAJOR)*10000000 + (MINOR)*100000 + (PATCH)) // GCC/Clang and GCC/MSVC are mutually exclusive, but Clang/MSVC are not because of clang-cl... @@ -367,8 +373,10 @@ DOCTEST_MSVC_SUPPRESS_WARNING(4623) // default constructor was implicitly define #ifndef DOCTEST_CONSTEXPR #if DOCTEST_MSVC && (DOCTEST_MSVC < DOCTEST_COMPILER(19, 0, 0)) #define DOCTEST_CONSTEXPR const +#define DOCTEST_CONSTEXPR_FUNC inline #else // DOCTEST_MSVC #define DOCTEST_CONSTEXPR constexpr +#define DOCTEST_CONSTEXPR_FUNC constexpr #endif // DOCTEST_MSVC #endif // DOCTEST_CONSTEXPR @@ -475,6 +483,7 @@ DOCTEST_MSVC_SUPPRESS_WARNING_WITH_PUSH(4643) namespace std { // NOLINT (cert-dcl58-cpp) typedef decltype(nullptr) nullptr_t; +typedef decltype(sizeof(void*)) size_t; template <class charT> struct char_traits; template <> @@ -482,6 +491,8 @@ struct char_traits<char>; template <class charT, class traits> class basic_ostream; typedef basic_ostream<char, char_traits<char>> ostream; +template<class traits> +basic_ostream<char, traits>& operator<<(basic_ostream<char, traits>&, const char*); template <class charT, class traits> class basic_istream; typedef basic_istream<char, char_traits<char>> istream; @@ -507,8 +518,14 @@ DOCTEST_MSVC_SUPPRESS_WARNING_POP namespace doctest { +using std::size_t; + DOCTEST_INTERFACE extern bool is_running_in_test; +#ifndef DOCTEST_CONFIG_STRING_SIZE_TYPE +#define DOCTEST_CONFIG_STRING_SIZE_TYPE unsigned +#endif + // A 24 byte string class (can be as small as 17 for x64 and 13 for x86) that can hold strings with length // of up to 23 chars on the stack before going on the heap - the last byte of the buffer is used for: // - "is small" bit - the highest bit - if "0" then it is small - otherwise its "1" (128) @@ -521,7 +538,6 @@ DOCTEST_INTERFACE extern bool is_running_in_test; // TODO: // - optimizations - like not deleting memory unnecessarily in operator= and etc. // - resize/reserve/clear -// - substr // - replace // - back/front // - iterator stuff @@ -531,14 +547,18 @@ DOCTEST_INTERFACE extern bool is_running_in_test; // - relational operators as free functions - taking const char* as one of the params class DOCTEST_INTERFACE String { - static const unsigned len = 24; //!OCLINT avoid private static members - static const unsigned last = len - 1; //!OCLINT avoid private static members +public: + using size_type = DOCTEST_CONFIG_STRING_SIZE_TYPE; + +private: + static DOCTEST_CONSTEXPR size_type len = 24; //!OCLINT avoid private static members + static DOCTEST_CONSTEXPR size_type last = len - 1; //!OCLINT avoid private static members struct view // len should be more than sizeof(view) - because of the final byte for flags { char* ptr; - unsigned size; - unsigned capacity; + size_type size; + size_type capacity; }; union @@ -547,23 +567,26 @@ class DOCTEST_INTERFACE String view data; }; - char* allocate(unsigned sz); + char* allocate(size_type sz); bool isOnStack() const { return (buf[last] & 128) == 0; } void setOnHeap(); - void setLast(unsigned in = last); + void setLast(size_type in = last); + void setSize(size_type sz); void copy(const String& other); public: + static DOCTEST_CONSTEXPR size_type npos = static_cast<size_type>(-1); + String(); ~String(); // cppcheck-suppress noExplicitConstructor String(const char* in); - String(const char* in, unsigned in_size); + String(const char* in, size_type in_size); - String(std::istream& in, unsigned in_size); + String(std::istream& in, size_type in_size); String(const String& other); String& operator=(const String& other); @@ -573,8 +596,8 @@ public: String(String&& other); String& operator=(String&& other); - char operator[](unsigned i) const; - char& operator[](unsigned i); + char operator[](size_type i) const; + char& operator[](size_type i); // the only functions I'm willing to leave in the interface - available for inlining const char* c_str() const { return const_cast<String*>(this)->c_str(); } // NOLINT @@ -584,11 +607,19 @@ public: return data.ptr; } - unsigned size() const; - unsigned capacity() const; + size_type size() const; + size_type capacity() const; + + String substr(size_type pos, size_type len = npos) &&; + String substr(size_type pos, size_type len = npos) const &; + + size_type find(char ch, size_type pos = 0) const; + size_type rfind(char ch, size_type pos = npos) const; int compare(const char* other, bool no_case = false) const; int compare(const String& other, bool no_case = false) const; + +friend DOCTEST_INTERFACE std::ostream& operator<<(std::ostream& s, const String& in); }; DOCTEST_INTERFACE String operator+(const String& lhs, const String& rhs); @@ -600,8 +631,6 @@ DOCTEST_INTERFACE bool operator>(const String& lhs, const String& rhs); DOCTEST_INTERFACE bool operator<=(const String& lhs, const String& rhs); DOCTEST_INTERFACE bool operator>=(const String& lhs, const String& rhs); -DOCTEST_INTERFACE std::ostream& operator<<(std::ostream& s, const String& in); - class DOCTEST_INTERFACE Contains { public: explicit Contains(const String& string); @@ -871,200 +900,173 @@ struct ContextOptions //!OCLINT too many fields }; namespace detail { - template <bool CONDITION, typename TYPE = void> - struct enable_if - {}; + namespace types { +#ifdef DOCTEST_CONFIG_INCLUDE_TYPE_TRAITS + using namespace std; +#else + template <bool COND, typename T = void> + struct enable_if { }; - template <typename TYPE> - struct enable_if<true, TYPE> - { typedef TYPE type; }; + template <typename T> + struct enable_if<true, T> { using type = T; }; - // clang-format off - template<class T> struct remove_reference { typedef T type; }; - template<class T> struct remove_reference<T&> { typedef T type; }; - template<class T> struct remove_reference<T&&> { typedef T type; }; + struct true_type { static DOCTEST_CONSTEXPR bool value = true; }; + struct false_type { static DOCTEST_CONSTEXPR bool value = false; }; + + template <typename T> struct remove_reference { using type = T; }; + template <typename T> struct remove_reference<T&> { using type = T; }; + template <typename T> struct remove_reference<T&&> { using type = T; }; - template<typename T, typename U = T&&> U declval(int); + template <typename T> struct is_rvalue_reference : false_type { }; + template <typename T> struct is_rvalue_reference<T&&> : true_type { }; - template<typename T> T declval(long); + template<typename T> struct remove_const { using type = T; }; + template <typename T> struct remove_const<const T> { using type = T; }; - template<typename T> auto declval() DOCTEST_NOEXCEPT -> decltype(declval<T>(0)) ; + // Compiler intrinsics + template <typename T> struct is_enum { DOCTEST_CONSTEXPR static bool value = __is_enum(T); }; + template <typename T> struct underlying_type { using type = __underlying_type(T); }; - template<class T> struct is_lvalue_reference { const static bool value=false; }; - template<class T> struct is_lvalue_reference<T&> { const static bool value=true; }; + template <typename T> struct is_pointer : false_type { }; + template <typename T> struct is_pointer<T*> : true_type { }; - template<class T> struct is_rvalue_reference { const static bool value=false; }; - template<class T> struct is_rvalue_reference<T&&> { const static bool value=true; }; + template <typename T> struct is_array : false_type { }; + template <typename T, size_t SIZE> struct is_array<T[SIZE]> : true_type { }; +#endif + } + + // <utility> + template <typename T> + T&& declval(); template <class T> - inline T&& forward(typename remove_reference<T>::type& t) DOCTEST_NOEXCEPT - { + DOCTEST_CONSTEXPR_FUNC T&& forward(typename types::remove_reference<T>::type& t) DOCTEST_NOEXCEPT { return static_cast<T&&>(t); } template <class T> - inline T&& forward(typename remove_reference<T>::type&& t) DOCTEST_NOEXCEPT - { - static_assert(!is_lvalue_reference<T>::value, - "Can not forward an rvalue as an lvalue."); + DOCTEST_CONSTEXPR_FUNC T&& forward(typename types::remove_reference<T>::type&& t) DOCTEST_NOEXCEPT { return static_cast<T&&>(t); } - template<class T> struct remove_const { typedef T type; }; - template<class T> struct remove_const<const T> { typedef T type; }; -#ifdef DOCTEST_CONFIG_INCLUDE_TYPE_TRAITS - template<class T> struct is_enum : public std::is_enum<T> {}; - template<class T> struct underlying_type : public std::underlying_type<T> {}; -#else - // Use compiler intrinsics - template<class T> struct is_enum { DOCTEST_CONSTEXPR static bool value = __is_enum(T); }; - template<class T> struct underlying_type { typedef __underlying_type(T) type; }; -#endif - // clang-format on + template <typename T> + struct deferred_false : types::false_type { }; + +// MSVS 2015 :( +#if defined(_MSC_VER) && _MSC_VER <= 1900 + template <typename T, typename = void> + struct has_global_insertion_operator : types::false_type { }; template <typename T> - struct deferred_false - // cppcheck-suppress unusedStructMember - { static const bool value = false; }; - - namespace has_insertion_operator_impl { - std::ostream &os(); - template<class T> - DOCTEST_REF_WRAP(T) val(); - - template<class, class = void> - struct check { - static DOCTEST_CONSTEXPR bool value = false; - }; + struct has_global_insertion_operator<T, decltype(::operator<<(declval<std::ostream&>(), declval<const T&>()), void())> : types::true_type { }; - template<class T> - struct check<T, decltype(os() << val<T>(), void())> { - static DOCTEST_CONSTEXPR bool value = true; - }; - } // namespace has_insertion_operator_impl + template <typename T, typename = void> + struct has_insertion_operator { static DOCTEST_CONSTEXPR bool value = has_global_insertion_operator<T>::value; }; + + template <typename T, bool global> + struct insert_hack; + + template <typename T> + struct insert_hack<T, true> { + static void insert(std::ostream& os, const T& t) { ::operator<<(os, t); } + }; - template<class T> - using has_insertion_operator = has_insertion_operator_impl::check<const T>; + template <typename T> + struct insert_hack<T, false> { + static void insert(std::ostream& os, const T& t) { operator<<(os, t); } + }; + + template <typename T> + using insert_hack_t = insert_hack<T, has_global_insertion_operator<T>::value>; +#else + template <typename T, typename = void> + struct has_insertion_operator : types::false_type { }; +#endif + +template <typename T> +struct has_insertion_operator<T, decltype(operator<<(declval<std::ostream&>(), declval<const T&>()), void())> : types::true_type { }; DOCTEST_INTERFACE std::ostream* tlssPush(); DOCTEST_INTERFACE String tlssPop(); - template <bool C> - struct StringMakerBase - { + struct StringMakerBase { template <typename T> static String convert(const DOCTEST_REF_WRAP(T)) { +#ifdef DOCTEST_CONFIG_REQUIRE_STRINGIFICATION_FOR_ALL_USED_TYPES + static_assert(deferred_false<T>::value, "No stringification detected for type T. See string conversion manual"); +#endif return "{?}"; } }; - // Vector<int> and various type other than pointer or array. - template<typename T> - struct filldata - { - static void fill(std::ostream* stream, const T &in) { - *stream << in; - } - }; - - template<typename T,unsigned long N> - struct filldata<T[N]> - { - static void fill(std::ostream* stream, const T (&in)[N]) { - for (unsigned long i = 0; i < N; i++) { - *stream << in[i]; - } - } - }; - - // Specialized since we don't want the terminating null byte! - template<unsigned long N> - struct filldata<const char[N]> - { - static void fill(std::ostream* stream, const char(&in)[N]) { - *stream << in; - } - }; + template <typename T> + struct filldata; - template<typename T> + template <typename T> void filloss(std::ostream* stream, const T& in) { filldata<T>::fill(stream, in); } - template<typename T,unsigned long N> + template <typename T, size_t N> void filloss(std::ostream* stream, const T (&in)[N]) { // T[N], T(&)[N], T(&&)[N] have same behaviour. // Hence remove reference. - filldata<typename remove_reference<decltype(in)>::type>::fill(stream, in); + filloss<typename types::remove_reference<decltype(in)>::type>(stream, in); + } + + template <typename T> + String toStream(const T& in) { + std::ostream* stream = tlssPush(); + filloss(stream, in); + return tlssPop(); } template <> - struct StringMakerBase<true> - { + struct StringMakerBase<true> { template <typename T> static String convert(const DOCTEST_REF_WRAP(T) in) { - /* When parameter "in" is a null terminated const char* it works. - * When parameter "in" is a T arr[N] without '\0' we can fill the - * stringstream with N objects (T=char).If in is char pointer * - * without '\0' , it would cause segfault - * stepping over unaccessible memory. - */ - - std::ostream* stream = tlssPush(); - filloss(stream, in); - return tlssPop(); + return toStream(in); } }; - - DOCTEST_INTERFACE String rawMemoryToString(const void* object, unsigned size); - - template <typename T> - String rawMemoryToString(const DOCTEST_REF_WRAP(T) object) { - return rawMemoryToString(&object, sizeof(object)); - } - - template <typename T> - const char* type_to_string() { - return "<>"; - } } // namespace detail template <typename T> -struct StringMaker : public detail::StringMakerBase<detail::has_insertion_operator<T>::value> +struct StringMaker : public detail::StringMakerBase< + detail::has_insertion_operator<T>::value || detail::types::is_pointer<T>::value || detail::types::is_array<T>::value> {}; template <typename T> -struct StringMaker<T*> -{ - template <typename U> - static String convert(U* p) { - if(p) - return detail::rawMemoryToString(p); - return "NULL"; - } -}; - -template <typename R, typename C> -struct StringMaker<R C::*> -{ - static String convert(R C::*p) { - if(p) - return detail::rawMemoryToString(p); - return "NULL"; - } -}; +String toString() { +#if DOCTEST_MSVC >= 0 && DOCTEST_CLANG == 0 && DOCTEST_GCC == 0 + String ret = __FUNCSIG__; // class doctest::String __cdecl doctest::toString<TYPE>(void) + String::size_type beginPos = ret.find('<'); + return ret.substr(beginPos + 1, ret.size() - beginPos - static_cast<String::size_type>(sizeof(">(void)"))); +#else + String ret = __PRETTY_FUNCTION__; // doctest::String toString() [with T = TYPE] + String::size_type begin = ret.find('=') + 2; + return ret.substr(begin, ret.size() - begin - 1); +#endif +} -template <typename T, typename detail::enable_if<!detail::is_enum<T>::value, bool>::type = true> +template <typename T, typename detail::types::enable_if<!detail::types::is_enum<T>::value, bool>::type = true> String toString(const DOCTEST_REF_WRAP(T) value) { return StringMaker<T>::convert(value); } #ifdef DOCTEST_CONFIG_TREAT_CHAR_STAR_AS_STRING -DOCTEST_INTERFACE String toString(char* in); DOCTEST_INTERFACE String toString(const char* in); #endif // DOCTEST_CONFIG_TREAT_CHAR_STAR_AS_STRING + +#if DOCTEST_MSVC >= DOCTEST_COMPILER(19, 20, 0) +// see this issue on why this is needed: https://github.com/doctest/doctest/issues/183 +DOCTEST_INTERFACE String toString(const std::string& in); +#endif // VS 2019 + +DOCTEST_INTERFACE String toString(std::nullptr_t); + DOCTEST_INTERFACE String toString(bool in); + DOCTEST_INTERFACE String toString(float in); DOCTEST_INTERFACE String toString(double in); DOCTEST_INTERFACE String toString(double long in); @@ -1072,38 +1074,77 @@ DOCTEST_INTERFACE String toString(double long in); DOCTEST_INTERFACE String toString(char in); DOCTEST_INTERFACE String toString(char signed in); DOCTEST_INTERFACE String toString(char unsigned in); -DOCTEST_INTERFACE String toString(int short in); -DOCTEST_INTERFACE String toString(int short unsigned in); -DOCTEST_INTERFACE String toString(int in); -DOCTEST_INTERFACE String toString(int unsigned in); -DOCTEST_INTERFACE String toString(int long in); -DOCTEST_INTERFACE String toString(int long unsigned in); -DOCTEST_INTERFACE String toString(int long long in); -DOCTEST_INTERFACE String toString(int long long unsigned in); -DOCTEST_INTERFACE String toString(std::nullptr_t in); - -template <typename T, typename detail::enable_if<detail::is_enum<T>::value, bool>::type = true> +DOCTEST_INTERFACE String toString(short in); +DOCTEST_INTERFACE String toString(short unsigned in); +DOCTEST_INTERFACE String toString(signed in); +DOCTEST_INTERFACE String toString(unsigned in); +DOCTEST_INTERFACE String toString(long in); +DOCTEST_INTERFACE String toString(long unsigned in); +DOCTEST_INTERFACE String toString(long long in); +DOCTEST_INTERFACE String toString(long long unsigned in); + +template <typename T, typename detail::types::enable_if<detail::types::is_enum<T>::value, bool>::type = true> String toString(const DOCTEST_REF_WRAP(T) value) { - typedef typename detail::underlying_type<T>::type UT; + typedef typename detail::types::underlying_type<T>::type UT; return toString(static_cast<UT>(value)); } -#if DOCTEST_MSVC >= DOCTEST_COMPILER(19, 20, 0) -// see this issue on why this is needed: https://github.com/doctest/doctest/issues/183 -DOCTEST_INTERFACE String toString(const std::string& in); -#endif // VS 2019 +namespace detail { + template <typename T> + struct filldata + { + static void fill(std::ostream* stream, const T& in) { +#if defined(_MSC_VER) && _MSC_VER <= 1900 + insert_hack_t<T>::insert(*stream, in); +#else + operator<<(*stream, in); +#endif + } + }; -class DOCTEST_INTERFACE Approx + template <typename T, size_t N> + struct filldata<T[N]> { + static void fill(std::ostream* stream, const T(&in)[N]) { + *stream << "["; + for (size_t i = 0; i < N; i++) { + if (i != 0) { *stream << ", "; } + *stream << toString(in[i]); + } + *stream << "]"; + } + }; + + // Specialized since we don't want the terminating null byte! + template <size_t N> + struct filldata<const char[N]> { + static void fill(std::ostream* stream, const char (&in)[N]) { + *stream << String(in, in[N - 1] ? N : N - 1); + } + }; + + template <> + struct filldata<const void*> { + static void fill(std::ostream* stream, const void* in); + }; + + template <typename T> + struct filldata<T*> { + static void fill(std::ostream* stream, const T* in) { + filldata<const void*>::fill(stream, in); + } + }; +} + +struct DOCTEST_INTERFACE Approx { -public: - explicit Approx(double value); + Approx(double value); Approx operator()(double value) const; #ifdef DOCTEST_CONFIG_INCLUDE_TYPE_TRAITS template <typename T> explicit Approx(const T& value, - typename detail::enable_if<std::is_constructible<double, T>::value>::type* = + typename detail::types::enable_if<std::is_constructible<double, T>::value>::type* = static_cast<T*>(nullptr)) { *this = Approx(static_cast<double>(value)); } @@ -1113,7 +1154,7 @@ public: #ifdef DOCTEST_CONFIG_INCLUDE_TYPE_TRAITS template <typename T> - typename detail::enable_if<std::is_constructible<double, T>::value, Approx&>::type epsilon( + typename std::enable_if<std::is_constructible<double, T>::value, Approx&>::type epsilon( const T& newEpsilon) { m_epsilon = static_cast<double>(newEpsilon); return *this; @@ -1124,7 +1165,7 @@ public: #ifdef DOCTEST_CONFIG_INCLUDE_TYPE_TRAITS template <typename T> - typename detail::enable_if<std::is_constructible<double, T>::value, Approx&>::type scale( + typename std::enable_if<std::is_constructible<double, T>::value, Approx&>::type scale( const T& newScale) { m_scale = static_cast<double>(newScale); return *this; @@ -1145,11 +1186,9 @@ public: DOCTEST_INTERFACE friend bool operator> (double lhs, const Approx & rhs); DOCTEST_INTERFACE friend bool operator> (const Approx & lhs, double rhs); - DOCTEST_INTERFACE friend String toString(const Approx& in); - #ifdef DOCTEST_CONFIG_INCLUDE_TYPE_TRAITS #define DOCTEST_APPROX_PREFIX \ - template <typename T> friend typename detail::enable_if<std::is_constructible<double, T>::value, bool>::type + template <typename T> friend typename std::enable_if<std::is_constructible<double, T>::value, bool>::type DOCTEST_APPROX_PREFIX operator==(const T& lhs, const Approx& rhs) { return operator==(double(lhs), rhs); } DOCTEST_APPROX_PREFIX operator==(const Approx& lhs, const T& rhs) { return operator==(rhs, lhs); } @@ -1168,7 +1207,6 @@ public: // clang-format on -private: double m_epsilon; double m_scale; double m_value; @@ -1181,8 +1219,9 @@ DOCTEST_INTERFACE const ContextOptions* getContextOptions(); template <typename F> struct DOCTEST_INTERFACE_DECL IsNaN { - F val; - IsNaN(F f) : val(f) { } + F value; bool flipped; + IsNaN(F f, bool flip = false) : value(f), flipped(flip) { } + IsNaN<F> operator!() const { return { value, !flipped }; } operator bool() const; }; #ifndef __MINGW32__ @@ -1190,9 +1229,9 @@ extern template struct DOCTEST_INTERFACE_DECL IsNaN<float>; extern template struct DOCTEST_INTERFACE_DECL IsNaN<double>; extern template struct DOCTEST_INTERFACE_DECL IsNaN<long double>; #endif -DOCTEST_INTERFACE std::ostream& operator<<(std::ostream& out, IsNaN<float> nanCheck); -DOCTEST_INTERFACE std::ostream& operator<<(std::ostream& out, IsNaN<double> nanCheck); -DOCTEST_INTERFACE std::ostream& operator<<(std::ostream& out, IsNaN<long double> nanCheck); +DOCTEST_INTERFACE String toString(IsNaN<float> in); +DOCTEST_INTERFACE String toString(IsNaN<double> in); +DOCTEST_INTERFACE String toString(IsNaN<double long> in); #ifndef DOCTEST_CONFIG_DISABLE @@ -1263,7 +1302,7 @@ DOCTEST_CLANG_SUPPRESS_WARNING_WITH_PUSH("-Wunused-comparison") return Result(res, stringifyBinaryExpr(lhs, op_str, rhs)); \ return Result(res); \ } \ - template <typename R ,typename enable_if<!doctest::detail::is_rvalue_reference<R>::value, void >::type* = nullptr> \ + template <typename R ,typename types::enable_if<!doctest::detail::types::is_rvalue_reference<R>::value, void >::type* = nullptr> \ DOCTEST_NOINLINE SFINAE_OP(Result,op) operator op(const R& rhs) { \ bool res = op_macro(doctest::detail::forward<const L>(lhs), rhs); \ if(m_at & assertType::is_false) \ @@ -1346,7 +1385,7 @@ DOCTEST_CLANG_SUPPRESS_WARNING_WITH_PUSH("-Wunused-comparison") #ifndef DOCTEST_CONFIG_TREAT_CHAR_STAR_AS_STRING #define DOCTEST_COMPARISON_RETURN_TYPE bool #else // DOCTEST_CONFIG_TREAT_CHAR_STAR_AS_STRING -#define DOCTEST_COMPARISON_RETURN_TYPE typename enable_if<can_use_op<L>::value || can_use_op<R>::value, bool>::type +#define DOCTEST_COMPARISON_RETURN_TYPE typename types::enable_if<can_use_op<L>::value || can_use_op<R>::value, bool>::type // NOLINTNEXTLINE(clang-analyzer-cplusplus.NewDeleteLeaks) inline bool eq(const char* lhs, const char* rhs) { return String(lhs) == String(rhs); } inline bool ne(const char* lhs, const char* rhs) { return String(lhs) != String(rhs); } @@ -1473,7 +1512,7 @@ DOCTEST_CLANG_SUPPRESS_WARNING_POP return Expression_lhs<L>(static_cast<L&&>(operand), m_at); } - template <typename L,typename enable_if<!doctest::detail::is_rvalue_reference<L>::value,void >::type* = nullptr> + template <typename L,typename types::enable_if<!doctest::detail::types::is_rvalue_reference<L>::value,void >::type* = nullptr> Expression_lhs<const L&> operator<<(const L &operand) { return Expression_lhs<const L&>(operand, m_at); } @@ -1506,12 +1545,12 @@ DOCTEST_CLANG_SUPPRESS_WARNING_POP { funcType m_test; // a function pointer to the test case - const char* m_type; // for templated test cases - gets appended to the real name + String m_type; // for templated test cases - gets appended to the real name int m_template_id; // an ID used to distinguish between the different versions of a templated test case String m_full_name; // contains the name (only for templated test cases!) + the template type TestCase(funcType test, const char* file, unsigned line, const TestSuite& test_suite, - const char* type = "", int template_id = -1); + const String& type = String(), int template_id = -1); TestCase(const TestCase& other); @@ -1709,58 +1748,6 @@ DOCTEST_CLANG_SUPPRESS_WARNING_POP DOCTEST_INTERFACE void registerExceptionTranslatorImpl(const IExceptionTranslator* et); - template <bool C> - struct StringStreamBase - { - template <typename T> - static void convert(std::ostream* s, const T& in) { - *s << toString(in); - } - - // always treat char* as a string in this context - no matter - // if DOCTEST_CONFIG_TREAT_CHAR_STAR_AS_STRING is defined - static void convert(std::ostream* s, const char* in) { *s << String(in); } - }; - - template <> - struct StringStreamBase<true> - { - template <typename T> - static void convert(std::ostream* s, const T& in) { - *s << in; - } - }; - - template <typename T> - struct StringStream : public StringStreamBase<has_insertion_operator<T>::value> - {}; - - template <typename T> - void toStream(std::ostream* s, const T& value) { - StringStream<T>::convert(s, value); - } - -#ifdef DOCTEST_CONFIG_TREAT_CHAR_STAR_AS_STRING - DOCTEST_INTERFACE void toStream(std::ostream* s, char* in); - DOCTEST_INTERFACE void toStream(std::ostream* s, const char* in); -#endif // DOCTEST_CONFIG_TREAT_CHAR_STAR_AS_STRING - DOCTEST_INTERFACE void toStream(std::ostream* s, bool in); - DOCTEST_INTERFACE void toStream(std::ostream* s, float in); - DOCTEST_INTERFACE void toStream(std::ostream* s, double in); - DOCTEST_INTERFACE void toStream(std::ostream* s, double long in); - - DOCTEST_INTERFACE void toStream(std::ostream* s, char in); - DOCTEST_INTERFACE void toStream(std::ostream* s, char signed in); - DOCTEST_INTERFACE void toStream(std::ostream* s, char unsigned in); - DOCTEST_INTERFACE void toStream(std::ostream* s, int short in); - DOCTEST_INTERFACE void toStream(std::ostream* s, int short unsigned in); - DOCTEST_INTERFACE void toStream(std::ostream* s, int in); - DOCTEST_INTERFACE void toStream(std::ostream* s, int unsigned in); - DOCTEST_INTERFACE void toStream(std::ostream* s, int long in); - DOCTEST_INTERFACE void toStream(std::ostream* s, int long unsigned in); - DOCTEST_INTERFACE void toStream(std::ostream* s, int long long in); - DOCTEST_INTERFACE void toStream(std::ostream* s, int long long unsigned in); - // ContextScope base class used to allow implementing methods of ContextScope // that don't depend on the template parameter in doctest.cpp. class DOCTEST_INTERFACE ContextScopeBase : public IContextScope { @@ -1802,7 +1789,7 @@ DOCTEST_CLANG_SUPPRESS_WARNING_POP // the preferred way of chaining parameters for stringification template <typename T> MessageBuilder& operator,(const T& in) { - toStream(m_stream, in); + *m_stream << toString(in); return *this; } @@ -2105,7 +2092,7 @@ int registerReporter(const char* name, int priority, bool isReporter) { DOCTEST_CREATE_AND_REGISTER_FUNCTION(DOCTEST_ANONYMOUS(DOCTEST_ANON_FUNC_), decorators) // for registering tests in classes - requires C++17 for inline variables! -#if __cplusplus >= 201703L || (DOCTEST_MSVC >= DOCTEST_COMPILER(19, 12, 0) && _MSVC_LANG >= 201703L) +#if DOCTEST_CPLUSPLUS >= 201703L #define DOCTEST_TEST_CASE_CLASS(decorators) \ DOCTEST_CREATE_AND_REGISTER_FUNCTION_IN_CLASS(DOCTEST_ANONYMOUS(DOCTEST_ANON_FUNC_), \ DOCTEST_ANONYMOUS(DOCTEST_ANON_PROXY_), \ @@ -2121,18 +2108,17 @@ int registerReporter(const char* name, int priority, bool isReporter) { DOCTEST_ANONYMOUS(DOCTEST_ANON_FUNC_), decorators) // for converting types to strings without the <typeinfo> header and demangling -#define DOCTEST_TYPE_TO_STRING_IMPL(...) \ - template <> \ - inline const char* type_to_string<__VA_ARGS__>() { \ - return "<" #__VA_ARGS__ ">"; \ - } -#define DOCTEST_TYPE_TO_STRING(...) \ - namespace doctest { namespace detail { \ - DOCTEST_TYPE_TO_STRING_IMPL(__VA_ARGS__) \ +#define DOCTEST_TYPE_TO_STRING_AS(str, ...) \ + namespace doctest { \ + template <> \ + inline String toString<__VA_ARGS__>() { \ + return str; \ } \ } \ static_assert(true, "") +#define DOCTEST_TYPE_TO_STRING(...) DOCTEST_TYPE_TO_STRING_AS(#__VA_ARGS__, __VA_ARGS__) + #define DOCTEST_TEST_CASE_TEMPLATE_DEFINE_IMPL(dec, T, iter, func) \ template <typename T> \ static void func(); \ @@ -2145,7 +2131,7 @@ int registerReporter(const char* name, int priority, bool isReporter) { iter(const char* file, unsigned line, int index) { \ doctest::detail::regTest(doctest::detail::TestCase(func<Type>, file, line, \ doctest_detail_test_suite_ns::getCurrentTestSuite(), \ - doctest::detail::type_to_string<Type>(), \ + doctest::toString<Type>(), \ int(line) * 1000 + index) \ * dec); \ iter<std::tuple<Rest...>>(file, line, index + 1); \ @@ -2344,8 +2330,8 @@ int registerReporter(const char* name, int priority, bool isReporter) { __LINE__, #expr, #__VA_ARGS__, message); \ try { \ DOCTEST_CAST_TO_VOID(expr) \ - } catch(const typename doctest::detail::remove_const< \ - typename doctest::detail::remove_reference<__VA_ARGS__>::type>::type&) { \ + } catch(const typename doctest::detail::types::remove_const< \ + typename doctest::detail::types::remove_reference<__VA_ARGS__>::type>::type&) {\ DOCTEST_RB.translateException(); \ DOCTEST_RB.m_threw_as = true; \ } catch(...) { DOCTEST_RB.translateException(); } \ @@ -2595,8 +2581,8 @@ int registerReporter(const char* name, int priority, bool isReporter) { DOCTEST_ANONYMOUS(DOCTEST_ANON_FUNC_), name) // for converting types to strings without the <typeinfo> header and demangling +#define DOCTEST_TYPE_TO_STRING_AS(str, ...) static_assert(true, "") #define DOCTEST_TYPE_TO_STRING(...) static_assert(true, "") -#define DOCTEST_TYPE_TO_STRING_IMPL(...) // for typed tests #define DOCTEST_TEST_CASE_TEMPLATE(name, type, ...) \ @@ -2846,6 +2832,7 @@ namespace detail { #define TEST_CASE(name) DOCTEST_TEST_CASE(name) #define TEST_CASE_CLASS(name) DOCTEST_TEST_CASE_CLASS(name) #define TEST_CASE_FIXTURE(x, name) DOCTEST_TEST_CASE_FIXTURE(x, name) +#define TYPE_TO_STRING_AS(str, ...) DOCTEST_TYPE_TO_STRING_AS(str, __VA_ARGS__) #define TYPE_TO_STRING(...) DOCTEST_TYPE_TO_STRING(__VA_ARGS__) #define TEST_CASE_TEMPLATE(name, T, ...) DOCTEST_TEST_CASE_TEMPLATE(name, T, __VA_ARGS__) #define TEST_CASE_TEMPLATE_DEFINE(name, T, id) DOCTEST_TEST_CASE_TEMPLATE_DEFINE(name, T, id) @@ -2983,28 +2970,6 @@ namespace detail { // this is here to clear the 'current test suite' for the current translation unit - at the top DOCTEST_TEST_SUITE_END(); -// add stringification for primitive/fundamental types -namespace doctest { namespace detail { - DOCTEST_TYPE_TO_STRING_IMPL(bool) - DOCTEST_TYPE_TO_STRING_IMPL(float) - DOCTEST_TYPE_TO_STRING_IMPL(double) - DOCTEST_TYPE_TO_STRING_IMPL(long double) - DOCTEST_TYPE_TO_STRING_IMPL(char) - DOCTEST_TYPE_TO_STRING_IMPL(signed char) - DOCTEST_TYPE_TO_STRING_IMPL(unsigned char) -#if !DOCTEST_MSVC || defined(_NATIVE_WCHAR_T_DEFINED) - DOCTEST_TYPE_TO_STRING_IMPL(wchar_t) -#endif // not MSVC or wchar_t support enabled - DOCTEST_TYPE_TO_STRING_IMPL(short int) - DOCTEST_TYPE_TO_STRING_IMPL(unsigned short int) - DOCTEST_TYPE_TO_STRING_IMPL(int) - DOCTEST_TYPE_TO_STRING_IMPL(unsigned int) - DOCTEST_TYPE_TO_STRING_IMPL(long int) - DOCTEST_TYPE_TO_STRING_IMPL(unsigned long int) - DOCTEST_TYPE_TO_STRING_IMPL(long long int) - DOCTEST_TYPE_TO_STRING_IMPL(unsigned long long int) -}} // namespace doctest::detail - #endif // DOCTEST_CONFIG_DISABLE DOCTEST_CLANG_SUPPRESS_WARNING_POP @@ -3116,6 +3081,10 @@ DOCTEST_MAKE_STD_HEADERS_CLEAN_FROM_WARNINGS_ON_WALL_BEGIN #include <cfloat> #include <cctype> #include <cstdint> +#if DOCTEST_MSVC >= DOCTEST_COMPILER(19, 20, 0) +// see this issue on why this is needed: https://github.com/doctest/doctest/issues/183 +#include <string> +#endif // VS 2019 #ifdef DOCTEST_PLATFORM_MAC #include <sys/types.h> @@ -3232,20 +3201,6 @@ namespace { } } - template <typename T> - String fpToString(T value, int precision) { - std::ostringstream oss; - oss << std::setprecision(precision) << std::fixed << value; - std::string d = oss.str(); - size_t i = d.find_last_not_of('0'); - if(i != std::string::npos && i != d.size() - 1) { - if(d[i] == '.') - i++; - d = d.substr(0, i + 1); - } - return d.c_str(); - } - struct Endianness { enum Arch @@ -3266,22 +3221,6 @@ namespace { } // namespace namespace detail { - String rawMemoryToString(const void* object, unsigned size) { - // Reverse order for little endian architectures - int i = 0, end = static_cast<int>(size), inc = 1; - if(Endianness::which() == Endianness::Little) { - i = end - 1; - end = inc = -1; - } - - unsigned const char* bytes = static_cast<unsigned const char*>(object); - std::ostream* oss = tlssPush(); - *oss << "0x" << std::setfill('0') << std::hex; - for(; i != end; i += inc) - *oss << std::setw(2) << static_cast<unsigned>(bytes[i]); - return tlssPop(); - } - DOCTEST_THREAD_LOCAL class { std::vector<std::streampos> stack; @@ -3545,7 +3484,7 @@ typedef timer_large_integer::type ticks_t; #endif // DOCTEST_CONFIG_DISABLE } // namespace detail -char* String::allocate(unsigned sz) { +char* String::allocate(size_type sz) { if (sz <= last) { buf[sz] = '\0'; setLast(last - sz); @@ -3561,7 +3500,11 @@ char* String::allocate(unsigned sz) { } void String::setOnHeap() { *reinterpret_cast<unsigned char*>(&buf[last]) = 128; } -void String::setLast(unsigned in) { buf[last] = char(in); } +void String::setLast(size_type in) { buf[last] = char(in); } +void String::setSize(size_type sz) { + if (isOnStack()) { buf[sz] = '\0'; setLast(last - sz); } + else { data.ptr[sz] = '\0'; data.size = sz; } +} void String::copy(const String& other) { if(other.isOnStack()) { @@ -3585,11 +3528,11 @@ String::~String() { String::String(const char* in) : String(in, strlen(in)) {} -String::String(const char* in, unsigned in_size) { +String::String(const char* in, size_type in_size) { memcpy(allocate(in_size), in, in_size); } -String::String(std::istream& in, unsigned in_size) { +String::String(std::istream& in, size_type in_size) { in.read(allocate(in_size), in_size); } @@ -3607,9 +3550,9 @@ String& String::operator=(const String& other) { } String& String::operator+=(const String& other) { - const unsigned my_old_size = size(); - const unsigned other_size = other.size(); - const unsigned total_size = my_old_size + other_size; + const size_type my_old_size = size(); + const size_type other_size = other.size(); + const size_type total_size = my_old_size + other_size; if(isOnStack()) { if(total_size < len) { // append to the current stack space @@ -3673,30 +3616,60 @@ String& String::operator=(String&& other) { return *this; } -char String::operator[](unsigned i) const { +char String::operator[](size_type i) const { return const_cast<String*>(this)->operator[](i); // NOLINT } -char& String::operator[](unsigned i) { +char& String::operator[](size_type i) { if(isOnStack()) return reinterpret_cast<char*>(buf)[i]; return data.ptr[i]; } DOCTEST_GCC_SUPPRESS_WARNING_WITH_PUSH("-Wmaybe-uninitialized") -unsigned String::size() const { +String::size_type String::size() const { if(isOnStack()) - return last - (unsigned(buf[last]) & 31); // using "last" would work only if "len" is 32 + return last - (size_type(buf[last]) & 31); // using "last" would work only if "len" is 32 return data.size; } DOCTEST_GCC_SUPPRESS_WARNING_POP -unsigned String::capacity() const { +String::size_type String::capacity() const { if(isOnStack()) return len; return data.capacity; } +String String::substr(size_type pos, size_type cnt) && { + cnt = std::min(cnt, size() - 1 - pos); + char* cptr = c_str(); + memmove(cptr, cptr + pos, cnt); + setSize(cnt); + return std::move(*this); +} + +String String::substr(size_type pos, size_type cnt) const & { + cnt = std::min(cnt, size() - 1 - pos); + return String{ c_str() + pos, cnt }; +} + +String::size_type String::find(char ch, size_type pos) const { + const char* begin = c_str(); + const char* end = begin + size(); + const char* it = begin + pos; + for (; it < end && *it != ch; it++); + if (it < end) { return static_cast<size_type>(it - begin); } + else { return npos; } +} + +String::size_type String::rfind(char ch, size_type pos) const { + const char* begin = c_str(); + const char* it = begin + std::min(pos, size() - 1); + for (; it >= begin && *it != ch; it--); + if (it >= begin) { return static_cast<size_type>(it - begin); } + else { return npos; } +} + int String::compare(const char* other, bool no_case) const { if(no_case) return doctest::stricmp(c_str(), other); @@ -3833,42 +3806,50 @@ bool SubcaseSignature::operator<(const SubcaseSignature& other) const { IContextScope::IContextScope() = default; IContextScope::~IContextScope() = default; +namespace detail { + void filldata<const void*>::fill(std::ostream* stream, const void* in) { + if (in) { *stream << in; } + else { *stream << "nullptr"; } + } + + template <typename T> + String toStreamLit(T t) { + std::ostream* os = tlssPush(); + os->operator<<(t); + return tlssPop(); + } +} + #ifdef DOCTEST_CONFIG_TREAT_CHAR_STAR_AS_STRING -String toString(char* in) { return toString(static_cast<const char*>(in)); } // NOLINTNEXTLINE(clang-analyzer-cplusplus.NewDeleteLeaks) String toString(const char* in) { return String("\"") + (in ? in : "{null string}") + "\""; } #endif // DOCTEST_CONFIG_TREAT_CHAR_STAR_AS_STRING -String toString(bool in) { return in ? "true" : "false"; } -String toString(float in) { return fpToString(in, 5) + "f"; } -String toString(double in) { return fpToString(in, 10); } -String toString(double long in) { return fpToString(in, 15); } - -#define DOCTEST_TO_STRING_OVERLOAD(type, fmt) \ - String toString(type in) { \ - char buf[64]; \ - std::sprintf(buf, fmt, in); \ - return buf; \ - } - -DOCTEST_TO_STRING_OVERLOAD(char, "%d") -DOCTEST_TO_STRING_OVERLOAD(char signed, "%d") -DOCTEST_TO_STRING_OVERLOAD(char unsigned, "%u") -DOCTEST_TO_STRING_OVERLOAD(int short, "%d") -DOCTEST_TO_STRING_OVERLOAD(int short unsigned, "%u") -DOCTEST_TO_STRING_OVERLOAD(int, "%d") -DOCTEST_TO_STRING_OVERLOAD(unsigned, "%u") -DOCTEST_TO_STRING_OVERLOAD(int long, "%ld") -DOCTEST_TO_STRING_OVERLOAD(int long unsigned, "%lu") -DOCTEST_TO_STRING_OVERLOAD(int long long, "%lld") -DOCTEST_TO_STRING_OVERLOAD(int long long unsigned, "%llu") - -String toString(std::nullptr_t) { return "NULL"; } #if DOCTEST_MSVC >= DOCTEST_COMPILER(19, 20, 0) // see this issue on why this is needed: https://github.com/doctest/doctest/issues/183 String toString(const std::string& in) { return in.c_str(); } #endif // VS 2019 +String toString(std::nullptr_t) { return "nullptr"; } + +String toString(bool in) { return in ? "true" : "false"; } + +String toString(float in) { return toStreamLit(in); } +String toString(double in) { return toStreamLit(in); } +String toString(double long in) { return toStreamLit(in); } + +String toString(char in) { return toStreamLit(static_cast<signed>(in)); } +String toString(char signed in) { return toStreamLit(static_cast<signed>(in)); } +String toString(char unsigned in) { return toStreamLit(static_cast<unsigned>(in)); } +String toString(short in) { return toStreamLit(in); } +String toString(short unsigned in) { return toStreamLit(in); } +String toString(signed in) { return toStreamLit(in); } +String toString(unsigned in) { return toStreamLit(in); } +String toString(long in) { return toStreamLit(in); } +String toString(long unsigned in) { return toStreamLit(in); } +String toString(long long in) { return toStreamLit(in); } +String toString(long long unsigned in) { return toStreamLit(in); } + Approx::Approx(double value) : m_epsilon(static_cast<double>(std::numeric_limits<float>::epsilon()) * 100) , m_scale(1.0) @@ -3908,7 +3889,6 @@ bool operator>(double lhs, const Approx& rhs) { return lhs > rhs.m_value && lhs bool operator>(const Approx& lhs, double rhs) { return lhs.m_value > rhs && lhs != rhs; } String toString(const Approx& in) { - // NOLINTNEXTLINE(clang-analyzer-cplusplus.NewDeleteLeaks) return "Approx( " + doctest::toString(in.m_value) + " )"; } const ContextOptions* getContextOptions() { return DOCTEST_BRANCH_ON_DISABLED(nullptr, g_cs); } @@ -3916,18 +3896,17 @@ const ContextOptions* getContextOptions() { return DOCTEST_BRANCH_ON_DISABLED(nu DOCTEST_MSVC_SUPPRESS_WARNING_WITH_PUSH(4738) template <typename F> IsNaN<F>::operator bool() const { - return std::isnan(val); + return std::isnan(value) ^ flipped; } DOCTEST_MSVC_SUPPRESS_WARNING_POP template struct DOCTEST_INTERFACE_DEF IsNaN<float>; template struct DOCTEST_INTERFACE_DEF IsNaN<double>; template struct DOCTEST_INTERFACE_DEF IsNaN<long double>; -std::ostream& operator<<(std::ostream& out, IsNaN<float> nanCheck) - { out << nanCheck.val; return out; } -std::ostream& operator<<(std::ostream& out, IsNaN<double> nanCheck) - { out << nanCheck.val; return out; } -std::ostream& operator<<(std::ostream& out, IsNaN<long double> nanCheck) - { out << nanCheck.val; return out; } +template <typename F> +String toString(IsNaN<F> nanCheck) { return String(nanCheck.flipped ? "! " : "") + "IsNaN( " + doctest::toString(nanCheck.value) + " )"; } +String toString(IsNaN<float> nanCheck) { return toString<float>(nanCheck); } +String toString(IsNaN<double> nanCheck) { return toString<double>(nanCheck); } +String toString(IsNaN<double long> nanCheck) { return toString<double long>(nanCheck); } } // namespace doctest @@ -4203,7 +4182,7 @@ namespace detail { } TestCase::TestCase(funcType test, const char* file, unsigned line, const TestSuite& test_suite, - const char* type, int template_id) { + const String& type, int template_id) { m_file = file; m_line = line; m_name = nullptr; // will be later overridden in operator* @@ -4228,10 +4207,8 @@ namespace detail { } DOCTEST_MSVC_SUPPRESS_WARNING_WITH_PUSH(26434) // hides a non-virtual function - DOCTEST_MSVC_SUPPRESS_WARNING(26437) // Do not slice TestCase& TestCase::operator=(const TestCase& other) { - static_cast<TestCaseData&>(*this) = static_cast<const TestCaseData&>(other); - + TestCaseData::operator=(other); m_test = other.m_test; m_type = other.m_type; m_template_id = other.m_template_id; @@ -4247,7 +4224,7 @@ namespace detail { m_name = in; // make a new name with an appended type for templated test case if(m_template_id != -1) { - m_full_name = String(m_name) + m_type; + m_full_name = String(m_name) + "<" + m_type + ">"; // redirect the name to point to the newly constructed full name m_name = m_full_name.c_str(); } @@ -4489,27 +4466,6 @@ namespace detail { getExceptionTranslators().push_back(et); } -#ifdef DOCTEST_CONFIG_TREAT_CHAR_STAR_AS_STRING - void toStream(std::ostream* s, char* in) { *s << in; } - void toStream(std::ostream* s, const char* in) { *s << in; } -#endif // DOCTEST_CONFIG_TREAT_CHAR_STAR_AS_STRING - void toStream(std::ostream* s, bool in) { *s << std::boolalpha << in << std::noboolalpha; } - void toStream(std::ostream* s, float in) { *s << in; } - void toStream(std::ostream* s, double in) { *s << in; } - void toStream(std::ostream* s, double long in) { *s << in; } - - void toStream(std::ostream* s, char in) { *s << in; } - void toStream(std::ostream* s, char signed in) { *s << in; } - void toStream(std::ostream* s, char unsigned in) { *s << in; } - void toStream(std::ostream* s, int short in) { *s << in; } - void toStream(std::ostream* s, int short unsigned in) { *s << in; } - void toStream(std::ostream* s, int in) { *s << in; } - void toStream(std::ostream* s, int unsigned in) { *s << in; } - void toStream(std::ostream* s, int long in) { *s << in; } - void toStream(std::ostream* s, int long unsigned in) { *s << in; } - void toStream(std::ostream* s, int long long in) { *s << in; } - void toStream(std::ostream* s, int long long unsigned in) { *s << in; } - DOCTEST_THREAD_LOCAL std::vector<IContextScope*> g_infoContexts; // for logging with INFO() ContextScopeBase::ContextScopeBase() { diff --git a/doctest/parts/doctest.cpp b/doctest/parts/doctest.cpp index 5d27a041..1115912a 100644 --- a/doctest/parts/doctest.cpp +++ b/doctest/parts/doctest.cpp @@ -95,6 +95,7 @@ DOCTEST_MAKE_STD_HEADERS_CLEAN_FROM_WARNINGS_ON_WALL_BEGIN #include <cfloat> #include <cctype> #include <cstdint> +#include <string> #ifdef DOCTEST_PLATFORM_MAC #include <sys/types.h> @@ -211,20 +212,6 @@ namespace { } } - template <typename T> - String fpToString(T value, int precision) { - std::ostringstream oss; - oss << std::setprecision(precision) << std::fixed << value; - std::string d = oss.str(); - size_t i = d.find_last_not_of('0'); - if(i != std::string::npos && i != d.size() - 1) { - if(d[i] == '.') - i++; - d = d.substr(0, i + 1); - } - return d.c_str(); - } - struct Endianness { enum Arch @@ -245,22 +232,6 @@ namespace { } // namespace namespace detail { - String rawMemoryToString(const void* object, unsigned size) { - // Reverse order for little endian architectures - int i = 0, end = static_cast<int>(size), inc = 1; - if(Endianness::which() == Endianness::Little) { - i = end - 1; - end = inc = -1; - } - - unsigned const char* bytes = static_cast<unsigned const char*>(object); - std::ostream* oss = tlssPush(); - *oss << "0x" << std::setfill('0') << std::hex; - for(; i != end; i += inc) - *oss << std::setw(2) << static_cast<unsigned>(bytes[i]); - return tlssPop(); - } - DOCTEST_THREAD_LOCAL class { std::vector<std::streampos> stack; @@ -524,7 +495,7 @@ typedef timer_large_integer::type ticks_t; #endif // DOCTEST_CONFIG_DISABLE } // namespace detail -char* String::allocate(unsigned sz) { +char* String::allocate(size_type sz) { if (sz <= last) { buf[sz] = '\0'; setLast(last - sz); @@ -540,7 +511,11 @@ char* String::allocate(unsigned sz) { } void String::setOnHeap() { *reinterpret_cast<unsigned char*>(&buf[last]) = 128; } -void String::setLast(unsigned in) { buf[last] = char(in); } +void String::setLast(size_type in) { buf[last] = char(in); } +void String::setSize(size_type sz) { + if (isOnStack()) { buf[sz] = '\0'; setLast(last - sz); } + else { data.ptr[sz] = '\0'; data.size = sz; } +} void String::copy(const String& other) { if(other.isOnStack()) { @@ -564,11 +539,11 @@ String::~String() { String::String(const char* in) : String(in, strlen(in)) {} -String::String(const char* in, unsigned in_size) { +String::String(const char* in, size_type in_size) { memcpy(allocate(in_size), in, in_size); } -String::String(std::istream& in, unsigned in_size) { +String::String(std::istream& in, size_type in_size) { in.read(allocate(in_size), in_size); } @@ -586,9 +561,9 @@ String& String::operator=(const String& other) { } String& String::operator+=(const String& other) { - const unsigned my_old_size = size(); - const unsigned other_size = other.size(); - const unsigned total_size = my_old_size + other_size; + const size_type my_old_size = size(); + const size_type other_size = other.size(); + const size_type total_size = my_old_size + other_size; if(isOnStack()) { if(total_size < len) { // append to the current stack space @@ -652,30 +627,60 @@ String& String::operator=(String&& other) { return *this; } -char String::operator[](unsigned i) const { +char String::operator[](size_type i) const { return const_cast<String*>(this)->operator[](i); // NOLINT } -char& String::operator[](unsigned i) { +char& String::operator[](size_type i) { if(isOnStack()) return reinterpret_cast<char*>(buf)[i]; return data.ptr[i]; } DOCTEST_GCC_SUPPRESS_WARNING_WITH_PUSH("-Wmaybe-uninitialized") -unsigned String::size() const { +String::size_type String::size() const { if(isOnStack()) - return last - (unsigned(buf[last]) & 31); // using "last" would work only if "len" is 32 + return last - (size_type(buf[last]) & 31); // using "last" would work only if "len" is 32 return data.size; } DOCTEST_GCC_SUPPRESS_WARNING_POP -unsigned String::capacity() const { +String::size_type String::capacity() const { if(isOnStack()) return len; return data.capacity; } +String String::substr(size_type pos, size_type cnt) && { + cnt = std::min(cnt, size() - 1 - pos); + char* cptr = c_str(); + memmove(cptr, cptr + pos, cnt); + setSize(cnt); + return std::move(*this); +} + +String String::substr(size_type pos, size_type cnt) const & { + cnt = std::min(cnt, size() - 1 - pos); + return String{ c_str() + pos, cnt }; +} + +String::size_type String::find(char ch, size_type pos) const { + const char* begin = c_str(); + const char* end = begin + size(); + const char* it = begin + pos; + for (; it < end && *it != ch; it++); + if (it < end) { return static_cast<size_type>(it - begin); } + else { return npos; } +} + +String::size_type String::rfind(char ch, size_type pos) const { + const char* begin = c_str(); + const char* it = begin + std::min(pos, size() - 1); + for (; it >= begin && *it != ch; it--); + if (it >= begin) { return static_cast<size_type>(it - begin); } + else { return npos; } +} + int String::compare(const char* other, bool no_case) const { if(no_case) return doctest::stricmp(c_str(), other); @@ -812,42 +817,50 @@ bool SubcaseSignature::operator<(const SubcaseSignature& other) const { IContextScope::IContextScope() = default; IContextScope::~IContextScope() = default; +namespace detail { + void filldata<const void*>::fill(std::ostream* stream, const void* in) { + if (in) { *stream << in; } + else { *stream << "nullptr"; } + } + + template <typename T> + String toStreamLit(T t) { + std::ostream* os = tlssPush(); + os->operator<<(t); + return tlssPop(); + } +} + #ifdef DOCTEST_CONFIG_TREAT_CHAR_STAR_AS_STRING -String toString(char* in) { return toString(static_cast<const char*>(in)); } // NOLINTNEXTLINE(clang-analyzer-cplusplus.NewDeleteLeaks) String toString(const char* in) { return String("\"") + (in ? in : "{null string}") + "\""; } #endif // DOCTEST_CONFIG_TREAT_CHAR_STAR_AS_STRING -String toString(bool in) { return in ? "true" : "false"; } -String toString(float in) { return fpToString(in, 5) + "f"; } -String toString(double in) { return fpToString(in, 10); } -String toString(double long in) { return fpToString(in, 15); } - -#define DOCTEST_TO_STRING_OVERLOAD(type, fmt) \ - String toString(type in) { \ - char buf[64]; \ - std::sprintf(buf, fmt, in); \ - return buf; \ - } - -DOCTEST_TO_STRING_OVERLOAD(char, "%d") -DOCTEST_TO_STRING_OVERLOAD(char signed, "%d") -DOCTEST_TO_STRING_OVERLOAD(char unsigned, "%u") -DOCTEST_TO_STRING_OVERLOAD(int short, "%d") -DOCTEST_TO_STRING_OVERLOAD(int short unsigned, "%u") -DOCTEST_TO_STRING_OVERLOAD(int, "%d") -DOCTEST_TO_STRING_OVERLOAD(unsigned, "%u") -DOCTEST_TO_STRING_OVERLOAD(int long, "%ld") -DOCTEST_TO_STRING_OVERLOAD(int long unsigned, "%lu") -DOCTEST_TO_STRING_OVERLOAD(int long long, "%lld") -DOCTEST_TO_STRING_OVERLOAD(int long long unsigned, "%llu") - -String toString(std::nullptr_t) { return "NULL"; } #if DOCTEST_MSVC >= DOCTEST_COMPILER(19, 20, 0) // see this issue on why this is needed: https://github.com/doctest/doctest/issues/183 String toString(const std::string& in) { return in.c_str(); } #endif // VS 2019 +String toString(std::nullptr_t) { return "nullptr"; } + +String toString(bool in) { return in ? "true" : "false"; } + +String toString(float in) { return toStreamLit(in); } +String toString(double in) { return toStreamLit(in); } +String toString(double long in) { return toStreamLit(in); } + +String toString(char in) { return toStreamLit(static_cast<signed>(in)); } +String toString(char signed in) { return toStreamLit(static_cast<signed>(in)); } +String toString(char unsigned in) { return toStreamLit(static_cast<unsigned>(in)); } +String toString(short in) { return toStreamLit(in); } +String toString(short unsigned in) { return toStreamLit(in); } +String toString(signed in) { return toStreamLit(in); } +String toString(unsigned in) { return toStreamLit(in); } +String toString(long in) { return toStreamLit(in); } +String toString(long unsigned in) { return toStreamLit(in); } +String toString(long long in) { return toStreamLit(in); } +String toString(long long unsigned in) { return toStreamLit(in); } + Approx::Approx(double value) : m_epsilon(static_cast<double>(std::numeric_limits<float>::epsilon()) * 100) , m_scale(1.0) @@ -887,7 +900,6 @@ bool operator>(double lhs, const Approx& rhs) { return lhs > rhs.m_value && lhs bool operator>(const Approx& lhs, double rhs) { return lhs.m_value > rhs && lhs != rhs; } String toString(const Approx& in) { - // NOLINTNEXTLINE(clang-analyzer-cplusplus.NewDeleteLeaks) return "Approx( " + doctest::toString(in.m_value) + " )"; } const ContextOptions* getContextOptions() { return DOCTEST_BRANCH_ON_DISABLED(nullptr, g_cs); } @@ -895,18 +907,17 @@ const ContextOptions* getContextOptions() { return DOCTEST_BRANCH_ON_DISABLED(nu DOCTEST_MSVC_SUPPRESS_WARNING_WITH_PUSH(4738) template <typename F> IsNaN<F>::operator bool() const { - return std::isnan(val); + return std::isnan(value) ^ flipped; } DOCTEST_MSVC_SUPPRESS_WARNING_POP template struct DOCTEST_INTERFACE_DEF IsNaN<float>; template struct DOCTEST_INTERFACE_DEF IsNaN<double>; template struct DOCTEST_INTERFACE_DEF IsNaN<long double>; -std::ostream& operator<<(std::ostream& out, IsNaN<float> nanCheck) - { out << nanCheck.val; return out; } -std::ostream& operator<<(std::ostream& out, IsNaN<double> nanCheck) - { out << nanCheck.val; return out; } -std::ostream& operator<<(std::ostream& out, IsNaN<long double> nanCheck) - { out << nanCheck.val; return out; } +template <typename F> +String toString(IsNaN<F> nanCheck) { return String(nanCheck.flipped ? "! " : "") + "IsNaN( " + doctest::toString(nanCheck.value) + " )"; } +String toString(IsNaN<float> nanCheck) { return toString<float>(nanCheck); } +String toString(IsNaN<double> nanCheck) { return toString<double>(nanCheck); } +String toString(IsNaN<double long> nanCheck) { return toString<double long>(nanCheck); } } // namespace doctest @@ -1182,7 +1193,7 @@ namespace detail { } TestCase::TestCase(funcType test, const char* file, unsigned line, const TestSuite& test_suite, - const char* type, int template_id) { + const String& type, int template_id) { m_file = file; m_line = line; m_name = nullptr; // will be later overridden in operator* @@ -1207,10 +1218,8 @@ namespace detail { } DOCTEST_MSVC_SUPPRESS_WARNING_WITH_PUSH(26434) // hides a non-virtual function - DOCTEST_MSVC_SUPPRESS_WARNING(26437) // Do not slice TestCase& TestCase::operator=(const TestCase& other) { - static_cast<TestCaseData&>(*this) = static_cast<const TestCaseData&>(other); - + TestCaseData::operator=(other); m_test = other.m_test; m_type = other.m_type; m_template_id = other.m_template_id; @@ -1226,7 +1235,7 @@ namespace detail { m_name = in; // make a new name with an appended type for templated test case if(m_template_id != -1) { - m_full_name = String(m_name) + m_type; + m_full_name = String(m_name) + "<" + m_type + ">"; // redirect the name to point to the newly constructed full name m_name = m_full_name.c_str(); } @@ -1468,27 +1477,6 @@ namespace detail { getExceptionTranslators().push_back(et); } -#ifdef DOCTEST_CONFIG_TREAT_CHAR_STAR_AS_STRING - void toStream(std::ostream* s, char* in) { *s << in; } - void toStream(std::ostream* s, const char* in) { *s << in; } -#endif // DOCTEST_CONFIG_TREAT_CHAR_STAR_AS_STRING - void toStream(std::ostream* s, bool in) { *s << std::boolalpha << in << std::noboolalpha; } - void toStream(std::ostream* s, float in) { *s << in; } - void toStream(std::ostream* s, double in) { *s << in; } - void toStream(std::ostream* s, double long in) { *s << in; } - - void toStream(std::ostream* s, char in) { *s << in; } - void toStream(std::ostream* s, char signed in) { *s << in; } - void toStream(std::ostream* s, char unsigned in) { *s << in; } - void toStream(std::ostream* s, int short in) { *s << in; } - void toStream(std::ostream* s, int short unsigned in) { *s << in; } - void toStream(std::ostream* s, int in) { *s << in; } - void toStream(std::ostream* s, int unsigned in) { *s << in; } - void toStream(std::ostream* s, int long in) { *s << in; } - void toStream(std::ostream* s, int long unsigned in) { *s << in; } - void toStream(std::ostream* s, int long long in) { *s << in; } - void toStream(std::ostream* s, int long long unsigned in) { *s << in; } - DOCTEST_THREAD_LOCAL std::vector<IContextScope*> g_infoContexts; // for logging with INFO() ContextScopeBase::ContextScopeBase() { diff --git a/doctest/parts/doctest_fwd.h b/doctest/parts/doctest_fwd.h index 877b24c2..b9a6dcc7 100644 --- a/doctest/parts/doctest_fwd.h +++ b/doctest/parts/doctest_fwd.h @@ -65,6 +65,12 @@ // ideas for the version stuff are taken from here: https://github.com/cxxstuff/cxx_detect +#ifdef _MSC_VER +#define DOCTEST_CPLUSPLUS _MSVC_LANG +#else +#define DOCTEST_CPLUSPLUS __cplusplus +#endif + #define DOCTEST_COMPILER(MAJOR, MINOR, PATCH) ((MAJOR)*10000000 + (MINOR)*100000 + (PATCH)) // GCC/Clang and GCC/MSVC are mutually exclusive, but Clang/MSVC are not because of clang-cl... @@ -364,8 +370,10 @@ DOCTEST_MSVC_SUPPRESS_WARNING(4623) // default constructor was implicitly define #ifndef DOCTEST_CONSTEXPR #if DOCTEST_MSVC && (DOCTEST_MSVC < DOCTEST_COMPILER(19, 0, 0)) #define DOCTEST_CONSTEXPR const +#define DOCTEST_CONSTEXPR_FUNC inline #else // DOCTEST_MSVC #define DOCTEST_CONSTEXPR constexpr +#define DOCTEST_CONSTEXPR_FUNC constexpr #endif // DOCTEST_MSVC #endif // DOCTEST_CONSTEXPR @@ -472,6 +480,7 @@ DOCTEST_MSVC_SUPPRESS_WARNING_WITH_PUSH(4643) namespace std { // NOLINT (cert-dcl58-cpp) typedef decltype(nullptr) nullptr_t; +typedef decltype(sizeof(void*)) size_t; template <class charT> struct char_traits; template <> @@ -479,6 +488,8 @@ struct char_traits<char>; template <class charT, class traits> class basic_ostream; typedef basic_ostream<char, char_traits<char>> ostream; +template<class traits> +basic_ostream<char, traits>& operator<<(basic_ostream<char, traits>&, const char*); template <class charT, class traits> class basic_istream; typedef basic_istream<char, char_traits<char>> istream; @@ -504,8 +515,14 @@ DOCTEST_MSVC_SUPPRESS_WARNING_POP namespace doctest { +using std::size_t; + DOCTEST_INTERFACE extern bool is_running_in_test; +#ifndef DOCTEST_CONFIG_STRING_SIZE_TYPE +#define DOCTEST_CONFIG_STRING_SIZE_TYPE unsigned +#endif + // A 24 byte string class (can be as small as 17 for x64 and 13 for x86) that can hold strings with length // of up to 23 chars on the stack before going on the heap - the last byte of the buffer is used for: // - "is small" bit - the highest bit - if "0" then it is small - otherwise its "1" (128) @@ -518,7 +535,6 @@ DOCTEST_INTERFACE extern bool is_running_in_test; // TODO: // - optimizations - like not deleting memory unnecessarily in operator= and etc. // - resize/reserve/clear -// - substr // - replace // - back/front // - iterator stuff @@ -528,14 +544,18 @@ DOCTEST_INTERFACE extern bool is_running_in_test; // - relational operators as free functions - taking const char* as one of the params class DOCTEST_INTERFACE String { - static const unsigned len = 24; //!OCLINT avoid private static members - static const unsigned last = len - 1; //!OCLINT avoid private static members +public: + using size_type = DOCTEST_CONFIG_STRING_SIZE_TYPE; + +private: + static DOCTEST_CONSTEXPR size_type len = 24; //!OCLINT avoid private static members + static DOCTEST_CONSTEXPR size_type last = len - 1; //!OCLINT avoid private static members struct view // len should be more than sizeof(view) - because of the final byte for flags { char* ptr; - unsigned size; - unsigned capacity; + size_type size; + size_type capacity; }; union @@ -544,23 +564,26 @@ class DOCTEST_INTERFACE String view data; }; - char* allocate(unsigned sz); + char* allocate(size_type sz); bool isOnStack() const { return (buf[last] & 128) == 0; } void setOnHeap(); - void setLast(unsigned in = last); + void setLast(size_type in = last); + void setSize(size_type sz); void copy(const String& other); public: + static DOCTEST_CONSTEXPR size_type npos = static_cast<size_type>(-1); + String(); ~String(); // cppcheck-suppress noExplicitConstructor String(const char* in); - String(const char* in, unsigned in_size); + String(const char* in, size_type in_size); - String(std::istream& in, unsigned in_size); + String(std::istream& in, size_type in_size); String(const String& other); String& operator=(const String& other); @@ -570,8 +593,8 @@ public: String(String&& other); String& operator=(String&& other); - char operator[](unsigned i) const; - char& operator[](unsigned i); + char operator[](size_type i) const; + char& operator[](size_type i); // the only functions I'm willing to leave in the interface - available for inlining const char* c_str() const { return const_cast<String*>(this)->c_str(); } // NOLINT @@ -581,11 +604,19 @@ public: return data.ptr; } - unsigned size() const; - unsigned capacity() const; + size_type size() const; + size_type capacity() const; + + String substr(size_type pos, size_type len = npos) &&; + String substr(size_type pos, size_type len = npos) const &; + + size_type find(char ch, size_type pos = 0) const; + size_type rfind(char ch, size_type pos = npos) const; int compare(const char* other, bool no_case = false) const; int compare(const String& other, bool no_case = false) const; + +friend DOCTEST_INTERFACE std::ostream& operator<<(std::ostream& s, const String& in); }; DOCTEST_INTERFACE String operator+(const String& lhs, const String& rhs); @@ -597,8 +628,6 @@ DOCTEST_INTERFACE bool operator>(const String& lhs, const String& rhs); DOCTEST_INTERFACE bool operator<=(const String& lhs, const String& rhs); DOCTEST_INTERFACE bool operator>=(const String& lhs, const String& rhs); -DOCTEST_INTERFACE std::ostream& operator<<(std::ostream& s, const String& in); - class DOCTEST_INTERFACE Contains { public: explicit Contains(const String& string); @@ -868,200 +897,173 @@ struct ContextOptions //!OCLINT too many fields }; namespace detail { - template <bool CONDITION, typename TYPE = void> - struct enable_if - {}; + namespace types { +#ifdef DOCTEST_CONFIG_INCLUDE_TYPE_TRAITS + using namespace std; +#else + template <bool COND, typename T = void> + struct enable_if { }; + + template <typename T> + struct enable_if<true, T> { using type = T; }; - template <typename TYPE> - struct enable_if<true, TYPE> - { typedef TYPE type; }; + struct true_type { static DOCTEST_CONSTEXPR bool value = true; }; + struct false_type { static DOCTEST_CONSTEXPR bool value = false; }; - // clang-format off - template<class T> struct remove_reference { typedef T type; }; - template<class T> struct remove_reference<T&> { typedef T type; }; - template<class T> struct remove_reference<T&&> { typedef T type; }; + template <typename T> struct remove_reference { using type = T; }; + template <typename T> struct remove_reference<T&> { using type = T; }; + template <typename T> struct remove_reference<T&&> { using type = T; }; - template<typename T, typename U = T&&> U declval(int); + template <typename T> struct is_rvalue_reference : false_type { }; + template <typename T> struct is_rvalue_reference<T&&> : true_type { }; - template<typename T> T declval(long); + template<typename T> struct remove_const { using type = T; }; + template <typename T> struct remove_const<const T> { using type = T; }; - template<typename T> auto declval() DOCTEST_NOEXCEPT -> decltype(declval<T>(0)) ; + // Compiler intrinsics + template <typename T> struct is_enum { DOCTEST_CONSTEXPR static bool value = __is_enum(T); }; + template <typename T> struct underlying_type { using type = __underlying_type(T); }; - template<class T> struct is_lvalue_reference { const static bool value=false; }; - template<class T> struct is_lvalue_reference<T&> { const static bool value=true; }; + template <typename T> struct is_pointer : false_type { }; + template <typename T> struct is_pointer<T*> : true_type { }; - template<class T> struct is_rvalue_reference { const static bool value=false; }; - template<class T> struct is_rvalue_reference<T&&> { const static bool value=true; }; + template <typename T> struct is_array : false_type { }; + template <typename T, size_t SIZE> struct is_array<T[SIZE]> : true_type { }; +#endif + } + + // <utility> + template <typename T> + T&& declval(); template <class T> - inline T&& forward(typename remove_reference<T>::type& t) DOCTEST_NOEXCEPT - { + DOCTEST_CONSTEXPR_FUNC T&& forward(typename types::remove_reference<T>::type& t) DOCTEST_NOEXCEPT { return static_cast<T&&>(t); } template <class T> - inline T&& forward(typename remove_reference<T>::type&& t) DOCTEST_NOEXCEPT - { - static_assert(!is_lvalue_reference<T>::value, - "Can not forward an rvalue as an lvalue."); + DOCTEST_CONSTEXPR_FUNC T&& forward(typename types::remove_reference<T>::type&& t) DOCTEST_NOEXCEPT { return static_cast<T&&>(t); } - template<class T> struct remove_const { typedef T type; }; - template<class T> struct remove_const<const T> { typedef T type; }; -#ifdef DOCTEST_CONFIG_INCLUDE_TYPE_TRAITS - template<class T> struct is_enum : public std::is_enum<T> {}; - template<class T> struct underlying_type : public std::underlying_type<T> {}; -#else - // Use compiler intrinsics - template<class T> struct is_enum { DOCTEST_CONSTEXPR static bool value = __is_enum(T); }; - template<class T> struct underlying_type { typedef __underlying_type(T) type; }; -#endif - // clang-format on + template <typename T> + struct deferred_false : types::false_type { }; + +// MSVS 2015 :( +#if defined(_MSC_VER) && _MSC_VER <= 1900 + template <typename T, typename = void> + struct has_global_insertion_operator : types::false_type { }; template <typename T> - struct deferred_false - // cppcheck-suppress unusedStructMember - { static const bool value = false; }; - - namespace has_insertion_operator_impl { - std::ostream &os(); - template<class T> - DOCTEST_REF_WRAP(T) val(); - - template<class, class = void> - struct check { - static DOCTEST_CONSTEXPR bool value = false; - }; + struct has_global_insertion_operator<T, decltype(::operator<<(declval<std::ostream&>(), declval<const T&>()), void())> : types::true_type { }; - template<class T> - struct check<T, decltype(os() << val<T>(), void())> { - static DOCTEST_CONSTEXPR bool value = true; - }; - } // namespace has_insertion_operator_impl + template <typename T, typename = void> + struct has_insertion_operator { static DOCTEST_CONSTEXPR bool value = has_global_insertion_operator<T>::value; }; + + template <typename T, bool global> + struct insert_hack; + + template <typename T> + struct insert_hack<T, true> { + static void insert(std::ostream& os, const T& t) { ::operator<<(os, t); } + }; + + template <typename T> + struct insert_hack<T, false> { + static void insert(std::ostream& os, const T& t) { operator<<(os, t); } + }; + + template <typename T> + using insert_hack_t = insert_hack<T, has_global_insertion_operator<T>::value>; +#else + template <typename T, typename = void> + struct has_insertion_operator : types::false_type { }; +#endif - template<class T> - using has_insertion_operator = has_insertion_operator_impl::check<const T>; +template <typename T> +struct has_insertion_operator<T, decltype(operator<<(declval<std::ostream&>(), declval<const T&>()), void())> : types::true_type { }; DOCTEST_INTERFACE std::ostream* tlssPush(); DOCTEST_INTERFACE String tlssPop(); - template <bool C> - struct StringMakerBase - { + struct StringMakerBase { template <typename T> static String convert(const DOCTEST_REF_WRAP(T)) { +#ifdef DOCTEST_CONFIG_REQUIRE_STRINGIFICATION_FOR_ALL_USED_TYPES + static_assert(deferred_false<T>::value, "No stringification detected for type T. See string conversion manual"); +#endif return "{?}"; } }; - // Vector<int> and various type other than pointer or array. - template<typename T> - struct filldata - { - static void fill(std::ostream* stream, const T &in) { - *stream << in; - } - }; - - template<typename T,unsigned long N> - struct filldata<T[N]> - { - static void fill(std::ostream* stream, const T (&in)[N]) { - for (unsigned long i = 0; i < N; i++) { - *stream << in[i]; - } - } - }; - - // Specialized since we don't want the terminating null byte! - template<unsigned long N> - struct filldata<const char[N]> - { - static void fill(std::ostream* stream, const char(&in)[N]) { - *stream << in; - } - }; + template <typename T> + struct filldata; - template<typename T> + template <typename T> void filloss(std::ostream* stream, const T& in) { filldata<T>::fill(stream, in); } - template<typename T,unsigned long N> + template <typename T, size_t N> void filloss(std::ostream* stream, const T (&in)[N]) { // T[N], T(&)[N], T(&&)[N] have same behaviour. // Hence remove reference. - filldata<typename remove_reference<decltype(in)>::type>::fill(stream, in); + filloss<typename types::remove_reference<decltype(in)>::type>(stream, in); + } + + template <typename T> + String toStream(const T& in) { + std::ostream* stream = tlssPush(); + filloss(stream, in); + return tlssPop(); } template <> - struct StringMakerBase<true> - { + struct StringMakerBase<true> { template <typename T> static String convert(const DOCTEST_REF_WRAP(T) in) { - /* When parameter "in" is a null terminated const char* it works. - * When parameter "in" is a T arr[N] without '\0' we can fill the - * stringstream with N objects (T=char).If in is char pointer * - * without '\0' , it would cause segfault - * stepping over unaccessible memory. - */ - - std::ostream* stream = tlssPush(); - filloss(stream, in); - return tlssPop(); + return toStream(in); } }; - - DOCTEST_INTERFACE String rawMemoryToString(const void* object, unsigned size); - - template <typename T> - String rawMemoryToString(const DOCTEST_REF_WRAP(T) object) { - return rawMemoryToString(&object, sizeof(object)); - } - - template <typename T> - const char* type_to_string() { - return "<>"; - } } // namespace detail template <typename T> -struct StringMaker : public detail::StringMakerBase<detail::has_insertion_operator<T>::value> +struct StringMaker : public detail::StringMakerBase< + detail::has_insertion_operator<T>::value || detail::types::is_pointer<T>::value || detail::types::is_array<T>::value> {}; template <typename T> -struct StringMaker<T*> -{ - template <typename U> - static String convert(U* p) { - if(p) - return detail::rawMemoryToString(p); - return "NULL"; - } -}; - -template <typename R, typename C> -struct StringMaker<R C::*> -{ - static String convert(R C::*p) { - if(p) - return detail::rawMemoryToString(p); - return "NULL"; - } -}; +String toString() { +#if DOCTEST_MSVC >= 0 && DOCTEST_CLANG == 0 && DOCTEST_GCC == 0 + String ret = __FUNCSIG__; // class doctest::String __cdecl doctest::toString<TYPE>(void) + String::size_type beginPos = ret.find('<'); + return ret.substr(beginPos + 1, ret.size() - beginPos - static_cast<String::size_type>(sizeof(">(void)"))); +#else + String ret = __PRETTY_FUNCTION__; // doctest::String toString() [with T = TYPE] + String::size_type begin = ret.find('=') + 2; + return ret.substr(begin, ret.size() - begin - 1); +#endif +} -template <typename T, typename detail::enable_if<!detail::is_enum<T>::value, bool>::type = true> +template <typename T, typename detail::types::enable_if<!detail::types::is_enum<T>::value, bool>::type = true> String toString(const DOCTEST_REF_WRAP(T) value) { return StringMaker<T>::convert(value); } #ifdef DOCTEST_CONFIG_TREAT_CHAR_STAR_AS_STRING -DOCTEST_INTERFACE String toString(char* in); DOCTEST_INTERFACE String toString(const char* in); #endif // DOCTEST_CONFIG_TREAT_CHAR_STAR_AS_STRING + +#if DOCTEST_MSVC >= DOCTEST_COMPILER(19, 20, 0) +// see this issue on why this is needed: https://github.com/doctest/doctest/issues/183 +DOCTEST_INTERFACE String toString(const std::string& in); +#endif // VS 2019 + +DOCTEST_INTERFACE String toString(std::nullptr_t); + DOCTEST_INTERFACE String toString(bool in); + DOCTEST_INTERFACE String toString(float in); DOCTEST_INTERFACE String toString(double in); DOCTEST_INTERFACE String toString(double long in); @@ -1069,38 +1071,77 @@ DOCTEST_INTERFACE String toString(double long in); DOCTEST_INTERFACE String toString(char in); DOCTEST_INTERFACE String toString(char signed in); DOCTEST_INTERFACE String toString(char unsigned in); -DOCTEST_INTERFACE String toString(int short in); -DOCTEST_INTERFACE String toString(int short unsigned in); -DOCTEST_INTERFACE String toString(int in); -DOCTEST_INTERFACE String toString(int unsigned in); -DOCTEST_INTERFACE String toString(int long in); -DOCTEST_INTERFACE String toString(int long unsigned in); -DOCTEST_INTERFACE String toString(int long long in); -DOCTEST_INTERFACE String toString(int long long unsigned in); -DOCTEST_INTERFACE String toString(std::nullptr_t in); - -template <typename T, typename detail::enable_if<detail::is_enum<T>::value, bool>::type = true> +DOCTEST_INTERFACE String toString(short in); +DOCTEST_INTERFACE String toString(short unsigned in); +DOCTEST_INTERFACE String toString(signed in); +DOCTEST_INTERFACE String toString(unsigned in); +DOCTEST_INTERFACE String toString(long in); +DOCTEST_INTERFACE String toString(long unsigned in); +DOCTEST_INTERFACE String toString(long long in); +DOCTEST_INTERFACE String toString(long long unsigned in); + +template <typename T, typename detail::types::enable_if<detail::types::is_enum<T>::value, bool>::type = true> String toString(const DOCTEST_REF_WRAP(T) value) { - typedef typename detail::underlying_type<T>::type UT; + typedef typename detail::types::underlying_type<T>::type UT; return toString(static_cast<UT>(value)); } -#if DOCTEST_MSVC >= DOCTEST_COMPILER(19, 20, 0) -// see this issue on why this is needed: https://github.com/doctest/doctest/issues/183 -DOCTEST_INTERFACE String toString(const std::string& in); -#endif // VS 2019 +namespace detail { + template <typename T> + struct filldata + { + static void fill(std::ostream* stream, const T& in) { +#if defined(_MSC_VER) && _MSC_VER <= 1900 + insert_hack_t<T>::insert(*stream, in); +#else + operator<<(*stream, in); +#endif + } + }; + + template <typename T, size_t N> + struct filldata<T[N]> { + static void fill(std::ostream* stream, const T(&in)[N]) { + *stream << "["; + for (size_t i = 0; i < N; i++) { + if (i != 0) { *stream << ", "; } + *stream << toString(in[i]); + } + *stream << "]"; + } + }; + + // Specialized since we don't want the terminating null byte! + template <size_t N> + struct filldata<const char[N]> { + static void fill(std::ostream* stream, const char (&in)[N]) { + *stream << String(in, in[N - 1] ? N : N - 1); + } + }; + + template <> + struct filldata<const void*> { + static void fill(std::ostream* stream, const void* in); + }; + + template <typename T> + struct filldata<T*> { + static void fill(std::ostream* stream, const T* in) { + filldata<const void*>::fill(stream, in); + } + }; +} -class DOCTEST_INTERFACE Approx +struct DOCTEST_INTERFACE Approx { -public: - explicit Approx(double value); + Approx(double value); Approx operator()(double value) const; #ifdef DOCTEST_CONFIG_INCLUDE_TYPE_TRAITS template <typename T> explicit Approx(const T& value, - typename detail::enable_if<std::is_constructible<double, T>::value>::type* = + typename detail::types::enable_if<std::is_constructible<double, T>::value>::type* = static_cast<T*>(nullptr)) { *this = Approx(static_cast<double>(value)); } @@ -1110,7 +1151,7 @@ public: #ifdef DOCTEST_CONFIG_INCLUDE_TYPE_TRAITS template <typename T> - typename detail::enable_if<std::is_constructible<double, T>::value, Approx&>::type epsilon( + typename std::enable_if<std::is_constructible<double, T>::value, Approx&>::type epsilon( const T& newEpsilon) { m_epsilon = static_cast<double>(newEpsilon); return *this; @@ -1121,7 +1162,7 @@ public: #ifdef DOCTEST_CONFIG_INCLUDE_TYPE_TRAITS template <typename T> - typename detail::enable_if<std::is_constructible<double, T>::value, Approx&>::type scale( + typename std::enable_if<std::is_constructible<double, T>::value, Approx&>::type scale( const T& newScale) { m_scale = static_cast<double>(newScale); return *this; @@ -1142,11 +1183,9 @@ public: DOCTEST_INTERFACE friend bool operator> (double lhs, const Approx & rhs); DOCTEST_INTERFACE friend bool operator> (const Approx & lhs, double rhs); - DOCTEST_INTERFACE friend String toString(const Approx& in); - #ifdef DOCTEST_CONFIG_INCLUDE_TYPE_TRAITS #define DOCTEST_APPROX_PREFIX \ - template <typename T> friend typename detail::enable_if<std::is_constructible<double, T>::value, bool>::type + template <typename T> friend typename std::enable_if<std::is_constructible<double, T>::value, bool>::type DOCTEST_APPROX_PREFIX operator==(const T& lhs, const Approx& rhs) { return operator==(double(lhs), rhs); } DOCTEST_APPROX_PREFIX operator==(const Approx& lhs, const T& rhs) { return operator==(rhs, lhs); } @@ -1165,7 +1204,6 @@ public: // clang-format on -private: double m_epsilon; double m_scale; double m_value; @@ -1178,8 +1216,9 @@ DOCTEST_INTERFACE const ContextOptions* getContextOptions(); template <typename F> struct DOCTEST_INTERFACE_DECL IsNaN { - F val; - IsNaN(F f) : val(f) { } + F value; bool flipped; + IsNaN(F f, bool flip = false) : value(f), flipped(flip) { } + IsNaN<F> operator!() const { return { value, !flipped }; } operator bool() const; }; #ifndef __MINGW32__ @@ -1187,9 +1226,9 @@ extern template struct DOCTEST_INTERFACE_DECL IsNaN<float>; extern template struct DOCTEST_INTERFACE_DECL IsNaN<double>; extern template struct DOCTEST_INTERFACE_DECL IsNaN<long double>; #endif -DOCTEST_INTERFACE std::ostream& operator<<(std::ostream& out, IsNaN<float> nanCheck); -DOCTEST_INTERFACE std::ostream& operator<<(std::ostream& out, IsNaN<double> nanCheck); -DOCTEST_INTERFACE std::ostream& operator<<(std::ostream& out, IsNaN<long double> nanCheck); +DOCTEST_INTERFACE String toString(IsNaN<float> in); +DOCTEST_INTERFACE String toString(IsNaN<double> in); +DOCTEST_INTERFACE String toString(IsNaN<double long> in); #ifndef DOCTEST_CONFIG_DISABLE @@ -1260,7 +1299,7 @@ DOCTEST_CLANG_SUPPRESS_WARNING_WITH_PUSH("-Wunused-comparison") return Result(res, stringifyBinaryExpr(lhs, op_str, rhs)); \ return Result(res); \ } \ - template <typename R ,typename enable_if<!doctest::detail::is_rvalue_reference<R>::value, void >::type* = nullptr> \ + template <typename R ,typename types::enable_if<!doctest::detail::types::is_rvalue_reference<R>::value, void >::type* = nullptr> \ DOCTEST_NOINLINE SFINAE_OP(Result,op) operator op(const R& rhs) { \ bool res = op_macro(doctest::detail::forward<const L>(lhs), rhs); \ if(m_at & assertType::is_false) \ @@ -1343,7 +1382,7 @@ DOCTEST_CLANG_SUPPRESS_WARNING_WITH_PUSH("-Wunused-comparison") #ifndef DOCTEST_CONFIG_TREAT_CHAR_STAR_AS_STRING #define DOCTEST_COMPARISON_RETURN_TYPE bool #else // DOCTEST_CONFIG_TREAT_CHAR_STAR_AS_STRING -#define DOCTEST_COMPARISON_RETURN_TYPE typename enable_if<can_use_op<L>::value || can_use_op<R>::value, bool>::type +#define DOCTEST_COMPARISON_RETURN_TYPE typename types::enable_if<can_use_op<L>::value || can_use_op<R>::value, bool>::type // NOLINTNEXTLINE(clang-analyzer-cplusplus.NewDeleteLeaks) inline bool eq(const char* lhs, const char* rhs) { return String(lhs) == String(rhs); } inline bool ne(const char* lhs, const char* rhs) { return String(lhs) != String(rhs); } @@ -1470,7 +1509,7 @@ DOCTEST_CLANG_SUPPRESS_WARNING_POP return Expression_lhs<L>(static_cast<L&&>(operand), m_at); } - template <typename L,typename enable_if<!doctest::detail::is_rvalue_reference<L>::value,void >::type* = nullptr> + template <typename L,typename types::enable_if<!doctest::detail::types::is_rvalue_reference<L>::value,void >::type* = nullptr> Expression_lhs<const L&> operator<<(const L &operand) { return Expression_lhs<const L&>(operand, m_at); } @@ -1503,12 +1542,12 @@ DOCTEST_CLANG_SUPPRESS_WARNING_POP { funcType m_test; // a function pointer to the test case - const char* m_type; // for templated test cases - gets appended to the real name + String m_type; // for templated test cases - gets appended to the real name int m_template_id; // an ID used to distinguish between the different versions of a templated test case String m_full_name; // contains the name (only for templated test cases!) + the template type TestCase(funcType test, const char* file, unsigned line, const TestSuite& test_suite, - const char* type = "", int template_id = -1); + const String& type = String(), int template_id = -1); TestCase(const TestCase& other); @@ -1706,58 +1745,6 @@ DOCTEST_CLANG_SUPPRESS_WARNING_POP DOCTEST_INTERFACE void registerExceptionTranslatorImpl(const IExceptionTranslator* et); - template <bool C> - struct StringStreamBase - { - template <typename T> - static void convert(std::ostream* s, const T& in) { - *s << toString(in); - } - - // always treat char* as a string in this context - no matter - // if DOCTEST_CONFIG_TREAT_CHAR_STAR_AS_STRING is defined - static void convert(std::ostream* s, const char* in) { *s << String(in); } - }; - - template <> - struct StringStreamBase<true> - { - template <typename T> - static void convert(std::ostream* s, const T& in) { - *s << in; - } - }; - - template <typename T> - struct StringStream : public StringStreamBase<has_insertion_operator<T>::value> - {}; - - template <typename T> - void toStream(std::ostream* s, const T& value) { - StringStream<T>::convert(s, value); - } - -#ifdef DOCTEST_CONFIG_TREAT_CHAR_STAR_AS_STRING - DOCTEST_INTERFACE void toStream(std::ostream* s, char* in); - DOCTEST_INTERFACE void toStream(std::ostream* s, const char* in); -#endif // DOCTEST_CONFIG_TREAT_CHAR_STAR_AS_STRING - DOCTEST_INTERFACE void toStream(std::ostream* s, bool in); - DOCTEST_INTERFACE void toStream(std::ostream* s, float in); - DOCTEST_INTERFACE void toStream(std::ostream* s, double in); - DOCTEST_INTERFACE void toStream(std::ostream* s, double long in); - - DOCTEST_INTERFACE void toStream(std::ostream* s, char in); - DOCTEST_INTERFACE void toStream(std::ostream* s, char signed in); - DOCTEST_INTERFACE void toStream(std::ostream* s, char unsigned in); - DOCTEST_INTERFACE void toStream(std::ostream* s, int short in); - DOCTEST_INTERFACE void toStream(std::ostream* s, int short unsigned in); - DOCTEST_INTERFACE void toStream(std::ostream* s, int in); - DOCTEST_INTERFACE void toStream(std::ostream* s, int unsigned in); - DOCTEST_INTERFACE void toStream(std::ostream* s, int long in); - DOCTEST_INTERFACE void toStream(std::ostream* s, int long unsigned in); - DOCTEST_INTERFACE void toStream(std::ostream* s, int long long in); - DOCTEST_INTERFACE void toStream(std::ostream* s, int long long unsigned in); - // ContextScope base class used to allow implementing methods of ContextScope // that don't depend on the template parameter in doctest.cpp. class DOCTEST_INTERFACE ContextScopeBase : public IContextScope { @@ -1799,7 +1786,7 @@ DOCTEST_CLANG_SUPPRESS_WARNING_POP // the preferred way of chaining parameters for stringification template <typename T> MessageBuilder& operator,(const T& in) { - toStream(m_stream, in); + *m_stream << toString(in); return *this; } @@ -2102,7 +2089,7 @@ int registerReporter(const char* name, int priority, bool isReporter) { DOCTEST_CREATE_AND_REGISTER_FUNCTION(DOCTEST_ANONYMOUS(DOCTEST_ANON_FUNC_), decorators) // for registering tests in classes - requires C++17 for inline variables! -#if __cplusplus >= 201703L || (DOCTEST_MSVC >= DOCTEST_COMPILER(19, 12, 0) && _MSVC_LANG >= 201703L) +#if DOCTEST_CPLUSPLUS >= 201703L #define DOCTEST_TEST_CASE_CLASS(decorators) \ DOCTEST_CREATE_AND_REGISTER_FUNCTION_IN_CLASS(DOCTEST_ANONYMOUS(DOCTEST_ANON_FUNC_), \ DOCTEST_ANONYMOUS(DOCTEST_ANON_PROXY_), \ @@ -2118,18 +2105,17 @@ int registerReporter(const char* name, int priority, bool isReporter) { DOCTEST_ANONYMOUS(DOCTEST_ANON_FUNC_), decorators) // for converting types to strings without the <typeinfo> header and demangling -#define DOCTEST_TYPE_TO_STRING_IMPL(...) \ - template <> \ - inline const char* type_to_string<__VA_ARGS__>() { \ - return "<" #__VA_ARGS__ ">"; \ - } -#define DOCTEST_TYPE_TO_STRING(...) \ - namespace doctest { namespace detail { \ - DOCTEST_TYPE_TO_STRING_IMPL(__VA_ARGS__) \ +#define DOCTEST_TYPE_TO_STRING_AS(str, ...) \ + namespace doctest { \ + template <> \ + inline String toString<__VA_ARGS__>() { \ + return str; \ } \ } \ static_assert(true, "") +#define DOCTEST_TYPE_TO_STRING(...) DOCTEST_TYPE_TO_STRING_AS(#__VA_ARGS__, __VA_ARGS__) + #define DOCTEST_TEST_CASE_TEMPLATE_DEFINE_IMPL(dec, T, iter, func) \ template <typename T> \ static void func(); \ @@ -2142,7 +2128,7 @@ int registerReporter(const char* name, int priority, bool isReporter) { iter(const char* file, unsigned line, int index) { \ doctest::detail::regTest(doctest::detail::TestCase(func<Type>, file, line, \ doctest_detail_test_suite_ns::getCurrentTestSuite(), \ - doctest::detail::type_to_string<Type>(), \ + doctest::toString<Type>(), \ int(line) * 1000 + index) \ * dec); \ iter<std::tuple<Rest...>>(file, line, index + 1); \ @@ -2341,8 +2327,8 @@ int registerReporter(const char* name, int priority, bool isReporter) { __LINE__, #expr, #__VA_ARGS__, message); \ try { \ DOCTEST_CAST_TO_VOID(expr) \ - } catch(const typename doctest::detail::remove_const< \ - typename doctest::detail::remove_reference<__VA_ARGS__>::type>::type&) { \ + } catch(const typename doctest::detail::types::remove_const< \ + typename doctest::detail::types::remove_reference<__VA_ARGS__>::type>::type&) {\ DOCTEST_RB.translateException(); \ DOCTEST_RB.m_threw_as = true; \ } catch(...) { DOCTEST_RB.translateException(); } \ @@ -2592,8 +2578,8 @@ int registerReporter(const char* name, int priority, bool isReporter) { DOCTEST_ANONYMOUS(DOCTEST_ANON_FUNC_), name) // for converting types to strings without the <typeinfo> header and demangling +#define DOCTEST_TYPE_TO_STRING_AS(str, ...) static_assert(true, "") #define DOCTEST_TYPE_TO_STRING(...) static_assert(true, "") -#define DOCTEST_TYPE_TO_STRING_IMPL(...) // for typed tests #define DOCTEST_TEST_CASE_TEMPLATE(name, type, ...) \ @@ -2843,6 +2829,7 @@ namespace detail { #define TEST_CASE(name) DOCTEST_TEST_CASE(name) #define TEST_CASE_CLASS(name) DOCTEST_TEST_CASE_CLASS(name) #define TEST_CASE_FIXTURE(x, name) DOCTEST_TEST_CASE_FIXTURE(x, name) +#define TYPE_TO_STRING_AS(str, ...) DOCTEST_TYPE_TO_STRING_AS(str, __VA_ARGS__) #define TYPE_TO_STRING(...) DOCTEST_TYPE_TO_STRING(__VA_ARGS__) #define TEST_CASE_TEMPLATE(name, T, ...) DOCTEST_TEST_CASE_TEMPLATE(name, T, __VA_ARGS__) #define TEST_CASE_TEMPLATE_DEFINE(name, T, id) DOCTEST_TEST_CASE_TEMPLATE_DEFINE(name, T, id) @@ -2980,28 +2967,6 @@ namespace detail { // this is here to clear the 'current test suite' for the current translation unit - at the top DOCTEST_TEST_SUITE_END(); -// add stringification for primitive/fundamental types -namespace doctest { namespace detail { - DOCTEST_TYPE_TO_STRING_IMPL(bool) - DOCTEST_TYPE_TO_STRING_IMPL(float) - DOCTEST_TYPE_TO_STRING_IMPL(double) - DOCTEST_TYPE_TO_STRING_IMPL(long double) - DOCTEST_TYPE_TO_STRING_IMPL(char) - DOCTEST_TYPE_TO_STRING_IMPL(signed char) - DOCTEST_TYPE_TO_STRING_IMPL(unsigned char) -#if !DOCTEST_MSVC || defined(_NATIVE_WCHAR_T_DEFINED) - DOCTEST_TYPE_TO_STRING_IMPL(wchar_t) -#endif // not MSVC or wchar_t support enabled - DOCTEST_TYPE_TO_STRING_IMPL(short int) - DOCTEST_TYPE_TO_STRING_IMPL(unsigned short int) - DOCTEST_TYPE_TO_STRING_IMPL(int) - DOCTEST_TYPE_TO_STRING_IMPL(unsigned int) - DOCTEST_TYPE_TO_STRING_IMPL(long int) - DOCTEST_TYPE_TO_STRING_IMPL(unsigned long int) - DOCTEST_TYPE_TO_STRING_IMPL(long long int) - DOCTEST_TYPE_TO_STRING_IMPL(unsigned long long int) -}} // namespace doctest::detail - #endif // DOCTEST_CONFIG_DISABLE DOCTEST_CLANG_SUPPRESS_WARNING_POP diff --git a/examples/all_features/header.h b/examples/all_features/header.h index 63b50eb5..6a46aec3 100644 --- a/examples/all_features/header.h +++ b/examples/all_features/header.h @@ -26,6 +26,7 @@ REGISTER_EXCEPTION_TRANSLATOR(int& in) { return doctest::toString(in); } +// Removes class on MSVC TYPE_TO_STRING(doctest::String); TEST_CASE_TEMPLATE("template 1", T, char) { diff --git a/examples/all_features/stringification.cpp b/examples/all_features/stringification.cpp index 2add560d..aa3167d8 100644 --- a/examples/all_features/stringification.cpp +++ b/examples/all_features/stringification.cpp @@ -1,7 +1,74 @@ +#ifdef _MSC_VER +__pragma(warning(push)) +__pragma(warning(disable : 4643)) +namespace std { + template <typename> struct char_traits; + template <typename, typename> class basic_ostream; + typedef basic_ostream<char, char_traits<char>> ostream; + template<class TRAITS> + basic_ostream<char, TRAITS>& operator<<(basic_ostream<char, TRAITS>&, const char*); +} +__pragma(warning(pop)) +#else +#include <iostream> +#endif + +namespace N { + struct A { }; + struct B { + friend std::ostream& operator<<(std::ostream& os, const B&) { return os << "B"; } + }; + struct C { }; + static std::ostream& operator<<(std::ostream& os, const C&) { return os << "C"; } +} + +static std::ostream& operator<<(std::ostream& os, const N::A&) { return os << "A"; } + #include <doctest/doctest.h> +#include <utility> + +TEST_CASE("operator<<") { + MESSAGE(N::A{ }); + MESSAGE(N::B{ }); + MESSAGE(N::C{ }); +} + #include "header.h" +// std::move is broken with VS <= 15 +#if defined(_MSC_VER) && _MSC_VER <= 1900 +#define MOVE(...) __VA_ARGS__ +#else +#define MOVE std::move +#endif + +TEST_CASE("no headers") { + char chs[] = { '1', 'a', 's' }; + MESSAGE(chs); CHECK(chs == nullptr); + MESSAGE("1as"); CHECK("1as" == nullptr); + + int ints[] = { 0, 1, 1, 2, 3, 5, 8, 13 }; + MESSAGE(ints); CHECK(ints == nullptr); + MESSAGE(MOVE(ints)); + + char* cptr = reinterpret_cast<char*>(ints + 4); + const char* ccptr = const_cast<const char*>(cptr); + void* vptr = reinterpret_cast<void*>(cptr); + CHECK(doctest::toString(cptr) == doctest::toString(ccptr)); + CHECK(doctest::toString(ccptr) == doctest::toString(vptr)); + + char* cnptr = nullptr; + MESSAGE(cnptr); CHECK(cnptr != nullptr); + + enum Test { + A = 0, B, C = 100, + }; + MESSAGE(A); CHECK(A == C); + + MESSAGE(doctest::toString<int>()); +} + DOCTEST_MAKE_STD_HEADERS_CLEAN_FROM_WARNINGS_ON_WALL_BEGIN #include <string> #include <vector> @@ -18,11 +85,10 @@ namespace std template <typename T> ostream& operator<<(ostream& stream, const vector<T>& in) { stream << "["; - for(size_t i = 0; i < in.size(); ++i) - if(i < in.size() - 1) - stream << in[i] << ", "; - else - stream << in[i]; + for (size_t i = 0; i < in.size(); ++i) { + if (i != 0) { stream << ", "; } + stream << in[i]; + } stream << "]"; return stream; } @@ -32,16 +98,17 @@ ostream& operator<<(ostream& stream, const vector<T>& in) { namespace doctest { template <typename T> -struct StringMaker<std::list<T> > +struct StringMaker<std::list<T>> { static String convert(const std::list<T>& in) { std::ostringstream oss; oss << "["; - for(typename std::list<T>::const_iterator it = in.begin(); it != in.end(); ++it) - oss << *it << ", "; + for (typename std::list<T>::const_iterator it = in.begin(); it != in.end();) { + oss << *it; + if (++it != in.end()) { oss << ", "; } + } oss << "]"; - return oss.str().c_str(); } }; @@ -100,18 +167,23 @@ REGISTER_EXCEPTION_TRANSLATOR(MyTypeInherited<int>& ex) { doctest::toString(ex.two) + ")"; } +#define CHECK_NOT_DEFAULT_STR(var) CHECK(toString(var) != "{?}") + TEST_CASE("all asserts should fail and show how the objects get stringified") { MyTypeInherited<int> bla1; bla1.one = 5; bla1.two = 4u; Bar::Foo f1; + MESSAGE(f1); Bar::Foo f2; CHECK(f1 == f2); // std::string already has an operator<< working with std::ostream std::string dummy = "omg"; + MESSAGE(dummy); + CHECK(dummy == "tralala"); // should fail CHECK("tralala" == dummy); // should fail @@ -120,6 +192,8 @@ TEST_CASE("all asserts should fail and show how the objects get stringified") { vec1.push_back(2); vec1.push_back(3); + MESSAGE(vec1); + std::vector<int> vec2; vec2.push_back(1); vec2.push_back(2); @@ -132,6 +206,8 @@ TEST_CASE("all asserts should fail and show how the objects get stringified") { lst_1.push_back(42); lst_1.push_back(3); + MESSAGE(lst_1); + std::list<int> lst_2; lst_2.push_back(1); lst_2.push_back(2); @@ -147,9 +223,9 @@ TEST_CASE("all asserts should fail and show how the objects get stringified") { CHECK_MESSAGE(s1 == s2, s1, " is not really ", s2); } - CHECK(doctest::IsNaN<double>(0.5)); - CHECK(doctest::IsNaN<float>(std::numeric_limits<float>::infinity())); - // can't test actual nan because it's implementation defined + CHECK_NOT_DEFAULT_STR(doctest::IsNaN<double>(0.5)); + CHECK_NOT_DEFAULT_STR(!doctest::IsNaN<float>(std::numeric_limits<float>::infinity())); + CHECK_NOT_DEFAULT_STR(doctest::IsNaN<double long>(std::numeric_limits<double long>::quiet_NaN())); CHECK("a" == doctest::Contains("aaa")); diff --git a/examples/all_features/templated_test_cases.cpp b/examples/all_features/templated_test_cases.cpp index 5c204a59..7c45f135 100644 --- a/examples/all_features/templated_test_cases.cpp +++ b/examples/all_features/templated_test_cases.cpp @@ -8,6 +8,8 @@ DOCTEST_MAKE_STD_HEADERS_CLEAN_FROM_WARNINGS_ON_WALL_END // NORMAL TEMPLATED TEST CASES // ================================================================================================= +TYPE_TO_STRING_AS("SHORT!!!", short); + TEST_CASE_TEMPLATE("signed integers stuff", T, signed char, short, int) { T var = T(); --var; @@ -47,6 +49,10 @@ struct TypePair typedef second B; }; +TYPE_TO_STRING_AS("Custom name test", TypePair<int, char>); +TYPE_TO_STRING_AS("Other custom name", TypePair<char, int>); +TYPE_TO_STRING(TypePair<bool, int>); + TEST_CASE_TEMPLATE("multiple types", T, TypePair<int, char>, TypePair<char, int>, TypePair<bool, int>) { typedef typename T::A T1; typedef typename T::B T2; diff --git a/examples/all_features/test_output/coverage_maxout.cpp.txt b/examples/all_features/test_output/coverage_maxout.cpp.txt index c0975470..e0c193dd 100644 --- a/examples/all_features/test_output/coverage_maxout.cpp.txt +++ b/examples/all_features/test_output/coverage_maxout.cpp.txt @@ -11,28 +11,10 @@ coverage_maxout.cpp(0): ERROR: CHECK( str.compare("omgomgomg", false) != 0 ) is values: CHECK( 0 != 0 ) logged: should fail -coverage_maxout.cpp(0): ERROR: CHECK( len_is_zero ) is NOT correct! - values: CHECK( false ) +coverage_maxout.cpp(0): ERROR: CHECK_FALSE( isThereAnything ) is NOT correct! + values: CHECK_FALSE( true ) logged: should fail -coverage_maxout.cpp(0): ERROR: CHECK( b == 5 ) is NOT correct! - values: CHECK( Approx( 7.0 ) == 5 ) - -coverage_maxout.cpp(0): ERROR: CHECK( b < 5 ) is NOT correct! - values: CHECK( Approx( 7.0 ) < 5 ) - -coverage_maxout.cpp(0): ERROR: CHECK( b <= 5 ) is NOT correct! - values: CHECK( Approx( 7.0 ) <= 5 ) - -coverage_maxout.cpp(0): ERROR: CHECK( 6 == a ) is NOT correct! - values: CHECK( 6 == Approx( 5.0 ) ) - -coverage_maxout.cpp(0): ERROR: CHECK( 6 < a ) is NOT correct! - values: CHECK( 6 < Approx( 5.0 ) ) - -coverage_maxout.cpp(0): ERROR: CHECK( 6 <= a ) is NOT correct! - values: CHECK( 6 <= Approx( 5.0 ) ) - =============================================================================== coverage_maxout.cpp(0): TEST SUITE: exception related @@ -56,6 +38,6 @@ coverage_maxout.cpp(0): ERROR: test case THREW exception: unknown exception =============================================================================== [doctest] test cases: 4 | 0 passed | 4 failed | -[doctest] assertions: 31 | 22 passed | 9 failed | +[doctest] assertions: 31 | 28 passed | 3 failed | [doctest] Status: FAILURE! Program code. diff --git a/examples/all_features/test_output/coverage_maxout.cpp_junit.txt b/examples/all_features/test_output/coverage_maxout.cpp_junit.txt index f93d418a..8f1e9920 100644 --- a/examples/all_features/test_output/coverage_maxout.cpp_junit.txt +++ b/examples/all_features/test_output/coverage_maxout.cpp_junit.txt @@ -1,6 +1,6 @@ <?xml version="1.0" encoding="UTF-8"?> <testsuites> - <testsuite name="all_features" errors="3" failures="9" tests="31"> + <testsuite name="all_features" errors="3" failures="3" tests="31"> <testcase classname="coverage_maxout.cpp" name="exercising tricky code paths of doctest" status="run"> <failure message="0 != 0" type="CHECK"> coverage_maxout.cpp(0): @@ -16,49 +16,13 @@ CHECK( str.compare("omgomgomg", false) != 0 ) is NOT correct! logged: should fail </failure> - <failure message="false" type="CHECK"> + <failure message="true" type="CHECK_FALSE"> coverage_maxout.cpp(0): -CHECK( len_is_zero ) is NOT correct! - values: CHECK( false ) +CHECK_FALSE( isThereAnything ) is NOT correct! + values: CHECK_FALSE( true ) logged: should fail </failure> - <failure message="Approx( 7.0 ) == 5" type="CHECK"> -coverage_maxout.cpp(0): -CHECK( b == 5 ) is NOT correct! - values: CHECK( Approx( 7.0 ) == 5 ) - - </failure> - <failure message="Approx( 7.0 ) < 5" type="CHECK"> -coverage_maxout.cpp(0): -CHECK( b < 5 ) is NOT correct! - values: CHECK( Approx( 7.0 ) < 5 ) - - </failure> - <failure message="Approx( 7.0 ) <= 5" type="CHECK"> -coverage_maxout.cpp(0): -CHECK( b <= 5 ) is NOT correct! - values: CHECK( Approx( 7.0 ) <= 5 ) - - </failure> - <failure message="6 == Approx( 5.0 )" type="CHECK"> -coverage_maxout.cpp(0): -CHECK( 6 == a ) is NOT correct! - values: CHECK( 6 == Approx( 5.0 ) ) - - </failure> - <failure message="6 < Approx( 5.0 )" type="CHECK"> -coverage_maxout.cpp(0): -CHECK( 6 < a ) is NOT correct! - values: CHECK( 6 < Approx( 5.0 ) ) - - </failure> - <failure message="6 <= Approx( 5.0 )" type="CHECK"> -coverage_maxout.cpp(0): -CHECK( 6 <= a ) is NOT correct! - values: CHECK( 6 <= Approx( 5.0 ) ) - - </failure> </testcase> <testcase classname="coverage_maxout.cpp" name="will end from a std::string exception" status="run"> <error message="exception"> diff --git a/examples/all_features/test_output/coverage_maxout.cpp_xml.txt b/examples/all_features/test_output/coverage_maxout.cpp_xml.txt index 1f81db18..9a9bf100 100644 --- a/examples/all_features/test_output/coverage_maxout.cpp_xml.txt +++ b/examples/all_features/test_output/coverage_maxout.cpp_xml.txt @@ -25,66 +25,18 @@ should fail </Info> </Expression> - <Expression success="false" type="CHECK" filename="coverage_maxout.cpp" line="0"> + <Expression success="false" type="CHECK_FALSE" filename="coverage_maxout.cpp" line="0"> <Original> - len_is_zero + isThereAnything </Original> <Expanded> - false + true </Expanded> <Info> should fail </Info> </Expression> - <Expression success="false" type="CHECK" filename="coverage_maxout.cpp" line="0"> - <Original> - b == 5 - </Original> - <Expanded> - Approx( 7.0 ) == 5 - </Expanded> - </Expression> - <Expression success="false" type="CHECK" filename="coverage_maxout.cpp" line="0"> - <Original> - b < 5 - </Original> - <Expanded> - Approx( 7.0 ) < 5 - </Expanded> - </Expression> - <Expression success="false" type="CHECK" filename="coverage_maxout.cpp" line="0"> - <Original> - b <= 5 - </Original> - <Expanded> - Approx( 7.0 ) <= 5 - </Expanded> - </Expression> - <Expression success="false" type="CHECK" filename="coverage_maxout.cpp" line="0"> - <Original> - 6 == a - </Original> - <Expanded> - 6 == Approx( 5.0 ) - </Expanded> - </Expression> - <Expression success="false" type="CHECK" filename="coverage_maxout.cpp" line="0"> - <Original> - 6 < a - </Original> - <Expanded> - 6 < Approx( 5.0 ) - </Expanded> - </Expression> - <Expression success="false" type="CHECK" filename="coverage_maxout.cpp" line="0"> - <Original> - 6 <= a - </Original> - <Expanded> - 6 <= Approx( 5.0 ) - </Expanded> - </Expression> - <OverallResultsAsserts successes="22" failures="9" test_case_success="false"/> + <OverallResultsAsserts successes="28" failures="3" test_case_success="false"/> </TestCase> </TestSuite> <TestSuite name="exception related"> @@ -107,7 +59,7 @@ <OverallResultsAsserts successes="0" failures="0" test_case_success="false"/> </TestCase> </TestSuite> - <OverallResultsAsserts successes="22" failures="9"/> + <OverallResultsAsserts successes="28" failures="3"/> <OverallResultsTestCases successes="0" failures="4"/> </doctest> Program code. diff --git a/examples/all_features/test_output/filter_2_xml.txt b/examples/all_features/test_output/filter_2_xml.txt index fa6cd147..ff3d9c7c 100644 --- a/examples/all_features/test_output/filter_2_xml.txt +++ b/examples/all_features/test_output/filter_2_xml.txt @@ -32,11 +32,11 @@ <TestCase name="check return values" filename="assertion_macros.cpp" line="0" skipped="true"/> <TestCase name="check return values no print" filename="assertion_macros.cpp" line="0" skipped="true"/> <TestCase name="custom macros" filename="alternative_macros.cpp" line="0" skipped="true"/> + <TestCase name="default construction<SHORT!!!>" filename="templated_test_cases.cpp" line="0" skipped="true"/> <TestCase name="default construction<char>" filename="templated_test_cases.cpp" line="0" skipped="true"/> <TestCase name="default construction<double>" filename="templated_test_cases.cpp" line="0" skipped="true"/> <TestCase name="default construction<double>" filename="templated_test_cases.cpp" line="0" skipped="true"/> <TestCase name="default construction<int>" filename="templated_test_cases.cpp" line="0" skipped="true"/> - <TestCase name="default construction<short int>" filename="templated_test_cases.cpp" line="0" skipped="true"/> <TestCase name="default construction<signed char>" filename="templated_test_cases.cpp" line="0" skipped="true"/> <TestCase name="default construction<unsigned char>" filename="templated_test_cases.cpp" line="0" skipped="true"/> </TestSuite> @@ -78,9 +78,9 @@ <TestSuite> <TestCase name="logging the counter of a loop" filename="logging.cpp" line="0" skipped="true"/> <TestCase name="lots of nested subcases" filename="subcases.cpp" line="0" skipped="true"/> - <TestCase name="multiple types<>" filename="templated_test_cases.cpp" line="0" skipped="true"/> - <TestCase name="multiple types<>" filename="templated_test_cases.cpp" line="0" skipped="true"/> - <TestCase name="multiple types<>" filename="templated_test_cases.cpp" line="0" skipped="true"/> + <TestCase name="multiple types<Custom name test>" filename="templated_test_cases.cpp" line="0" skipped="true"/> + <TestCase name="multiple types<Other custom name>" filename="templated_test_cases.cpp" line="0" skipped="true"/> + <TestCase name="multiple types<TypePair<bool, int>>" filename="templated_test_cases.cpp" line="0" skipped="true"/> <TestCase name="namespace 1 global operator" filename="namespace1.cpp" line="0" skipped="true"/> <TestCase name="namespace 2 friend operator" filename="namespace2.cpp" line="0" skipped="true"/> <TestCase name="namespace 3 member operator" filename="namespace3.cpp" line="0" skipped="true"/> @@ -91,11 +91,15 @@ <TestCase name="namespace 8 friend vs global" filename="namespace8.cpp" line="0" skipped="true"/> <TestCase name="namespace 9 both global" filename="namespace9.cpp" line="0" skipped="true"/> <TestCase name="no checks" filename="no_failures.cpp" line="0" skipped="true"/> + <TestCase name="no headers" filename="stringification.cpp" line="0" skipped="true"/> <TestCase name="normal macros" filename="assertion_macros.cpp" line="0" skipped="true"/> </TestSuite> <TestSuite name="ts1"> <TestCase name="normal test in a test suite from a decorator" filename="test_cases_and_suites.cpp" line="0" skipped="true"/> </TestSuite> + <TestSuite> + <TestCase name="operator<<" filename="stringification.cpp" line="0" skipped="true"/> + </TestSuite> <TestSuite name="scoped test suite"> <TestCase name="part of scoped" filename="test_cases_and_suites.cpp" line="0" skipped="true"/> <TestCase name="part of scoped 2" filename="test_cases_and_suites.cpp" line="0" skipped="true"/> @@ -108,8 +112,8 @@ <TestCase name="should fail and no output" filename="no_failures.cpp" line="0" should_fail="true" skipped="true"/> <TestCase name="should fail and no output" filename="test_cases_and_suites.cpp" line="0" should_fail="true" skipped="true"/> <TestCase name="should fail because of an exception" filename="test_cases_and_suites.cpp" line="0" skipped="true"/> + <TestCase name="signed integers stuff<SHORT!!!>" filename="templated_test_cases.cpp" line="0" skipped="true"/> <TestCase name="signed integers stuff<int>" filename="templated_test_cases.cpp" line="0" skipped="true"/> - <TestCase name="signed integers stuff<short int>" filename="templated_test_cases.cpp" line="0" skipped="true"/> <TestCase name="signed integers stuff<signed char>" filename="templated_test_cases.cpp" line="0" skipped="true"/> <TestCase name="simple check" filename="no_failures.cpp" line="0" skipped="true"/> </TestSuite> diff --git a/examples/all_features/test_output/stringification.cpp.txt b/examples/all_features/test_output/stringification.cpp.txt index 650063ec..9f4cece4 100644 --- a/examples/all_features/test_output/stringification.cpp.txt +++ b/examples/all_features/test_output/stringification.cpp.txt @@ -1,22 +1,73 @@ [doctest] run with "--help" for options =============================================================================== stringification.cpp(0): +TEST CASE: operator<< + +stringification.cpp(0): MESSAGE: A + +stringification.cpp(0): MESSAGE: B + +stringification.cpp(0): MESSAGE: C + +=============================================================================== +stringification.cpp(0): +TEST CASE: no headers + +stringification.cpp(0): MESSAGE: 1as + +stringification.cpp(0): ERROR: CHECK( chs == nullptr ) is NOT correct! + values: CHECK( 1as == nullptr ) + +stringification.cpp(0): MESSAGE: 1as + +stringification.cpp(0): ERROR: CHECK( "1as" == nullptr ) is NOT correct! + values: CHECK( 1as == nullptr ) + +stringification.cpp(0): MESSAGE: [0, 1, 1, 2, 3, 5, 8, 13] + +stringification.cpp(0): ERROR: CHECK( ints == nullptr ) is NOT correct! + values: CHECK( [0, 1, 1, 2, 3, 5, 8, 13] == nullptr ) + +stringification.cpp(0): MESSAGE: [0, 1, 1, 2, 3, 5, 8, 13] + +stringification.cpp(0): MESSAGE: nullptr + +stringification.cpp(0): ERROR: CHECK( cnptr != nullptr ) is NOT correct! + values: CHECK( nullptr != nullptr ) + +stringification.cpp(0): MESSAGE: 0 + +stringification.cpp(0): ERROR: CHECK( A == C ) is NOT correct! + values: CHECK( 0 == 100 ) + +stringification.cpp(0): MESSAGE: int + +=============================================================================== +stringification.cpp(0): TEST CASE: all asserts should fail and show how the objects get stringified +stringification.cpp(0): MESSAGE: Foo{} + stringification.cpp(0): ERROR: CHECK( f1 == f2 ) is NOT correct! values: CHECK( Foo{} == Foo{} ) +stringification.cpp(0): MESSAGE: omg + stringification.cpp(0): ERROR: CHECK( dummy == "tralala" ) is NOT correct! values: CHECK( omg == tralala ) stringification.cpp(0): ERROR: CHECK( "tralala" == dummy ) is NOT correct! values: CHECK( tralala == omg ) +stringification.cpp(0): MESSAGE: [1, 2, 3] + stringification.cpp(0): ERROR: CHECK( vec1 == vec2 ) is NOT correct! values: CHECK( [1, 2, 3] == [1, 2, 4] ) +stringification.cpp(0): MESSAGE: [1, 42, 3] + stringification.cpp(0): ERROR: CHECK( lst_1 == lst_2 ) is NOT correct! - values: CHECK( [1, 42, 3, ] == [1, 2, 666, ] ) + values: CHECK( [1, 42, 3] == [1, 2, 666] ) stringification.cpp(0): ERROR: CHECK( s1 == s2 ) is NOT correct! values: CHECK( MyOtherType: 42 == MyOtherType: 666 ) @@ -27,12 +78,6 @@ stringification.cpp(0): ERROR: CHECK( s1 == s2 ) is NOT correct! logged: s1=MyOtherType: 42 s2=MyOtherType: 666 MyOtherType: 42 is not really MyOtherType: 666 -stringification.cpp(0): ERROR: CHECK( doctest::IsNaN<double>(0.5) ) is NOT correct! - values: CHECK( 0.5 ) - -stringification.cpp(0): ERROR: CHECK( doctest::IsNaN<float>(std::numeric_limits<float>::infinity()) ) is NOT correct! - values: CHECK( inf ) - stringification.cpp(0): ERROR: CHECK( "a" == doctest::Contains("aaa") ) is NOT correct! values: CHECK( a == Contains( aaa ) ) @@ -45,7 +90,7 @@ TEST CASE: a test case that registers an exception translator for int and then stringification.cpp(0): ERROR: test case THREW exception: 5 =============================================================================== -[doctest] test cases: 2 | 0 passed | 2 failed | -[doctest] assertions: 10 | 0 passed | 10 failed | +[doctest] test cases: 4 | 1 passed | 3 failed | +[doctest] assertions: 18 | 5 passed | 13 failed | [doctest] Status: FAILURE! Program code. diff --git a/examples/all_features/test_output/stringification.cpp_junit.txt b/examples/all_features/test_output/stringification.cpp_junit.txt index eac29e12..93cc0769 100644 --- a/examples/all_features/test_output/stringification.cpp_junit.txt +++ b/examples/all_features/test_output/stringification.cpp_junit.txt @@ -1,6 +1,39 @@ <?xml version="1.0" encoding="UTF-8"?> <testsuites> - <testsuite name="all_features" errors="2" failures="10" tests="10"> + <testsuite name="all_features" errors="2" failures="13" tests="18"> + <testcase classname="stringification.cpp" name="operator<<" status="run"/> + <testcase classname="stringification.cpp" name="no headers" status="run"> + <failure message="1as == nullptr" type="CHECK"> +stringification.cpp(0): +CHECK( chs == nullptr ) is NOT correct! + values: CHECK( 1as == nullptr ) + + </failure> + <failure message="1as == nullptr" type="CHECK"> +stringification.cpp(0): +CHECK( "1as" == nullptr ) is NOT correct! + values: CHECK( 1as == nullptr ) + + </failure> + <failure message="[0, 1, 1, 2, 3, 5, 8, 13] == nullptr" type="CHECK"> +stringification.cpp(0): +CHECK( ints == nullptr ) is NOT correct! + values: CHECK( [0, 1, 1, 2, 3, 5, 8, 13] == nullptr ) + + </failure> + <failure message="nullptr != nullptr" type="CHECK"> +stringification.cpp(0): +CHECK( cnptr != nullptr ) is NOT correct! + values: CHECK( nullptr != nullptr ) + + </failure> + <failure message="0 == 100" type="CHECK"> +stringification.cpp(0): +CHECK( A == C ) is NOT correct! + values: CHECK( 0 == 100 ) + + </failure> + </testcase> <testcase classname="stringification.cpp" name="all asserts should fail and show how the objects get stringified" status="run"> <failure message="Foo{} == Foo{}" type="CHECK"> stringification.cpp(0): @@ -26,10 +59,10 @@ CHECK( vec1 == vec2 ) is NOT correct! values: CHECK( [1, 2, 3] == [1, 2, 4] ) </failure> - <failure message="[1, 42, 3, ] == [1, 2, 666, ]" type="CHECK"> + <failure message="[1, 42, 3] == [1, 2, 666]" type="CHECK"> stringification.cpp(0): CHECK( lst_1 == lst_2 ) is NOT correct! - values: CHECK( [1, 42, 3, ] == [1, 2, 666, ] ) + values: CHECK( [1, 42, 3] == [1, 2, 666] ) </failure> <failure message="MyOtherType: 42 == MyOtherType: 666" type="CHECK"> @@ -47,18 +80,6 @@ CHECK( s1 == s2 ) is NOT correct! MyOtherType: 42 is not really MyOtherType: 666 </failure> - <failure message="0.5" type="CHECK"> -stringification.cpp(0): -CHECK( doctest::IsNaN<double>(0.5) ) is NOT correct! - values: CHECK( 0.5 ) - - </failure> - <failure message="inf" type="CHECK"> -stringification.cpp(0): -CHECK( doctest::IsNaN<float>(std::numeric_limits<float>::infinity()) ) is NOT correct! - values: CHECK( inf ) - - </failure> <failure message="a == Contains( aaa )" type="CHECK"> stringification.cpp(0): CHECK( "a" == doctest::Contains("aaa") ) is NOT correct! diff --git a/examples/all_features/test_output/stringification.cpp_xml.txt b/examples/all_features/test_output/stringification.cpp_xml.txt index 066df99f..e4de969a 100644 --- a/examples/all_features/test_output/stringification.cpp_xml.txt +++ b/examples/all_features/test_output/stringification.cpp_xml.txt @@ -2,7 +2,108 @@ <doctest binary="all_features"> <Options order_by="file" rand_seed="324" first="0" last="4294967295" abort_after="0" subcase_filter_levels="2147483647" case_sensitive="false" no_throw="false" no_skip="false"/> <TestSuite> + <TestCase name="operator<<" filename="stringification.cpp" line="0"> + <Message type="WARNING" filename="stringification.cpp" line="0"> + <Text> + A + </Text> + </Message> + <Message type="WARNING" filename="stringification.cpp" line="0"> + <Text> + B + </Text> + </Message> + <Message type="WARNING" filename="stringification.cpp" line="0"> + <Text> + C + </Text> + </Message> + <OverallResultsAsserts successes="0" failures="0" test_case_success="true"/> + </TestCase> + <TestCase name="no headers" filename="stringification.cpp" line="0"> + <Message type="WARNING" filename="stringification.cpp" line="0"> + <Text> + 1as + </Text> + </Message> + <Expression success="false" type="CHECK" filename="stringification.cpp" line="0"> + <Original> + chs == nullptr + </Original> + <Expanded> + 1as == nullptr + </Expanded> + </Expression> + <Message type="WARNING" filename="stringification.cpp" line="0"> + <Text> + 1as + </Text> + </Message> + <Expression success="false" type="CHECK" filename="stringification.cpp" line="0"> + <Original> + "1as" == nullptr + </Original> + <Expanded> + 1as == nullptr + </Expanded> + </Expression> + <Message type="WARNING" filename="stringification.cpp" line="0"> + <Text> + [0, 1, 1, 2, 3, 5, 8, 13] + </Text> + </Message> + <Expression success="false" type="CHECK" filename="stringification.cpp" line="0"> + <Original> + ints == nullptr + </Original> + <Expanded> + [0, 1, 1, 2, 3, 5, 8, 13] == nullptr + </Expanded> + </Expression> + <Message type="WARNING" filename="stringification.cpp" line="0"> + <Text> + [0, 1, 1, 2, 3, 5, 8, 13] + </Text> + </Message> + <Message type="WARNING" filename="stringification.cpp" line="0"> + <Text> + nullptr + </Text> + </Message> + <Expression success="false" type="CHECK" filename="stringification.cpp" line="0"> + <Original> + cnptr != nullptr + </Original> + <Expanded> + nullptr != nullptr + </Expanded> + </Expression> + <Message type="WARNING" filename="stringification.cpp" line="0"> + <Text> + 0 + </Text> + </Message> + <Expression success="false" type="CHECK" filename="stringification.cpp" line="0"> + <Original> + A == C + </Original> + <Expanded> + 0 == 100 + </Expanded> + </Expression> + <Message type="WARNING" filename="stringification.cpp" line="0"> + <Text> + int + </Text> + </Message> + <OverallResultsAsserts successes="2" failures="5" test_case_success="false"/> + </TestCase> <TestCase name="all asserts should fail and show how the objects get stringified" filename="stringification.cpp" line="0"> + <Message type="WARNING" filename="stringification.cpp" line="0"> + <Text> + Foo{} + </Text> + </Message> <Expression success="false" type="CHECK" filename="stringification.cpp" line="0"> <Original> f1 == f2 @@ -11,6 +112,11 @@ Foo{} == Foo{} </Expanded> </Expression> + <Message type="WARNING" filename="stringification.cpp" line="0"> + <Text> + omg + </Text> + </Message> <Expression success="false" type="CHECK" filename="stringification.cpp" line="0"> <Original> dummy == "tralala" @@ -27,6 +133,11 @@ tralala == omg </Expanded> </Expression> + <Message type="WARNING" filename="stringification.cpp" line="0"> + <Text> + [1, 2, 3] + </Text> + </Message> <Expression success="false" type="CHECK" filename="stringification.cpp" line="0"> <Original> vec1 == vec2 @@ -35,12 +146,17 @@ [1, 2, 3] == [1, 2, 4] </Expanded> </Expression> + <Message type="WARNING" filename="stringification.cpp" line="0"> + <Text> + [1, 42, 3] + </Text> + </Message> <Expression success="false" type="CHECK" filename="stringification.cpp" line="0"> <Original> lst_1 == lst_2 </Original> <Expanded> - [1, 42, 3, ] == [1, 2, 666, ] + [1, 42, 3] == [1, 2, 666] </Expanded> </Expression> <Expression success="false" type="CHECK" filename="stringification.cpp" line="0"> @@ -70,22 +186,6 @@ </Expression> <Expression success="false" type="CHECK" filename="stringification.cpp" line="0"> <Original> - doctest::IsNaN<double>(0.5) - </Original> - <Expanded> - 0.5 - </Expanded> - </Expression> - <Expression success="false" type="CHECK" filename="stringification.cpp" line="0"> - <Original> - doctest::IsNaN<float>(std::numeric_limits<float>::infinity()) - </Original> - <Expanded> - inf - </Expanded> - </Expression> - <Expression success="false" type="CHECK" filename="stringification.cpp" line="0"> - <Original> "a" == doctest::Contains("aaa") </Original> <Expanded> @@ -95,7 +195,7 @@ <Exception crash="false"> MyTypeInherited<int>(5, 4) </Exception> - <OverallResultsAsserts successes="0" failures="10" test_case_success="false"/> + <OverallResultsAsserts successes="3" failures="8" test_case_success="false"/> </TestCase> <TestCase name="a test case that registers an exception translator for int and then throws one" filename="stringification.cpp" line="0"> <Exception crash="false"> @@ -104,7 +204,7 @@ <OverallResultsAsserts successes="0" failures="0" test_case_success="false"/> </TestCase> </TestSuite> - <OverallResultsAsserts successes="0" failures="10"/> - <OverallResultsTestCases successes="0" failures="2"/> + <OverallResultsAsserts successes="5" failures="13"/> + <OverallResultsTestCases successes="1" failures="3"/> </doctest> Program code. diff --git a/examples/all_features/test_output/templated_test_cases.cpp.txt b/examples/all_features/test_output/templated_test_cases.cpp.txt index 05195f0e..d4296938 100644 --- a/examples/all_features/test_output/templated_test_cases.cpp.txt +++ b/examples/all_features/test_output/templated_test_cases.cpp.txt @@ -8,21 +8,21 @@ templated_test_cases.cpp(0): ERROR: CHECK( vec.size() == 20 ) is NOT correct! =============================================================================== templated_test_cases.cpp(0): -TEST CASE: multiple types<> +TEST CASE: multiple types<Custom name test> templated_test_cases.cpp(0): ERROR: CHECK( t2 != T2() ) is NOT correct! values: CHECK( 0 != 0 ) =============================================================================== templated_test_cases.cpp(0): -TEST CASE: multiple types<> +TEST CASE: multiple types<Other custom name> templated_test_cases.cpp(0): ERROR: CHECK( t2 != T2() ) is NOT correct! values: CHECK( 0 != 0 ) =============================================================================== templated_test_cases.cpp(0): -TEST CASE: multiple types<> +TEST CASE: multiple types<TypePair<bool, int>> templated_test_cases.cpp(0): ERROR: CHECK( t2 != T2() ) is NOT correct! values: CHECK( 0 != 0 ) diff --git a/examples/all_features/test_output/templated_test_cases.cpp_junit.txt b/examples/all_features/test_output/templated_test_cases.cpp_junit.txt index 90e9df8d..2c9d17de 100644 --- a/examples/all_features/test_output/templated_test_cases.cpp_junit.txt +++ b/examples/all_features/test_output/templated_test_cases.cpp_junit.txt @@ -2,7 +2,7 @@ <testsuites> <testsuite name="all_features" errors="0" failures="5" tests="19"> <testcase classname="templated_test_cases.cpp" name="signed integers stuff<signed char>" status="run"/> - <testcase classname="templated_test_cases.cpp" name="signed integers stuff<short int>" status="run"/> + <testcase classname="templated_test_cases.cpp" name="signed integers stuff<SHORT!!!>" status="run"/> <testcase classname="templated_test_cases.cpp" name="signed integers stuff<int>" status="run"/> <testcase classname="templated_test_cases.cpp" name="vector stuff<std::vector<int>>" status="run"> <failure message="10 == 20" type="CHECK"> @@ -13,13 +13,13 @@ CHECK( vec.size() == 20 ) is NOT correct! </failure> </testcase> <testcase classname="templated_test_cases.cpp" name="default construction<signed char>" status="run"/> - <testcase classname="templated_test_cases.cpp" name="default construction<short int>" status="run"/> + <testcase classname="templated_test_cases.cpp" name="default construction<SHORT!!!>" status="run"/> <testcase classname="templated_test_cases.cpp" name="default construction<int>" status="run"/> <testcase classname="templated_test_cases.cpp" name="default construction<double>" status="run"/> <testcase classname="templated_test_cases.cpp" name="default construction<double>" status="run"/> <testcase classname="templated_test_cases.cpp" name="default construction<unsigned char>" status="run"/> <testcase classname="templated_test_cases.cpp" name="default construction<char>" status="run"/> - <testcase classname="templated_test_cases.cpp" name="multiple types<>" status="run"> + <testcase classname="templated_test_cases.cpp" name="multiple types<Custom name test>" status="run"> <failure message="0 != 0" type="CHECK"> templated_test_cases.cpp(0): CHECK( t2 != T2() ) is NOT correct! @@ -27,7 +27,7 @@ CHECK( t2 != T2() ) is NOT correct! </failure> </testcase> - <testcase classname="templated_test_cases.cpp" name="multiple types<>" status="run"> + <testcase classname="templated_test_cases.cpp" name="multiple types<Other custom name>" status="run"> <failure message="0 != 0" type="CHECK"> templated_test_cases.cpp(0): CHECK( t2 != T2() ) is NOT correct! @@ -35,7 +35,7 @@ CHECK( t2 != T2() ) is NOT correct! </failure> </testcase> - <testcase classname="templated_test_cases.cpp" name="multiple types<>" status="run"> + <testcase classname="templated_test_cases.cpp" name="multiple types<TypePair<bool, int>>" status="run"> <failure message="0 != 0" type="CHECK"> templated_test_cases.cpp(0): CHECK( t2 != T2() ) is NOT correct! diff --git a/examples/all_features/test_output/templated_test_cases.cpp_xml.txt b/examples/all_features/test_output/templated_test_cases.cpp_xml.txt index c2f8b9e4..2b43e30e 100644 --- a/examples/all_features/test_output/templated_test_cases.cpp_xml.txt +++ b/examples/all_features/test_output/templated_test_cases.cpp_xml.txt @@ -5,7 +5,7 @@ <TestCase name="signed integers stuff<signed char>" filename="templated_test_cases.cpp" line="0"> <OverallResultsAsserts successes="1" failures="0" test_case_success="true"/> </TestCase> - <TestCase name="signed integers stuff<short int>" filename="templated_test_cases.cpp" line="0"> + <TestCase name="signed integers stuff<SHORT!!!>" filename="templated_test_cases.cpp" line="0"> <OverallResultsAsserts successes="1" failures="0" test_case_success="true"/> </TestCase> <TestCase name="signed integers stuff<int>" filename="templated_test_cases.cpp" line="0"> @@ -25,7 +25,7 @@ <TestCase name="default construction<signed char>" filename="templated_test_cases.cpp" line="0"> <OverallResultsAsserts successes="1" failures="0" test_case_success="true"/> </TestCase> - <TestCase name="default construction<short int>" filename="templated_test_cases.cpp" line="0"> + <TestCase name="default construction<SHORT!!!>" filename="templated_test_cases.cpp" line="0"> <OverallResultsAsserts successes="1" failures="0" test_case_success="true"/> </TestCase> <TestCase name="default construction<int>" filename="templated_test_cases.cpp" line="0"> @@ -43,7 +43,7 @@ <TestCase name="default construction<char>" filename="templated_test_cases.cpp" line="0"> <OverallResultsAsserts successes="1" failures="0" test_case_success="true"/> </TestCase> - <TestCase name="multiple types<>" filename="templated_test_cases.cpp" line="0"> + <TestCase name="multiple types<Custom name test>" filename="templated_test_cases.cpp" line="0"> <Expression success="false" type="CHECK" filename="templated_test_cases.cpp" line="0"> <Original> t2 != T2() @@ -54,7 +54,7 @@ </Expression> <OverallResultsAsserts successes="1" failures="1" test_case_success="false"/> </TestCase> - <TestCase name="multiple types<>" filename="templated_test_cases.cpp" line="0"> + <TestCase name="multiple types<Other custom name>" filename="templated_test_cases.cpp" line="0"> <Expression success="false" type="CHECK" filename="templated_test_cases.cpp" line="0"> <Original> t2 != T2() @@ -65,7 +65,7 @@ </Expression> <OverallResultsAsserts successes="1" failures="1" test_case_success="false"/> </TestCase> - <TestCase name="multiple types<>" filename="templated_test_cases.cpp" line="0"> + <TestCase name="multiple types<TypePair<bool, int>>" filename="templated_test_cases.cpp" line="0"> <Expression success="false" type="CHECK" filename="templated_test_cases.cpp" line="0"> <Original> t2 != T2() diff --git a/examples/executable_dll_and_plugin/dll.cpp b/examples/executable_dll_and_plugin/dll.cpp index 22e498b4..b436e8fa 100644 --- a/examples/executable_dll_and_plugin/dll.cpp +++ b/examples/executable_dll_and_plugin/dll.cpp @@ -8,7 +8,6 @@ DOCTEST_MAKE_STD_HEADERS_CLEAN_FROM_WARNINGS_ON_WALL_END TEST_CASE("dll") { std::cout << "I am a test from the dll!\n"; CHECK(true); - CHECK(doctest::IsNaN<long double>(0.1L)); } DOCTEST_SYMBOL_EXPORT void from_dll(); // to silence "-Wmissing-declarations" with GCC diff --git a/examples/executable_dll_and_plugin/test_output/executable_dll_and_plugin.txt b/examples/executable_dll_and_plugin/test_output/executable_dll_and_plugin.txt index d489082d..bc674380 100644 --- a/examples/executable_dll_and_plugin/test_output/executable_dll_and_plugin.txt +++ b/examples/executable_dll_and_plugin/test_output/executable_dll_and_plugin.txt @@ -1,12 +1,5 @@ [doctest] run with "--help" for options I am a test from the dll! -=============================================================================== -dll.cpp(0): -TEST CASE: dll - -dll.cpp(0): ERROR: CHECK( doctest::IsNaN<long double>(0.1L) ) is NOT correct! - values: CHECK( 0.1 ) - I am a test from the implementation! I am a test from the implementation_2! I am a test from the executable! @@ -33,6 +26,6 @@ plugin.cpp(0): FATAL ERROR: certain death! logged: some info =============================================================================== -[doctest] test cases: 5 | 2 passed | 3 failed | 0 skipped -[doctest] assertions: 4 | 1 passed | 3 failed | +[doctest] test cases: 5 | 3 passed | 2 failed | 0 skipped +[doctest] assertions: 3 | 1 passed | 2 failed | [doctest] Status: FAILURE! diff --git a/examples/executable_dll_and_plugin/test_output/executable_dll_and_plugin_junit.txt b/examples/executable_dll_and_plugin/test_output/executable_dll_and_plugin_junit.txt index 00d66345..e5ff58ee 100644 --- a/examples/executable_dll_and_plugin/test_output/executable_dll_and_plugin_junit.txt +++ b/examples/executable_dll_and_plugin/test_output/executable_dll_and_plugin_junit.txt @@ -4,15 +4,8 @@ I am a test from the implementation! I am a test from the implementation_2! I am a test from the executable! <testsuites> - <testsuite name="executable_dll_and_plugin" errors="1" failures="2" tests="4"> - <testcase classname="dll.cpp" name="dll" status="run"> - <failure message="0.1" type="CHECK"> -dll.cpp(0): -CHECK( doctest::IsNaN<long double>(0.1L) ) is NOT correct! - values: CHECK( 0.1 ) - - </failure> - </testcase> + <testsuite name="executable_dll_and_plugin" errors="1" failures="1" tests="3"> + <testcase classname="dll.cpp" name="dll" status="run"/> <testcase classname="implementation.cpp" name="implementation" status="run"/> <testcase classname="implementation_2.cpp" name="implementation_2" status="run"/> <testcase classname="main.cpp" name="executable" status="run"> diff --git a/examples/executable_dll_and_plugin/test_output/executable_dll_and_plugin_xml.txt b/examples/executable_dll_and_plugin/test_output/executable_dll_and_plugin_xml.txt index 960f0312..b300e78c 100644 --- a/examples/executable_dll_and_plugin/test_output/executable_dll_and_plugin_xml.txt +++ b/examples/executable_dll_and_plugin/test_output/executable_dll_and_plugin_xml.txt @@ -4,15 +4,7 @@ <TestSuite> <TestCase name="dll" filename="dll.cpp" line="0"> I am a test from the dll! - <Expression success="false" type="CHECK" filename="dll.cpp" line="0"> - <Original> - doctest::IsNaN<long double>(0.1L) - </Original> - <Expanded> - 0.1 - </Expanded> - </Expression> - <OverallResultsAsserts successes="1" failures="1" test_case_success="false"/> + <OverallResultsAsserts successes="1" failures="0" test_case_success="true"/> </TestCase> <TestCase name="implementation" filename="implementation.cpp" line="0"> I am a test from the implementation! @@ -64,6 +56,6 @@ I am a test from the executable! <OverallResultsAsserts successes="0" failures="2" test_case_success="false"/> </TestCase> </TestSuite> - <OverallResultsAsserts successes="1" failures="3"/> - <OverallResultsTestCases successes="2" failures="3" skipped="0"/> + <OverallResultsAsserts successes="1" failures="2"/> + <OverallResultsTestCases successes="3" failures="2" skipped="0"/> </doctest> diff --git a/scripts/coverage_maxout.cpp b/scripts/coverage_maxout.cpp index 8b6e574f..ad55ef42 100644 --- a/scripts/coverage_maxout.cpp +++ b/scripts/coverage_maxout.cpp @@ -61,37 +61,19 @@ TEST_CASE("exercising tricky code paths of doctest") { str += toString("aaa") // + toString(nullptr) // + toString(true) // - + toString(static_cast<unsigned int>(0)) // - + toString(0.5f) // - + toString(0.5) // - + toString(static_cast<long double>(0.1)) // + + toString(0u) // + toString('c') // + toString(static_cast<signed char>('c')) // + toString(static_cast<unsigned char>(1)) // + toString(static_cast<short>(1)) // - + toString(static_cast<long>(1)) // - + toString(static_cast<unsigned long>(1)) // + + toString(1L) // + + toString(1UL) // + toString(static_cast<unsigned short>(1)) // - + toString(static_cast<long long>(1)) // - + toString(static_cast<unsigned long long>(1)); + + toString(1LL) // + + toString(1ULL); std::ostringstream oss; - // toStream - detail::toStream(&oss, true); - detail::toStream(&oss, 0.5f); - detail::toStream(&oss, 0.5); - detail::toStream(&oss, static_cast<long double>(0.1)); - detail::toStream(&oss, 'c'); - detail::toStream(&oss, static_cast<signed char>('c')); - detail::toStream(&oss, static_cast<unsigned char>(1)); - detail::toStream(&oss, static_cast<short>(1)); - detail::toStream(&oss, static_cast<long>(1)); - detail::toStream(&oss, static_cast<unsigned long>(1)); - detail::toStream(&oss, static_cast<unsigned short>(1)); - detail::toStream(&oss, static_cast<long long>(1)); - detail::toStream(&oss, static_cast<unsigned long long>(1)); - // trigger code path for String to ostream through operator<< oss << str; // trigger code path for assert string of a non-existent assert type @@ -102,32 +84,31 @@ TEST_CASE("exercising tricky code paths of doctest") { #endif str += oss.str().c_str(); str += failureString(assertType::is_normal); - CHECK(str == "omgomgomgaaaNULLtrue00.5f0.50.199991111111true0.50.50.1cc" - "111111omgomgomgaaaNULLtrue00.5f0.50.199991111111"); + CHECK(str == "omgomgomgaaanullptrtrue099991111111" + "omgomgomgaaanullptrtrue099991111111"); // trigger code path for rawMemoryToString bool isThereAnything = str.size() > 0u; - bool len_is_zero = detail::rawMemoryToString(isThereAnything).size() == 0u; String unknown = toString(skip()); // trigger code path for "{?}" str = unknown; // trigger code path for deleting memory in operator= - CHECK_MESSAGE(len_is_zero, "should fail"); + CHECK_FALSE_MESSAGE(isThereAnything, "should fail"); Approx a(5); a.scale(4); Approx b = a(7); - CHECK(b == 5); - CHECK(b != 5); - CHECK(b > 5); - CHECK(b < 5); - CHECK(b >= 5); - CHECK(b <= 5); + CHECK(b == 7); + CHECK(b != 6); + CHECK(b > 6); + CHECK(b < 8); + CHECK(b >= 7); + CHECK(b <= 7); - CHECK(6 == a); + CHECK(5 == a); CHECK(6 != a); CHECK(6 > a); - CHECK(6 < a); - CHECK(6 >= a); - CHECK(6 <= a); + CHECK(4 < a); + CHECK(5 >= a); + CHECK(5 <= a); // trigger another single line of code... lol auto oldVal = const_cast<ContextOptions*>(getContextOptions())->no_path_in_filenames; |