from now on the library should be developed using the 2 headers in doctest/parts/ and doctest/doctest.h gets generated from them!
updated docs
diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md
new file mode 100644
index 0000000..9628b03
--- /dev/null
+++ b/CONTRIBUTING.md
@@ -0,0 +1,26 @@
+## Contributing
+
+Support the development of the project with donations! There is a list of planned features which are all important and big - see the [**roadmap**](doc/markdown/roadmap.md). I took a break from working in the industry to make open source software so every cent is a big deal.
+
+[![Donate to support](https://pledgie.com/campaigns/31280.png)](https://pledgie.com/campaigns/31280)
+
+## pull requests
+
+Consider opening an issue for a discussion before making a pull request to make sure the contribution goes smoothly.
+
+All pull requests should be made against the ```dev``` branch because the ```master``` is the stable one with the latest release.
+
+If you're going to change something in the library itself - make sure you don't modify ```doctest/doctest.h``` because it's generated from ```doctest/parts/doctest_fwd.h``` and ```doctest/parts/doctest_impl.h``` - they get concatenated by CMake!
+
+This framework has some design goals which must be kept. Make sure you have read the [**features and design goals**](doc/markdown/features.md) page.
+
+If your changes also change the output of the library - you should also update the reference output for the tests or otherwise the CI builds (```travis``` and ```appveyor```) will fail when they compare the latest output to the outdated reference output (which is committed in the repository). To do this run CMake with the ```TEST_MODE``` variable set to ```COLLECT``` (making the new reference output) and then run ```ctest``` and commit the changed ```.txt``` files too. The default ```TEST_MODE``` is ```COMPARE```.
+
+Example: ```cmake -DTEST_MODE=COLLECT path/to/sources && ctest```
+
+Code should be formatted with a recent-enough ```clang-format``` using the config file in the root of the repo (or I will do it...)
+
+Testing with compilers different from GCC/Clang/MSVC (and more platforms) is something the project would benefit from.
+
+
+
diff --git a/README.md b/README.md
index f126af3..586a32e 100644
--- a/README.md
+++ b/README.md
@@ -1,5 +1,6 @@
-The lightest feature rich C++ single header testing framework
--------
+### The lightest feature-rich C++ single-header testing framework for unit tests and TDD
+
+---------
The **doctest** library is inspired by the [**```unittest {}```**](https://wiki.dlang.org/Unittest) functionality of the **D** programming language and **Python**'s [**docstrings**](https://en.wikipedia.org/wiki/Docstring) - tests can be considered a form of documentation and should be able to reside near the production code which they test.
@@ -80,20 +81,8 @@
Contributing
------------
-Support the development of the project with donations! There is a list of planned features which are all important and big - see the [**roadmap**](doc/markdown/roadmap.md). I work on this project in my spare time and every cent is a big deal. I took a break from working in the industry to make open source software.
+Support the development of the project with donations! There is a list of planned features which are all important and big - see the [**roadmap**](doc/markdown/roadmap.md). I took a break from working in the industry to make open source software so every cent is a big deal.
[![Donate to support](https://pledgie.com/campaigns/31280.png)](https://pledgie.com/campaigns/31280)
-Contributions in the form of issues and pull requests are welcome as well.
-
-Open an issue for a discussion before making a pull request to make sure the contribution goes smoothly.
-
-This framework has some design goals which must be kept. Make sure you have read the [**features and design goals**](doc/markdown/features.md) page.
-
-The ```master``` branch is the stable one with the latest release and the ```dev``` branch is on the bleeding edge.
-
-All the framework tests (using ctest) have their output collected when the CMake ```TEST_MODE``` variable is set to ```COLLECT``` (making the new reference output) and later the tests are ran on the CI services (```travis``` and ```appveyor```) - their output is compared with the current reference output in the repository with the ```COMPARE``` mode (default mode is ```COMPARE```).
-
-Code should be formatted with a recent-enough ```clang-format``` using the config file in the root of the repo (or I will do it...)
-
-Testing with compilers different from GCC/Clang/MSVC (and more platforms) is something the project would benefit from.
+Contributions in the form of issues and pull requests are welcome as well - check out the [**Contributing**](CONTRIBUTING.md) page.
diff --git a/doc/markdown/main.md b/doc/markdown/main.md
index 6a48ef9..9e5db2c 100644
--- a/doc/markdown/main.md
+++ b/doc/markdown/main.md
@@ -28,7 +28,7 @@
int res = context.run(); // run
- if(context.shouldExit()) // important - query flags (and --no-run) rely on the user doing this
+ if(context.shouldExit()) // important - query flags (and --exit) rely on the user doing this
return res; // propagate the result of the tests
int client_stuff_return_code = 0;
diff --git a/doc/markdown/readme.md b/doc/markdown/readme.md
index d803d51..712486b 100644
--- a/doc/markdown/readme.md
+++ b/doc/markdown/readme.md
@@ -17,6 +17,6 @@
- [```main()``` entry point and dealing with shared objects](main.md)
- [FAQ](faq.md)
-Support the development of the project with donations! There is a list of planned features which are all important and big - see the [**roadmap**](roadmap.md). I work on this project in my spare time and every cent is a big deal.
+Support the development of the project with donations! There is a list of planned features which are all important and big - see the [**roadmap**](roadmap.md). I took a break from working in the industry to make open source software so every cent is a big deal.
[![Donate to support](https://pledgie.com/campaigns/31280.png)](https://pledgie.com/campaigns/31280)
\ No newline at end of file
diff --git a/doc/markdown/roadmap.md b/doc/markdown/roadmap.md
index 3b2c1aa..fa798b4 100644
--- a/doc/markdown/roadmap.md
+++ b/doc/markdown/roadmap.md
@@ -44,6 +44,7 @@
And here is a list of things that are being considered but not part of the roadmap yet:
+- rework the cmake single header assembly - to use a ```.cmake``` script that sets the permissions of ```doctest.h``` as read only
- look into MSTest integration
- http://accu.org/index.php/journals/1851
- https://msdn.microsoft.com/en-us/library/hh270865.aspx
diff --git a/doctest/doctest.h b/doctest/doctest.h
index d96c28e..3d82b07 100644
--- a/doctest/doctest.h
+++ b/doctest/doctest.h
@@ -1,5 +1,8 @@
+// ======================================================================
+// == DO NOT MODIFY THIS FILE BY HAND - IT IS AUTO GENERATED BY CMAKE! ==
+// ======================================================================
//
-// doctest.h - the lightest feature rich C++ single header testing framework
+// doctest.h - the lightest feature-rich C++ single-header testing framework for unit tests and TDD
//
// Copyright (c) 2016 Viktor Kirilov
//
@@ -10,16 +13,24 @@
// The documentation can be found at the library's page:
// https://github.com/onqtam/doctest/blob/master/doc/markdown/readme.md
//
+// =================================================================================================
+// =================================================================================================
+// =================================================================================================
+//
// The library is heavily influenced by Catch - https://github.com/philsquared/Catch
// which uses the Boost Software License - Version 1.0
// see here - https://github.com/philsquared/Catch/blob/master/LICENSE_1_0.txt
// The concept of subcases (sections in Catch) and expression decomposition are from there.
// Some parts of the code are taken directly:
-// - stringification - the detection of "ostream& operator<<(ostream&, const T&)" and the StringMaker class
+// - stringification - the detection of "ostream& operator<<(ostream&, const T&)" and StringMaker<>
// - the Approx() helper class for floating point comparison
// - colors in the console
// - breaking into a debugger
-
+//
+// The expression decomposing templates are taken from lest - https://github.com/martinmoene/lest
+// which uses the Boost Software License - Version 1.0
+// see here - https://github.com/martinmoene/lest/blob/master/LICENSE_1_0.txt
+//
// =================================================================================================
// =================================================================================================
// =================================================================================================
@@ -59,7 +70,6 @@
#if __GNUC__ > 4 || (__GNUC__ == 4 && __GNUC_MINOR__ > 6)
#pragma GCC diagnostic ignored "-Wzero-as-null-pointer-constant"
#endif // > gcc 4.6
-//#pragma GCC diagnostic ignored "-Wlong-long"
#endif // __GNUC__
#ifdef _MSC_VER
@@ -565,7 +575,7 @@
typename enable_if<can_use_op<L>::value || can_use_op<R>::value, bool>::type lte(const L& lhs, const R& rhs) { return neq(lhs, rhs) ? lhs < rhs : true; }
template <typename L, typename R>
typename enable_if<can_use_op<L>::value || can_use_op<R>::value, bool>::type gte(const L& lhs, const R& rhs) { return neq(lhs, rhs) ? lhs > rhs : true; }
-
+
inline bool eq (const char* lhs, const char* rhs) { return String(lhs) == String(rhs); }
inline bool neq(const char* lhs, const char* rhs) { return String(lhs) != String(rhs); }
inline bool lt (const char* lhs, const char* rhs) { return String(lhs) < String(rhs); }
@@ -1008,9 +1018,57 @@
#endif // DOCTEST_LIBRARY_INCLUDED
-// =================================================================================================
-// == WHAT FOLLOWS IS THE IMPLEMENTATION OF THE TEST RUNNER ==
-// =================================================================================================
+#if defined(__clang__)
+#pragma clang diagnostic pop
+#endif // __clang__
+
+#if defined(__GNUC__) && !defined(__clang__)
+#if __GNUC__ > 4 || (__GNUC__ == 4 && __GNUC_MINOR__ > 6)
+#pragma GCC diagnostic pop
+#endif // > gcc 4.6
+#endif // __GNUC__
+
+#ifdef _MSC_VER
+#pragma warning(pop)
+#endif // _MSC_VER
+#if defined(__clang__)
+#pragma clang diagnostic push
+#pragma clang diagnostic ignored "-Wpadded"
+#pragma clang diagnostic ignored "-Wglobal-constructors"
+#pragma clang diagnostic ignored "-Wexit-time-destructors"
+#pragma clang diagnostic ignored "-Wmissing-prototypes"
+#pragma clang diagnostic ignored "-Wsign-conversion"
+#pragma clang diagnostic ignored "-Wshorten-64-to-32"
+#pragma clang diagnostic ignored "-Wmissing-variable-declarations"
+#pragma clang diagnostic ignored "-Wcovered-switch-default"
+#pragma clang diagnostic ignored "-Wmissing-noreturn"
+#endif // __clang__
+
+#if defined(__GNUC__) && !defined(__clang__)
+#if __GNUC__ > 4 || (__GNUC__ == 4 && __GNUC_MINOR__ > 6)
+#pragma GCC diagnostic push
+#endif // > gcc 4.6
+#pragma GCC diagnostic ignored "-Wconversion"
+#pragma GCC diagnostic ignored "-Weffc++"
+#pragma GCC diagnostic ignored "-Wsign-conversion"
+#pragma GCC diagnostic ignored "-Wstrict-overflow"
+#pragma GCC diagnostic ignored "-Wmissing-declarations"
+#pragma GCC diagnostic ignored "-Winline"
+#if __GNUC__ > 4 || (__GNUC__ == 4 && __GNUC_MINOR__ > 6)
+#pragma GCC diagnostic ignored "-Wzero-as-null-pointer-constant"
+#endif // > gcc 4.6
+//#pragma GCC diagnostic ignored "-Wlong-long"
+#endif // __GNUC__
+
+#ifdef _MSC_VER
+#pragma warning(push)
+#pragma warning(disable : 4996) // The compiler encountered a deprecated declaration
+#pragma warning(disable : 4267) // 'var' : conversion from 'size_t' to 'type', possible loss of data
+#pragma warning(disable : 4706) // assignment within conditional expression
+#pragma warning(disable : 4512) // 'class' : assignment operator could not be generated
+#pragma warning(disable : 4127) // conditional expression is constant
+#endif // _MSC_VER
+
#if(defined(DOCTEST_CONFIG_IMPLEMENT) || defined(DOCTEST_CONFIG_IMPLEMENT_WITH_MAIN))
#ifndef DOCTEST_LIBRARY_IMPLEMENTATION
#define DOCTEST_LIBRARY_IMPLEMENTATION
diff --git a/doctest/parts/doctest_fwd.h b/doctest/parts/doctest_fwd.h
new file mode 100644
index 0000000..f80f232
--- /dev/null
+++ b/doctest/parts/doctest_fwd.h
@@ -0,0 +1,1030 @@
+//
+// doctest.h - the lightest feature-rich C++ single-header testing framework for unit tests and TDD
+//
+// Copyright (c) 2016 Viktor Kirilov
+//
+// Distributed under the MIT Software License
+// See accompanying file LICENSE.txt or copy at
+// https://opensource.org/licenses/MIT
+//
+// The documentation can be found at the library's page:
+// https://github.com/onqtam/doctest/blob/master/doc/markdown/readme.md
+//
+// =================================================================================================
+// =================================================================================================
+// =================================================================================================
+//
+// The library is heavily influenced by Catch - https://github.com/philsquared/Catch
+// which uses the Boost Software License - Version 1.0
+// see here - https://github.com/philsquared/Catch/blob/master/LICENSE_1_0.txt
+// The concept of subcases (sections in Catch) and expression decomposition are from there.
+// Some parts of the code are taken directly:
+// - stringification - the detection of "ostream& operator<<(ostream&, const T&)" and StringMaker<>
+// - the Approx() helper class for floating point comparison
+// - colors in the console
+// - breaking into a debugger
+//
+// The expression decomposing templates are taken from lest - https://github.com/martinmoene/lest
+// which uses the Boost Software License - Version 1.0
+// see here - https://github.com/martinmoene/lest/blob/master/LICENSE_1_0.txt
+//
+// =================================================================================================
+// =================================================================================================
+// =================================================================================================
+
+// Suppress this globally - there is no way to silence it in the expression decomposition macros
+// _Pragma() in macros doesn't work for the c++ front-end of g++
+// https://gcc.gnu.org/bugzilla/show_bug.cgi?id=55578
+// https://gcc.gnu.org/bugzilla/show_bug.cgi?id=69543
+// Also the warning is completely worthless nowadays - http://stackoverflow.com/questions/14016993
+#if defined(__GNUC__) && !defined(__clang__)
+#pragma GCC diagnostic ignored "-Waggregate-return"
+#endif
+
+#if defined(__clang__)
+#pragma clang diagnostic push
+#pragma clang diagnostic ignored "-Wpadded"
+#pragma clang diagnostic ignored "-Wglobal-constructors"
+#pragma clang diagnostic ignored "-Wexit-time-destructors"
+#pragma clang diagnostic ignored "-Wmissing-prototypes"
+#pragma clang diagnostic ignored "-Wsign-conversion"
+#pragma clang diagnostic ignored "-Wshorten-64-to-32"
+#pragma clang diagnostic ignored "-Wmissing-variable-declarations"
+#pragma clang diagnostic ignored "-Wcovered-switch-default"
+#pragma clang diagnostic ignored "-Wmissing-noreturn"
+#endif // __clang__
+
+#if defined(__GNUC__) && !defined(__clang__)
+#if __GNUC__ > 4 || (__GNUC__ == 4 && __GNUC_MINOR__ > 6)
+#pragma GCC diagnostic push
+#endif // > gcc 4.6
+#pragma GCC diagnostic ignored "-Wconversion"
+#pragma GCC diagnostic ignored "-Weffc++"
+#pragma GCC diagnostic ignored "-Wsign-conversion"
+#pragma GCC diagnostic ignored "-Wstrict-overflow"
+#pragma GCC diagnostic ignored "-Wmissing-declarations"
+#pragma GCC diagnostic ignored "-Winline"
+#if __GNUC__ > 4 || (__GNUC__ == 4 && __GNUC_MINOR__ > 6)
+#pragma GCC diagnostic ignored "-Wzero-as-null-pointer-constant"
+#endif // > gcc 4.6
+#endif // __GNUC__
+
+#ifdef _MSC_VER
+#pragma warning(push)
+#pragma warning(disable : 4996) // The compiler encountered a deprecated declaration
+#pragma warning(disable : 4267) // 'var' : conversion from 'size_t' to 'type', possible loss of data
+#pragma warning(disable : 4706) // assignment within conditional expression
+#pragma warning(disable : 4512) // 'class' : assignment operator could not be generated
+#pragma warning(disable : 4127) // conditional expression is constant
+#endif // _MSC_VER
+
+#ifndef DOCTEST_LIBRARY_INCLUDED
+#define DOCTEST_LIBRARY_INCLUDED
+
+#define DOCTEST_VERSION_MAJOR 1
+#define DOCTEST_VERSION_MINOR 0
+#define DOCTEST_VERSION_PATCH 0
+#define DOCTEST_VERSION "1.0.0"
+
+// internal macros for string concatenation and anonymous variable name generation
+#define DOCTEST_CONCAT_IMPL(s1, s2) s1##s2
+#define DOCTEST_CONCAT(s1, s2) DOCTEST_CONCAT_IMPL(s1, s2)
+#ifdef __COUNTER__ // not standard and may be missing for some compilers
+#define DOCTEST_ANONYMOUS(x) DOCTEST_CONCAT(x, __COUNTER__)
+#else // __COUNTER__
+#define DOCTEST_ANONYMOUS(x) DOCTEST_CONCAT(x, __LINE__)
+#endif // __COUNTER__
+
+// internal macro for making a string
+#define DOCTEST_TOSTR_IMPL(x) #x
+#define DOCTEST_TOSTR(x) DOCTEST_TOSTR_IMPL(x)
+
+// internal macro for concatenating 2 literals and making the result a string
+#define DOCTEST_STR_CONCAT_TOSTR(s1, s2) DOCTEST_TOSTR(s1) DOCTEST_TOSTR(s2)
+
+// not using __APPLE__ because... this is how Catch does it
+#if defined(__MAC_OS_X_VERSION_MIN_REQUIRED)
+#define DOCTEST_PLATFORM_MAC
+#elif defined(__IPHONE_OS_VERSION_MIN_REQUIRED)
+#define DOCTEST_PLATFORM_IPHONE
+#elif defined(_WIN32) || defined(_MSC_VER)
+#define DOCTEST_PLATFORM_WINDOWS
+#else
+#define DOCTEST_PLATFORM_LINUX
+#endif
+
+#define DOCTEST_COUNTOF(x) (sizeof(x) / sizeof(x[0]))
+
+#define DOCTEST_GCS doctest::detail::getTestsContextState
+
+// snprintf() not in the C++98 standard
+#ifdef _MSC_VER
+#define DOCTEST_SNPRINTF _snprintf
+#else
+#define DOCTEST_SNPRINTF snprintf
+#endif
+
+// for anything below Visual Studio 2005 (VC++6 has no __debugbreak() - not sure about VS 2003)
+#if defined(_MSC_VER) && _MSC_VER < 1400
+#define __debugbreak() __asm { int 3}
+#endif
+
+#ifdef DOCTEST_PLATFORM_MAC
+// The following code snippet based on:
+// http://cocoawithlove.com/2008/03/break-into-debugger.html
+#ifdef DEBUG
+#if defined(__ppc64__) || defined(__ppc__)
+#define DOCTEST_BREAK_INTO_DEBUGGER() \
+ if(doctest::detail::isDebuggerActive() && !DOCTEST_GCS()->no_breaks) \
+ __asm__("li r0, 20\nsc\nnop\nli r0, 37\nli r4, 2\nsc\nnop\n" : : : "memory", "r0", "r3", "r4")
+#else // __ppc64__ || __ppc__
+#define DOCTEST_BREAK_INTO_DEBUGGER() \
+ if(doctest::detail::isDebuggerActive() && !DOCTEST_GCS()->no_breaks) \
+ __asm__("int $3\n" : :)
+#endif // __ppc64__ || __ppc__
+#endif // DEBUG
+#elif defined(_MSC_VER)
+#define DOCTEST_BREAK_INTO_DEBUGGER() \
+ if(doctest::detail::isDebuggerActive() && !DOCTEST_GCS()->no_breaks) \
+ __debugbreak()
+#elif defined(__MINGW32__)
+extern "C" __declspec(dllimport) void __stdcall DebugBreak();
+#define DOCTEST_BREAK_INTO_DEBUGGER() \
+ if(doctest::detail::isDebuggerActive() && !DOCTEST_GCS()->no_breaks) \
+ ::DebugBreak()
+#else // linux
+#define DOCTEST_BREAK_INTO_DEBUGGER() ((void)0)
+#endif // linux
+
+#ifdef __clang__
+#include <ciso646>
+#endif // __clang__
+
+#ifdef _LIBCPP_VERSION
+#include <iosfwd>
+#else // _LIBCPP_VERSION
+#ifndef DOCTEST_CONFIG_USE_IOSFWD
+namespace std
+{
+template <class charT>
+struct char_traits;
+template <>
+struct char_traits<char>;
+template <class charT, class traits>
+class basic_ostream;
+typedef basic_ostream<char, char_traits<char> > ostream;
+}
+#else // DOCTEST_CONFIG_USE_IOSFWD
+#include <iosfwd>
+#endif // DOCTEST_CONFIG_USE_IOSFWD
+#endif // _LIBCPP_VERSION
+
+#ifndef DOCTEST_CONFIG_WITH_LONG_LONG
+#if __cplusplus >= 201103L || (defined(_MSC_VER) && (_MSC_VER >= 1400))
+#define DOCTEST_CONFIG_WITH_LONG_LONG
+#endif // __cplusplus / _MSC_VER
+#endif // DOCTEST_CONFIG_WITH_LONG_LONG
+
+namespace doctest
+{
+class String
+{
+ char* m_str;
+
+ void copy(const String& other);
+
+public:
+ String(const char* in = "");
+ String(const String& other);
+ ~String();
+
+ String& operator=(const String& other);
+
+ String operator+(const String& other) const;
+ String& operator+=(const String& other);
+
+ char& operator[](unsigned pos) { return m_str[pos]; }
+ const char& operator[](unsigned pos) const { return m_str[pos]; }
+
+ char* c_str() { return m_str; }
+ const char* c_str() const { return m_str; }
+
+ unsigned size() const;
+ unsigned length() const;
+
+ int compare(const char* other, bool no_case = false) const;
+ int compare(const String& other, bool no_case = false) const;
+};
+
+// clang-format off
+inline bool operator==(const String& lhs, const String& rhs) { return lhs.compare(rhs) == 0; }
+inline bool operator!=(const String& lhs, const String& rhs) { return lhs.compare(rhs) != 0; }
+inline bool operator< (const String& lhs, const String& rhs) { return lhs.compare(rhs) < 0; }
+inline bool operator> (const String& lhs, const String& rhs) { return lhs.compare(rhs) > 0; }
+inline bool operator<=(const String& lhs, const String& rhs) { return (lhs != rhs) ? lhs.compare(rhs) < 0 : true; }
+inline bool operator>=(const String& lhs, const String& rhs) { return (lhs != rhs) ? lhs.compare(rhs) > 0 : true; }
+// clang-format on
+
+std::ostream& operator<<(std::ostream& stream, const String& in);
+
+namespace detail
+{
+ template <bool>
+ struct STATIC_ASSERT_No_output_stream_operator_found_for_type;
+
+ template <>
+ struct STATIC_ASSERT_No_output_stream_operator_found_for_type<true>
+ {};
+
+ namespace has_insertion_operator_impl
+ {
+ typedef char no;
+ typedef char yes[2];
+
+ template <bool in>
+ void f() {
+ STATIC_ASSERT_No_output_stream_operator_found_for_type<in>();
+ }
+
+ struct any_t
+ {
+ template <typename T>
+ any_t(const T&) {
+ f<false>();
+ }
+ };
+
+ yes& testStreamable(std::ostream&);
+ no testStreamable(no);
+
+ no operator<<(const std::ostream&, const any_t&);
+
+ template <typename T>
+ struct has_insertion_operator
+ {
+ static std::ostream& s;
+ static const T& t;
+// for anything below Visual Studio 2005 (VC++6 is troublesome - not sure about VS 2003)
+#if defined(_MSC_VER) && _MSC_VER < 1400
+ enum
+ {
+ value
+ };
+#else // _MSC_VER
+ static const bool value = sizeof(testStreamable(s << t)) == sizeof(yes);
+#endif // _MSC_VER
+ };
+ } // namespace has_insertion_operator_impl
+
+ template <typename T>
+ struct has_insertion_operator : has_insertion_operator_impl::has_insertion_operator<T>
+ {};
+
+ std::ostream* createStream();
+ String getStreamResult(std::ostream*);
+ void freeStream(std::ostream*);
+
+ template <bool C>
+ struct StringMakerBase
+ {
+ template <typename T>
+ static String convert(const T&) {
+ return "{?}";
+ }
+ };
+
+ template <>
+ struct StringMakerBase<true>
+ {
+ template <typename T>
+ static String convert(const T& in) {
+ std::ostream* stream = createStream();
+ *stream << in;
+ String result = getStreamResult(stream);
+ freeStream(stream);
+ return result;
+ }
+ };
+
+ String rawMemoryToString(const void* object, unsigned size);
+
+ template <typename T>
+ String rawMemoryToString(const T& object) {
+ return rawMemoryToString(&object, sizeof(object));
+ }
+} // namespace detail
+
+// for anything below Visual Studio 2005 (VC++6 is troublesome - not sure about VS 2003)
+#if defined(_MSC_VER) && _MSC_VER < 1400
+template <typename T>
+struct StringMaker : detail::StringMakerBase<false>
+{};
+#else // _MSC_VER
+template <typename T>
+struct StringMaker : detail::StringMakerBase<detail::has_insertion_operator<T>::value>
+{};
+#endif // _MSC_VER
+
+// not for anything below Visual Studio 2005 (VC++6 is troublesome - not sure about VS 2003)
+#if defined(_MSC_VER) && _MSC_VER >= 1400
+template <typename T>
+struct StringMaker<T*>
+{
+ template <typename U>
+ static String convert(U* p) {
+ if(!p)
+ return "NULL";
+ else
+ return detail::rawMemoryToString(p);
+ }
+};
+
+template <typename R, typename C>
+struct StringMaker<R C::*>
+{
+ static String convert(R C::*p) {
+ if(!p)
+ return "NULL";
+ else
+ return detail::rawMemoryToString(p);
+ }
+};
+#endif // _MSC_VER
+
+template <typename T>
+String toString(const T& value) {
+ return StringMaker<T>::convert(value);
+}
+
+String toString(const char* in);
+String toString(bool in);
+String toString(float in);
+String toString(double in);
+String toString(double long in);
+
+String toString(char in);
+String toString(char unsigned in);
+String toString(int short in);
+String toString(int short unsigned in);
+String toString(int in);
+String toString(int unsigned in);
+String toString(int long in);
+String toString(int long unsigned in);
+
+#ifdef DOCTEST_CONFIG_WITH_LONG_LONG
+String toString(int long long in);
+String toString(int long long unsigned in);
+#endif // DOCTEST_CONFIG_WITH_LONG_LONG
+
+class Approx
+{
+public:
+ explicit Approx(double value);
+
+ Approx(Approx const& other)
+ : m_epsilon(other.m_epsilon)
+ , m_scale(other.m_scale)
+ , m_value(other.m_value) {}
+
+ Approx operator()(double value) {
+ Approx approx(value);
+ approx.epsilon(m_epsilon);
+ approx.scale(m_scale);
+ return approx;
+ }
+
+ friend bool operator==(double lhs, Approx const& rhs);
+ friend bool operator==(Approx const& lhs, double rhs) { return operator==(rhs, lhs); }
+ friend bool operator!=(double lhs, Approx const& rhs) { return !operator==(lhs, rhs); }
+ friend bool operator!=(Approx const& lhs, double rhs) { return !operator==(rhs, lhs); }
+
+ Approx& epsilon(double newEpsilon) {
+ m_epsilon = newEpsilon;
+ return *this;
+ }
+
+ Approx& scale(double newScale) {
+ m_scale = newScale;
+ return *this;
+ }
+
+ String toString() const;
+
+private:
+ double m_epsilon;
+ double m_scale;
+ double m_value;
+};
+
+template <>
+inline String toString<Approx>(Approx const& value) {
+ return value.toString();
+}
+
+#if !defined(DOCTEST_CONFIG_DISABLE)
+
+namespace detail
+{
+ // the function type this library works with
+ typedef void (*funcType)(void);
+
+// not for anything below Visual Studio 2005 (VC++6 has no SFINAE - not sure about VS 2003)
+#if !defined(_MSC_VER) || _MSC_VER >= 1400
+ // clang-format off
+ template<class T> struct decay_array { typedef T type; };
+ template<class T, unsigned N> struct decay_array<T[N]> { typedef T* type; };
+ template<class T> struct decay_array<T[]> { typedef T* type; };
+
+ template<class T> struct not_char_pointer { enum { value = true }; };
+ template<> struct not_char_pointer<char*> { enum { value = false }; };
+ template<> struct not_char_pointer<const char*> { enum { value = false }; };
+
+ template<class T> struct can_use_op : not_char_pointer<typename decay_array<T>::type> {};
+
+ template<bool, class = void> struct enable_if {};
+ template<class T> struct enable_if<true, T> { typedef T type; };
+// clang-format on
+#endif // _MSC_VER
+
+ struct TestFailureException
+ {};
+
+ void checkIfShouldThrow(const char* assert_name);
+ void throwException();
+ bool always_false();
+ void* getNullPtr();
+
+ // a struct defining a registered test callback
+ struct TestData
+ {
+ // not used for determining uniqueness
+ const char* m_suite; // the test suite in which the test was added
+ const char* m_name; // name of the test function
+ funcType m_f; // a function pointer to the test function
+
+ // fields by which uniqueness of test cases shall be determined
+ const char* m_file; // the file in which the test was registered
+ unsigned m_line; // the line where the test was registered
+
+ TestData(const char* suite, const char* name, funcType f, const char* file, int line)
+ : m_suite(suite)
+ , m_name(name)
+ , m_f(f)
+ , m_file(file)
+ , m_line(line) {}
+
+ bool operator==(const TestData& other) const;
+ };
+
+ struct Subcase
+ {
+ const char* m_name;
+ const char* m_file;
+ int m_line;
+ bool m_entered;
+
+ Subcase(const char* name, const char* file, int line);
+ ~Subcase();
+ Subcase(const Subcase& other);
+ Subcase& operator=(const Subcase& other);
+
+ bool operator==(const Subcase& other) const;
+ operator bool() const { return m_entered; }
+ };
+
+ template <typename L, typename R>
+ String stringifyBinaryExpr(const L& lhs, const char* op, const R& rhs) {
+ return toString(lhs) + " " + op + " " + toString(rhs);
+ }
+
+ // TODO: think about this
+ //struct STATIC_ASSERT_Expression_Too_Complex_Please_Rewrite_As_Binary_Comparison;
+
+ struct Result
+ {
+ bool m_passed;
+ String m_decomposition;
+
+// to fix gcc 4.7 "-Winline" warnings
+#if defined(__GNUC__) && !defined(__clang__)
+ __attribute__((noinline))
+#endif
+ ~Result() {
+ }
+
+ Result(bool passed = false, const String& decomposition = String())
+ : m_passed(passed)
+ , m_decomposition(decomposition) {}
+
+// to fix gcc 4.7 "-Winline" warnings
+#if defined(__GNUC__) && !defined(__clang__)
+ __attribute__((noinline))
+#endif
+ Result&
+ operator=(const Result& other) {
+ m_passed = other.m_passed;
+ m_decomposition = other.m_decomposition;
+
+ return *this;
+ }
+
+ operator bool() { return !m_passed; }
+
+ void invert() { m_passed = !m_passed; }
+
+ // clang-format off
+ //template <typename R> STATIC_ASSERT_Expression_Too_Complex_Please_Rewrite_As_Binary_Comparison& operator+(const R&);
+ //template <typename R> STATIC_ASSERT_Expression_Too_Complex_Please_Rewrite_As_Binary_Comparison& operator-(const R&);
+ //template <typename R> STATIC_ASSERT_Expression_Too_Complex_Please_Rewrite_As_Binary_Comparison& operator/(const R&);
+ //template <typename R> STATIC_ASSERT_Expression_Too_Complex_Please_Rewrite_As_Binary_Comparison& operator*(const R&);
+ //template <typename R> STATIC_ASSERT_Expression_Too_Complex_Please_Rewrite_As_Binary_Comparison& operator&&(const R&);
+ //template <typename R> STATIC_ASSERT_Expression_Too_Complex_Please_Rewrite_As_Binary_Comparison& operator||(const R&);
+ //
+ //template <typename R> STATIC_ASSERT_Expression_Too_Complex_Please_Rewrite_As_Binary_Comparison& operator==(const R&);
+ //template <typename R> STATIC_ASSERT_Expression_Too_Complex_Please_Rewrite_As_Binary_Comparison& operator!=(const R&);
+ //template <typename R> STATIC_ASSERT_Expression_Too_Complex_Please_Rewrite_As_Binary_Comparison& operator<(const R&);
+ //template <typename R> STATIC_ASSERT_Expression_Too_Complex_Please_Rewrite_As_Binary_Comparison& operator<=(const R&);
+ //template <typename R> STATIC_ASSERT_Expression_Too_Complex_Please_Rewrite_As_Binary_Comparison& operator>(const R&);
+ //template <typename R> STATIC_ASSERT_Expression_Too_Complex_Please_Rewrite_As_Binary_Comparison& operator>=(const R&);
+ // clang-format on
+ };
+
+// clang-format off
+
+// for anything below Visual Studio 2005 (VC++6 has no SFINAE - not sure about VS 2003)
+#if defined(_MSC_VER) && _MSC_VER < 1400
+ template <typename L, typename R> bool eq (const L& lhs, const R& rhs) { return lhs == rhs; }
+ template <typename L, typename R> bool neq(const L& lhs, const R& rhs) { return lhs != rhs; }
+ template <typename L, typename R> bool lt (const L& lhs, const R& rhs) { return lhs < rhs; }
+ template <typename L, typename R> bool gt (const L& lhs, const R& rhs) { return lhs > rhs; }
+ template <typename L, typename R> bool lte(const L& lhs, const R& rhs) { return eq(lhs, rhs) != 0 ? lhs < rhs : true; }
+ template <typename L, typename R> bool gte(const L& lhs, const R& rhs) { return eq(lhs, rhs) != 0 ? lhs > rhs : true; }
+#else // _MSC_VER
+ template <typename L, typename R>
+ typename enable_if<can_use_op<L>::value || can_use_op<R>::value, bool>::type eq (const L& lhs, const R& rhs) { return lhs == rhs; }
+ template <typename L, typename R>
+ typename enable_if<can_use_op<L>::value || can_use_op<R>::value, bool>::type neq(const L& lhs, const R& rhs) { return lhs != rhs; }
+ template <typename L, typename R>
+ typename enable_if<can_use_op<L>::value || can_use_op<R>::value, bool>::type lt (const L& lhs, const R& rhs) { return lhs < rhs; }
+ template <typename L, typename R>
+ typename enable_if<can_use_op<L>::value || can_use_op<R>::value, bool>::type gt (const L& lhs, const R& rhs) { return lhs > rhs; }
+ template <typename L, typename R>
+ typename enable_if<can_use_op<L>::value || can_use_op<R>::value, bool>::type lte(const L& lhs, const R& rhs) { return neq(lhs, rhs) ? lhs < rhs : true; }
+ template <typename L, typename R>
+ typename enable_if<can_use_op<L>::value || can_use_op<R>::value, bool>::type gte(const L& lhs, const R& rhs) { return neq(lhs, rhs) ? lhs > rhs : true; }
+
+ inline bool eq (const char* lhs, const char* rhs) { return String(lhs) == String(rhs); }
+ inline bool neq(const char* lhs, const char* rhs) { return String(lhs) != String(rhs); }
+ inline bool lt (const char* lhs, const char* rhs) { return String(lhs) < String(rhs); }
+ inline bool gt (const char* lhs, const char* rhs) { return String(lhs) > String(rhs); }
+ inline bool lte(const char* lhs, const char* rhs) { return String(lhs) <= String(rhs); }
+ inline bool gte(const char* lhs, const char* rhs) { return String(lhs) >= String(rhs); }
+#endif // _MSC_VER
+
+ // clang-format on
+
+ template <typename L>
+ struct Expression_lhs
+ {
+ L lhs;
+
+ Expression_lhs(L in)
+ : lhs(in) {}
+
+ Expression_lhs(const Expression_lhs& other)
+ : lhs(other.lhs) {}
+
+ operator Result() { return Result(!!lhs, toString(lhs)); }
+
+ // clang-format off
+ template <typename R> Result operator==(const R& rhs) { return Result(eq (lhs, rhs), stringifyBinaryExpr(lhs, "==", rhs)); }
+ template <typename R> Result operator!=(const R& rhs) { return Result(neq(lhs, rhs), stringifyBinaryExpr(lhs, "!=", rhs)); }
+ template <typename R> Result operator< (const R& rhs) { return Result(lt (lhs, rhs), stringifyBinaryExpr(lhs, "<" , rhs)); }
+ template <typename R> Result operator<=(const R& rhs) { return Result(lte(lhs, rhs), stringifyBinaryExpr(lhs, "<=", rhs)); }
+ template <typename R> Result operator> (const R& rhs) { return Result(gt (lhs, rhs), stringifyBinaryExpr(lhs, ">" , rhs)); }
+ template <typename R> Result operator>=(const R& rhs) { return Result(gte(lhs, rhs), stringifyBinaryExpr(lhs, ">=", rhs)); }
+ // clang-format on
+ };
+
+ struct ExpressionDecomposer
+ {
+ template <typename L>
+ Expression_lhs<const L&> operator<<(const L& operand) {
+ return Expression_lhs<const L&>(operand);
+ }
+ };
+
+ // forward declarations of functions used by the macros
+ int regTest(void (*f)(void), unsigned line, const char* file, const char* name);
+ int setTestSuiteName(const char* name);
+
+ void addFailedAssert(const char* assert_name);
+
+ void logTestStart(const char* name, const char* file, unsigned line);
+ void logTestEnd();
+
+ void logTestCrashed();
+
+ void logAssert(bool passed, const char* decomposition, bool threw, const char* expr,
+ const char* assert_name, const char* file, int line);
+
+ void logAssertThrows(bool threw, const char* expr, const char* assert_name, const char* file,
+ int line);
+
+ void logAssertThrowsAs(bool threw, bool threw_as, const char* as, const char* expr,
+ const char* assert_name, const char* file, int line);
+
+ void logAssertNothrow(bool threw, const char* expr, const char* assert_name, const char* file,
+ int line);
+
+ bool isDebuggerActive();
+ void writeToDebugConsole(const String&);
+
+ struct TestAccessibleContextState
+ {
+ bool success; // include successful assertions in output
+ bool no_throw; // to skip exceptions-related assertion macros
+ bool no_breaks; // to not break into the debugger
+ const TestData* currentTest;
+ bool hasLoggedCurrentTestStart;
+ int numAssertionsForCurrentTestcase;
+ };
+
+ struct ContextState;
+
+ TestAccessibleContextState* getTestsContextState();
+} // namespace detail
+
+#endif // DOCTEST_CONFIG_DISABLE
+
+class Context
+{
+#if !defined(DOCTEST_CONFIG_DISABLE)
+ detail::ContextState* p;
+
+ void parseArgs(int argc, const char* const* argv, bool withDefaults = false);
+
+#endif // DOCTEST_CONFIG_DISABLE
+
+public:
+ Context(int argc, const char* const* argv);
+
+// to fix gcc 4.7 "-Winline" warnings
+#if defined(__GNUC__) && !defined(__clang__)
+ __attribute__((noinline))
+#endif
+ ~Context();
+
+ void addFilter(const char* filter, const char* value);
+ void setOption(const char* option, int value);
+ void setOption(const char* option, const char* value);
+
+ bool shouldExit();
+
+ int run();
+};
+
+} // namespace doctest
+
+// if registering is not disabled
+#if !defined(DOCTEST_CONFIG_DISABLE)
+
+// registers the test by initializing a dummy var with a function
+#if defined(__GNUC__) && !defined(__clang__)
+#define DOCTEST_REGISTER_FUNCTION(f, name) \
+ static int DOCTEST_ANONYMOUS(DOCTEST_AUTOGEN_VAR_) __attribute__((unused)) = \
+ doctest::detail::regTest(f, __LINE__, __FILE__, name);
+#elif defined(__clang__)
+#define DOCTEST_REGISTER_FUNCTION(f, name) \
+ _Pragma("clang diagnostic push") \
+ _Pragma("clang diagnostic ignored \"-Wglobal-constructors\"") static int \
+ DOCTEST_ANONYMOUS(DOCTEST_AUTOGEN_VAR_) = \
+ doctest::detail::regTest(f, __LINE__, __FILE__, name); \
+ _Pragma("clang diagnostic pop")
+#else // MSVC
+#define DOCTEST_REGISTER_FUNCTION(f, name) \
+ static int DOCTEST_ANONYMOUS(DOCTEST_AUTOGEN_VAR_) = \
+ doctest::detail::regTest(f, __LINE__, __FILE__, name);
+#endif // MSVC
+
+#define DOCTEST_IMPLEMENT_FIXTURE(der, base, func, name) \
+ namespace \
+ { \
+ struct der : base \
+ { void f(); }; \
+ static void func() { \
+ der v; \
+ v.f(); \
+ } \
+ DOCTEST_REGISTER_FUNCTION(func, name) \
+ } \
+ inline void der::f()
+
+#define DOCTEST_CREATE_AND_REGISTER_FUNCTION(f, name) \
+ static void f(); \
+ DOCTEST_REGISTER_FUNCTION(f, name) \
+ inline void f()
+
+// for registering tests
+#define DOCTEST_TEST_CASE(name) \
+ DOCTEST_CREATE_AND_REGISTER_FUNCTION(DOCTEST_ANONYMOUS(DOCTEST_AUTOGEN_FUNC_), name)
+
+// for registering tests with a fixture
+#define DOCTEST_TEST_CASE_FIXTURE(c, name) \
+ DOCTEST_IMPLEMENT_FIXTURE(DOCTEST_ANONYMOUS(DOCTEST_AUTOGEN_CLASS_), c, \
+ DOCTEST_ANONYMOUS(DOCTEST_AUTOGEN_FUNC_), name)
+
+// for subcases
+#if defined(__GNUC__)
+#define DOCTEST_SUBCASE(name) \
+ if(const doctest::detail::Subcase & DOCTEST_ANONYMOUS(DOCTEST_AUTOGEN_SUBCASE_) \
+ __attribute__((unused)) = \
+ doctest::detail::Subcase(name, __FILE__, __LINE__))
+#else // __GNUC__
+#define DOCTEST_SUBCASE(name) \
+ if(const doctest::detail::Subcase & DOCTEST_ANONYMOUS(DOCTEST_AUTOGEN_SUBCASE_) = \
+ doctest::detail::Subcase(name, __FILE__, __LINE__))
+#endif // __GNUC__
+
+// for starting a testsuite block
+#if defined(__GNUC__) && !defined(__clang__)
+#define DOCTEST_TEST_SUITE(name) \
+ static int DOCTEST_ANONYMOUS(DOCTEST_AUTOGEN_VAR_) __attribute__((unused)) = \
+ doctest::detail::setTestSuiteName(name); \
+ void DOCTEST_ANONYMOUS(DOCTEST_AUTOGEN_FOR_SEMICOLON_)()
+#elif defined(__clang__)
+#define DOCTEST_TEST_SUITE(name) \
+ _Pragma("clang diagnostic push") \
+ _Pragma("clang diagnostic ignored \"-Wglobal-constructors\"") static int \
+ DOCTEST_ANONYMOUS(DOCTEST_AUTOGEN_VAR_) = \
+ doctest::detail::setTestSuiteName(name); \
+ _Pragma("clang diagnostic pop") void DOCTEST_ANONYMOUS(DOCTEST_AUTOGEN_FOR_SEMICOLON_)()
+#else // MSVC
+#define DOCTEST_TEST_SUITE(name) \
+ static int DOCTEST_ANONYMOUS(DOCTEST_AUTOGEN_VAR_) = doctest::detail::setTestSuiteName(name); \
+ void DOCTEST_ANONYMOUS(DOCTEST_AUTOGEN_FOR_SEMICOLON_)()
+#endif // MSVC
+
+// for ending a testsuite block
+#if defined(__GNUC__) && !defined(__clang__)
+#define DOCTEST_TEST_SUITE_END \
+ static int DOCTEST_ANONYMOUS(DOCTEST_AUTOGEN_VAR_) __attribute__((unused)) = \
+ doctest::detail::setTestSuiteName(""); \
+ void DOCTEST_ANONYMOUS(DOCTEST_AUTOGEN_TESTSUITE_END_)
+#elif defined(__clang__)
+#define DOCTEST_TEST_SUITE_END \
+ _Pragma("clang diagnostic push") \
+ _Pragma("clang diagnostic ignored \"-Wglobal-constructors\"") static int \
+ DOCTEST_ANONYMOUS(DOCTEST_AUTOGEN_VAR_) = \
+ doctest::detail::setTestSuiteName(""); \
+ _Pragma("clang diagnostic pop") void DOCTEST_ANONYMOUS(DOCTEST_AUTOGEN_TESTSUITE_END_)
+#else // MSVC
+#define DOCTEST_TEST_SUITE_END \
+ static int DOCTEST_ANONYMOUS(DOCTEST_AUTOGEN_VAR_) = doctest::detail::setTestSuiteName(""); \
+ void DOCTEST_ANONYMOUS(DOCTEST_AUTOGEN_TESTSUITE_END_)
+#endif // MSVC
+
+#define DOCTEST_LOG_START() \
+ do { \
+ if(!DOCTEST_GCS()->hasLoggedCurrentTestStart) { \
+ doctest::detail::logTestStart(DOCTEST_GCS()->currentTest->m_name, \
+ DOCTEST_GCS()->currentTest->m_file, \
+ DOCTEST_GCS()->currentTest->m_line); \
+ DOCTEST_GCS()->hasLoggedCurrentTestStart = true; \
+ } \
+ } while(doctest::detail::always_false())
+
+#define DOCTEST_ASSERT_IMPLEMENT(expr, assert_name, false_invert_op) \
+ doctest::detail::Result res; \
+ bool threw = false; \
+ try { \
+ res = doctest::detail::ExpressionDecomposer() << expr; \
+ } catch(...) { threw = true; } \
+ false_invert_op; \
+ if(res || DOCTEST_GCS()->success) { \
+ DOCTEST_LOG_START(); \
+ doctest::detail::logAssert(res.m_passed, res.m_decomposition.c_str(), threw, #expr, \
+ assert_name, __FILE__, __LINE__); \
+ } \
+ DOCTEST_GCS()->numAssertionsForCurrentTestcase++; \
+ if(res) { \
+ doctest::detail::addFailedAssert(assert_name); \
+ DOCTEST_BREAK_INTO_DEBUGGER(); \
+ doctest::detail::checkIfShouldThrow(assert_name); \
+ }
+
+#if defined(__clang__)
+#define DOCTEST_ASSERT_PROXY(expr, assert_name, false_invert_op) \
+ do { \
+ _Pragma("clang diagnostic push") \
+ _Pragma("clang diagnostic ignored \"-Woverloaded-shift-op-parentheses\"") \
+ DOCTEST_ASSERT_IMPLEMENT(expr, assert_name, false_invert_op) \
+ _Pragma("clang diagnostic pop") \
+ } while(doctest::detail::always_false())
+#else // __clang__
+#define DOCTEST_ASSERT_PROXY(expr, assert_name, false_invert_op) \
+ do { \
+ DOCTEST_ASSERT_IMPLEMENT(expr, assert_name, false_invert_op) \
+ } while(doctest::detail::always_false())
+#endif // __clang__
+
+#define DOCTEST_WARN(expr) DOCTEST_ASSERT_PROXY(expr, "WARN", ((void)0))
+#define DOCTEST_CHECK(expr) DOCTEST_ASSERT_PROXY(expr, "CHECK", ((void)0))
+#define DOCTEST_REQUIRE(expr) DOCTEST_ASSERT_PROXY(expr, "REQUIRE", ((void)0))
+
+#define DOCTEST_WARN_FALSE(expr) DOCTEST_ASSERT_PROXY(expr, "WARN_FALSE", res.invert())
+#define DOCTEST_CHECK_FALSE(expr) DOCTEST_ASSERT_PROXY(expr, "CHECK_FALSE", res.invert())
+#define DOCTEST_REQUIRE_FALSE(expr) DOCTEST_ASSERT_PROXY(expr, "REQUIRE_FALSE", res.invert())
+
+#define DOCTEST_ASSERT_THROWS(expr, assert_name) \
+ do { \
+ if(!DOCTEST_GCS()->no_throw) { \
+ bool threw = false; \
+ try { \
+ expr; \
+ } catch(...) { threw = true; } \
+ if(!threw || DOCTEST_GCS()->success) { \
+ DOCTEST_LOG_START(); \
+ doctest::detail::logAssertThrows(threw, #expr, assert_name, __FILE__, __LINE__); \
+ } \
+ DOCTEST_GCS()->numAssertionsForCurrentTestcase++; \
+ if(!threw) { \
+ doctest::detail::addFailedAssert(assert_name); \
+ DOCTEST_BREAK_INTO_DEBUGGER(); \
+ doctest::detail::checkIfShouldThrow(assert_name); \
+ } \
+ } \
+ } while(doctest::detail::always_false())
+
+#define DOCTEST_ASSERT_THROWS_AS(expr, as, assert_name) \
+ do { \
+ if(!DOCTEST_GCS()->no_throw) { \
+ bool threw = false; \
+ bool threw_as = false; \
+ try { \
+ expr; \
+ } catch(as) { \
+ threw = true; \
+ threw_as = true; \
+ } catch(...) { threw = true; } \
+ if(!threw_as || DOCTEST_GCS()->success) { \
+ DOCTEST_LOG_START(); \
+ doctest::detail::logAssertThrowsAs(threw, threw_as, #as, #expr, assert_name, \
+ __FILE__, __LINE__); \
+ } \
+ DOCTEST_GCS()->numAssertionsForCurrentTestcase++; \
+ if(!threw_as) { \
+ doctest::detail::addFailedAssert(assert_name); \
+ DOCTEST_BREAK_INTO_DEBUGGER(); \
+ doctest::detail::checkIfShouldThrow(assert_name); \
+ } \
+ } \
+ } while(doctest::detail::always_false())
+
+#define DOCTEST_ASSERT_NOTHROW(expr, assert_name) \
+ do { \
+ if(!DOCTEST_GCS()->no_throw) { \
+ bool threw = false; \
+ try { \
+ expr; \
+ } catch(...) { threw = true; } \
+ if(threw || DOCTEST_GCS()->success) { \
+ DOCTEST_LOG_START(); \
+ doctest::detail::logAssertNothrow(threw, #expr, assert_name, __FILE__, __LINE__); \
+ } \
+ DOCTEST_GCS()->numAssertionsForCurrentTestcase++; \
+ if(threw) { \
+ doctest::detail::addFailedAssert(assert_name); \
+ DOCTEST_BREAK_INTO_DEBUGGER(); \
+ doctest::detail::checkIfShouldThrow(assert_name); \
+ } \
+ } \
+ } while(doctest::detail::always_false())
+
+#define DOCTEST_WARN_THROWS(expr) DOCTEST_ASSERT_THROWS(expr, "WARN_THROWS")
+#define DOCTEST_CHECK_THROWS(expr) DOCTEST_ASSERT_THROWS(expr, "CHECK_THROWS")
+#define DOCTEST_REQUIRE_THROWS(expr) DOCTEST_ASSERT_THROWS(expr, "REQUIRE_THROWS")
+
+#define DOCTEST_WARN_THROWS_AS(expr, ex) DOCTEST_ASSERT_THROWS_AS(expr, ex, "WARN_THROWS_AS")
+#define DOCTEST_CHECK_THROWS_AS(expr, ex) DOCTEST_ASSERT_THROWS_AS(expr, ex, "CHECK_THROWS_AS")
+#define DOCTEST_REQUIRE_THROWS_AS(expr, ex) DOCTEST_ASSERT_THROWS_AS(expr, ex, "REQUIRE_THROWS_AS")
+
+#define DOCTEST_WARN_NOTHROW(expr) DOCTEST_ASSERT_NOTHROW(expr, "WARN_NOTHROW")
+#define DOCTEST_CHECK_NOTHROW(expr) DOCTEST_ASSERT_NOTHROW(expr, "CHECK_NOTHROW")
+#define DOCTEST_REQUIRE_NOTHROW(expr) DOCTEST_ASSERT_NOTHROW(expr, "REQUIRE_NOTHROW")
+
+// =================================================================================================
+// == WHAT FOLLOWS IS VERSIONS OF THE MACROS THAT DO NOT DO ANY REGISTERING! ==
+// == THIS CAN BE ENABLED BY DEFINING DOCTEST_CONFIG_DISABLE GLOBALLY! ==
+// =================================================================================================
+#else // DOCTEST_CONFIG_DISABLE
+
+#define DOCTEST_IMPLEMENT_FIXTURE(der, base, func, name) \
+ namespace \
+ { \
+ template <typename T> \
+ struct der : base \
+ { void f(); }; \
+ } \
+ template <typename T> \
+ inline void der<T>::f()
+
+#define DOCTEST_CREATE_AND_REGISTER_FUNCTION(f, name) \
+ template <typename T> \
+ static inline void f()
+
+// for registering tests
+#define DOCTEST_TEST_CASE(name) \
+ DOCTEST_CREATE_AND_REGISTER_FUNCTION(DOCTEST_ANONYMOUS(DOCTEST_AUTOGEN_FUNC_), name)
+
+// for registering tests with a fixture
+#define DOCTEST_TEST_CASE_FIXTURE(x, name) \
+ DOCTEST_IMPLEMENT_FIXTURE(DOCTEST_ANONYMOUS(DOCTEST_AUTOGEN_CLASS_), x, \
+ DOCTEST_ANONYMOUS(DOCTEST_AUTOGEN_FUNC_), name)
+
+// for subcases
+#define DOCTEST_SUBCASE(name)
+
+// for starting a testsuite block
+#define DOCTEST_TEST_SUITE(name) void DOCTEST_ANONYMOUS(DOCTEST_AUTOGEN_FOR_SEMICOLON_)()
+
+// for ending a testsuite block
+#define DOCTEST_TEST_SUITE_END void DOCTEST_ANONYMOUS(DOCTEST_AUTOGEN_TESTSUITE_END_)
+
+#define DOCTEST_WARN(expr) ((void)0)
+#define DOCTEST_WARN_FALSE(expr) ((void)0)
+#define DOCTEST_WARN_THROWS(expr) ((void)0)
+#define DOCTEST_WARN_THROWS_AS(expr, ex) ((void)0)
+#define DOCTEST_WARN_NOTHROW(expr) ((void)0)
+#define DOCTEST_CHECK(expr) ((void)0)
+#define DOCTEST_CHECK_FALSE(expr) ((void)0)
+#define DOCTEST_CHECK_THROWS(expr) ((void)0)
+#define DOCTEST_CHECK_THROWS_AS(expr, ex) ((void)0)
+#define DOCTEST_CHECK_NOTHROW(expr) ((void)0)
+#define DOCTEST_REQUIRE(expr) ((void)0)
+#define DOCTEST_REQUIRE_FALSE(expr) ((void)0)
+#define DOCTEST_REQUIRE_THROWS(expr) ((void)0)
+#define DOCTEST_REQUIRE_THROWS_AS(expr, ex) ((void)0)
+#define DOCTEST_REQUIRE_NOTHROW(expr) ((void)0)
+
+#endif // DOCTEST_CONFIG_DISABLE
+
+// BDD style macros
+#define DOCTEST_SCENARIO(name) TEST_CASE("Scenario: " name)
+#define DOCTEST_GIVEN(name) SUBCASE(" Given: " name)
+#define DOCTEST_WHEN(name) SUBCASE(" When: " name)
+#define DOCTEST_AND_WHEN(name) SUBCASE("And when: " name)
+#define DOCTEST_THEN(name) SUBCASE(" Then: " name)
+#define DOCTEST_AND_THEN(name) SUBCASE(" And: " name)
+
+// == SHORT VERSIONS OF THE MACROS
+#if !defined(DOCTEST_CONFIG_NO_SHORT_MACRO_NAMES)
+
+#define TEST_CASE DOCTEST_TEST_CASE
+#define TEST_CASE_FIXTURE DOCTEST_TEST_CASE_FIXTURE
+#define SUBCASE DOCTEST_SUBCASE
+#define TEST_SUITE DOCTEST_TEST_SUITE
+#define TEST_SUITE_END DOCTEST_TEST_SUITE_END
+#define WARN DOCTEST_WARN
+#define WARN_FALSE DOCTEST_WARN_FALSE
+#define WARN_THROWS DOCTEST_WARN_THROWS
+#define WARN_THROWS_AS DOCTEST_WARN_THROWS_AS
+#define WARN_NOTHROW DOCTEST_WARN_NOTHROW
+#define CHECK DOCTEST_CHECK
+#define CHECK_FALSE DOCTEST_CHECK_FALSE
+#define CHECK_THROWS DOCTEST_CHECK_THROWS
+#define CHECK_THROWS_AS DOCTEST_CHECK_THROWS_AS
+#define CHECK_NOTHROW DOCTEST_CHECK_NOTHROW
+#define REQUIRE DOCTEST_REQUIRE
+#define REQUIRE_FALSE DOCTEST_REQUIRE_FALSE
+#define REQUIRE_THROWS DOCTEST_REQUIRE_THROWS
+#define REQUIRE_THROWS_AS DOCTEST_REQUIRE_THROWS_AS
+#define REQUIRE_NOTHROW DOCTEST_REQUIRE_NOTHROW
+
+#define SCENARIO DOCTEST_SCENARIO
+#define GIVEN DOCTEST_GIVEN
+#define WHEN DOCTEST_WHEN
+#define AND_WHEN DOCTEST_AND_WHEN
+#define THEN DOCTEST_THEN
+#define AND_THEN DOCTEST_AND_THEN
+
+#endif // DOCTEST_CONFIG_NO_SHORT_MACRO_NAMES
+
+// this is here to clear the 'current test suite' for the current translation unit - at the top
+DOCTEST_TEST_SUITE_END();
+
+#endif // DOCTEST_LIBRARY_INCLUDED
+
+#if defined(__clang__)
+#pragma clang diagnostic pop
+#endif // __clang__
+
+#if defined(__GNUC__) && !defined(__clang__)
+#if __GNUC__ > 4 || (__GNUC__ == 4 && __GNUC_MINOR__ > 6)
+#pragma GCC diagnostic pop
+#endif // > gcc 4.6
+#endif // __GNUC__
+
+#ifdef _MSC_VER
+#pragma warning(pop)
+#endif // _MSC_VER
diff --git a/doctest/parts/doctest_impl.h b/doctest/parts/doctest_impl.h
new file mode 100644
index 0000000..0bd37d9
--- /dev/null
+++ b/doctest/parts/doctest_impl.h
@@ -0,0 +1,1742 @@
+#if defined(__clang__)
+#pragma clang diagnostic push
+#pragma clang diagnostic ignored "-Wpadded"
+#pragma clang diagnostic ignored "-Wglobal-constructors"
+#pragma clang diagnostic ignored "-Wexit-time-destructors"
+#pragma clang diagnostic ignored "-Wmissing-prototypes"
+#pragma clang diagnostic ignored "-Wsign-conversion"
+#pragma clang diagnostic ignored "-Wshorten-64-to-32"
+#pragma clang diagnostic ignored "-Wmissing-variable-declarations"
+#pragma clang diagnostic ignored "-Wcovered-switch-default"
+#pragma clang diagnostic ignored "-Wmissing-noreturn"
+#endif // __clang__
+
+#if defined(__GNUC__) && !defined(__clang__)
+#if __GNUC__ > 4 || (__GNUC__ == 4 && __GNUC_MINOR__ > 6)
+#pragma GCC diagnostic push
+#endif // > gcc 4.6
+#pragma GCC diagnostic ignored "-Wconversion"
+#pragma GCC diagnostic ignored "-Weffc++"
+#pragma GCC diagnostic ignored "-Wsign-conversion"
+#pragma GCC diagnostic ignored "-Wstrict-overflow"
+#pragma GCC diagnostic ignored "-Wmissing-declarations"
+#pragma GCC diagnostic ignored "-Winline"
+#if __GNUC__ > 4 || (__GNUC__ == 4 && __GNUC_MINOR__ > 6)
+#pragma GCC diagnostic ignored "-Wzero-as-null-pointer-constant"
+#endif // > gcc 4.6
+//#pragma GCC diagnostic ignored "-Wlong-long"
+#endif // __GNUC__
+
+#ifdef _MSC_VER
+#pragma warning(push)
+#pragma warning(disable : 4996) // The compiler encountered a deprecated declaration
+#pragma warning(disable : 4267) // 'var' : conversion from 'size_t' to 'type', possible loss of data
+#pragma warning(disable : 4706) // assignment within conditional expression
+#pragma warning(disable : 4512) // 'class' : assignment operator could not be generated
+#pragma warning(disable : 4127) // conditional expression is constant
+#endif // _MSC_VER
+
+#if(defined(DOCTEST_CONFIG_IMPLEMENT) || defined(DOCTEST_CONFIG_IMPLEMENT_WITH_MAIN))
+#ifndef DOCTEST_LIBRARY_IMPLEMENTATION
+#define DOCTEST_LIBRARY_IMPLEMENTATION
+
+// required includes - will go only in one translation unit!
+#include <ctime>
+#include <cmath>
+#include <new>
+#include <cstdio>
+#include <cstdlib>
+#include <cstring>
+#include <limits>
+#include <sstream>
+#include <iomanip>
+
+namespace doctest
+{
+namespace detail
+{
+ // not using std::strlen() because of valgrind errors when optimizations are turned on
+ // 'Invalid read of size 4' when the test suite len (with '\0') is not a multiple of 4
+ // for details see http://stackoverflow.com/questions/35671155
+ size_t my_strlen(const char* in) {
+ const char* temp = in;
+ while(*temp)
+ ++temp;
+ return temp - in;
+ }
+
+ template <typename T>
+ T my_max(const T& lhs, const T& rhs) {
+ return lhs > rhs ? lhs : rhs;
+ }
+
+ // case insensitive strcmp
+ int stricmp(char const* a, char const* b) {
+ for(;; a++, b++) {
+ int d = tolower(*a) - tolower(*b);
+ if(d != 0 || !*a)
+ return d;
+ }
+ }
+
+ 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
+ {
+ Big,
+ Little
+ };
+
+ static Arch which() {
+ union _
+ {
+ int asInt;
+ char asChar[sizeof(int)];
+ } u;
+
+ u.asInt = 1;
+ return (u.asChar[sizeof(int) - 1] == 1) ? Big : Little;
+ }
+ };
+
+ 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 char const* bytes = static_cast<unsigned char const*>(object);
+ std::ostringstream os;
+ os << "0x" << std::setfill('0') << std::hex;
+ for(; i != end; i += inc)
+ os << std::setw(2) << static_cast<unsigned>(bytes[i]);
+ return os.str().c_str();
+ }
+
+ std::ostream* createStream() { return new std::ostringstream(); }
+ String getStreamResult(std::ostream* in) {
+ return static_cast<std::ostringstream*>(in)->str().c_str();
+ }
+ void freeStream(std::ostream* in) { delete in; }
+
+#ifndef DOCTEST_CONFIG_DISABLE
+ template <class T>
+ class Vector
+ {
+ unsigned m_size;
+ unsigned m_capacity;
+ T* m_buffer;
+
+ public:
+ Vector();
+ Vector(unsigned num, const T& val = T());
+ Vector(const Vector& other);
+ ~Vector();
+ Vector& operator=(const Vector& other);
+
+ T* data() { return m_buffer; }
+ const T* data() const { return m_buffer; }
+ unsigned size() const { return m_size; }
+
+ T& operator[](unsigned index) { return m_buffer[index]; }
+ const T& operator[](unsigned index) const { return m_buffer[index]; }
+
+ void clear();
+ void pop_back();
+ void push_back(const T& item);
+ void resize(unsigned num, const T& val = T());
+ };
+
+ // the default Hash() implementation that the HashTable class uses - returns 0 - very naive
+ // specialize for better HashTable performance
+ template <typename T>
+ unsigned Hash(const T&) {
+ return 0;
+ }
+
+ template <class T>
+ class HashTable
+ {
+ Vector<Vector<T> > buckets;
+
+ public:
+ explicit HashTable(unsigned num_buckets)
+ : buckets(num_buckets) {}
+
+ bool has(const T& in) const {
+ const Vector<T>& bucket = buckets[Hash(in) % buckets.size()];
+ for(unsigned i = 0; i < bucket.size(); ++i)
+ if(bucket[i] == in)
+ return true;
+ return false;
+ }
+
+ void insert(const T& in) {
+ if(!has(in))
+ buckets[Hash(in) % buckets.size()].push_back(in);
+ }
+
+ void clear() {
+ for(unsigned i = 0; i < buckets.size(); ++i)
+ buckets[i].clear();
+ }
+
+ const Vector<Vector<T> >& getBuckets() const { return buckets; }
+ };
+
+ // this holds both parameters for the command line and runtime data for tests
+ struct ContextState : TestAccessibleContextState
+ {
+ // == parameters from the command line
+
+ detail::Vector<detail::Vector<String> > filters;
+
+ String order_by; // how tests should be ordered
+ unsigned rand_seed; // the seed for rand ordering
+
+ unsigned first; // the first (matching) test to be executed
+ unsigned last; // the last (matching) test to be executed
+
+ int abort_after; // stop tests after this many failed assertions
+ bool case_sensitive; // if filtering should be case sensitive
+ bool exit; // if the program should be exited after the tests are ran/whatever
+ bool no_overrides; // to disable overrides from code
+ bool no_exitcode; // if the framework should return 0 as the exitcode
+ bool no_run; // to not run the tests at all (can be done with an "*" exclude)
+ bool no_colors; // if output to the console should be colorized
+ bool no_path_in_filenames; // if the path to files should be removed from the output
+
+ bool help; // to print the help
+ bool version; // to print the version
+ bool count; // if only the count of matching tests is to be retreived
+ bool list_test_cases; // to list all tests matching the filters
+ bool list_test_suites; // to list all suites matching the filters
+ bool hash_table_histogram; // if the hash table should be printed as a histogram
+
+ // == data for the tests being ran
+
+ int numAssertions;
+ int numFailedAssertions;
+ int numFailedAssertionsForCurrentTestcase;
+
+ // stuff for subcases
+ HashTable<Subcase> subcasesPassed;
+ HashTable<int> subcasesEnteredLevels;
+ Vector<Subcase> subcasesStack;
+ int subcasesCurrentLevel;
+ bool subcasesHasSkipped;
+
+ void resetRunData() {
+ numAssertions = 0;
+ numFailedAssertions = 0;
+ }
+
+ ContextState()
+ : filters(6) // 6 different filters total
+ , subcasesPassed(100)
+ , subcasesEnteredLevels(100) {
+ resetRunData();
+ }
+ };
+
+ ContextState*& getContextState();
+#endif
+} // namespace detail
+
+String::String(const char* in) {
+ m_str = static_cast<char*>(malloc(detail::my_strlen(in) + 1));
+ strcpy(m_str, in);
+}
+
+String::String(const String& other)
+ : m_str(0) {
+ copy(other);
+}
+
+void String::copy(const String& other) {
+ if(m_str)
+ free(m_str);
+ m_str = 0;
+
+ if(other.m_str) {
+ m_str = static_cast<char*>(malloc(detail::my_strlen(other.m_str) + 1));
+ strcpy(m_str, other.m_str);
+ }
+}
+
+String::~String() {
+ if(m_str)
+ free(m_str);
+}
+
+String& String::operator=(const String& other) {
+ if(this != &other)
+ copy(other);
+ return *this;
+}
+
+String String::operator+(const String& other) const { return String(m_str) += other; }
+
+String& String::operator+=(const String& other) {
+ using namespace detail;
+ if(m_str == 0) {
+ copy(other);
+ } else if(other.m_str != 0) {
+ char* newStr = static_cast<char*>(malloc(my_strlen(m_str) + my_strlen(other.m_str) + 1));
+ strcpy(newStr, m_str);
+ strcpy(newStr + my_strlen(m_str), other.m_str);
+ free(m_str);
+ m_str = newStr;
+ }
+ return *this;
+}
+
+unsigned String::size() const { return m_str ? detail::my_strlen(m_str) : 0; }
+unsigned String::length() const { return size(); }
+
+int String::compare(const char* other, bool no_case) const {
+ if(no_case)
+ return detail::stricmp(m_str, other);
+ return strcmp(m_str, other);
+}
+
+int String::compare(const String& other, bool no_case) const {
+ if(no_case)
+ return detail::stricmp(m_str, other.m_str);
+ return strcmp(m_str, other.m_str);
+}
+
+std::ostream& operator<<(std::ostream& stream, const String& in) {
+ stream << in.c_str();
+ return stream;
+}
+
+Approx::Approx(double value)
+ : m_epsilon(static_cast<double>(std::numeric_limits<float>::epsilon()) * 100)
+ , m_scale(1.0)
+ , m_value(value) {}
+
+bool operator==(double lhs, Approx const& rhs) {
+ // Thanks to Richard Harris for his help refining this formula
+ return fabs(lhs - rhs.m_value) <
+ rhs.m_epsilon * (rhs.m_scale + detail::my_max(fabs(lhs), fabs(rhs.m_value)));
+}
+
+String Approx::toString() const { return String("Approx( ") + doctest::toString(m_value) + " )"; }
+
+String toString(const char* in) { return String("\"") + (in ? in : "{null string}") + "\""; }
+String toString(bool in) { return in ? "true" : "false"; }
+String toString(float in) { return detail::fpToString(in, 5) + "f"; }
+String toString(double in) { return detail::fpToString(in, 10); }
+String toString(double long in) { return detail::fpToString(in, 15); }
+
+String toString(char in) {
+ char buf[64];
+ if(in < ' ')
+ sprintf(buf, "%d", in);
+ else
+ sprintf(buf, "%c", in);
+ return buf;
+}
+
+String toString(char unsigned in) {
+ char buf[64];
+ if(in < ' ')
+ sprintf(buf, "%ud", in);
+ else
+ sprintf(buf, "%c", in);
+ return buf;
+}
+
+String toString(int short in) {
+ char buf[64];
+ sprintf(buf, "%d", in);
+ return buf;
+}
+
+String toString(int short unsigned in) {
+ char buf[64];
+ sprintf(buf, "%u", in);
+ return buf;
+}
+
+String toString(int in) {
+ char buf[64];
+ sprintf(buf, "%d", in);
+ return buf;
+}
+
+String toString(int unsigned in) {
+ char buf[64];
+ sprintf(buf, "%u", in);
+ return buf;
+}
+
+String toString(int long in) {
+ char buf[64];
+ sprintf(buf, "%ld", in);
+ return buf;
+}
+
+String toString(int long unsigned in) {
+ char buf[64];
+ sprintf(buf, "%lu", in);
+ return buf;
+}
+
+#ifdef DOCTEST_CONFIG_WITH_LONG_LONG
+String toString(int long long in) {
+ char buf[64];
+ sprintf(buf, "%lld", in);
+ return buf;
+}
+String toString(int long long unsigned in) {
+ char buf[64];
+ sprintf(buf, "%llu", in);
+ return buf;
+}
+#endif // DOCTEST_CONFIG_WITH_LONG_LONG
+
+} // namespace doctest
+
+#if defined(DOCTEST_CONFIG_DISABLE)
+namespace doctest
+{
+Context::Context(int, const char* const*) {}
+Context::~Context() {}
+void Context::addFilter(const char*, const char*) {}
+void Context::setOption(const char*, int) {}
+void Context::setOption(const char*, const char*) {}
+bool Context::shouldExit() { return false; }
+int Context::run() { return 0; }
+} // namespace doctest
+#else // DOCTEST_CONFIG_DISABLE
+
+#if !defined(DOCTEST_CONFIG_COLORS_NONE)
+#if !defined(DOCTEST_CONFIG_COLORS_WINDOWS) && !defined(DOCTEST_CONFIG_COLORS_ANSI)
+#ifdef DOCTEST_PLATFORM_WINDOWS
+#define DOCTEST_CONFIG_COLORS_WINDOWS
+#else // linux
+#define DOCTEST_CONFIG_COLORS_ANSI
+#endif // platform
+#endif // DOCTEST_CONFIG_COLORS_WINDOWS && DOCTEST_CONFIG_COLORS_ANSI
+#endif // DOCTEST_CONFIG_COLORS_NONE
+
+#define DOCTEST_PRINTF_COLORED(buffer, color) \
+ do { \
+ if(buffer[0] != 0) { \
+ doctest::detail::Color col(color); \
+ printf("%s", buffer); \
+ } \
+ } while(doctest::detail::always_false())
+
+// the number of buckets used for the hash set
+#if !defined(DOCTEST_HASH_TABLE_NUM_BUCKETS)
+#define DOCTEST_HASH_TABLE_NUM_BUCKETS 1024
+#endif // DOCTEST_HASH_TABLE_NUM_BUCKETS
+
+// the buffer size used for snprintf() calls
+#if !defined(DOCTEST_SNPRINTF_BUFFER_LENGTH)
+#define DOCTEST_SNPRINTF_BUFFER_LENGTH 1024
+#endif // DOCTEST_SNPRINTF_BUFFER_LENGTH
+
+#if defined(_MSC_VER) || defined(__MINGW32__)
+extern "C" __declspec(dllimport) void __stdcall OutputDebugStringA(const char*);
+extern "C" __declspec(dllimport) int __stdcall IsDebuggerPresent();
+#endif // DOCTEST_PLATFORM_WINDOWS
+
+#ifdef DOCTEST_CONFIG_COLORS_ANSI
+#include <unistd.h>
+#endif // DOCTEST_CONFIG_COLORS_ANSI
+
+#ifdef DOCTEST_CONFIG_COLORS_WINDOWS
+
+// defines for a leaner windows.h
+#ifndef WIN32_MEAN_AND_LEAN
+#define WIN32_MEAN_AND_LEAN
+#endif // WIN32_MEAN_AND_LEAN
+#ifndef VC_EXTRA_LEAN
+#define VC_EXTRA_LEAN
+#endif // VC_EXTRA_LEAN
+#ifndef NOMINMAX
+#define NOMINMAX
+#endif // NOMINMAX
+
+// not sure what AfxWin.h is for - here I do what Catch does
+#ifdef __AFXDLL
+#include <AfxWin.h>
+#else
+#include <windows.h>
+#endif
+
+#endif // DOCTEST_CONFIG_COLORS_WINDOWS
+
+namespace doctest
+{
+namespace detail
+{
+ bool TestData::operator==(const TestData& other) const {
+ return m_line == other.m_line && strcmp(m_file, other.m_file) == 0;
+ }
+
+ void checkIfShouldThrow(const char* assert_name) {
+ if(strncmp(assert_name, "REQUIRE", 7) == 0)
+ throwException();
+
+ if(strncmp(assert_name, "CHECK", 5) == 0 && getContextState()->abort_after > 0) {
+ if(getContextState()->numFailedAssertions >= getContextState()->abort_after)
+ throwException();
+ }
+ }
+ void throwException() { throw doctest::detail::TestFailureException(); }
+ bool always_false() { return false; }
+ void* getNullPtr() { return 0; }
+
+ // lowers ascii letters
+ char tolower(const char c) { return ((c >= 'A' && c <= 'Z') ? static_cast<char>(c + 32) : c); }
+
+ // matching of a string against a wildcard mask (case sensitivity configurable) taken from
+ // http://www.emoticode.net/c/simple-wildcard-string-compare-globbing-function.html
+ int wildcmp(const char* str, const char* wild, bool caseSensitive) {
+ const char* cp = 0;
+ const char* mp = 0;
+
+ // rolled my own tolower() to not include more headers
+ while((*str) && (*wild != '*')) {
+ if((caseSensitive ? (*wild != *str) : (tolower(*wild) != tolower(*str))) &&
+ (*wild != '?')) {
+ return 0;
+ }
+ wild++;
+ str++;
+ }
+
+ while(*str) {
+ if(*wild == '*') {
+ if(!*++wild) {
+ return 1;
+ }
+ mp = wild;
+ cp = str + 1;
+ } else if((caseSensitive ? (*wild == *str) : (tolower(*wild) == tolower(*str))) ||
+ (*wild == '?')) {
+ wild++;
+ str++;
+ } else {
+ wild = mp;
+ str = cp++;
+ }
+ }
+
+ while(*wild == '*') {
+ wild++;
+ }
+ return !*wild;
+ }
+
+ // C string hash function (djb2) - taken from http://www.cse.yorku.ca/~oz/hash.html
+ unsigned hashStr(unsigned const char* str) {
+ unsigned long hash = 5381;
+ char c;
+
+ while((c = *str++))
+ hash = ((hash << 5) + hash) + c; // hash * 33 + c
+
+ return hash;
+ }
+
+ // checks if the name matches any of the filters (and can be configured what to do when empty)
+ int matchesAny(const char* name, Vector<String> filters, int matchEmpty, bool caseSensitive) {
+ if(filters.size() == 0 && matchEmpty)
+ return 1;
+ for(unsigned i = 0; i < filters.size(); ++i)
+ if(wildcmp(name, filters[i].c_str(), caseSensitive))
+ return 1;
+ return 0;
+ }
+
+ template <class T>
+ Vector<T>::Vector()
+ : m_size(0)
+ , m_capacity(0)
+ , m_buffer(0) {}
+
+ template <class T>
+ Vector<T>::Vector(unsigned num, const T& val)
+ : m_size(num)
+ , m_capacity(num)
+ , m_buffer(static_cast<T*>(malloc(sizeof(T) * m_capacity))) {
+ for(unsigned i = 0; i < m_size; ++i)
+ new(m_buffer + i) T(val);
+ }
+
+ template <class T>
+ Vector<T>::Vector(const Vector& other)
+ : m_size(other.m_size)
+ , m_capacity(other.m_capacity)
+ , m_buffer(static_cast<T*>(malloc(sizeof(T) * m_capacity))) {
+ for(unsigned i = 0; i < m_size; ++i)
+ new(m_buffer + i) T(other.m_buffer[i]);
+ }
+
+ template <class T>
+ Vector<T>::~Vector() {
+ for(unsigned i = 0; i < m_size; ++i)
+ (*(m_buffer + i)).~T();
+ free(m_buffer);
+ }
+
+ template <class T>
+ Vector<T>& Vector<T>::operator=(const Vector& other) {
+ if(this != &other) {
+ for(unsigned i = 0; i < m_size; ++i)
+ (*(m_buffer + i)).~T();
+ free(m_buffer);
+
+ m_size = other.m_size;
+ m_capacity = other.m_capacity;
+
+ m_buffer = static_cast<T*>(malloc(sizeof(T) * m_capacity));
+ for(unsigned i = 0; i < m_size; ++i)
+ new(m_buffer + i) T(other.m_buffer[i]);
+ }
+ return *this;
+ }
+
+ template <class T>
+ void Vector<T>::clear() {
+ for(unsigned i = 0; i < m_size; ++i)
+ (*(m_buffer + i)).~T();
+ m_size = 0;
+ }
+
+ template <class T>
+ void Vector<T>::pop_back() {
+ if(m_size > 0)
+ (*(m_buffer + --m_size)).~T();
+ }
+
+ template <class T>
+ void Vector<T>::push_back(const T& item) {
+ if(m_size < m_capacity) {
+ new(m_buffer + m_size++) T(item);
+ } else {
+ if(m_capacity == 0)
+ m_capacity = 5; // initial capacity
+ else
+ m_capacity *= 2; // capacity growth factor
+ T* temp = static_cast<T*>(malloc(sizeof(T) * m_capacity));
+ for(unsigned i = 0; i < m_size; ++i) {
+ new(temp + i) T(m_buffer[i]);
+ (*(m_buffer + i)).~T();
+ }
+ new(temp + m_size++) T(item);
+ free(m_buffer);
+ m_buffer = temp;
+ }
+ }
+
+ template <class T>
+ void Vector<T>::resize(unsigned num, const T& val) {
+ if(num < m_size) {
+ for(unsigned i = num; i < m_size; ++i)
+ (*(m_buffer + i)).~T();
+ m_size = num;
+ } else {
+ if(num > m_capacity) {
+ if(m_capacity == 0) {
+ m_buffer = static_cast<T*>(malloc(sizeof(T) * num));
+ } else {
+ T* temp = static_cast<T*>(malloc(sizeof(T) * num));
+ for(unsigned i = 0; i < m_size; ++i) {
+ new(temp + i) T(m_buffer[i]);
+ (*(m_buffer + i)).~T();
+ }
+ }
+
+ for(unsigned i = m_size; i < num; ++i)
+ new(m_buffer + i) T(val);
+
+ m_size = num;
+ m_capacity = num;
+ }
+ }
+ }
+
+ template <>
+ unsigned Hash(const Subcase& in) {
+ return hashStr(reinterpret_cast<unsigned const char*>(in.m_file)) ^ in.m_line;
+ }
+
+ template <>
+ unsigned Hash(const String& in) {
+ return hashStr(reinterpret_cast<unsigned const char*>(in.c_str()));
+ }
+
+ template <>
+ unsigned Hash(const int& in) {
+ return in;
+ }
+
+ // the current ContextState with which tests are being executed
+ ContextState*& getContextState() {
+ static ContextState* data = 0;
+ return data;
+ }
+
+ TestAccessibleContextState* getTestsContextState() { return getContextState(); }
+
+ Subcase::Subcase(const char* name, const char* file, int line)
+ : m_name(name)
+ , m_file(file)
+ , m_line(line)
+ , m_entered(false) {
+ ContextState* s = getContextState();
+
+ // if we have already completed it
+ if(s->subcasesPassed.has(*this))
+ return;
+
+ // if a Subcase on the same level has already been entered
+ if(s->subcasesEnteredLevels.has(s->subcasesCurrentLevel)) {
+ s->subcasesHasSkipped = true;
+ return;
+ }
+
+ s->subcasesStack.push_back(*this);
+ s->hasLoggedCurrentTestStart = false;
+ if(s->hasLoggedCurrentTestStart)
+ logTestEnd();
+
+ s->subcasesEnteredLevels.insert(s->subcasesCurrentLevel++);
+ m_entered = true;
+ }
+
+ Subcase::~Subcase() {
+ if(m_entered) {
+ ContextState* s = getContextState();
+
+ s->subcasesCurrentLevel--;
+ // only mark the subcase as passed if no subcases have been skipped
+ if(s->subcasesHasSkipped == false)
+ s->subcasesPassed.insert(*this);
+
+ s->subcasesStack.pop_back();
+ s->hasLoggedCurrentTestStart = false;
+ if(s->hasLoggedCurrentTestStart)
+ logTestEnd();
+ }
+ }
+
+ Subcase::Subcase(const Subcase& other)
+ : m_name(other.m_name)
+ , m_file(other.m_file)
+ , m_line(other.m_line)
+ , m_entered(other.m_entered) {}
+
+ Subcase& Subcase::operator=(const Subcase& other) {
+ m_name = other.m_name;
+ m_file = other.m_file;
+ m_line = other.m_line;
+ m_entered = other.m_entered;
+ return *this;
+ }
+
+ bool Subcase::operator==(const Subcase& other) const {
+ return m_line == other.m_line && strcmp(m_file, other.m_file) == 0;
+ }
+
+ unsigned Hash(const TestData& in) {
+ return hashStr(reinterpret_cast<unsigned const char*>(in.m_file)) ^ in.m_line;
+ }
+
+ // for sorting tests by file/line
+ int fileOrderComparator(const void* a, const void* b) {
+ const TestData* lhs = *static_cast<TestData* const*>(a);
+ const TestData* rhs = *static_cast<TestData* const*>(b);
+#ifdef _MSC_VER
+ // this is needed because MSVC gives different case for drive letters
+ // for __FILE__ when evaluated in a header and a source file
+ int res = stricmp(lhs->m_file, rhs->m_file);
+#else // _MSC_VER
+ int res = strcmp(lhs->m_file, rhs->m_file);
+#endif // _MSC_VER
+ if(res != 0)
+ return res;
+ return static_cast<int>(lhs->m_line - rhs->m_line);
+ }
+
+ // for sorting tests by suite/file/line
+ int suiteOrderComparator(const void* a, const void* b) {
+ const TestData* lhs = *static_cast<TestData* const*>(a);
+ const TestData* rhs = *static_cast<TestData* const*>(b);
+
+ int res = strcmp(lhs->m_suite, rhs->m_suite);
+ if(res != 0)
+ return res;
+ return fileOrderComparator(a, b);
+ }
+
+ // for sorting tests by name/suite/file/line
+ int nameOrderComparator(const void* a, const void* b) {
+ const TestData* lhs = *static_cast<TestData* const*>(a);
+ const TestData* rhs = *static_cast<TestData* const*>(b);
+
+ int res = strcmp(lhs->m_name, rhs->m_name);
+ if(res != 0)
+ return res;
+ return suiteOrderComparator(a, b);
+ }
+
+ // holds the current test suite
+ const char*& getCurrentTestSuite() {
+ static const char* data = 0;
+ return data;
+ }
+
+ // sets the current test suite
+ int setTestSuiteName(const char* name) {
+ getCurrentTestSuite() = name;
+ return 0;
+ }
+
+ // all the registered tests
+ HashTable<TestData>& getRegisteredTests() {
+ static HashTable<TestData> data(DOCTEST_HASH_TABLE_NUM_BUCKETS);
+ return data;
+ }
+
+ // used by the macros for registering tests
+ int regTest(funcType f, unsigned line, const char* file, const char* name) {
+ getRegisteredTests().insert(TestData(getCurrentTestSuite(), name, f, file, line));
+ return 0;
+ }
+
+ struct Color
+ {
+ enum Code
+ {
+ None = 0,
+ White,
+ Red,
+ Green,
+ Blue,
+ Cyan,
+ Yellow,
+ Grey,
+
+ Bright = 0x10,
+
+ BrightRed = Bright | Red,
+ BrightGreen = Bright | Green,
+ LightGrey = Bright | Grey,
+ BrightWhite = Bright | White
+ };
+ Color(Code code) { use(code); }
+ ~Color() { use(None); }
+
+ void use(Code code);
+
+ private:
+ Color(Color const& other);
+ };
+
+ void Color::use(Code
+#ifndef DOCTEST_CONFIG_COLORS_NONE
+ code
+#endif // DOCTEST_CONFIG_COLORS_NONE
+ ) {
+ ContextState* p = getContextState();
+ if(p->no_colors)
+ return;
+#ifdef DOCTEST_CONFIG_COLORS_ANSI
+ if(isatty(STDOUT_FILENO)) {
+ const char* col = "";
+ // clang-format off
+ switch(code) {
+ case Color::Red: col = "[0;31m"; break;
+ case Color::Green: col = "[0;32m"; break;
+ case Color::Blue: col = "[0:34m"; break;
+ case Color::Cyan: col = "[0;36m"; break;
+ case Color::Yellow: col = "[0;33m"; break;
+ case Color::Grey: col = "[1;30m"; break;
+ case Color::LightGrey: col = "[0;37m"; break;
+ case Color::BrightRed: col = "[1;31m"; break;
+ case Color::BrightGreen: col = "[1;32m"; break;
+ case Color::BrightWhite: col = "[1;37m"; break;
+ case Color::Bright: // invalid
+ case Color::None:
+ case Color::White:
+ default: col = "[0m";
+ }
+ // clang-format on
+ printf("\033%s", col);
+ }
+#endif // DOCTEST_CONFIG_COLORS_ANSI
+
+#ifdef DOCTEST_CONFIG_COLORS_WINDOWS
+ static HANDLE stdoutHandle(GetStdHandle(STD_OUTPUT_HANDLE));
+ static WORD originalForegroundAttributes;
+ static WORD originalBackgroundAttributes;
+ static bool attrsInitted = false;
+ if(!attrsInitted) {
+ attrsInitted = true;
+ CONSOLE_SCREEN_BUFFER_INFO csbiInfo;
+ GetConsoleScreenBufferInfo(stdoutHandle, &csbiInfo);
+ originalForegroundAttributes =
+ csbiInfo.wAttributes &
+ ~(BACKGROUND_GREEN | BACKGROUND_RED | BACKGROUND_BLUE | BACKGROUND_INTENSITY);
+ originalBackgroundAttributes =
+ csbiInfo.wAttributes &
+ ~(FOREGROUND_GREEN | FOREGROUND_RED | FOREGROUND_BLUE | FOREGROUND_INTENSITY);
+ }
+
+#define DOCTEST_SET_ATTR(x) SetConsoleTextAttribute(stdoutHandle, x | originalBackgroundAttributes)
+
+ // clang-format off
+ switch (code) {
+ case Color::White: DOCTEST_SET_ATTR(FOREGROUND_GREEN | FOREGROUND_RED | FOREGROUND_BLUE); break;
+ case Color::Red: DOCTEST_SET_ATTR(FOREGROUND_RED); break;
+ case Color::Green: DOCTEST_SET_ATTR(FOREGROUND_GREEN); break;
+ case Color::Blue: DOCTEST_SET_ATTR(FOREGROUND_BLUE); break;
+ case Color::Cyan: DOCTEST_SET_ATTR(FOREGROUND_BLUE | FOREGROUND_GREEN); break;
+ case Color::Yellow: DOCTEST_SET_ATTR(FOREGROUND_RED | FOREGROUND_GREEN); break;
+ case Color::Grey: DOCTEST_SET_ATTR(0); break;
+ case Color::LightGrey: DOCTEST_SET_ATTR(FOREGROUND_INTENSITY); break;
+ case Color::BrightRed: DOCTEST_SET_ATTR(FOREGROUND_INTENSITY | FOREGROUND_RED); break;
+ case Color::BrightGreen: DOCTEST_SET_ATTR(FOREGROUND_INTENSITY | FOREGROUND_GREEN); break;
+ case Color::BrightWhite: DOCTEST_SET_ATTR(FOREGROUND_INTENSITY | FOREGROUND_GREEN | FOREGROUND_RED | FOREGROUND_BLUE); break;
+ case Color::None:
+ case Color::Bright: // invalid
+ default: DOCTEST_SET_ATTR(originalForegroundAttributes);
+ }
+// clang-format on
+#undef DOCTEST_SET_ATTR
+#endif // DOCTEST_CONFIG_COLORS_WINDOWS
+ }
+
+ // this is needed because MSVC does not permit mixing 2 exception handling schemes in a function
+ int callTestFunc(funcType f) {
+ int res = EXIT_SUCCESS;
+ try {
+ f();
+ if(getContextState()->numFailedAssertionsForCurrentTestcase)
+ res = EXIT_FAILURE;
+ } catch(const TestFailureException&) { res = EXIT_FAILURE; } catch(...) {
+ DOCTEST_LOG_START();
+ logTestCrashed();
+ res = EXIT_FAILURE;
+ }
+ return res;
+ }
+
+ // depending on the current options this will remove the path of filenames
+ const char* fileForOutput(const char* file) {
+ if(getContextState()->no_path_in_filenames) {
+ const char* back = strrchr(file, '\\');
+ const char* forward = strrchr(file, '/');
+ if(back || forward) {
+ if(back > forward)
+ forward = back;
+ return forward + 1;
+ }
+ }
+ return file;
+ }
+
+#ifdef DOCTEST_PLATFORM_MAC
+#include <sys/types.h>
+#include <unistd.h>
+#include <sys/sysctl.h>
+ // The following function is taken directly from the following technical note:
+ // http://developer.apple.com/library/mac/#qa/qa2004/qa1361.html
+ // Returns true if the current process is being debugged (either
+ // running under the debugger or has a debugger attached post facto).
+ bool isDebuggerActive() {
+ int mib[4];
+ struct kinfo_proc info;
+ size_t size;
+ // Initialize the flags so that, if sysctl fails for some bizarre
+ // reason, we get a predictable result.
+ info.kp_proc.p_flag = 0;
+ // Initialize mib, which tells sysctl the info we want, in this case
+ // we're looking for information about a specific process ID.
+ mib[0] = CTL_KERN;
+ mib[1] = KERN_PROC;
+ mib[2] = KERN_PROC_PID;
+ mib[3] = getpid();
+ // Call sysctl.
+ size = sizeof(info);
+ if(sysctl(mib, sizeof(mib) / sizeof(*mib), &info, &size, 0, 0) != 0) {
+ fprintf(stderr, "\n** Call to sysctl failed - unable to determine if debugger is "
+ "active **\n\n");
+ return false;
+ }
+ // We're being debugged if the P_TRACED flag is set.
+ return ((info.kp_proc.p_flag & P_TRACED) != 0);
+ }
+#elif defined(_MSC_VER) || defined(__MINGW32__)
+ bool isDebuggerActive() { return ::IsDebuggerPresent() != 0; }
+#else
+ bool isDebuggerActive() { return false; }
+#endif // Platform
+
+#ifdef DOCTEST_PLATFORM_WINDOWS
+ void myOutputDebugString(const String& text) { ::OutputDebugStringA(text.c_str()); }
+#else
+ // TODO: integration with XCode and other IDEs
+ void myOutputDebugString(const String&) {}
+#endif // Platform
+
+ const char* getSeparator() {
+ return "===============================================================================\n";
+ }
+
+ void printToDebugConsole(const String& text) {
+ if(isDebuggerActive())
+ myOutputDebugString(text.c_str());
+ }
+
+ void addFailedAssert(const char* assert_name) {
+ if(strncmp(assert_name, "WARN", 4) != 0) {
+ getContextState()->numFailedAssertionsForCurrentTestcase++;
+ getContextState()->numFailedAssertions++;
+ }
+ }
+
+ void logTestStart(const char* name, const char* file, unsigned line) {
+ const char* newLine = "\n";
+
+ char loc[DOCTEST_SNPRINTF_BUFFER_LENGTH];
+ DOCTEST_SNPRINTF(loc, DOCTEST_COUNTOF(loc), "%s(%d)\n", fileForOutput(file), line);
+
+ char msg[DOCTEST_SNPRINTF_BUFFER_LENGTH];
+ DOCTEST_SNPRINTF(msg, DOCTEST_COUNTOF(msg), "%s\n", name);
+
+ DOCTEST_PRINTF_COLORED(getSeparator(), Color::Yellow);
+ DOCTEST_PRINTF_COLORED(loc, Color::LightGrey);
+ DOCTEST_PRINTF_COLORED(msg, Color::None);
+
+ String subcaseStuff = "";
+ Vector<Subcase>& subcasesStack = getContextState()->subcasesStack;
+ String tabulation;
+ for(unsigned i = 0; i < subcasesStack.size(); ++i) {
+ tabulation += " ";
+ char subcase[DOCTEST_SNPRINTF_BUFFER_LENGTH];
+ DOCTEST_SNPRINTF(subcase, DOCTEST_COUNTOF(loc), "%s%s\n", tabulation.c_str(),
+ subcasesStack[i].m_name);
+ DOCTEST_PRINTF_COLORED(subcase, Color::None);
+ subcaseStuff += subcase;
+ }
+
+ DOCTEST_PRINTF_COLORED(newLine, Color::None);
+
+ printToDebugConsole(String(getSeparator()) + loc + msg + subcaseStuff.c_str() + newLine);
+ }
+
+ void logTestEnd() {}
+
+ void logTestCrashed() {
+ char msg[DOCTEST_SNPRINTF_BUFFER_LENGTH];
+
+ DOCTEST_SNPRINTF(msg, DOCTEST_COUNTOF(msg), " TEST CASE FAILED! (threw exception)\n\n");
+
+ DOCTEST_PRINTF_COLORED(msg, Color::Red);
+
+ printToDebugConsole(String(msg));
+ }
+
+ void logAssert(bool passed, const char* decomposition, bool threw, const char* expr,
+ const char* assert_name, const char* file, int line) {
+ char loc[DOCTEST_SNPRINTF_BUFFER_LENGTH];
+ DOCTEST_SNPRINTF(loc, DOCTEST_COUNTOF(loc), "%s(%d)", fileForOutput(file), line);
+
+ char msg[DOCTEST_SNPRINTF_BUFFER_LENGTH];
+ if(passed)
+ DOCTEST_SNPRINTF(msg, DOCTEST_COUNTOF(msg), " PASSED!\n");
+ else
+ DOCTEST_SNPRINTF(msg, DOCTEST_COUNTOF(msg), " FAILED! %s\n",
+ (threw ? "(threw exception)" : ""));
+
+ char info1[DOCTEST_SNPRINTF_BUFFER_LENGTH];
+ DOCTEST_SNPRINTF(info1, DOCTEST_COUNTOF(info1), " %s( %s )\n", assert_name, expr);
+
+ char info2[DOCTEST_SNPRINTF_BUFFER_LENGTH];
+ char info3[DOCTEST_SNPRINTF_BUFFER_LENGTH];
+ info2[0] = 0;
+ info3[0] = 0;
+ if(!threw) {
+ DOCTEST_SNPRINTF(info2, DOCTEST_COUNTOF(info2), "with expansion:\n");
+ DOCTEST_SNPRINTF(info3, DOCTEST_COUNTOF(info3), " %s( %s )\n", assert_name,
+ decomposition);
+ }
+
+ DOCTEST_PRINTF_COLORED(loc, Color::LightGrey);
+ DOCTEST_PRINTF_COLORED(msg, passed ? Color::BrightGreen : Color::Red);
+ DOCTEST_PRINTF_COLORED(info1, Color::Green);
+ DOCTEST_PRINTF_COLORED(info2, Color::None);
+ DOCTEST_PRINTF_COLORED(info3, Color::Green);
+ DOCTEST_PRINTF_COLORED("\n", Color::None);
+
+ printToDebugConsole(String(loc) + msg + info1 + info2 + info3 + "\n");
+ }
+
+ void logAssertThrows(bool threw, const char* expr, const char* assert_name, const char* file,
+ int line) {
+ char loc[DOCTEST_SNPRINTF_BUFFER_LENGTH];
+ DOCTEST_SNPRINTF(loc, DOCTEST_COUNTOF(loc), "%s(%d)", fileForOutput(file), line);
+
+ char msg[DOCTEST_SNPRINTF_BUFFER_LENGTH];
+ if(threw)
+ DOCTEST_SNPRINTF(msg, DOCTEST_COUNTOF(msg), " PASSED!\n");
+ else
+ DOCTEST_SNPRINTF(msg, DOCTEST_COUNTOF(msg), " FAILED!\n");
+
+ char info1[DOCTEST_SNPRINTF_BUFFER_LENGTH];
+ DOCTEST_SNPRINTF(info1, DOCTEST_COUNTOF(info1), " %s( %s )\n\n", assert_name, expr);
+
+ DOCTEST_PRINTF_COLORED(loc, Color::LightGrey);
+ DOCTEST_PRINTF_COLORED(msg, threw ? Color::BrightGreen : Color::Red);
+ DOCTEST_PRINTF_COLORED(info1, Color::Green);
+
+ printToDebugConsole(String(loc) + msg + info1);
+ }
+
+ void logAssertThrowsAs(bool threw, bool threw_as, const char* as, const char* expr,
+ const char* assert_name, const char* file, int line) {
+ char loc[DOCTEST_SNPRINTF_BUFFER_LENGTH];
+ DOCTEST_SNPRINTF(loc, DOCTEST_COUNTOF(loc), "%s(%d)", fileForOutput(file), line);
+
+ char msg[DOCTEST_SNPRINTF_BUFFER_LENGTH];
+ if(threw_as)
+ DOCTEST_SNPRINTF(msg, DOCTEST_COUNTOF(msg), " PASSED!\n");
+ else
+ DOCTEST_SNPRINTF(msg, DOCTEST_COUNTOF(msg), " FAILED! %s\n",
+ (threw ? "(threw something else)" : "(didn't throw at all)"));
+
+ char info1[DOCTEST_SNPRINTF_BUFFER_LENGTH];
+ DOCTEST_SNPRINTF(info1, DOCTEST_COUNTOF(info1), " %s( %s, %s )\n\n", assert_name, expr,
+ as);
+
+ DOCTEST_PRINTF_COLORED(loc, Color::LightGrey);
+ DOCTEST_PRINTF_COLORED(msg, threw_as ? Color::BrightGreen : Color::Red);
+ DOCTEST_PRINTF_COLORED(info1, Color::Green);
+
+ printToDebugConsole(String(loc) + msg + info1);
+ }
+
+ void logAssertNothrow(bool threw, const char* expr, const char* assert_name, const char* file,
+ int line) {
+ char loc[DOCTEST_SNPRINTF_BUFFER_LENGTH];
+ DOCTEST_SNPRINTF(loc, DOCTEST_COUNTOF(loc), "%s(%d)", fileForOutput(file), line);
+
+ char msg[DOCTEST_SNPRINTF_BUFFER_LENGTH];
+ if(!threw)
+ DOCTEST_SNPRINTF(msg, DOCTEST_COUNTOF(msg), " PASSED!\n");
+ else
+ DOCTEST_SNPRINTF(msg, DOCTEST_COUNTOF(msg), " FAILED!\n");
+
+ char info1[DOCTEST_SNPRINTF_BUFFER_LENGTH];
+ DOCTEST_SNPRINTF(info1, DOCTEST_COUNTOF(info1), " %s( %s )\n\n", assert_name, expr);
+
+ DOCTEST_PRINTF_COLORED(loc, Color::LightGrey);
+ DOCTEST_PRINTF_COLORED(msg, !threw ? Color::BrightGreen : Color::Red);
+ DOCTEST_PRINTF_COLORED(info1, Color::Green);
+
+ printToDebugConsole(String(loc) + msg + info1);
+ }
+
+ // the implementation of parseFlag()
+ bool parseFlagImpl(int argc, const char* const* argv, const char* pattern) {
+ for(int i = argc - 1; i >= 0; --i) {
+ const char* temp = strstr(argv[i], pattern);
+ if(temp && my_strlen(temp) == my_strlen(pattern)) {
+ // eliminate strings in which the chars before the option are not '-'
+ bool noBadCharsFound = true;
+ while(temp != argv[i]) {
+ if(*--temp != '-') {
+ noBadCharsFound = false;
+ break;
+ }
+ }
+ if(noBadCharsFound)
+ return true;
+ }
+ }
+ return false;
+ }
+
+ // locates a flag on the command line
+ bool parseFlag(int argc, const char* const* argv, const char* pattern) {
+#ifndef DOCTEST_CONFIG_NO_UNPREFIXED_OPTIONS
+ if(!parseFlagImpl(argc, argv, pattern))
+ return parseFlagImpl(argc, argv, pattern + 3); // 3 for "dt-"
+ return true;
+#else // DOCTEST_CONFIG_NO_UNPREFIXED_OPTIONS
+ return parseFlagImpl(argc, argv, pattern);
+#endif // DOCTEST_CONFIG_NO_UNPREFIXED_OPTIONS
+ }
+
+ // the implementation of parseOption()
+ bool parseOptionImpl(int argc, const char* const* argv, const char* pattern, String& res) {
+ for(int i = argc - 1; i >= 0; --i) {
+ const char* temp = strstr(argv[i], pattern);
+ if(temp) {
+ // eliminate matches in which the chars before the option are not '-'
+ bool noBadCharsFound = true;
+ const char* curr = argv[i];
+ while(curr != temp) {
+ if(*curr++ != '-') {
+ noBadCharsFound = false;
+ break;
+ }
+ }
+ if(noBadCharsFound) {
+ temp += my_strlen(pattern);
+ unsigned len = my_strlen(temp);
+ if(len) {
+ res = temp;
+ return true;
+ }
+ }
+ }
+ }
+ return false;
+ }
+
+ // parses an option and returns the string after the '=' character
+ bool parseOption(int argc, const char* const* argv, const char* pattern, String& res,
+ const String& defaultVal = String()) {
+ res = defaultVal;
+#ifndef DOCTEST_CONFIG_NO_UNPREFIXED_OPTIONS
+ if(!parseOptionImpl(argc, argv, pattern, res))
+ return parseOptionImpl(argc, argv, pattern + 3, res); // 3 for "dt-"
+ return true;
+#else // DOCTEST_CONFIG_NO_UNPREFIXED_OPTIONS
+ return parseOptionImpl(argc, argv, pattern, res);
+#endif // DOCTEST_CONFIG_NO_UNPREFIXED_OPTIONS
+ }
+
+ // parses a comma separated list of words after a pattern in one of the arguments in argv
+ bool parseCommaSepArgs(int argc, const char* const* argv, const char* pattern,
+ Vector<String>& res) {
+ String filtersString;
+ if(parseOption(argc, argv, pattern, filtersString)) {
+ // tokenize with "," as a separator
+ char* pch = strtok(filtersString.c_str(), ","); // modifies the string
+ while(pch != 0) {
+ if(my_strlen(pch))
+ res.push_back(pch);
+ pch = strtok(0, ","); // uses the strtok() internal state to go to the next token
+ }
+ return true;
+ }
+ return false;
+ }
+
+ enum optionType
+ {
+ option_bool,
+ option_int
+ };
+
+ // parses an int/bool option from the command line
+ bool parseIntOption(int argc, const char* const* argv, const char* pattern, optionType type,
+ int& res) {
+ String parsedValue;
+ if(parseOption(argc, argv, pattern, parsedValue)) {
+ if(type == 0) {
+ // boolean
+ const char positive[][5] = {"1", "true", "on", "yes"}; // 5 - strlen("true") + 1
+ const char negative[][6] = {"0", "false", "off", "no"}; // 6 - strlen("false") + 1
+
+ // if the value matches any of the positive/negative possibilities
+ for(unsigned i = 0; i < 4; i++) {
+ if(parsedValue.compare(positive[i], true) == 0) {
+ res = 1;
+ return true;
+ }
+ if(parsedValue.compare(negative[i], true) == 0) {
+ res = 0;
+ return true;
+ }
+ }
+ } else {
+ // integer
+ int theInt = atoi(parsedValue.c_str());
+ if(theInt != 0) {
+ res = theInt;
+ return true;
+ }
+ }
+ }
+ return false;
+ }
+
+ void printVersion() {
+ DOCTEST_PRINTF_COLORED("[doctest] ", Color::Cyan);
+ printf("doctest version is \"%s\"\n", DOCTEST_VERSION);
+ }
+
+ void printHelp() {
+ printVersion();
+ DOCTEST_PRINTF_COLORED("[doctest]\n", Color::Cyan);
+ DOCTEST_PRINTF_COLORED("[doctest] ", Color::Cyan);
+ printf("boolean values: \"1/on/yes/true\" or \"0/off/no/false\"\n");
+ DOCTEST_PRINTF_COLORED("[doctest] ", Color::Cyan);
+ printf("filter values: \"str1,str2,str3\" (comma separated strings)\n");
+ DOCTEST_PRINTF_COLORED("[doctest]\n", Color::Cyan);
+ DOCTEST_PRINTF_COLORED("[doctest] ", Color::Cyan);
+ printf("filters use wildcards for matching strings\n");
+ DOCTEST_PRINTF_COLORED("[doctest] ", Color::Cyan);
+ printf("something passes a filter if any of the strings in a filter matches\n");
+ DOCTEST_PRINTF_COLORED("[doctest]\n", Color::Cyan);
+ DOCTEST_PRINTF_COLORED("[doctest] ", Color::Cyan);
+ printf("ALL FLAGS, OPTIONS AND FILTERS ALSO AVAILABLE WITH A \"dt-\" PREFIX!!!\n");
+ DOCTEST_PRINTF_COLORED("[doctest]\n", Color::Cyan);
+ DOCTEST_PRINTF_COLORED("[doctest] ", Color::Cyan);
+ printf("Query flags - the program quits after them. Available:\n\n");
+ printf(" -?, --help, -h prints this message\n");
+ printf(" -v, --version prints the version\n");
+ printf(" -c, --count prints the number of matching tests\n");
+ printf(" -ltc, --list-test-cases lists all matching tests by name\n");
+ printf(" -lts, --list-test-suites lists all matching test suites\n\n");
+ //printf(" -hth, --hash-table-histogram undocumented\n");
+ // ==================================================================================== << 79
+ DOCTEST_PRINTF_COLORED("[doctest] ", Color::Cyan);
+ printf("The available <int>/<string> options/filters are:\n\n");
+ printf(" -tc, --test-case=<filters> filters tests by their name\n");
+ printf(" -tce, --test-case-exclude=<filters> filters OUT tests by their name\n");
+ printf(" -sf, --source-file=<filters> filters tests by their file\n");
+ printf(" -sfe, --source-file-exclude=<filters> filters OUT tests by their file\n");
+ printf(" -ts, --test-suite=<filters> filters tests by their test suite\n");
+ printf(" -tse, --test-suite-exclude=<filters> filters OUT tests by their test suite\n");
+ printf(" -ob, --order-by=<string> how the tests should be ordered\n");
+ printf(" <string> - by [file/suite/name/rand]\n");
+ printf(" -rs, --rand-seed=<int> seed for random ordering\n");
+ printf(" -f, --first=<int> the first test passing the filters to\n");
+ printf(" execute - for range-based execution\n");
+ printf(" -l, --last=<int> the last test passing the filters to\n");
+ printf(" execute - for range-based execution\n");
+ printf(" -aa, --abort-after=<int> stop after <int> failed assertions\n\n");
+ DOCTEST_PRINTF_COLORED("[doctest] ", Color::Cyan);
+ printf("Bool options - can be used like flags and true is assumed. Available:\n\n");
+ printf(" -s, --success=<bool> include successful assertions in output\n");
+ printf(" -cs, --case-sensitive=<bool> filters being treated as case sensitive\n");
+ printf(" -e, --exit=<bool> exits after the tests finish\n");
+ printf(" -no, --no-overrides=<bool> disables procedural overrides of options\n");
+ printf(" -nt, --no-throw=<bool> skips exceptions-related assert checks\n");
+ printf(" -ne, --no-exitcode=<bool> returns (or exits) always with success\n");
+ printf(" -nr, --no-run=<bool> skips all runtime doctest operations\n");
+ printf(" -nc, --no-colors=<bool> disables colors in output\n");
+ printf(" -nb, --no-breaks=<bool> disables breakpoints in debuggers\n");
+ printf(" -npf, --no-path-filenames=<bool> only filenames and no paths in output\n\n");
+ // ==================================================================================== << 79
+
+ DOCTEST_PRINTF_COLORED("[doctest] ", Color::Cyan);
+ printf("for more information visit the project documentation\n\n");
+ }
+} // namespace detail
+
+Context::Context(int argc, const char* const* argv)
+ : p(new detail::ContextState) {
+ using namespace detail;
+
+ parseArgs(argc, argv, true);
+
+ p->help = false;
+ p->version = false;
+ p->count = false;
+ p->list_test_cases = false;
+ p->list_test_suites = false;
+ p->hash_table_histogram = false;
+ if(parseFlag(argc, argv, "dt-help") || parseFlag(argc, argv, "dt-h") ||
+ parseFlag(argc, argv, "dt-?")) {
+ p->help = true;
+ p->exit = true;
+ }
+ if(parseFlag(argc, argv, "dt-version") || parseFlag(argc, argv, "dt-v")) {
+ p->version = true;
+ p->exit = true;
+ }
+ if(parseFlag(argc, argv, "dt-count") || parseFlag(argc, argv, "dt-c")) {
+ p->count = true;
+ p->exit = true;
+ }
+ if(parseFlag(argc, argv, "dt-list-test-cases") || parseFlag(argc, argv, "dt-ltc")) {
+ p->list_test_cases = true;
+ p->exit = true;
+ }
+ if(parseFlag(argc, argv, "dt-list-test-suites") || parseFlag(argc, argv, "dt-lts")) {
+ p->list_test_suites = true;
+ p->exit = true;
+ }
+ if(parseFlag(argc, argv, "dt-hash-table-histogram") || parseFlag(argc, argv, "dt-hth")) {
+ p->hash_table_histogram = true;
+ p->exit = true;
+ }
+}
+
+Context::~Context() { delete p; }
+
+// parses args
+void Context::parseArgs(int argc, const char* const* argv, bool withDefaults) {
+ using namespace detail;
+
+ // clang-format off
+ parseCommaSepArgs(argc, argv, "dt-source-file=", p->filters[0]);
+ parseCommaSepArgs(argc, argv, "dt-sf=", p->filters[0]);
+ parseCommaSepArgs(argc, argv, "dt-source-file-exclude=",p->filters[1]);
+ parseCommaSepArgs(argc, argv, "dt-sfe=", p->filters[1]);
+ parseCommaSepArgs(argc, argv, "dt-test-suite=", p->filters[2]);
+ parseCommaSepArgs(argc, argv, "dt-ts=", p->filters[2]);
+ parseCommaSepArgs(argc, argv, "dt-test-suite-exclude=", p->filters[3]);
+ parseCommaSepArgs(argc, argv, "dt-tse=", p->filters[3]);
+ parseCommaSepArgs(argc, argv, "dt-test-case=", p->filters[4]);
+ parseCommaSepArgs(argc, argv, "dt-tc=", p->filters[4]);
+ parseCommaSepArgs(argc, argv, "dt-test-case-exclude=", p->filters[5]);
+ parseCommaSepArgs(argc, argv, "dt-tce=", p->filters[5]);
+ // clang-format on
+
+ int intRes = 0;
+ String strRes;
+
+#define DOCTEST_PARSE_AS_BOOL_OR_FLAG(name, sname, var, default) \
+ if(parseIntOption(argc, argv, DOCTEST_STR_CONCAT_TOSTR(name, =), option_bool, intRes) || \
+ parseIntOption(argc, argv, DOCTEST_STR_CONCAT_TOSTR(sname, =), option_bool, intRes)) \
+ p->var = !!intRes; \
+ else if(parseFlag(argc, argv, #name) || parseFlag(argc, argv, #sname)) \
+ p->var = 1; \
+ else if(withDefaults) \
+ p->var = default
+
+#define DOCTEST_PARSE_INT_OPTION(name, sname, var, default) \
+ if(parseIntOption(argc, argv, DOCTEST_STR_CONCAT_TOSTR(name, =), option_int, intRes) || \
+ parseIntOption(argc, argv, DOCTEST_STR_CONCAT_TOSTR(sname, =), option_int, intRes)) \
+ p->var = intRes; \
+ else if(withDefaults) \
+ p->var = default
+
+#define DOCTEST_PARSE_STR_OPTION(name, sname, var, default) \
+ if(parseOption(argc, argv, DOCTEST_STR_CONCAT_TOSTR(name, =), strRes, default) || \
+ parseOption(argc, argv, DOCTEST_STR_CONCAT_TOSTR(sname, =), strRes, default) || \
+ withDefaults) \
+ p->var = strRes
+
+ // clang-format off
+ DOCTEST_PARSE_STR_OPTION(dt-order-by, dt-ob, order_by, "file");
+ DOCTEST_PARSE_INT_OPTION(dt-rand-seed, dt-rs, rand_seed, 0);
+
+ DOCTEST_PARSE_INT_OPTION(dt-first, dt-f, first, 1);
+ DOCTEST_PARSE_INT_OPTION(dt-last, dt-l, last, 0);
+
+ DOCTEST_PARSE_INT_OPTION(dt-abort-after, dt-aa, abort_after, 0);
+
+ DOCTEST_PARSE_AS_BOOL_OR_FLAG(dt-success, dt-s, success, 0);
+ DOCTEST_PARSE_AS_BOOL_OR_FLAG(dt-case-sensitive, dt-cs, case_sensitive, 0);
+ DOCTEST_PARSE_AS_BOOL_OR_FLAG(dt-exit, dt-e, exit, 0);
+ DOCTEST_PARSE_AS_BOOL_OR_FLAG(dt-no-overrides, dt-no, no_overrides, 0);
+ DOCTEST_PARSE_AS_BOOL_OR_FLAG(dt-no-throw, dt-nt, no_throw, 0);
+ DOCTEST_PARSE_AS_BOOL_OR_FLAG(dt-no-exitcode, dt-ne, no_exitcode, 0);
+ DOCTEST_PARSE_AS_BOOL_OR_FLAG(dt-no-run, dt-nr, no_run, 0);
+ DOCTEST_PARSE_AS_BOOL_OR_FLAG(dt-no-colors, dt-nc, no_colors, 0);
+ DOCTEST_PARSE_AS_BOOL_OR_FLAG(dt-no-breaks, dt-nb, no_breaks, 0);
+ DOCTEST_PARSE_AS_BOOL_OR_FLAG(dt-no-path-filenames, dt-npf, no_path_in_filenames, 0);
+// clang-format on
+
+#undef DOCTEST_PARSE_STR_OPTION
+#undef DOCTEST_PARSE_INT_OPTION
+#undef DOCTEST_PARSE_AS_BOOL_OR_FLAG
+}
+
+// allows the user to add procedurally to the filters from the command line
+void Context::addFilter(const char* filter, const char* value) { setOption(filter, value); }
+
+// allows the user to override procedurally the int/bool options from the command line
+void Context::setOption(const char* option, int value) {
+ setOption(option, toString(value).c_str());
+}
+
+// allows the user to override procedurally the string options from the command line
+void Context::setOption(const char* option, const char* value) {
+ using namespace detail;
+
+ if(!p->no_overrides) {
+ String argv = String(option) + "=" + value;
+ const char* lvalue = argv.c_str();
+ parseArgs(1, &lvalue);
+ }
+}
+
+// users should query this in their main() and exit the program if true
+bool Context::shouldExit() { return p->exit; }
+
+// the main function that does all the filtering and test running
+int Context::run() {
+ using namespace detail;
+
+ getContextState() = p;
+ p->resetRunData();
+
+ // handle version, help and no_run
+ if(p->no_run || p->version || p->help) {
+ if(p->version)
+ printVersion();
+ if(p->help)
+ printHelp();
+
+ return EXIT_SUCCESS;
+ }
+
+ printVersion();
+ DOCTEST_PRINTF_COLORED("[doctest] ", Color::Cyan);
+ printf("run with \"-dt-help\" for options\n");
+
+ unsigned i = 0; // counter used for loops - here for VC6
+ const Vector<Vector<TestData> >& buckets = getRegisteredTests().getBuckets();
+
+ Vector<const TestData*> testArray;
+ for(i = 0; i < buckets.size(); i++)
+ for(unsigned k = 0; k < buckets[i].size(); k++)
+ testArray.push_back(&buckets[i][k]);
+
+ if(p->hash_table_histogram) {
+ // find the most full bucket
+ unsigned maxInBucket = 1;
+ for(i = 0; i < buckets.size(); i++)
+ if(buckets[i].size() > maxInBucket)
+ maxInBucket = buckets[i].size();
+
+ // print a prettified histogram
+ DOCTEST_PRINTF_COLORED("[doctest] ", Color::Cyan);
+ printf("hash table bucket histogram\n");
+ printf("============================================================\n");
+ printf("#bucket |count| relative count\n");
+ printf("============================================================\n");
+ for(i = 0; i < buckets.size(); i++) {
+ printf("bucket %4d |%4d |", static_cast<int>(i), buckets[i].size());
+
+ float ratio = static_cast<float>(buckets[i].size()) / static_cast<float>(maxInBucket);
+ unsigned numStars = static_cast<unsigned>(ratio * 41);
+ for(unsigned k = 0; k < numStars; ++k)
+ printf("*");
+ printf("\n");
+ }
+ printf("\n");
+ return EXIT_SUCCESS;
+ }
+
+ // sort the collected records
+ if(p->order_by.compare("file", true) == 0) {
+ qsort(testArray.data(), testArray.size(), sizeof(TestData*), fileOrderComparator);
+ } else if(p->order_by.compare("suite", true) == 0) {
+ qsort(testArray.data(), testArray.size(), sizeof(TestData*), suiteOrderComparator);
+ } else if(p->order_by.compare("name", true) == 0) {
+ qsort(testArray.data(), testArray.size(), sizeof(TestData*), nameOrderComparator);
+ } else if(p->order_by.compare("rand", true) == 0) {
+ srand(p->rand_seed);
+
+ // random_shuffle implementation
+ const TestData** first = testArray.data();
+ for(i = testArray.size() - 1; i > 0; --i) {
+ int idxToSwap = rand() % (i + 1);
+
+ const TestData* temp = first[i];
+
+ first[i] = first[idxToSwap];
+ first[idxToSwap] = temp;
+ }
+ }
+
+ if(p->list_test_cases) {
+ DOCTEST_PRINTF_COLORED("[doctest] ", Color::Cyan);
+ printf("listing all test case names\n");
+ }
+
+ HashTable<String> testSuitesPassingFilters(100);
+ if(p->list_test_suites) {
+ DOCTEST_PRINTF_COLORED("[doctest] ", Color::Cyan);
+ printf("listing all test suites\n");
+ }
+
+ unsigned numTestsPassingFilters = 0;
+ unsigned numFailed = 0;
+ // invoke the registered functions if they match the filter criteria (or just count them)
+ for(i = 0; i < testArray.size(); i++) {
+ const TestData& data = *testArray[i];
+ if(!matchesAny(data.m_file, p->filters[0], 1, p->case_sensitive))
+ continue;
+ if(matchesAny(data.m_file, p->filters[1], 0, p->case_sensitive))
+ continue;
+ if(!matchesAny(data.m_suite, p->filters[2], 1, p->case_sensitive))
+ continue;
+ if(matchesAny(data.m_suite, p->filters[3], 0, p->case_sensitive))
+ continue;
+ if(!matchesAny(data.m_name, p->filters[4], 1, p->case_sensitive))
+ continue;
+ if(matchesAny(data.m_name, p->filters[5], 0, p->case_sensitive))
+ continue;
+
+ numTestsPassingFilters++;
+
+ // do not execute the test if we are to only count the number of filter passing tests
+ if(p->count)
+ continue;
+
+ // print the name of the test and don't execute it
+ if(p->list_test_cases) {
+ printf("%s\n", data.m_name);
+ continue;
+ }
+
+ // print the name of the test suite if not done already and don't execute it
+ if(p->list_test_suites) {
+ if(!testSuitesPassingFilters.has(data.m_suite)) {
+ printf("%s\n", data.m_suite);
+ testSuitesPassingFilters.insert(data.m_suite);
+ }
+ continue;
+ }
+
+ // skip the test if it is not in the execution range
+ if((p->last < numTestsPassingFilters && p->first <= p->last) ||
+ (p->first > numTestsPassingFilters))
+ continue;
+
+ // execute the test if it passes all the filtering
+ {
+#ifdef _MSC_VER
+//__try {
+#endif // _MSC_VER
+
+ p->currentTest = &data;
+
+ // if logging successful tests - force the start log
+ p->hasLoggedCurrentTestStart = false;
+ if(p->success)
+ DOCTEST_LOG_START();
+
+ unsigned didFail = 0;
+ p->subcasesPassed.clear();
+ do {
+ // reset the assertion state
+ p->numAssertionsForCurrentTestcase = 0;
+ p->numFailedAssertionsForCurrentTestcase = 0;
+
+ // reset some of the fields for subcases (except for the set of fully passed ones)
+ p->subcasesHasSkipped = false;
+ p->subcasesCurrentLevel = 0;
+ p->subcasesEnteredLevels.clear();
+
+ // execute the test
+ didFail += callTestFunc(data.m_f);
+ p->numAssertions += p->numAssertionsForCurrentTestcase;
+
+ // exit this loop if enough assertions have failed
+ if(p->abort_after > 0 && p->numFailedAssertions >= p->abort_after)
+ p->subcasesHasSkipped = false;
+
+ // if the start has been logged
+ if(p->hasLoggedCurrentTestStart)
+ logTestEnd();
+ p->hasLoggedCurrentTestStart = false;
+
+ } while(p->subcasesHasSkipped == true);
+
+ if(didFail > 0)
+ numFailed++;
+
+ // stop executing tests if enough assertions have failed
+ if(p->abort_after > 0 && p->numFailedAssertions >= p->abort_after)
+ break;
+
+#ifdef _MSC_VER
+//} __except(1) {
+// printf("Unknown SEH exception caught!\n");
+// numFailed++;
+//}
+#endif // _MSC_VER
+ }
+ }
+
+ DOCTEST_PRINTF_COLORED(getSeparator(), Color::Yellow);
+ if(p->count || p->list_test_cases || p->list_test_suites) {
+ DOCTEST_PRINTF_COLORED("[doctest] ", Color::Cyan);
+ printf("number of tests passing the current filters: %d\n", numTestsPassingFilters);
+ } else {
+ char buff[DOCTEST_SNPRINTF_BUFFER_LENGTH];
+
+ DOCTEST_PRINTF_COLORED("[doctest] ", Color::Cyan);
+
+ DOCTEST_SNPRINTF(buff, DOCTEST_COUNTOF(buff), "test cases: %4d", numTestsPassingFilters);
+ DOCTEST_PRINTF_COLORED(buff, Color::None);
+ DOCTEST_SNPRINTF(buff, DOCTEST_COUNTOF(buff), " | ");
+ DOCTEST_PRINTF_COLORED(buff, Color::None);
+ DOCTEST_SNPRINTF(buff, DOCTEST_COUNTOF(buff), "%4d passed",
+ numTestsPassingFilters - numFailed);
+ DOCTEST_PRINTF_COLORED(buff, Color::Green);
+ DOCTEST_SNPRINTF(buff, DOCTEST_COUNTOF(buff), " | ");
+ DOCTEST_PRINTF_COLORED(buff, Color::None);
+ DOCTEST_SNPRINTF(buff, DOCTEST_COUNTOF(buff), "%4d failed\n", numFailed);
+ DOCTEST_PRINTF_COLORED(buff, Color::Red);
+
+ DOCTEST_PRINTF_COLORED("[doctest] ", Color::Cyan);
+
+ DOCTEST_SNPRINTF(buff, DOCTEST_COUNTOF(buff), "assertions: %4d", p->numAssertions);
+ DOCTEST_PRINTF_COLORED(buff, Color::None);
+ DOCTEST_SNPRINTF(buff, DOCTEST_COUNTOF(buff), " | ");
+ DOCTEST_PRINTF_COLORED(buff, Color::None);
+ DOCTEST_SNPRINTF(buff, DOCTEST_COUNTOF(buff), "%4d passed",
+ p->numAssertions - p->numFailedAssertions);
+ DOCTEST_PRINTF_COLORED(buff, Color::Green);
+ DOCTEST_SNPRINTF(buff, DOCTEST_COUNTOF(buff), " | ");
+ DOCTEST_PRINTF_COLORED(buff, Color::None);
+ DOCTEST_SNPRINTF(buff, DOCTEST_COUNTOF(buff), "%4d failed\n", p->numFailedAssertions);
+ DOCTEST_PRINTF_COLORED(buff, Color::Red);
+ }
+
+ if(numFailed && !p->no_exitcode)
+ return EXIT_FAILURE;
+ return EXIT_SUCCESS;
+}
+} // namespace doctest
+
+#endif // DOCTEST_CONFIG_DISABLE
+#endif // DOCTEST_LIBRARY_IMPLEMENTATION
+#endif // DOCTEST_CONFIG_IMPLEMENT
+
+// == THIS SUPPLIES A MAIN FUNCTION AND SHOULD BE DONE ONLY IN ONE TRANSLATION UNIT
+#if defined(DOCTEST_CONFIG_IMPLEMENT_WITH_MAIN) && !defined(DOCTEST_MAIN_CONFIGURED)
+#define DOCTEST_MAIN_CONFIGURED
+int main(int argc, char** argv) { return doctest::Context(argc, argv).run(); }
+#endif // DOCTEST_MAIN_CONFIGURED
+
+#if defined(__clang__)
+#pragma clang diagnostic pop
+#endif // __clang__
+
+#if defined(__GNUC__) && !defined(__clang__)
+#if __GNUC__ > 4 || (__GNUC__ == 4 && __GNUC_MINOR__ > 6)
+#pragma GCC diagnostic pop
+#endif // > gcc 4.6
+#endif // __GNUC__
+
+#ifdef _MSC_VER
+#pragma warning(pop)
+#endif // _MSC_VER
diff --git a/examples/user_supplied_main/main.cpp b/examples/user_supplied_main/main.cpp
index aeb76fd..7c648c7 100644
--- a/examples/user_supplied_main/main.cpp
+++ b/examples/user_supplied_main/main.cpp
@@ -14,7 +14,7 @@
int res = context.run(); // run queries, or run tests unless --no-run is specified
- if(context.shouldExit()) // important - query flags (and --no-run) rely on the user doing this
+ if(context.shouldExit()) // important - query flags (and --exit) rely on the user doing this
return res; // propagate the result of the tests
int client_stuff_return_code = program();
diff --git a/scripts/common.cmake b/scripts/common.cmake
index b7fd72c..2216e09 100644
--- a/scripts/common.cmake
+++ b/scripts/common.cmake
@@ -142,3 +142,32 @@
add_compiler_flags(/WX)
add_compiler_flags(/W4) # /Wall is too aggressive - even the standard C headers give thousands of errors...
endif()
+
+# add a custom target that assembles the single header when any of the parts are touched
+
+set(cat_cmd "cat")
+set(doctest_include_folder "${CURRENT_LIST_DIR_CACHED}/../doctest/")
+set(doctest_parts_folder "${CURRENT_LIST_DIR_CACHED}/../doctest/parts/")
+if(WIN32)
+ set(cat_cmd "type")
+ STRING(REGEX REPLACE "/" "\\\\" doctest_include_folder ${doctest_include_folder})
+ STRING(REGEX REPLACE "/" "\\\\" doctest_parts_folder ${doctest_parts_folder})
+endif()
+
+add_custom_command(
+ OUTPUT ${doctest_include_folder}doctest.h
+ DEPENDS
+ ${doctest_parts_folder}doctest_fwd.h
+ ${doctest_parts_folder}doctest_impl.h
+ COMMAND echo // ====================================================================== > ${doctest_include_folder}doctest.h
+ COMMAND echo // == DO NOT MODIFY THIS FILE BY HAND - IT IS AUTO GENERATED BY CMAKE! == >> ${doctest_include_folder}doctest.h
+ COMMAND echo // ====================================================================== >> ${doctest_include_folder}doctest.h
+ COMMAND
+ ${cat_cmd}
+ ${doctest_parts_folder}doctest_fwd.h
+ ${doctest_parts_folder}doctest_impl.h
+ >>
+ ${doctest_include_folder}doctest.h
+ COMMENT "assembling the single header")
+
+add_custom_target(assemble_single_header ALL DEPENDS ${doctest_include_folder}doctest.h)