diff --git a/.github/actions/run-tests/action.yml b/.github/actions/run-tests/action.yml new file mode 100644 index 00000000..7c8a3f06 --- /dev/null +++ b/.github/actions/run-tests/action.yml @@ -0,0 +1,36 @@ +name: 'Package plugin' +description: 'Packages the plugin for specified architecture and build config.' +inputs: + target: + description: 'Build target' + required: true + workingDirectory: + description: 'Working directory' + required: false + default: ${{ github.workspace }} +runs: + using: 'composite' + steps: + - name: Run macOS tests + if: ${{ runner.os == 'macOS' }} + shell: zsh {0} + run: | + if [[ '${{ inputs.target }}' != 'x86_64' ]]; then + exit 0 + fi + ${{ inputs.workingDirectory }}/build_x86_64/tests/advanced-scene-switcher-tests + + - name: Run Linux packaging + if: ${{ runner.os == 'Linux' }} + shell: bash + run: | + if [[ '${{ inputs.target }}' != 'x86_64' ]]; then + exit 0 + fi + ${{ inputs.workingDirectory }}/build_x86_64/tests/advanced-scene-switcher-tests + + - name: Run Windows packaging + if: ${{ runner.os == 'Windows' }} + shell: pwsh + run: | + ${{ inputs.workingDirectory }}/build_x64/tests/RelWithDebInfo/advanced-scene-switcher-tests.exe diff --git a/.github/workflows/main.yml b/.github/workflows/main.yml index 16f6e75a..76547f96 100644 --- a/.github/workflows/main.yml +++ b/.github/workflows/main.yml @@ -155,6 +155,12 @@ jobs: codesign: 'true' codesignIdent: ${{ env.CODESIGN_IDENT }} + - name: Run tests + uses: ./plugin/.github/actions/run-tests + with: + workingDirectory: ${{ github.workspace }}/plugin + target: ${{ matrix.arch }} + - name: Package Plugin uses: ./plugin/.github/actions/package-plugin with: @@ -250,6 +256,12 @@ jobs: config: RelWithDebInfo portable: ${{ matrix.portable }} + - name: Run tests + uses: ./plugin/.github/actions/run-tests + with: + workingDirectory: ${{ github.workspace }}/plugin + target: ${{ matrix.arch }} + - name: Package Plugin uses: ./plugin/.github/actions/package-plugin with: @@ -353,6 +365,12 @@ jobs: config: RelWithDebInfo visualStudio: 'Visual Studio 17 2022' + - name: Run tests + uses: ./plugin/.github/actions/run-tests + with: + workingDirectory: ${{ github.workspace }}/plugin + target: ${{ matrix.arch }} + - name: Package Plugin uses: ./plugin/.github/actions/package-plugin with: diff --git a/CMakeLists.txt b/CMakeLists.txt index 12ae1715..0ee27c3f 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -471,3 +471,4 @@ endif() # --- End of section --- add_subdirectory(src/macro-external) +add_subdirectory(tests) diff --git a/src/utils/math-helpers.cpp b/src/utils/math-helpers.cpp index 28f09ea2..c0782a2d 100644 --- a/src/utils/math-helpers.cpp +++ b/src/utils/math-helpers.cpp @@ -6,7 +6,14 @@ #include #include +#ifdef UNIT_TEST +const char *obs_module_text(const char *text) +{ + return text; +} +#else #include +#endif static std::vector nonOperatorSeparators = { " ", diff --git a/tests/CMakeLists.txt b/tests/CMakeLists.txt new file mode 100644 index 00000000..aade33c2 --- /dev/null +++ b/tests/CMakeLists.txt @@ -0,0 +1,18 @@ +cmake_minimum_required(VERSION 3.14) +project(advanced-scene-switcher-tests) + +get_target_property(ADVSS_SOURCE_DIR advanced-scene-switcher-lib SOURCE_DIR) +add_executable(${PROJECT_NAME}) +target_compile_definitions(${PROJECT_NAME} PRIVATE UNIT_TEST) +target_compile_features(${PROJECT_NAME} PRIVATE cxx_std_17) +target_sources(${PROJECT_NAME} + PRIVATE tests.cpp ${ADVSS_SOURCE_DIR}/src/utils/math-helpers.cpp) +target_include_directories( + ${PROJECT_NAME} + PRIVATE "${ADVSS_SOURCE_DIR}/src" "${ADVSS_SOURCE_DIR}/src/legacy" + "${ADVSS_SOURCE_DIR}/src/macro-core" "${ADVSS_SOURCE_DIR}/src/utils" + "${ADVSS_SOURCE_DIR}/forms") +install(TARGETS advanced-scene-switcher-lib + DESTINATION ${CMAKE_CURRENT_BINARY_DIR}) + +enable_testing() diff --git a/tests/catch.hpp b/tests/catch.hpp new file mode 100644 index 00000000..dfd9aa90 --- /dev/null +++ b/tests/catch.hpp @@ -0,0 +1,21029 @@ +/* + * Catch v2.13.8 + * Generated: 2022-01-03 21:20:09.589503 + * ---------------------------------------------------------- + * This file has been merged from multiple headers. Please don't edit it directly + * Copyright (c) 2022 Two Blue Cubes Ltd. All rights reserved. + * + * Distributed under the Boost Software License, Version 1.0. (See accompanying + * file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) + */ +#ifndef TWOBLUECUBES_SINGLE_INCLUDE_CATCH_HPP_INCLUDED +#define TWOBLUECUBES_SINGLE_INCLUDE_CATCH_HPP_INCLUDED +// start catch.hpp + +#define CATCH_VERSION_MAJOR 2 +#define CATCH_VERSION_MINOR 13 +#define CATCH_VERSION_PATCH 8 + +#ifdef __clang__ +#pragma clang system_header +#elif defined __GNUC__ +#pragma GCC system_header +#endif + +// start catch_suppress_warnings.h + +#ifdef __clang__ +#ifdef __ICC // icpc defines the __clang__ macro +#pragma warning(push) +#pragma warning(disable : 161 1682) +#else // __ICC +#pragma clang diagnostic push +#pragma clang diagnostic ignored "-Wpadded" +#pragma clang diagnostic ignored "-Wswitch-enum" +#pragma clang diagnostic ignored "-Wcovered-switch-default" +#endif +#elif defined __GNUC__ +// Because REQUIREs trigger GCC's -Wparentheses, and because still +// supported version of g++ have only buggy support for _Pragmas, +// Wparentheses have to be suppressed globally. +#pragma GCC diagnostic ignored "-Wparentheses" // See #674 for details + +#pragma GCC diagnostic push +#pragma GCC diagnostic ignored "-Wunused-variable" +#pragma GCC diagnostic ignored "-Wpadded" +#endif +// end catch_suppress_warnings.h +#if defined(CATCH_CONFIG_MAIN) || defined(CATCH_CONFIG_RUNNER) +#define CATCH_IMPL +#define CATCH_CONFIG_ALL_PARTS +#endif + +// In the impl file, we want to have access to all parts of the headers +// Can also be used to sanely support PCHs +#if defined(CATCH_CONFIG_ALL_PARTS) +#define CATCH_CONFIG_EXTERNAL_INTERFACES +#if defined(CATCH_CONFIG_DISABLE_MATCHERS) +#undef CATCH_CONFIG_DISABLE_MATCHERS +#endif +#if !defined(CATCH_CONFIG_ENABLE_CHRONO_STRINGMAKER) +#define CATCH_CONFIG_ENABLE_CHRONO_STRINGMAKER +#endif +#endif + +#if !defined(CATCH_CONFIG_IMPL_ONLY) +// start catch_platform.h + +// See e.g.: +// https://opensource.apple.com/source/CarbonHeaders/CarbonHeaders-18.1/TargetConditionals.h.auto.html +#ifdef __APPLE__ +#include +#if (defined(TARGET_OS_OSX) && TARGET_OS_OSX == 1) || \ + (defined(TARGET_OS_MAC) && TARGET_OS_MAC == 1) +#define CATCH_PLATFORM_MAC +#elif (defined(TARGET_OS_IPHONE) && TARGET_OS_IPHONE == 1) +#define CATCH_PLATFORM_IPHONE +#endif + +#elif defined(linux) || defined(__linux) || defined(__linux__) +#define CATCH_PLATFORM_LINUX + +#elif defined(WIN32) || defined(__WIN32__) || defined(_WIN32) || \ + defined(_MSC_VER) || defined(__MINGW32__) +#define CATCH_PLATFORM_WINDOWS +#endif + +// end catch_platform.h + +#ifdef CATCH_IMPL +#ifndef CLARA_CONFIG_MAIN +#define CLARA_CONFIG_MAIN_NOT_DEFINED +#define CLARA_CONFIG_MAIN +#endif +#endif + +// start catch_user_interfaces.h + +namespace Catch { +unsigned int rngSeed(); +} + +// end catch_user_interfaces.h +// start catch_tag_alias_autoregistrar.h + +// start catch_common.h + +// start catch_compiler_capabilities.h + +// Detect a number of compiler features - by compiler +// The following features are defined: +// +// CATCH_CONFIG_COUNTER : is the __COUNTER__ macro supported? +// CATCH_CONFIG_WINDOWS_SEH : is Windows SEH supported? +// CATCH_CONFIG_POSIX_SIGNALS : are POSIX signals supported? +// CATCH_CONFIG_DISABLE_EXCEPTIONS : Are exceptions enabled? +// **************** +// Note to maintainers: if new toggles are added please document them +// in configuration.md, too +// **************** + +// In general each macro has a _NO_ form +// (e.g. CATCH_CONFIG_NO_POSIX_SIGNALS) which disables the feature. +// Many features, at point of detection, define an _INTERNAL_ macro, so they +// can be combined, en-mass, with the _NO_ forms later. + +#ifdef __cplusplus + +#if (__cplusplus >= 201402L) || (defined(_MSVC_LANG) && _MSVC_LANG >= 201402L) +#define CATCH_CPP14_OR_GREATER +#endif + +#if (__cplusplus >= 201703L) || (defined(_MSVC_LANG) && _MSVC_LANG >= 201703L) +#define CATCH_CPP17_OR_GREATER +#endif + +#endif + +// Only GCC compiler should be used in this block, so other compilers trying to +// mask themselves as GCC should be ignored. +#if defined(__GNUC__) && !defined(__clang__) && !defined(__ICC) && \ + !defined(__CUDACC__) && !defined(__LCC__) +#define CATCH_INTERNAL_START_WARNINGS_SUPPRESSION _Pragma("GCC diagnostic push") +#define CATCH_INTERNAL_STOP_WARNINGS_SUPPRESSION _Pragma("GCC diagnostic pop") + +#define CATCH_INTERNAL_IGNORE_BUT_WARN(...) \ + (void)__builtin_constant_p(__VA_ARGS__) + +#endif + +#if defined(__clang__) + +#define CATCH_INTERNAL_START_WARNINGS_SUPPRESSION \ + _Pragma("clang diagnostic push") +#define CATCH_INTERNAL_STOP_WARNINGS_SUPPRESSION _Pragma("clang diagnostic pop") + +// As of this writing, IBM XL's implementation of __builtin_constant_p has a bug +// which results in calls to destructors being emitted for each temporary, +// without a matching initialization. In practice, this can result in something +// like `std::string::~string` being called on an uninitialized value. +// +// For example, this code will likely segfault under IBM XL: +// ``` +// REQUIRE(std::string("12") + "34" == "1234") +// ``` +// +// Therefore, `CATCH_INTERNAL_IGNORE_BUT_WARN` is not implemented. +#if !defined(__ibmxl__) && !defined(__CUDACC__) +#define CATCH_INTERNAL_IGNORE_BUT_WARN(...) \ + (void)__builtin_constant_p( \ + __VA_ARGS__) /* NOLINT(cppcoreguidelines-pro-type-vararg, hicpp-vararg) */ +#endif + +#define CATCH_INTERNAL_SUPPRESS_GLOBALS_WARNINGS \ + _Pragma("clang diagnostic ignored \"-Wexit-time-destructors\"") \ + _Pragma("clang diagnostic ignored \"-Wglobal-constructors\"") + +#define CATCH_INTERNAL_SUPPRESS_PARENTHESES_WARNINGS \ + _Pragma("clang diagnostic ignored \"-Wparentheses\"") + +#define CATCH_INTERNAL_SUPPRESS_UNUSED_WARNINGS \ + _Pragma("clang diagnostic ignored \"-Wunused-variable\"") + +#define CATCH_INTERNAL_SUPPRESS_ZERO_VARIADIC_WARNINGS \ + _Pragma("clang diagnostic ignored \"-Wgnu-zero-variadic-macro-arguments\"") + +#define CATCH_INTERNAL_SUPPRESS_UNUSED_TEMPLATE_WARNINGS \ + _Pragma("clang diagnostic ignored \"-Wunused-template\"") + +#endif // __clang__ + +//////////////////////////////////////////////////////////////////////////////// +// Assume that non-Windows platforms support posix signals by default +#if !defined(CATCH_PLATFORM_WINDOWS) +#define CATCH_INTERNAL_CONFIG_POSIX_SIGNALS +#endif + +//////////////////////////////////////////////////////////////////////////////// +// We know some environments not to support full POSIX signals +#if defined(__CYGWIN__) || defined(__QNX__) || defined(__EMSCRIPTEN__) || \ + defined(__DJGPP__) +#define CATCH_INTERNAL_CONFIG_NO_POSIX_SIGNALS +#endif + +#ifdef __OS400__ +#define CATCH_INTERNAL_CONFIG_NO_POSIX_SIGNALS +#define CATCH_CONFIG_COLOUR_NONE +#endif + +//////////////////////////////////////////////////////////////////////////////// +// Android somehow still does not support std::to_string +#if defined(__ANDROID__) +#define CATCH_INTERNAL_CONFIG_NO_CPP11_TO_STRING +#define CATCH_INTERNAL_CONFIG_ANDROID_LOGWRITE +#endif + +//////////////////////////////////////////////////////////////////////////////// +// Not all Windows environments support SEH properly +#if defined(__MINGW32__) +#define CATCH_INTERNAL_CONFIG_NO_WINDOWS_SEH +#endif + +//////////////////////////////////////////////////////////////////////////////// +// PS4 +#if defined(__ORBIS__) +#define CATCH_INTERNAL_CONFIG_NO_NEW_CAPTURE +#endif + +//////////////////////////////////////////////////////////////////////////////// +// Cygwin +#ifdef __CYGWIN__ + +// Required for some versions of Cygwin to declare gettimeofday +// see: http://stackoverflow.com/questions/36901803/gettimeofday-not-declared-in-this-scope-cygwin +#define _BSD_SOURCE +// some versions of cygwin (most) do not support std::to_string. Use the libstd check. +// https://gcc.gnu.org/onlinedocs/gcc-4.8.2/libstdc++/api/a01053_source.html line 2812-2813 +#if !((__cplusplus >= 201103L) && defined(_GLIBCXX_USE_C99) && \ + !defined(_GLIBCXX_HAVE_BROKEN_VSWPRINTF)) + +#define CATCH_INTERNAL_CONFIG_NO_CPP11_TO_STRING + +#endif +#endif // __CYGWIN__ + +//////////////////////////////////////////////////////////////////////////////// +// Visual C++ +#if defined(_MSC_VER) + +// Universal Windows platform does not support SEH +// Or console colours (or console at all...) +#if defined(WINAPI_FAMILY) && (WINAPI_FAMILY == WINAPI_FAMILY_APP) +#define CATCH_CONFIG_COLOUR_NONE +#else +#define CATCH_INTERNAL_CONFIG_WINDOWS_SEH +#endif + +#if !defined(__clang__) // Handle Clang masquerading for msvc + +// MSVC traditional preprocessor needs some workaround for __VA_ARGS__ +// _MSVC_TRADITIONAL == 0 means new conformant preprocessor +// _MSVC_TRADITIONAL == 1 means old traditional non-conformant preprocessor +#if !defined(_MSVC_TRADITIONAL) || \ + (defined(_MSVC_TRADITIONAL) && _MSVC_TRADITIONAL) +#define CATCH_INTERNAL_CONFIG_TRADITIONAL_MSVC_PREPROCESSOR +#endif // MSVC_TRADITIONAL + +// Only do this if we're not using clang on Windows, which uses `diagnostic push` & `diagnostic pop` +#define CATCH_INTERNAL_START_WARNINGS_SUPPRESSION __pragma(warning(push)) +#define CATCH_INTERNAL_STOP_WARNINGS_SUPPRESSION __pragma(warning(pop)) +#endif // __clang__ + +#endif // _MSC_VER + +#if defined(_REENTRANT) || defined(_MSC_VER) +// Enable async processing, as -pthread is specified or no additional linking is required +#define CATCH_INTERNAL_CONFIG_USE_ASYNC +#endif // _MSC_VER + +//////////////////////////////////////////////////////////////////////////////// +// Check if we are compiled with -fno-exceptions or equivalent +#if defined(__EXCEPTIONS) || defined(__cpp_exceptions) || defined(_CPPUNWIND) +#define CATCH_INTERNAL_CONFIG_EXCEPTIONS_ENABLED +#endif + +//////////////////////////////////////////////////////////////////////////////// +// DJGPP +#ifdef __DJGPP__ +#define CATCH_INTERNAL_CONFIG_NO_WCHAR +#endif // __DJGPP__ + +//////////////////////////////////////////////////////////////////////////////// +// Embarcadero C++Build +#if defined(__BORLANDC__) +#define CATCH_INTERNAL_CONFIG_POLYFILL_ISNAN +#endif + +//////////////////////////////////////////////////////////////////////////////// + +// Use of __COUNTER__ is suppressed during code analysis in +// CLion/AppCode 2017.2.x and former, because __COUNTER__ is not properly +// handled by it. +// Otherwise all supported compilers support COUNTER macro, +// but user still might want to turn it off +#if (!defined(__JETBRAINS_IDE__) || __JETBRAINS_IDE__ >= 20170300L) +#define CATCH_INTERNAL_CONFIG_COUNTER +#endif + +//////////////////////////////////////////////////////////////////////////////// + +// RTX is a special version of Windows that is real time. +// This means that it is detected as Windows, but does not provide +// the same set of capabilities as real Windows does. +#if defined(UNDER_RTSS) || defined(RTX64_BUILD) +#define CATCH_INTERNAL_CONFIG_NO_WINDOWS_SEH +#define CATCH_INTERNAL_CONFIG_NO_ASYNC +#define CATCH_CONFIG_COLOUR_NONE +#endif + +#if !defined(_GLIBCXX_USE_C99_MATH_TR1) +#define CATCH_INTERNAL_CONFIG_GLOBAL_NEXTAFTER +#endif + +// Various stdlib support checks that require __has_include +#if defined(__has_include) +// Check if string_view is available and usable +#if __has_include() && defined(CATCH_CPP17_OR_GREATER) +#define CATCH_INTERNAL_CONFIG_CPP17_STRING_VIEW +#endif + +// Check if optional is available and usable +#if __has_include() && defined(CATCH_CPP17_OR_GREATER) +#define CATCH_INTERNAL_CONFIG_CPP17_OPTIONAL +#endif // __has_include() && defined(CATCH_CPP17_OR_GREATER) + +// Check if byte is available and usable +#if __has_include() && defined(CATCH_CPP17_OR_GREATER) +#include +#if defined(__cpp_lib_byte) && (__cpp_lib_byte > 0) +#define CATCH_INTERNAL_CONFIG_CPP17_BYTE +#endif +#endif // __has_include() && defined(CATCH_CPP17_OR_GREATER) + +// Check if variant is available and usable +#if __has_include() && defined(CATCH_CPP17_OR_GREATER) +#if defined(__clang__) && (__clang_major__ < 8) +// work around clang bug with libstdc++ https://bugs.llvm.org/show_bug.cgi?id=31852 +// fix should be in clang 8, workaround in libstdc++ 8.2 +#include +#if defined(__GLIBCXX__) && defined(_GLIBCXX_RELEASE) && (_GLIBCXX_RELEASE < 9) +#define CATCH_CONFIG_NO_CPP17_VARIANT +#else +#define CATCH_INTERNAL_CONFIG_CPP17_VARIANT +#endif // defined(__GLIBCXX__) && defined(_GLIBCXX_RELEASE) && (_GLIBCXX_RELEASE < 9) +#else +#define CATCH_INTERNAL_CONFIG_CPP17_VARIANT +#endif // defined(__clang__) && (__clang_major__ < 8) +#endif // __has_include() && defined(CATCH_CPP17_OR_GREATER) +#endif // defined(__has_include) + +#if defined(CATCH_INTERNAL_CONFIG_COUNTER) && \ + !defined(CATCH_CONFIG_NO_COUNTER) && !defined(CATCH_CONFIG_COUNTER) +#define CATCH_CONFIG_COUNTER +#endif +#if defined(CATCH_INTERNAL_CONFIG_WINDOWS_SEH) && \ + !defined(CATCH_CONFIG_NO_WINDOWS_SEH) && \ + !defined(CATCH_CONFIG_WINDOWS_SEH) && \ + !defined(CATCH_INTERNAL_CONFIG_NO_WINDOWS_SEH) +#define CATCH_CONFIG_WINDOWS_SEH +#endif +// This is set by default, because we assume that unix compilers are posix-signal-compatible by default. +#if defined(CATCH_INTERNAL_CONFIG_POSIX_SIGNALS) && \ + !defined(CATCH_INTERNAL_CONFIG_NO_POSIX_SIGNALS) && \ + !defined(CATCH_CONFIG_NO_POSIX_SIGNALS) && \ + !defined(CATCH_CONFIG_POSIX_SIGNALS) +#define CATCH_CONFIG_POSIX_SIGNALS +#endif +// This is set by default, because we assume that compilers with no wchar_t support are just rare exceptions. +#if !defined(CATCH_INTERNAL_CONFIG_NO_WCHAR) && \ + !defined(CATCH_CONFIG_NO_WCHAR) && !defined(CATCH_CONFIG_WCHAR) +#define CATCH_CONFIG_WCHAR +#endif + +#if !defined(CATCH_INTERNAL_CONFIG_NO_CPP11_TO_STRING) && \ + !defined(CATCH_CONFIG_NO_CPP11_TO_STRING) && \ + !defined(CATCH_CONFIG_CPP11_TO_STRING) +#define CATCH_CONFIG_CPP11_TO_STRING +#endif + +#if defined(CATCH_INTERNAL_CONFIG_CPP17_OPTIONAL) && \ + !defined(CATCH_CONFIG_NO_CPP17_OPTIONAL) && \ + !defined(CATCH_CONFIG_CPP17_OPTIONAL) +#define CATCH_CONFIG_CPP17_OPTIONAL +#endif + +#if defined(CATCH_INTERNAL_CONFIG_CPP17_STRING_VIEW) && \ + !defined(CATCH_CONFIG_NO_CPP17_STRING_VIEW) && \ + !defined(CATCH_CONFIG_CPP17_STRING_VIEW) +#define CATCH_CONFIG_CPP17_STRING_VIEW +#endif + +#if defined(CATCH_INTERNAL_CONFIG_CPP17_VARIANT) && \ + !defined(CATCH_CONFIG_NO_CPP17_VARIANT) && \ + !defined(CATCH_CONFIG_CPP17_VARIANT) +#define CATCH_CONFIG_CPP17_VARIANT +#endif + +#if defined(CATCH_INTERNAL_CONFIG_CPP17_BYTE) && \ + !defined(CATCH_CONFIG_NO_CPP17_BYTE) && \ + !defined(CATCH_CONFIG_CPP17_BYTE) +#define CATCH_CONFIG_CPP17_BYTE +#endif + +#if defined(CATCH_CONFIG_EXPERIMENTAL_REDIRECT) +#define CATCH_INTERNAL_CONFIG_NEW_CAPTURE +#endif + +#if defined(CATCH_INTERNAL_CONFIG_NEW_CAPTURE) && \ + !defined(CATCH_INTERNAL_CONFIG_NO_NEW_CAPTURE) && \ + !defined(CATCH_CONFIG_NO_NEW_CAPTURE) && \ + !defined(CATCH_CONFIG_NEW_CAPTURE) +#define CATCH_CONFIG_NEW_CAPTURE +#endif + +#if !defined(CATCH_INTERNAL_CONFIG_EXCEPTIONS_ENABLED) && \ + !defined(CATCH_CONFIG_DISABLE_EXCEPTIONS) +#define CATCH_CONFIG_DISABLE_EXCEPTIONS +#endif + +#if defined(CATCH_INTERNAL_CONFIG_POLYFILL_ISNAN) && \ + !defined(CATCH_CONFIG_NO_POLYFILL_ISNAN) && \ + !defined(CATCH_CONFIG_POLYFILL_ISNAN) +#define CATCH_CONFIG_POLYFILL_ISNAN +#endif + +#if defined(CATCH_INTERNAL_CONFIG_USE_ASYNC) && \ + !defined(CATCH_INTERNAL_CONFIG_NO_ASYNC) && \ + !defined(CATCH_CONFIG_NO_USE_ASYNC) && \ + !defined(CATCH_CONFIG_USE_ASYNC) +#define CATCH_CONFIG_USE_ASYNC +#endif + +#if defined(CATCH_INTERNAL_CONFIG_ANDROID_LOGWRITE) && \ + !defined(CATCH_CONFIG_NO_ANDROID_LOGWRITE) && \ + !defined(CATCH_CONFIG_ANDROID_LOGWRITE) +#define CATCH_CONFIG_ANDROID_LOGWRITE +#endif + +#if defined(CATCH_INTERNAL_CONFIG_GLOBAL_NEXTAFTER) && \ + !defined(CATCH_CONFIG_NO_GLOBAL_NEXTAFTER) && \ + !defined(CATCH_CONFIG_GLOBAL_NEXTAFTER) +#define CATCH_CONFIG_GLOBAL_NEXTAFTER +#endif + +// Even if we do not think the compiler has that warning, we still have +// to provide a macro that can be used by the code. +#if !defined(CATCH_INTERNAL_START_WARNINGS_SUPPRESSION) +#define CATCH_INTERNAL_START_WARNINGS_SUPPRESSION +#endif +#if !defined(CATCH_INTERNAL_STOP_WARNINGS_SUPPRESSION) +#define CATCH_INTERNAL_STOP_WARNINGS_SUPPRESSION +#endif +#if !defined(CATCH_INTERNAL_SUPPRESS_PARENTHESES_WARNINGS) +#define CATCH_INTERNAL_SUPPRESS_PARENTHESES_WARNINGS +#endif +#if !defined(CATCH_INTERNAL_SUPPRESS_GLOBALS_WARNINGS) +#define CATCH_INTERNAL_SUPPRESS_GLOBALS_WARNINGS +#endif +#if !defined(CATCH_INTERNAL_SUPPRESS_UNUSED_WARNINGS) +#define CATCH_INTERNAL_SUPPRESS_UNUSED_WARNINGS +#endif +#if !defined(CATCH_INTERNAL_SUPPRESS_ZERO_VARIADIC_WARNINGS) +#define CATCH_INTERNAL_SUPPRESS_ZERO_VARIADIC_WARNINGS +#endif + +// The goal of this macro is to avoid evaluation of the arguments, but +// still have the compiler warn on problems inside... +#if !defined(CATCH_INTERNAL_IGNORE_BUT_WARN) +#define CATCH_INTERNAL_IGNORE_BUT_WARN(...) +#endif + +#if defined(__APPLE__) && defined(__apple_build_version__) && \ + (__clang_major__ < 10) +#undef CATCH_INTERNAL_SUPPRESS_UNUSED_TEMPLATE_WARNINGS +#elif defined(__clang__) && (__clang_major__ < 5) +#undef CATCH_INTERNAL_SUPPRESS_UNUSED_TEMPLATE_WARNINGS +#endif + +#if !defined(CATCH_INTERNAL_SUPPRESS_UNUSED_TEMPLATE_WARNINGS) +#define CATCH_INTERNAL_SUPPRESS_UNUSED_TEMPLATE_WARNINGS +#endif + +#if defined(CATCH_CONFIG_DISABLE_EXCEPTIONS) +#define CATCH_TRY if ((true)) +#define CATCH_CATCH_ALL if ((false)) +#define CATCH_CATCH_ANON(type) if ((false)) +#else +#define CATCH_TRY try +#define CATCH_CATCH_ALL catch (...) +#define CATCH_CATCH_ANON(type) catch (type) +#endif + +#if defined(CATCH_INTERNAL_CONFIG_TRADITIONAL_MSVC_PREPROCESSOR) && \ + !defined(CATCH_CONFIG_NO_TRADITIONAL_MSVC_PREPROCESSOR) && \ + !defined(CATCH_CONFIG_TRADITIONAL_MSVC_PREPROCESSOR) +#define CATCH_CONFIG_TRADITIONAL_MSVC_PREPROCESSOR +#endif + +// end catch_compiler_capabilities.h +#define INTERNAL_CATCH_UNIQUE_NAME_LINE2(name, line) name##line +#define INTERNAL_CATCH_UNIQUE_NAME_LINE(name, line) \ + INTERNAL_CATCH_UNIQUE_NAME_LINE2(name, line) +#ifdef CATCH_CONFIG_COUNTER +#define INTERNAL_CATCH_UNIQUE_NAME(name) \ + INTERNAL_CATCH_UNIQUE_NAME_LINE(name, __COUNTER__) +#else +#define INTERNAL_CATCH_UNIQUE_NAME(name) \ + INTERNAL_CATCH_UNIQUE_NAME_LINE(name, __LINE__) +#endif + +#include +#include +#include + +// We need a dummy global operator<< so we can bring it into Catch namespace later +struct Catch_global_namespace_dummy { +}; +std::ostream &operator<<(std::ostream &, Catch_global_namespace_dummy); + +namespace Catch { + +struct CaseSensitive { + enum Choice { Yes, No }; +}; + +class NonCopyable { + NonCopyable(NonCopyable const &) = delete; + NonCopyable(NonCopyable &&) = delete; + NonCopyable &operator=(NonCopyable const &) = delete; + NonCopyable &operator=(NonCopyable &&) = delete; + +protected: + NonCopyable(); + virtual ~NonCopyable(); +}; + +struct SourceLineInfo { + + SourceLineInfo() = delete; + SourceLineInfo(char const *_file, std::size_t _line) noexcept + : file(_file), line(_line) + { + } + + SourceLineInfo(SourceLineInfo const &other) = default; + SourceLineInfo &operator=(SourceLineInfo const &) = default; + SourceLineInfo(SourceLineInfo &&) noexcept = default; + SourceLineInfo &operator=(SourceLineInfo &&) noexcept = default; + + bool empty() const noexcept { return file[0] == '\0'; } + bool operator==(SourceLineInfo const &other) const noexcept; + bool operator<(SourceLineInfo const &other) const noexcept; + + char const *file; + std::size_t line; +}; + +std::ostream &operator<<(std::ostream &os, SourceLineInfo const &info); + +// Bring in operator<< from global namespace into Catch namespace +// This is necessary because the overload of operator<< above makes +// lookup stop at namespace Catch +using ::operator<<; + +// Use this in variadic streaming macros to allow +// >> +StreamEndStop +// as well as +// >> stuff +StreamEndStop +struct StreamEndStop { + std::string operator+() const; +}; +template T const &operator+(T const &value, StreamEndStop) +{ + return value; +} +} + +#define CATCH_INTERNAL_LINEINFO \ + ::Catch::SourceLineInfo(__FILE__, static_cast(__LINE__)) + +// end catch_common.h +namespace Catch { + +struct RegistrarForTagAliases { + RegistrarForTagAliases(char const *alias, char const *tag, + SourceLineInfo const &lineInfo); +}; + +} // end namespace Catch + +#define CATCH_REGISTER_TAG_ALIAS(alias, spec) \ + CATCH_INTERNAL_START_WARNINGS_SUPPRESSION \ + CATCH_INTERNAL_SUPPRESS_GLOBALS_WARNINGS \ + namespace { \ + Catch::RegistrarForTagAliases INTERNAL_CATCH_UNIQUE_NAME( \ + AutoRegisterTagAlias)(alias, spec, CATCH_INTERNAL_LINEINFO); \ + } \ + CATCH_INTERNAL_STOP_WARNINGS_SUPPRESSION + +// end catch_tag_alias_autoregistrar.h +// start catch_test_registry.h + +// start catch_interfaces_testcase.h + +#include + +namespace Catch { + +class TestSpec; + +struct ITestInvoker { + virtual void invoke() const = 0; + virtual ~ITestInvoker(); +}; + +class TestCase; +struct IConfig; + +struct ITestCaseRegistry { + virtual ~ITestCaseRegistry(); + virtual std::vector const &getAllTests() const = 0; + virtual std::vector const & + getAllTestsSorted(IConfig const &config) const = 0; +}; + +bool isThrowSafe(TestCase const &testCase, IConfig const &config); +bool matchTest(TestCase const &testCase, TestSpec const &testSpec, + IConfig const &config); +std::vector filterTests(std::vector const &testCases, + TestSpec const &testSpec, + IConfig const &config); +std::vector const &getAllTestCasesSorted(IConfig const &config); + +} + +// end catch_interfaces_testcase.h +// start catch_stringref.h + +#include +#include +#include +#include + +namespace Catch { + +/// A non-owning string class (similar to the forthcoming std::string_view) +/// Note that, because a StringRef may be a substring of another string, +/// it may not be null terminated. +class StringRef { +public: + using size_type = std::size_t; + using const_iterator = const char *; + +private: + static constexpr char const *const s_empty = ""; + + char const *m_start = s_empty; + size_type m_size = 0; + +public: // construction + constexpr StringRef() noexcept = default; + + StringRef(char const *rawChars) noexcept; + + constexpr StringRef(char const *rawChars, size_type size) noexcept + : m_start(rawChars), m_size(size) + { + } + + StringRef(std::string const &stdString) noexcept + : m_start(stdString.c_str()), m_size(stdString.size()) + { + } + + explicit operator std::string() const + { + return std::string(m_start, m_size); + } + +public: // operators + auto operator==(StringRef const &other) const noexcept -> bool; + auto operator!=(StringRef const &other) const noexcept -> bool + { + return !(*this == other); + } + + auto operator[](size_type index) const noexcept -> char + { + assert(index < m_size); + return m_start[index]; + } + +public: // named queries + constexpr auto empty() const noexcept -> bool { return m_size == 0; } + constexpr auto size() const noexcept -> size_type { return m_size; } + + // Returns the current start pointer. If the StringRef is not + // null-terminated, throws std::domain_exception + auto c_str() const -> char const *; + +public: // substrings and searches + // Returns a substring of [start, start + length). + // If start + length > size(), then the substring is [start, size()). + // If start > size(), then the substring is empty. + auto substr(size_type start, size_type length) const noexcept + -> StringRef; + + // Returns the current start pointer. May not be null-terminated. + auto data() const noexcept -> char const *; + + constexpr auto isNullTerminated() const noexcept -> bool + { + return m_start[m_size] == '\0'; + } + +public: // iterators + constexpr const_iterator begin() const { return m_start; } + constexpr const_iterator end() const { return m_start + m_size; } +}; + +auto operator+=(std::string &lhs, StringRef const &sr) -> std::string &; +auto operator<<(std::ostream &os, StringRef const &sr) -> std::ostream &; + +constexpr auto operator"" _sr(char const *rawChars, std::size_t size) noexcept + -> StringRef +{ + return StringRef(rawChars, size); +} +} // namespace Catch + +constexpr auto operator"" _catch_sr(char const *rawChars, + std::size_t size) noexcept + -> Catch::StringRef +{ + return Catch::StringRef(rawChars, size); +} + +// end catch_stringref.h +// start catch_preprocessor.hpp + +#define CATCH_RECURSION_LEVEL0(...) __VA_ARGS__ +#define CATCH_RECURSION_LEVEL1(...) \ + CATCH_RECURSION_LEVEL0( \ + CATCH_RECURSION_LEVEL0(CATCH_RECURSION_LEVEL0(__VA_ARGS__))) +#define CATCH_RECURSION_LEVEL2(...) \ + CATCH_RECURSION_LEVEL1( \ + CATCH_RECURSION_LEVEL1(CATCH_RECURSION_LEVEL1(__VA_ARGS__))) +#define CATCH_RECURSION_LEVEL3(...) \ + CATCH_RECURSION_LEVEL2( \ + CATCH_RECURSION_LEVEL2(CATCH_RECURSION_LEVEL2(__VA_ARGS__))) +#define CATCH_RECURSION_LEVEL4(...) \ + CATCH_RECURSION_LEVEL3( \ + CATCH_RECURSION_LEVEL3(CATCH_RECURSION_LEVEL3(__VA_ARGS__))) +#define CATCH_RECURSION_LEVEL5(...) \ + CATCH_RECURSION_LEVEL4( \ + CATCH_RECURSION_LEVEL4(CATCH_RECURSION_LEVEL4(__VA_ARGS__))) + +#ifdef CATCH_CONFIG_TRADITIONAL_MSVC_PREPROCESSOR +#define INTERNAL_CATCH_EXPAND_VARGS(...) __VA_ARGS__ +// MSVC needs more evaluations +#define CATCH_RECURSION_LEVEL6(...) \ + CATCH_RECURSION_LEVEL5( \ + CATCH_RECURSION_LEVEL5(CATCH_RECURSION_LEVEL5(__VA_ARGS__))) +#define CATCH_RECURSE(...) \ + CATCH_RECURSION_LEVEL6(CATCH_RECURSION_LEVEL6(__VA_ARGS__)) +#else +#define CATCH_RECURSE(...) CATCH_RECURSION_LEVEL5(__VA_ARGS__) +#endif + +#define CATCH_REC_END(...) +#define CATCH_REC_OUT + +#define CATCH_EMPTY() +#define CATCH_DEFER(id) id CATCH_EMPTY() + +#define CATCH_REC_GET_END2() 0, CATCH_REC_END +#define CATCH_REC_GET_END1(...) CATCH_REC_GET_END2 +#define CATCH_REC_GET_END(...) CATCH_REC_GET_END1 +#define CATCH_REC_NEXT0(test, next, ...) next CATCH_REC_OUT +#define CATCH_REC_NEXT1(test, next) CATCH_DEFER(CATCH_REC_NEXT0)(test, next, 0) +#define CATCH_REC_NEXT(test, next) CATCH_REC_NEXT1(CATCH_REC_GET_END test, next) + +#define CATCH_REC_LIST0(f, x, peek, ...) \ + , f(x) CATCH_DEFER(CATCH_REC_NEXT(peek, CATCH_REC_LIST1))(f, peek, \ + __VA_ARGS__) +#define CATCH_REC_LIST1(f, x, peek, ...) \ + , f(x) CATCH_DEFER(CATCH_REC_NEXT(peek, CATCH_REC_LIST0))(f, peek, \ + __VA_ARGS__) +#define CATCH_REC_LIST2(f, x, peek, ...) \ + f(x) CATCH_DEFER(CATCH_REC_NEXT(peek, CATCH_REC_LIST1))(f, peek, \ + __VA_ARGS__) + +#define CATCH_REC_LIST0_UD(f, userdata, x, peek, ...) \ + , f(userdata, x) CATCH_DEFER(CATCH_REC_NEXT( \ + peek, CATCH_REC_LIST1_UD))(f, userdata, peek, __VA_ARGS__) +#define CATCH_REC_LIST1_UD(f, userdata, x, peek, ...) \ + , f(userdata, x) CATCH_DEFER(CATCH_REC_NEXT( \ + peek, CATCH_REC_LIST0_UD))(f, userdata, peek, __VA_ARGS__) +#define CATCH_REC_LIST2_UD(f, userdata, x, peek, ...) \ + f(userdata, x) CATCH_DEFER(CATCH_REC_NEXT(peek, CATCH_REC_LIST1_UD))( \ + f, userdata, peek, __VA_ARGS__) + +// Applies the function macro `f` to each of the remaining parameters, inserts commas between the results, +// and passes userdata as the first parameter to each invocation, +// e.g. CATCH_REC_LIST_UD(f, x, a, b, c) evaluates to f(x, a), f(x, b), f(x, c) +#define CATCH_REC_LIST_UD(f, userdata, ...) \ + CATCH_RECURSE(CATCH_REC_LIST2_UD(f, userdata, __VA_ARGS__, ()()(), \ + ()()(), ()()(), 0)) + +#define CATCH_REC_LIST(f, ...) \ + CATCH_RECURSE( \ + CATCH_REC_LIST2(f, __VA_ARGS__, ()()(), ()()(), ()()(), 0)) + +#define INTERNAL_CATCH_EXPAND1(param) INTERNAL_CATCH_EXPAND2(param) +#define INTERNAL_CATCH_EXPAND2(...) INTERNAL_CATCH_NO##__VA_ARGS__ +#define INTERNAL_CATCH_DEF(...) INTERNAL_CATCH_DEF __VA_ARGS__ +#define INTERNAL_CATCH_NOINTERNAL_CATCH_DEF +#define INTERNAL_CATCH_STRINGIZE(...) INTERNAL_CATCH_STRINGIZE2(__VA_ARGS__) +#ifndef CATCH_CONFIG_TRADITIONAL_MSVC_PREPROCESSOR +#define INTERNAL_CATCH_STRINGIZE2(...) #__VA_ARGS__ +#define INTERNAL_CATCH_STRINGIZE_WITHOUT_PARENS(param) \ + INTERNAL_CATCH_STRINGIZE(INTERNAL_CATCH_REMOVE_PARENS(param)) +#else +// MSVC is adding extra space and needs another indirection to expand INTERNAL_CATCH_NOINTERNAL_CATCH_DEF +#define INTERNAL_CATCH_STRINGIZE2(...) INTERNAL_CATCH_STRINGIZE3(__VA_ARGS__) +#define INTERNAL_CATCH_STRINGIZE3(...) #__VA_ARGS__ +#define INTERNAL_CATCH_STRINGIZE_WITHOUT_PARENS(param) \ + (INTERNAL_CATCH_STRINGIZE(INTERNAL_CATCH_REMOVE_PARENS(param)) + 1) +#endif + +#define INTERNAL_CATCH_MAKE_NAMESPACE2(...) ns_##__VA_ARGS__ +#define INTERNAL_CATCH_MAKE_NAMESPACE(name) INTERNAL_CATCH_MAKE_NAMESPACE2(name) + +#define INTERNAL_CATCH_REMOVE_PARENS(...) \ + INTERNAL_CATCH_EXPAND1(INTERNAL_CATCH_DEF __VA_ARGS__) + +#ifndef CATCH_CONFIG_TRADITIONAL_MSVC_PREPROCESSOR +#define INTERNAL_CATCH_MAKE_TYPE_LIST2(...) \ + decltype(get_wrapper()) +#define INTERNAL_CATCH_MAKE_TYPE_LIST(...) \ + INTERNAL_CATCH_MAKE_TYPE_LIST2( \ + INTERNAL_CATCH_REMOVE_PARENS(__VA_ARGS__)) +#else +#define INTERNAL_CATCH_MAKE_TYPE_LIST2(...) \ + INTERNAL_CATCH_EXPAND_VARGS( \ + decltype(get_wrapper())) +#define INTERNAL_CATCH_MAKE_TYPE_LIST(...) \ + INTERNAL_CATCH_EXPAND_VARGS(INTERNAL_CATCH_MAKE_TYPE_LIST2( \ + INTERNAL_CATCH_REMOVE_PARENS(__VA_ARGS__))) +#endif + +#define INTERNAL_CATCH_MAKE_TYPE_LISTS_FROM_TYPES(...) \ + CATCH_REC_LIST(INTERNAL_CATCH_MAKE_TYPE_LIST, __VA_ARGS__) + +#define INTERNAL_CATCH_REMOVE_PARENS_1_ARG(_0) INTERNAL_CATCH_REMOVE_PARENS(_0) +#define INTERNAL_CATCH_REMOVE_PARENS_2_ARG(_0, _1) \ + INTERNAL_CATCH_REMOVE_PARENS(_0), INTERNAL_CATCH_REMOVE_PARENS_1_ARG(_1) +#define INTERNAL_CATCH_REMOVE_PARENS_3_ARG(_0, _1, _2) \ + INTERNAL_CATCH_REMOVE_PARENS(_0), \ + INTERNAL_CATCH_REMOVE_PARENS_2_ARG(_1, _2) +#define INTERNAL_CATCH_REMOVE_PARENS_4_ARG(_0, _1, _2, _3) \ + INTERNAL_CATCH_REMOVE_PARENS(_0), \ + INTERNAL_CATCH_REMOVE_PARENS_3_ARG(_1, _2, _3) +#define INTERNAL_CATCH_REMOVE_PARENS_5_ARG(_0, _1, _2, _3, _4) \ + INTERNAL_CATCH_REMOVE_PARENS(_0), \ + INTERNAL_CATCH_REMOVE_PARENS_4_ARG(_1, _2, _3, _4) +#define INTERNAL_CATCH_REMOVE_PARENS_6_ARG(_0, _1, _2, _3, _4, _5) \ + INTERNAL_CATCH_REMOVE_PARENS(_0), \ + INTERNAL_CATCH_REMOVE_PARENS_5_ARG(_1, _2, _3, _4, _5) +#define INTERNAL_CATCH_REMOVE_PARENS_7_ARG(_0, _1, _2, _3, _4, _5, _6) \ + INTERNAL_CATCH_REMOVE_PARENS(_0), \ + INTERNAL_CATCH_REMOVE_PARENS_6_ARG(_1, _2, _3, _4, _5, _6) +#define INTERNAL_CATCH_REMOVE_PARENS_8_ARG(_0, _1, _2, _3, _4, _5, _6, _7) \ + INTERNAL_CATCH_REMOVE_PARENS(_0), \ + INTERNAL_CATCH_REMOVE_PARENS_7_ARG(_1, _2, _3, _4, _5, _6, _7) +#define INTERNAL_CATCH_REMOVE_PARENS_9_ARG(_0, _1, _2, _3, _4, _5, _6, _7, _8) \ + INTERNAL_CATCH_REMOVE_PARENS(_0), \ + INTERNAL_CATCH_REMOVE_PARENS_8_ARG(_1, _2, _3, _4, _5, _6, _7, \ + _8) +#define INTERNAL_CATCH_REMOVE_PARENS_10_ARG(_0, _1, _2, _3, _4, _5, _6, _7, \ + _8, _9) \ + INTERNAL_CATCH_REMOVE_PARENS(_0), \ + INTERNAL_CATCH_REMOVE_PARENS_9_ARG(_1, _2, _3, _4, _5, _6, _7, \ + _8, _9) +#define INTERNAL_CATCH_REMOVE_PARENS_11_ARG(_0, _1, _2, _3, _4, _5, _6, _7, \ + _8, _9, _10) \ + INTERNAL_CATCH_REMOVE_PARENS(_0), \ + INTERNAL_CATCH_REMOVE_PARENS_10_ARG(_1, _2, _3, _4, _5, _6, \ + _7, _8, _9, _10) + +#define INTERNAL_CATCH_VA_NARGS_IMPL(_0, _1, _2, _3, _4, _5, _6, _7, _8, _9, \ + _10, N, ...) \ + N + +#define INTERNAL_CATCH_TYPE_GEN \ + template struct TypeList { \ + }; \ + template \ + constexpr auto get_wrapper() noexcept->TypeList \ + { \ + return {}; \ + } \ + template class...> struct TemplateTypeList { \ + }; \ + template class... Cs> \ + constexpr auto get_wrapper() noexcept->TemplateTypeList \ + { \ + return {}; \ + } \ + template struct append; \ + template struct rewrap; \ + template class, typename...> struct create; \ + template class, typename> struct convert; \ + \ + template struct append { \ + using type = T; \ + }; \ + template class L1, typename... E1, \ + template class L2, typename... E2, \ + typename... Rest> \ + struct append, L2, Rest...> { \ + using type = typename append, Rest...>::type; \ + }; \ + template class L1, typename... E1, \ + typename... Rest> \ + struct append, TypeList, Rest...> { \ + using type = L1; \ + }; \ + \ + template class Container, \ + template class List, typename... elems> \ + struct rewrap, List> { \ + using type = TypeList>; \ + }; \ + template class Container, \ + template class List, class... Elems, \ + typename... Elements> \ + struct rewrap, List, \ + Elements...> { \ + using type = typename append< \ + TypeList>, \ + typename rewrap, \ + Elements...>::type>::type; \ + }; \ + \ + template class Final, \ + template class... Containers, typename... Types> \ + struct create, \ + TypeList> { \ + using type = typename append< \ + Final<>, typename rewrap, \ + Types...>::type...>::type; \ + }; \ + template class Final, \ + template class List, typename... Ts> \ + struct convert> { \ + using type = typename append, TypeList...>::type; \ + }; + +#define INTERNAL_CATCH_NTTP_1(signature, ...) \ + template struct Nttp { \ + }; \ + template \ + constexpr auto get_wrapper() noexcept->Nttp<__VA_ARGS__> \ + { \ + return {}; \ + } \ + template class...> \ + struct NttpTemplateTypeList { \ + }; \ + template class... Cs> \ + constexpr auto get_wrapper() noexcept->NttpTemplateTypeList \ + { \ + return {}; \ + } \ + \ + template \ + class Container, \ + template class List, \ + INTERNAL_CATCH_REMOVE_PARENS(signature)> \ + struct rewrap, List<__VA_ARGS__>> { \ + using type = TypeList>; \ + }; \ + template \ + class Container, \ + template class List, \ + INTERNAL_CATCH_REMOVE_PARENS(signature), typename... Elements> \ + struct rewrap, List<__VA_ARGS__>, \ + Elements...> { \ + using type = typename append< \ + TypeList>, \ + typename rewrap, \ + Elements...>::type>::type; \ + }; \ + template class Final, \ + template \ + class... Containers, \ + typename... Types> \ + struct create, \ + TypeList> { \ + using type = typename append< \ + Final<>, \ + typename rewrap, \ + Types...>::type...>::type; \ + }; + +#define INTERNAL_CATCH_DECLARE_SIG_TEST0(TestName) +#define INTERNAL_CATCH_DECLARE_SIG_TEST1(TestName, signature) \ + template static void TestName() +#define INTERNAL_CATCH_DECLARE_SIG_TEST_X(TestName, signature, ...) \ + template static void TestName() + +#define INTERNAL_CATCH_DEFINE_SIG_TEST0(TestName) +#define INTERNAL_CATCH_DEFINE_SIG_TEST1(TestName, signature) \ + template static void TestName() +#define INTERNAL_CATCH_DEFINE_SIG_TEST_X(TestName, signature, ...) \ + template static void TestName() + +#define INTERNAL_CATCH_NTTP_REGISTER0(TestFunc, signature) \ + template \ + void reg_test(TypeList, Catch::NameAndTags nameAndTags) \ + { \ + Catch::AutoReg(Catch::makeTestInvoker(&TestFunc), \ + CATCH_INTERNAL_LINEINFO, Catch::StringRef(), \ + nameAndTags); \ + } + +#define INTERNAL_CATCH_NTTP_REGISTER(TestFunc, signature, ...) \ + template \ + void reg_test(Nttp<__VA_ARGS__>, Catch::NameAndTags nameAndTags) \ + { \ + Catch::AutoReg(Catch::makeTestInvoker(&TestFunc<__VA_ARGS__>), \ + CATCH_INTERNAL_LINEINFO, Catch::StringRef(), \ + nameAndTags); \ + } + +#define INTERNAL_CATCH_NTTP_REGISTER_METHOD0(TestName, signature, ...) \ + template \ + void reg_test(TypeList, Catch::StringRef className, \ + Catch::NameAndTags nameAndTags) \ + { \ + Catch::AutoReg(Catch::makeTestInvoker(&TestName::test), \ + CATCH_INTERNAL_LINEINFO, className, \ + nameAndTags); \ + } + +#define INTERNAL_CATCH_NTTP_REGISTER_METHOD(TestName, signature, ...) \ + template \ + void reg_test(Nttp<__VA_ARGS__>, Catch::StringRef className, \ + Catch::NameAndTags nameAndTags) \ + { \ + Catch::AutoReg( \ + Catch::makeTestInvoker(&TestName<__VA_ARGS__>::test), \ + CATCH_INTERNAL_LINEINFO, className, nameAndTags); \ + } + +#define INTERNAL_CATCH_DECLARE_SIG_TEST_METHOD0(TestName, ClassName) +#define INTERNAL_CATCH_DECLARE_SIG_TEST_METHOD1(TestName, ClassName, \ + signature) \ + template \ + struct TestName : INTERNAL_CATCH_REMOVE_PARENS(ClassName) { \ + void test(); \ + } + +#define INTERNAL_CATCH_DECLARE_SIG_TEST_METHOD_X(TestName, ClassName, \ + signature, ...) \ + template \ + struct TestName \ + : INTERNAL_CATCH_REMOVE_PARENS(ClassName)<__VA_ARGS__> { \ + void test(); \ + } + +#define INTERNAL_CATCH_DEFINE_SIG_TEST_METHOD0(TestName) +#define INTERNAL_CATCH_DEFINE_SIG_TEST_METHOD1(TestName, signature) \ + template \ + void INTERNAL_CATCH_MAKE_NAMESPACE( \ + TestName)::TestName::test() +#define INTERNAL_CATCH_DEFINE_SIG_TEST_METHOD_X(TestName, signature, ...) \ + template \ + void INTERNAL_CATCH_MAKE_NAMESPACE( \ + TestName)::TestName<__VA_ARGS__>::test() + +#ifndef CATCH_CONFIG_TRADITIONAL_MSVC_PREPROCESSOR +#define INTERNAL_CATCH_NTTP_0 +#define INTERNAL_CATCH_NTTP_GEN(...) \ + INTERNAL_CATCH_VA_NARGS_IMPL(__VA_ARGS__, \ + INTERNAL_CATCH_NTTP_1(__VA_ARGS__), \ + INTERNAL_CATCH_NTTP_1(__VA_ARGS__), \ + INTERNAL_CATCH_NTTP_1(__VA_ARGS__), \ + INTERNAL_CATCH_NTTP_1(__VA_ARGS__), \ + INTERNAL_CATCH_NTTP_1(__VA_ARGS__), \ + INTERNAL_CATCH_NTTP_1(__VA_ARGS__), \ + INTERNAL_CATCH_NTTP_1(__VA_ARGS__), \ + INTERNAL_CATCH_NTTP_1(__VA_ARGS__), \ + INTERNAL_CATCH_NTTP_1(__VA_ARGS__), \ + INTERNAL_CATCH_NTTP_1(__VA_ARGS__), \ + INTERNAL_CATCH_NTTP_0) +#define INTERNAL_CATCH_DEFINE_SIG_TEST_METHOD(TestName, ...) \ + INTERNAL_CATCH_VA_NARGS_IMPL("dummy", __VA_ARGS__, \ + INTERNAL_CATCH_DEFINE_SIG_TEST_METHOD_X, \ + INTERNAL_CATCH_DEFINE_SIG_TEST_METHOD_X, \ + INTERNAL_CATCH_DEFINE_SIG_TEST_METHOD_X, \ + INTERNAL_CATCH_DEFINE_SIG_TEST_METHOD_X, \ + INTERNAL_CATCH_DEFINE_SIG_TEST_METHOD_X, \ + INTERNAL_CATCH_DEFINE_SIG_TEST_METHOD_X, \ + INTERNAL_CATCH_DEFINE_SIG_TEST_METHOD_X, \ + INTERNAL_CATCH_DEFINE_SIG_TEST_METHOD_X, \ + INTERNAL_CATCH_DEFINE_SIG_TEST_METHOD_X, \ + INTERNAL_CATCH_DEFINE_SIG_TEST_METHOD1, \ + INTERNAL_CATCH_DEFINE_SIG_TEST_METHOD0) \ + (TestName, __VA_ARGS__) +#define INTERNAL_CATCH_DECLARE_SIG_TEST_METHOD(TestName, ClassName, ...) \ + INTERNAL_CATCH_VA_NARGS_IMPL("dummy", __VA_ARGS__, \ + INTERNAL_CATCH_DECLARE_SIG_TEST_METHOD_X, \ + INTERNAL_CATCH_DECLARE_SIG_TEST_METHOD_X, \ + INTERNAL_CATCH_DECLARE_SIG_TEST_METHOD_X, \ + INTERNAL_CATCH_DECLARE_SIG_TEST_METHOD_X, \ + INTERNAL_CATCH_DECLARE_SIG_TEST_METHOD_X, \ + INTERNAL_CATCH_DECLARE_SIG_TEST_METHOD_X, \ + INTERNAL_CATCH_DECLARE_SIG_TEST_METHOD_X, \ + INTERNAL_CATCH_DECLARE_SIG_TEST_METHOD_X, \ + INTERNAL_CATCH_DECLARE_SIG_TEST_METHOD_X, \ + INTERNAL_CATCH_DECLARE_SIG_TEST_METHOD1, \ + INTERNAL_CATCH_DECLARE_SIG_TEST_METHOD0) \ + (TestName, ClassName, __VA_ARGS__) +#define INTERNAL_CATCH_NTTP_REG_METHOD_GEN(TestName, ...) \ + INTERNAL_CATCH_VA_NARGS_IMPL("dummy", __VA_ARGS__, \ + INTERNAL_CATCH_NTTP_REGISTER_METHOD, \ + INTERNAL_CATCH_NTTP_REGISTER_METHOD, \ + INTERNAL_CATCH_NTTP_REGISTER_METHOD, \ + INTERNAL_CATCH_NTTP_REGISTER_METHOD, \ + INTERNAL_CATCH_NTTP_REGISTER_METHOD, \ + INTERNAL_CATCH_NTTP_REGISTER_METHOD, \ + INTERNAL_CATCH_NTTP_REGISTER_METHOD, \ + INTERNAL_CATCH_NTTP_REGISTER_METHOD, \ + INTERNAL_CATCH_NTTP_REGISTER_METHOD, \ + INTERNAL_CATCH_NTTP_REGISTER_METHOD0, \ + INTERNAL_CATCH_NTTP_REGISTER_METHOD0) \ + (TestName, __VA_ARGS__) +#define INTERNAL_CATCH_NTTP_REG_GEN(TestFunc, ...) \ + INTERNAL_CATCH_VA_NARGS_IMPL( \ + "dummy", __VA_ARGS__, INTERNAL_CATCH_NTTP_REGISTER, \ + INTERNAL_CATCH_NTTP_REGISTER, INTERNAL_CATCH_NTTP_REGISTER, \ + INTERNAL_CATCH_NTTP_REGISTER, INTERNAL_CATCH_NTTP_REGISTER, \ + INTERNAL_CATCH_NTTP_REGISTER, INTERNAL_CATCH_NTTP_REGISTER, \ + INTERNAL_CATCH_NTTP_REGISTER, INTERNAL_CATCH_NTTP_REGISTER, \ + INTERNAL_CATCH_NTTP_REGISTER0, INTERNAL_CATCH_NTTP_REGISTER0) \ + (TestFunc, __VA_ARGS__) +#define INTERNAL_CATCH_DEFINE_SIG_TEST(TestName, ...) \ + INTERNAL_CATCH_VA_NARGS_IMPL("dummy", __VA_ARGS__, \ + INTERNAL_CATCH_DEFINE_SIG_TEST_X, \ + INTERNAL_CATCH_DEFINE_SIG_TEST_X, \ + INTERNAL_CATCH_DEFINE_SIG_TEST_X, \ + INTERNAL_CATCH_DEFINE_SIG_TEST_X, \ + INTERNAL_CATCH_DEFINE_SIG_TEST_X, \ + INTERNAL_CATCH_DEFINE_SIG_TEST_X, \ + INTERNAL_CATCH_DEFINE_SIG_TEST_X, \ + INTERNAL_CATCH_DEFINE_SIG_TEST_X, \ + INTERNAL_CATCH_DEFINE_SIG_TEST_X, \ + INTERNAL_CATCH_DEFINE_SIG_TEST1, \ + INTERNAL_CATCH_DEFINE_SIG_TEST0) \ + (TestName, __VA_ARGS__) +#define INTERNAL_CATCH_DECLARE_SIG_TEST(TestName, ...) \ + INTERNAL_CATCH_VA_NARGS_IMPL("dummy", __VA_ARGS__, \ + INTERNAL_CATCH_DECLARE_SIG_TEST_X, \ + INTERNAL_CATCH_DECLARE_SIG_TEST_X, \ + INTERNAL_CATCH_DECLARE_SIG_TEST_X, \ + INTERNAL_CATCH_DECLARE_SIG_TEST_X, \ + INTERNAL_CATCH_DECLARE_SIG_TEST_X, \ + INTERNAL_CATCH_DECLARE_SIG_TEST_X, \ + INTERNAL_CATCH_DEFINE_SIG_TEST_X, \ + INTERNAL_CATCH_DECLARE_SIG_TEST_X, \ + INTERNAL_CATCH_DECLARE_SIG_TEST_X, \ + INTERNAL_CATCH_DECLARE_SIG_TEST1, \ + INTERNAL_CATCH_DECLARE_SIG_TEST0) \ + (TestName, __VA_ARGS__) +#define INTERNAL_CATCH_REMOVE_PARENS_GEN(...) \ + INTERNAL_CATCH_VA_NARGS_IMPL(__VA_ARGS__, \ + INTERNAL_CATCH_REMOVE_PARENS_11_ARG, \ + INTERNAL_CATCH_REMOVE_PARENS_10_ARG, \ + INTERNAL_CATCH_REMOVE_PARENS_9_ARG, \ + INTERNAL_CATCH_REMOVE_PARENS_8_ARG, \ + INTERNAL_CATCH_REMOVE_PARENS_7_ARG, \ + INTERNAL_CATCH_REMOVE_PARENS_6_ARG, \ + INTERNAL_CATCH_REMOVE_PARENS_5_ARG, \ + INTERNAL_CATCH_REMOVE_PARENS_4_ARG, \ + INTERNAL_CATCH_REMOVE_PARENS_3_ARG, \ + INTERNAL_CATCH_REMOVE_PARENS_2_ARG, \ + INTERNAL_CATCH_REMOVE_PARENS_1_ARG) \ + (__VA_ARGS__) +#else +#define INTERNAL_CATCH_NTTP_0(signature) +#define INTERNAL_CATCH_NTTP_GEN(...) \ + INTERNAL_CATCH_EXPAND_VARGS(INTERNAL_CATCH_VA_NARGS_IMPL( \ + __VA_ARGS__, INTERNAL_CATCH_NTTP_1, INTERNAL_CATCH_NTTP_1, \ + INTERNAL_CATCH_NTTP_1, INTERNAL_CATCH_NTTP_1, \ + INTERNAL_CATCH_NTTP_1, INTERNAL_CATCH_NTTP_1, \ + INTERNAL_CATCH_NTTP_1, INTERNAL_CATCH_NTTP_1, \ + INTERNAL_CATCH_NTTP_1, INTERNAL_CATCH_NTTP_1, \ + INTERNAL_CATCH_NTTP_0)(__VA_ARGS__)) +#define INTERNAL_CATCH_DEFINE_SIG_TEST_METHOD(TestName, ...) \ + INTERNAL_CATCH_EXPAND_VARGS(INTERNAL_CATCH_VA_NARGS_IMPL( \ + "dummy", __VA_ARGS__, INTERNAL_CATCH_DEFINE_SIG_TEST_METHOD_X, \ + INTERNAL_CATCH_DEFINE_SIG_TEST_METHOD_X, \ + INTERNAL_CATCH_DEFINE_SIG_TEST_METHOD_X, \ + INTERNAL_CATCH_DEFINE_SIG_TEST_METHOD_X, \ + INTERNAL_CATCH_DEFINE_SIG_TEST_METHOD_X, \ + INTERNAL_CATCH_DEFINE_SIG_TEST_METHOD_X, \ + INTERNAL_CATCH_DEFINE_SIG_TEST_METHOD_X, \ + INTERNAL_CATCH_DEFINE_SIG_TEST_METHOD_X, \ + INTERNAL_CATCH_DEFINE_SIG_TEST_METHOD_X, \ + INTERNAL_CATCH_DEFINE_SIG_TEST_METHOD1, \ + INTERNAL_CATCH_DEFINE_SIG_TEST_METHOD0)(TestName, \ + __VA_ARGS__)) +#define INTERNAL_CATCH_DECLARE_SIG_TEST_METHOD(TestName, ClassName, ...) \ + INTERNAL_CATCH_EXPAND_VARGS(INTERNAL_CATCH_VA_NARGS_IMPL( \ + "dummy", __VA_ARGS__, \ + INTERNAL_CATCH_DECLARE_SIG_TEST_METHOD_X, \ + INTERNAL_CATCH_DECLARE_SIG_TEST_METHOD_X, \ + INTERNAL_CATCH_DECLARE_SIG_TEST_METHOD_X, \ + INTERNAL_CATCH_DECLARE_SIG_TEST_METHOD_X, \ + INTERNAL_CATCH_DECLARE_SIG_TEST_METHOD_X, \ + INTERNAL_CATCH_DECLARE_SIG_TEST_METHOD_X, \ + INTERNAL_CATCH_DECLARE_SIG_TEST_METHOD_X, \ + INTERNAL_CATCH_DECLARE_SIG_TEST_METHOD_X, \ + INTERNAL_CATCH_DECLARE_SIG_TEST_METHOD_X, \ + INTERNAL_CATCH_DECLARE_SIG_TEST_METHOD1, \ + INTERNAL_CATCH_DECLARE_SIG_TEST_METHOD0)(TestName, ClassName, \ + __VA_ARGS__)) +#define INTERNAL_CATCH_NTTP_REG_METHOD_GEN(TestName, ...) \ + INTERNAL_CATCH_EXPAND_VARGS(INTERNAL_CATCH_VA_NARGS_IMPL( \ + "dummy", __VA_ARGS__, INTERNAL_CATCH_NTTP_REGISTER_METHOD, \ + INTERNAL_CATCH_NTTP_REGISTER_METHOD, \ + INTERNAL_CATCH_NTTP_REGISTER_METHOD, \ + INTERNAL_CATCH_NTTP_REGISTER_METHOD, \ + INTERNAL_CATCH_NTTP_REGISTER_METHOD, \ + INTERNAL_CATCH_NTTP_REGISTER_METHOD, \ + INTERNAL_CATCH_NTTP_REGISTER_METHOD, \ + INTERNAL_CATCH_NTTP_REGISTER_METHOD, \ + INTERNAL_CATCH_NTTP_REGISTER_METHOD, \ + INTERNAL_CATCH_NTTP_REGISTER_METHOD0, \ + INTERNAL_CATCH_NTTP_REGISTER_METHOD0)(TestName, __VA_ARGS__)) +#define INTERNAL_CATCH_NTTP_REG_GEN(TestFunc, ...) \ + INTERNAL_CATCH_EXPAND_VARGS(INTERNAL_CATCH_VA_NARGS_IMPL( \ + "dummy", __VA_ARGS__, INTERNAL_CATCH_NTTP_REGISTER, \ + INTERNAL_CATCH_NTTP_REGISTER, INTERNAL_CATCH_NTTP_REGISTER, \ + INTERNAL_CATCH_NTTP_REGISTER, INTERNAL_CATCH_NTTP_REGISTER, \ + INTERNAL_CATCH_NTTP_REGISTER, INTERNAL_CATCH_NTTP_REGISTER, \ + INTERNAL_CATCH_NTTP_REGISTER, INTERNAL_CATCH_NTTP_REGISTER, \ + INTERNAL_CATCH_NTTP_REGISTER0, \ + INTERNAL_CATCH_NTTP_REGISTER0)(TestFunc, __VA_ARGS__)) +#define INTERNAL_CATCH_DEFINE_SIG_TEST(TestName, ...) \ + INTERNAL_CATCH_EXPAND_VARGS(INTERNAL_CATCH_VA_NARGS_IMPL( \ + "dummy", __VA_ARGS__, INTERNAL_CATCH_DEFINE_SIG_TEST_X, \ + INTERNAL_CATCH_DEFINE_SIG_TEST_X, \ + INTERNAL_CATCH_DEFINE_SIG_TEST_X, \ + INTERNAL_CATCH_DEFINE_SIG_TEST_X, \ + INTERNAL_CATCH_DEFINE_SIG_TEST_X, \ + INTERNAL_CATCH_DEFINE_SIG_TEST_X, \ + INTERNAL_CATCH_DEFINE_SIG_TEST_X, \ + INTERNAL_CATCH_DEFINE_SIG_TEST_X, \ + INTERNAL_CATCH_DEFINE_SIG_TEST_X, \ + INTERNAL_CATCH_DEFINE_SIG_TEST1, \ + INTERNAL_CATCH_DEFINE_SIG_TEST0)(TestName, __VA_ARGS__)) +#define INTERNAL_CATCH_DECLARE_SIG_TEST(TestName, ...) \ + INTERNAL_CATCH_EXPAND_VARGS(INTERNAL_CATCH_VA_NARGS_IMPL( \ + "dummy", __VA_ARGS__, INTERNAL_CATCH_DECLARE_SIG_TEST_X, \ + INTERNAL_CATCH_DECLARE_SIG_TEST_X, \ + INTERNAL_CATCH_DECLARE_SIG_TEST_X, \ + INTERNAL_CATCH_DECLARE_SIG_TEST_X, \ + INTERNAL_CATCH_DECLARE_SIG_TEST_X, \ + INTERNAL_CATCH_DECLARE_SIG_TEST_X, \ + INTERNAL_CATCH_DEFINE_SIG_TEST_X, \ + INTERNAL_CATCH_DECLARE_SIG_TEST_X, \ + INTERNAL_CATCH_DECLARE_SIG_TEST_X, \ + INTERNAL_CATCH_DECLARE_SIG_TEST1, \ + INTERNAL_CATCH_DECLARE_SIG_TEST0)(TestName, __VA_ARGS__)) +#define INTERNAL_CATCH_REMOVE_PARENS_GEN(...) \ + INTERNAL_CATCH_EXPAND_VARGS(INTERNAL_CATCH_VA_NARGS_IMPL( \ + __VA_ARGS__, INTERNAL_CATCH_REMOVE_PARENS_11_ARG, \ + INTERNAL_CATCH_REMOVE_PARENS_10_ARG, \ + INTERNAL_CATCH_REMOVE_PARENS_9_ARG, \ + INTERNAL_CATCH_REMOVE_PARENS_8_ARG, \ + INTERNAL_CATCH_REMOVE_PARENS_7_ARG, \ + INTERNAL_CATCH_REMOVE_PARENS_6_ARG, \ + INTERNAL_CATCH_REMOVE_PARENS_5_ARG, \ + INTERNAL_CATCH_REMOVE_PARENS_4_ARG, \ + INTERNAL_CATCH_REMOVE_PARENS_3_ARG, \ + INTERNAL_CATCH_REMOVE_PARENS_2_ARG, \ + INTERNAL_CATCH_REMOVE_PARENS_1_ARG)(__VA_ARGS__)) +#endif + +// end catch_preprocessor.hpp +// start catch_meta.hpp + +#include + +namespace Catch { +template struct always_false : std::false_type { +}; + +template struct true_given : std::true_type { +}; +struct is_callable_tester { + template + true_given()( + std::declval()...))> static test(int); + template std::false_type static test(...); +}; + +template struct is_callable; + +template +struct is_callable + : decltype(is_callable_tester::test(0)) { +}; + +#if defined(__cpp_lib_is_invocable) && __cpp_lib_is_invocable >= 201703 +// std::result_of is deprecated in C++17 and removed in C++20. Hence, it is +// replaced with std::invoke_result here. +template +using FunctionReturnType = std::remove_reference_t< + std::remove_cv_t>>; +#else +// Keep ::type here because we still support C++11 +template +using FunctionReturnType = + typename std::remove_reference::type>::type>::type; +#endif + +} // namespace Catch + +namespace mpl_ { +struct na; +} + +// end catch_meta.hpp +namespace Catch { + +template class TestInvokerAsMethod : public ITestInvoker { + void (C::*m_testAsMethod)(); + +public: + TestInvokerAsMethod(void (C::*testAsMethod)()) noexcept + : m_testAsMethod(testAsMethod) + { + } + + void invoke() const override + { + C obj; + (obj.*m_testAsMethod)(); + } +}; + +auto makeTestInvoker(void (*testAsFunction)()) noexcept -> ITestInvoker *; + +template +auto makeTestInvoker(void (C::*testAsMethod)()) noexcept -> ITestInvoker * +{ + return new (std::nothrow) TestInvokerAsMethod(testAsMethod); +} + +struct NameAndTags { + NameAndTags(StringRef const &name_ = StringRef(), + StringRef const &tags_ = StringRef()) noexcept; + StringRef name; + StringRef tags; +}; + +struct AutoReg : NonCopyable { + AutoReg(ITestInvoker *invoker, SourceLineInfo const &lineInfo, + StringRef const &classOrMethod, + NameAndTags const &nameAndTags) noexcept; + ~AutoReg(); +}; + +} // end namespace Catch + +#if defined(CATCH_CONFIG_DISABLE) +#define INTERNAL_CATCH_TESTCASE_NO_REGISTRATION(TestName, ...) \ + static void TestName() +#define INTERNAL_CATCH_TESTCASE_METHOD_NO_REGISTRATION(TestName, ClassName, \ + ...) \ + namespace { \ + struct TestName : INTERNAL_CATCH_REMOVE_PARENS(ClassName) { \ + void test(); \ + }; \ + } \ + void TestName::test() +#define INTERNAL_CATCH_TEMPLATE_TEST_CASE_NO_REGISTRATION_2( \ + TestName, TestFunc, Name, Tags, Signature, ...) \ + INTERNAL_CATCH_DEFINE_SIG_TEST( \ + TestFunc, INTERNAL_CATCH_REMOVE_PARENS(Signature)) +#define INTERNAL_CATCH_TEMPLATE_TEST_CASE_METHOD_NO_REGISTRATION_2( \ + TestNameClass, TestName, ClassName, Name, Tags, Signature, ...) \ + namespace { \ + namespace INTERNAL_CATCH_MAKE_NAMESPACE(TestName) \ + { \ + INTERNAL_CATCH_DECLARE_SIG_TEST_METHOD( \ + TestName, ClassName, \ + INTERNAL_CATCH_REMOVE_PARENS(Signature)); \ + } \ + } \ + INTERNAL_CATCH_DEFINE_SIG_TEST_METHOD( \ + TestName, INTERNAL_CATCH_REMOVE_PARENS(Signature)) + +#ifndef CATCH_CONFIG_TRADITIONAL_MSVC_PREPROCESSOR +#define INTERNAL_CATCH_TEMPLATE_TEST_CASE_NO_REGISTRATION(Name, Tags, ...) \ + INTERNAL_CATCH_TEMPLATE_TEST_CASE_NO_REGISTRATION_2( \ + INTERNAL_CATCH_UNIQUE_NAME( \ + C_A_T_C_H_T_E_M_P_L_A_T_E_T_E_S_T_), \ + INTERNAL_CATCH_UNIQUE_NAME( \ + C_A_T_C_H_T_E_M_P_L_A_T_E_T_E_S_T_F_U_N_C_), \ + Name, Tags, typename TestType, __VA_ARGS__) +#else +#define INTERNAL_CATCH_TEMPLATE_TEST_CASE_NO_REGISTRATION(Name, Tags, ...) \ + INTERNAL_CATCH_EXPAND_VARGS( \ + INTERNAL_CATCH_TEMPLATE_TEST_CASE_NO_REGISTRATION_2( \ + INTERNAL_CATCH_UNIQUE_NAME( \ + C_A_T_C_H_T_E_M_P_L_A_T_E_T_E_S_T_), \ + INTERNAL_CATCH_UNIQUE_NAME( \ + C_A_T_C_H_T_E_M_P_L_A_T_E_T_E_S_T_F_U_N_C_), \ + Name, Tags, typename TestType, __VA_ARGS__)) +#endif + +#ifndef CATCH_CONFIG_TRADITIONAL_MSVC_PREPROCESSOR +#define INTERNAL_CATCH_TEMPLATE_TEST_CASE_SIG_NO_REGISTRATION(Name, Tags, \ + Signature, ...) \ + INTERNAL_CATCH_TEMPLATE_TEST_CASE_NO_REGISTRATION_2( \ + INTERNAL_CATCH_UNIQUE_NAME( \ + C_A_T_C_H_T_E_M_P_L_A_T_E_T_E_S_T_), \ + INTERNAL_CATCH_UNIQUE_NAME( \ + C_A_T_C_H_T_E_M_P_L_A_T_E_T_E_S_T_F_U_N_C_), \ + Name, Tags, Signature, __VA_ARGS__) +#else +#define INTERNAL_CATCH_TEMPLATE_TEST_CASE_SIG_NO_REGISTRATION(Name, Tags, \ + Signature, ...) \ + INTERNAL_CATCH_EXPAND_VARGS( \ + INTERNAL_CATCH_TEMPLATE_TEST_CASE_NO_REGISTRATION_2( \ + INTERNAL_CATCH_UNIQUE_NAME( \ + C_A_T_C_H_T_E_M_P_L_A_T_E_T_E_S_T_), \ + INTERNAL_CATCH_UNIQUE_NAME( \ + C_A_T_C_H_T_E_M_P_L_A_T_E_T_E_S_T_F_U_N_C_), \ + Name, Tags, Signature, __VA_ARGS__)) +#endif + +#ifndef CATCH_CONFIG_TRADITIONAL_MSVC_PREPROCESSOR +#define INTERNAL_CATCH_TEMPLATE_TEST_CASE_METHOD_NO_REGISTRATION( \ + ClassName, Name, Tags, ...) \ + INTERNAL_CATCH_TEMPLATE_TEST_CASE_METHOD_NO_REGISTRATION_2( \ + INTERNAL_CATCH_UNIQUE_NAME( \ + C_A_T_C_H_T_E_M_P_L_A_T_E_T_E_S_T_C_L_A_S_S_), \ + INTERNAL_CATCH_UNIQUE_NAME( \ + C_A_T_C_H_T_E_M_P_L_A_T_E_T_E_S_T_), \ + ClassName, Name, Tags, typename T, __VA_ARGS__) +#else +#define INTERNAL_CATCH_TEMPLATE_TEST_CASE_METHOD_NO_REGISTRATION( \ + ClassName, Name, Tags, ...) \ + INTERNAL_CATCH_EXPAND_VARGS( \ + INTERNAL_CATCH_TEMPLATE_TEST_CASE_METHOD_NO_REGISTRATION_2( \ + INTERNAL_CATCH_UNIQUE_NAME( \ + C_A_T_C_H_T_E_M_P_L_A_T_E_T_E_S_T_C_L_A_S_S_), \ + INTERNAL_CATCH_UNIQUE_NAME( \ + C_A_T_C_H_T_E_M_P_L_A_T_E_T_E_S_T_), \ + ClassName, Name, Tags, typename T, __VA_ARGS__)) +#endif + +#ifndef CATCH_CONFIG_TRADITIONAL_MSVC_PREPROCESSOR +#define INTERNAL_CATCH_TEMPLATE_TEST_CASE_METHOD_SIG_NO_REGISTRATION( \ + ClassName, Name, Tags, Signature, ...) \ + INTERNAL_CATCH_TEMPLATE_TEST_CASE_METHOD_NO_REGISTRATION_2( \ + INTERNAL_CATCH_UNIQUE_NAME( \ + C_A_T_C_H_T_E_M_P_L_A_T_E_T_E_S_T_C_L_A_S_S_), \ + INTERNAL_CATCH_UNIQUE_NAME( \ + C_A_T_C_H_T_E_M_P_L_A_T_E_T_E_S_T_), \ + ClassName, Name, Tags, Signature, __VA_ARGS__) +#else +#define INTERNAL_CATCH_TEMPLATE_TEST_CASE_METHOD_SIG_NO_REGISTRATION( \ + ClassName, Name, Tags, Signature, ...) \ + INTERNAL_CATCH_EXPAND_VARGS( \ + INTERNAL_CATCH_TEMPLATE_TEST_CASE_METHOD_NO_REGISTRATION_2( \ + INTERNAL_CATCH_UNIQUE_NAME( \ + C_A_T_C_H_T_E_M_P_L_A_T_E_T_E_S_T_C_L_A_S_S_), \ + INTERNAL_CATCH_UNIQUE_NAME( \ + C_A_T_C_H_T_E_M_P_L_A_T_E_T_E_S_T_), \ + ClassName, Name, Tags, Signature, __VA_ARGS__)) +#endif +#endif + +/////////////////////////////////////////////////////////////////////////////// +#define INTERNAL_CATCH_TESTCASE2(TestName, ...) \ + static void TestName(); \ + CATCH_INTERNAL_START_WARNINGS_SUPPRESSION \ + CATCH_INTERNAL_SUPPRESS_GLOBALS_WARNINGS \ + namespace { \ + Catch::AutoReg INTERNAL_CATCH_UNIQUE_NAME(autoRegistrar)( \ + Catch::makeTestInvoker(&TestName), CATCH_INTERNAL_LINEINFO, \ + Catch::StringRef(), Catch::NameAndTags{__VA_ARGS__}); \ + } /* NOLINT */ \ + CATCH_INTERNAL_STOP_WARNINGS_SUPPRESSION \ + static void TestName() +#define INTERNAL_CATCH_TESTCASE(...) \ + INTERNAL_CATCH_TESTCASE2( \ + INTERNAL_CATCH_UNIQUE_NAME(C_A_T_C_H_T_E_S_T_), __VA_ARGS__) + +/////////////////////////////////////////////////////////////////////////////// +#define INTERNAL_CATCH_METHOD_AS_TEST_CASE(QualifiedMethod, ...) \ + CATCH_INTERNAL_START_WARNINGS_SUPPRESSION \ + CATCH_INTERNAL_SUPPRESS_GLOBALS_WARNINGS \ + namespace { \ + Catch::AutoReg INTERNAL_CATCH_UNIQUE_NAME(autoRegistrar)( \ + Catch::makeTestInvoker(&QualifiedMethod), \ + CATCH_INTERNAL_LINEINFO, "&" #QualifiedMethod, \ + Catch::NameAndTags{__VA_ARGS__}); \ + } /* NOLINT */ \ + CATCH_INTERNAL_STOP_WARNINGS_SUPPRESSION + +/////////////////////////////////////////////////////////////////////////////// +#define INTERNAL_CATCH_TEST_CASE_METHOD2(TestName, ClassName, ...) \ + CATCH_INTERNAL_START_WARNINGS_SUPPRESSION \ + CATCH_INTERNAL_SUPPRESS_GLOBALS_WARNINGS \ + namespace { \ + struct TestName : INTERNAL_CATCH_REMOVE_PARENS(ClassName) { \ + void test(); \ + }; \ + Catch::AutoReg INTERNAL_CATCH_UNIQUE_NAME(autoRegistrar)( \ + Catch::makeTestInvoker(&TestName::test), \ + CATCH_INTERNAL_LINEINFO, #ClassName, \ + Catch::NameAndTags{__VA_ARGS__}); /* NOLINT */ \ + } \ + CATCH_INTERNAL_STOP_WARNINGS_SUPPRESSION \ + void TestName::test() +#define INTERNAL_CATCH_TEST_CASE_METHOD(ClassName, ...) \ + INTERNAL_CATCH_TEST_CASE_METHOD2( \ + INTERNAL_CATCH_UNIQUE_NAME(C_A_T_C_H_T_E_S_T_), ClassName, \ + __VA_ARGS__) + +/////////////////////////////////////////////////////////////////////////////// +#define INTERNAL_CATCH_REGISTER_TESTCASE(Function, ...) \ + CATCH_INTERNAL_START_WARNINGS_SUPPRESSION \ + CATCH_INTERNAL_SUPPRESS_GLOBALS_WARNINGS \ + Catch::AutoReg INTERNAL_CATCH_UNIQUE_NAME( \ + autoRegistrar)(Catch::makeTestInvoker(Function), \ + CATCH_INTERNAL_LINEINFO, Catch::StringRef(), \ + Catch::NameAndTags{__VA_ARGS__}); /* NOLINT */ \ + CATCH_INTERNAL_STOP_WARNINGS_SUPPRESSION + +/////////////////////////////////////////////////////////////////////////////// +#define INTERNAL_CATCH_TEMPLATE_TEST_CASE_2(TestName, TestFunc, Name, Tags, \ + Signature, ...) \ + CATCH_INTERNAL_START_WARNINGS_SUPPRESSION \ + CATCH_INTERNAL_SUPPRESS_GLOBALS_WARNINGS \ + CATCH_INTERNAL_SUPPRESS_ZERO_VARIADIC_WARNINGS \ + CATCH_INTERNAL_SUPPRESS_UNUSED_TEMPLATE_WARNINGS \ + INTERNAL_CATCH_DECLARE_SIG_TEST( \ + TestFunc, INTERNAL_CATCH_REMOVE_PARENS(Signature)); \ + namespace { \ + namespace INTERNAL_CATCH_MAKE_NAMESPACE(TestName) \ + { \ + INTERNAL_CATCH_TYPE_GEN \ + INTERNAL_CATCH_NTTP_GEN( \ + INTERNAL_CATCH_REMOVE_PARENS(Signature)) \ + INTERNAL_CATCH_NTTP_REG_GEN( \ + TestFunc, INTERNAL_CATCH_REMOVE_PARENS(Signature)) \ + template struct TestName { \ + TestName() \ + { \ + int index = 0; \ + constexpr char const *tmpl_types[] = {CATCH_REC_LIST( \ + INTERNAL_CATCH_STRINGIZE_WITHOUT_PARENS, \ + __VA_ARGS__)}; \ + using expander = int[]; \ + (void)expander{( \ + reg_test( \ + Types{}, \ + Catch::NameAndTags{ \ + Name " - " + \ + std::string( \ + tmpl_types \ + [index]), \ + Tags}), \ + index++)...}; /* NOLINT */ \ + } \ + }; \ + static int INTERNAL_CATCH_UNIQUE_NAME( \ + globalRegistrar) = []() { \ + TestName(); \ + return 0; \ + }(); \ + } \ + } \ + CATCH_INTERNAL_STOP_WARNINGS_SUPPRESSION \ + INTERNAL_CATCH_DEFINE_SIG_TEST( \ + TestFunc, INTERNAL_CATCH_REMOVE_PARENS(Signature)) + +#ifndef CATCH_CONFIG_TRADITIONAL_MSVC_PREPROCESSOR +#define INTERNAL_CATCH_TEMPLATE_TEST_CASE(Name, Tags, ...) \ + INTERNAL_CATCH_TEMPLATE_TEST_CASE_2( \ + INTERNAL_CATCH_UNIQUE_NAME( \ + C_A_T_C_H_T_E_M_P_L_A_T_E_T_E_S_T_), \ + INTERNAL_CATCH_UNIQUE_NAME( \ + C_A_T_C_H_T_E_M_P_L_A_T_E_T_E_S_T_F_U_N_C_), \ + Name, Tags, typename TestType, __VA_ARGS__) +#else +#define INTERNAL_CATCH_TEMPLATE_TEST_CASE(Name, Tags, ...) \ + INTERNAL_CATCH_EXPAND_VARGS(INTERNAL_CATCH_TEMPLATE_TEST_CASE_2( \ + INTERNAL_CATCH_UNIQUE_NAME(C_A_T_C_H_T_E_M_P_L_A_T_E_T_E_S_T_), \ + INTERNAL_CATCH_UNIQUE_NAME( \ + C_A_T_C_H_T_E_M_P_L_A_T_E_T_E_S_T_F_U_N_C_), \ + Name, Tags, typename TestType, __VA_ARGS__)) +#endif + +#ifndef CATCH_CONFIG_TRADITIONAL_MSVC_PREPROCESSOR +#define INTERNAL_CATCH_TEMPLATE_TEST_CASE_SIG(Name, Tags, Signature, ...) \ + INTERNAL_CATCH_TEMPLATE_TEST_CASE_2( \ + INTERNAL_CATCH_UNIQUE_NAME( \ + C_A_T_C_H_T_E_M_P_L_A_T_E_T_E_S_T_), \ + INTERNAL_CATCH_UNIQUE_NAME( \ + C_A_T_C_H_T_E_M_P_L_A_T_E_T_E_S_T_F_U_N_C_), \ + Name, Tags, Signature, __VA_ARGS__) +#else +#define INTERNAL_CATCH_TEMPLATE_TEST_CASE_SIG(Name, Tags, Signature, ...) \ + INTERNAL_CATCH_EXPAND_VARGS(INTERNAL_CATCH_TEMPLATE_TEST_CASE_2( \ + INTERNAL_CATCH_UNIQUE_NAME(C_A_T_C_H_T_E_M_P_L_A_T_E_T_E_S_T_), \ + INTERNAL_CATCH_UNIQUE_NAME( \ + C_A_T_C_H_T_E_M_P_L_A_T_E_T_E_S_T_F_U_N_C_), \ + Name, Tags, Signature, __VA_ARGS__)) +#endif + +#define INTERNAL_CATCH_TEMPLATE_PRODUCT_TEST_CASE2( \ + TestName, TestFuncName, Name, Tags, Signature, TmplTypes, TypesList) \ + CATCH_INTERNAL_START_WARNINGS_SUPPRESSION \ + CATCH_INTERNAL_SUPPRESS_GLOBALS_WARNINGS \ + CATCH_INTERNAL_SUPPRESS_ZERO_VARIADIC_WARNINGS \ + CATCH_INTERNAL_SUPPRESS_UNUSED_TEMPLATE_WARNINGS \ + template static void TestFuncName(); \ + namespace { \ + namespace INTERNAL_CATCH_MAKE_NAMESPACE(TestName) \ + { \ + INTERNAL_CATCH_TYPE_GEN \ + INTERNAL_CATCH_NTTP_GEN( \ + INTERNAL_CATCH_REMOVE_PARENS(Signature)) \ + template struct TestName { \ + void reg_tests() \ + { \ + int index = 0; \ + using expander = int[]; \ + constexpr char const *tmpl_types[] = {CATCH_REC_LIST( \ + INTERNAL_CATCH_STRINGIZE_WITHOUT_PARENS, \ + INTERNAL_CATCH_REMOVE_PARENS( \ + TmplTypes))}; \ + constexpr char const *types_list[] = {CATCH_REC_LIST( \ + INTERNAL_CATCH_STRINGIZE_WITHOUT_PARENS, \ + INTERNAL_CATCH_REMOVE_PARENS( \ + TypesList))}; \ + constexpr auto num_types = \ + sizeof(types_list) / \ + sizeof(types_list[0]); \ + (void)expander{( \ + Catch::AutoReg( \ + Catch::makeTestInvoker( \ + &TestFuncName), \ + CATCH_INTERNAL_LINEINFO, \ + Catch::StringRef(), \ + Catch::NameAndTags{ \ + Name " - " + \ + std::string( \ + tmpl_types \ + [index / \ + num_types]) + \ + "<" + \ + std::string( \ + types_list \ + [index % \ + num_types]) + \ + ">", \ + Tags}), \ + index++)...}; /* NOLINT */ \ + } \ + }; \ + static int INTERNAL_CATCH_UNIQUE_NAME( \ + globalRegistrar) = []() { \ + using TestInit = typename create< \ + TestName, \ + decltype(get_wrapper< \ + INTERNAL_CATCH_REMOVE_PARENS( \ + TmplTypes)>()), \ + TypeList>::type; \ + TestInit t; \ + t.reg_tests(); \ + return 0; \ + }(); \ + } \ + } \ + CATCH_INTERNAL_STOP_WARNINGS_SUPPRESSION \ + template static void TestFuncName() + +#ifndef CATCH_CONFIG_TRADITIONAL_MSVC_PREPROCESSOR +#define INTERNAL_CATCH_TEMPLATE_PRODUCT_TEST_CASE(Name, Tags, ...) \ + INTERNAL_CATCH_TEMPLATE_PRODUCT_TEST_CASE2( \ + INTERNAL_CATCH_UNIQUE_NAME( \ + C_A_T_C_H_T_E_M_P_L_A_T_E_T_E_S_T_), \ + INTERNAL_CATCH_UNIQUE_NAME( \ + C_A_T_C_H_T_E_M_P_L_A_T_E_T_E_S_T_F_U_N_C_), \ + Name, Tags, typename T, __VA_ARGS__) +#else +#define INTERNAL_CATCH_TEMPLATE_PRODUCT_TEST_CASE(Name, Tags, ...) \ + INTERNAL_CATCH_EXPAND_VARGS( \ + INTERNAL_CATCH_TEMPLATE_PRODUCT_TEST_CASE2( \ + INTERNAL_CATCH_UNIQUE_NAME( \ + C_A_T_C_H_T_E_M_P_L_A_T_E_T_E_S_T_), \ + INTERNAL_CATCH_UNIQUE_NAME( \ + C_A_T_C_H_T_E_M_P_L_A_T_E_T_E_S_T_F_U_N_C_), \ + Name, Tags, typename T, __VA_ARGS__)) +#endif + +#ifndef CATCH_CONFIG_TRADITIONAL_MSVC_PREPROCESSOR +#define INTERNAL_CATCH_TEMPLATE_PRODUCT_TEST_CASE_SIG(Name, Tags, Signature, \ + ...) \ + INTERNAL_CATCH_TEMPLATE_PRODUCT_TEST_CASE2( \ + INTERNAL_CATCH_UNIQUE_NAME( \ + C_A_T_C_H_T_E_M_P_L_A_T_E_T_E_S_T_), \ + INTERNAL_CATCH_UNIQUE_NAME( \ + C_A_T_C_H_T_E_M_P_L_A_T_E_T_E_S_T_F_U_N_C_), \ + Name, Tags, Signature, __VA_ARGS__) +#else +#define INTERNAL_CATCH_TEMPLATE_PRODUCT_TEST_CASE_SIG(Name, Tags, Signature, \ + ...) \ + INTERNAL_CATCH_EXPAND_VARGS( \ + INTERNAL_CATCH_TEMPLATE_PRODUCT_TEST_CASE2( \ + INTERNAL_CATCH_UNIQUE_NAME( \ + C_A_T_C_H_T_E_M_P_L_A_T_E_T_E_S_T_), \ + INTERNAL_CATCH_UNIQUE_NAME( \ + C_A_T_C_H_T_E_M_P_L_A_T_E_T_E_S_T_F_U_N_C_), \ + Name, Tags, Signature, __VA_ARGS__)) +#endif + +#define INTERNAL_CATCH_TEMPLATE_LIST_TEST_CASE_2(TestName, TestFunc, Name, \ + Tags, TmplList) \ + CATCH_INTERNAL_START_WARNINGS_SUPPRESSION \ + CATCH_INTERNAL_SUPPRESS_GLOBALS_WARNINGS \ + CATCH_INTERNAL_SUPPRESS_UNUSED_TEMPLATE_WARNINGS \ + template static void TestFunc(); \ + namespace { \ + namespace INTERNAL_CATCH_MAKE_NAMESPACE(TestName) \ + { \ + INTERNAL_CATCH_TYPE_GEN \ + template struct TestName { \ + void reg_tests() \ + { \ + int index = 0; \ + using expander = int[]; \ + (void)expander{( \ + Catch::AutoReg( \ + Catch::makeTestInvoker( \ + &TestFunc), \ + CATCH_INTERNAL_LINEINFO, \ + Catch::StringRef(), \ + Catch::NameAndTags{ \ + Name " - " + \ + std::string(INTERNAL_CATCH_STRINGIZE( \ + TmplList)) + \ + " - " + \ + std::to_string( \ + index), \ + Tags}), \ + index++)...}; /* NOLINT */ \ + } \ + }; \ + static int INTERNAL_CATCH_UNIQUE_NAME( \ + globalRegistrar) = []() { \ + using TestInit = \ + typename convert::type; \ + TestInit t; \ + t.reg_tests(); \ + return 0; \ + }(); \ + } \ + } \ + CATCH_INTERNAL_STOP_WARNINGS_SUPPRESSION \ + template static void TestFunc() + +#define INTERNAL_CATCH_TEMPLATE_LIST_TEST_CASE(Name, Tags, TmplList) \ + INTERNAL_CATCH_TEMPLATE_LIST_TEST_CASE_2( \ + INTERNAL_CATCH_UNIQUE_NAME( \ + C_A_T_C_H_T_E_M_P_L_A_T_E_T_E_S_T_), \ + INTERNAL_CATCH_UNIQUE_NAME( \ + C_A_T_C_H_T_E_M_P_L_A_T_E_T_E_S_T_F_U_N_C_), \ + Name, Tags, TmplList) + +#define INTERNAL_CATCH_TEMPLATE_TEST_CASE_METHOD_2( \ + TestNameClass, TestName, ClassName, Name, Tags, Signature, ...) \ + CATCH_INTERNAL_START_WARNINGS_SUPPRESSION \ + CATCH_INTERNAL_SUPPRESS_GLOBALS_WARNINGS \ + CATCH_INTERNAL_SUPPRESS_ZERO_VARIADIC_WARNINGS \ + CATCH_INTERNAL_SUPPRESS_UNUSED_TEMPLATE_WARNINGS \ + namespace { \ + namespace INTERNAL_CATCH_MAKE_NAMESPACE(TestName) \ + { \ + INTERNAL_CATCH_TYPE_GEN \ + INTERNAL_CATCH_NTTP_GEN( \ + INTERNAL_CATCH_REMOVE_PARENS(Signature)) \ + INTERNAL_CATCH_DECLARE_SIG_TEST_METHOD( \ + TestName, ClassName, \ + INTERNAL_CATCH_REMOVE_PARENS(Signature)); \ + INTERNAL_CATCH_NTTP_REG_METHOD_GEN( \ + TestName, INTERNAL_CATCH_REMOVE_PARENS(Signature)) \ + template struct TestNameClass { \ + TestNameClass() \ + { \ + int index = 0; \ + constexpr char const *tmpl_types[] = {CATCH_REC_LIST( \ + INTERNAL_CATCH_STRINGIZE_WITHOUT_PARENS, \ + __VA_ARGS__)}; \ + using expander = int[]; \ + (void)expander{( \ + reg_test( \ + Types{}, #ClassName, \ + Catch::NameAndTags{ \ + Name " - " + \ + std::string( \ + tmpl_types \ + [index]), \ + Tags}), \ + index++)...}; /* NOLINT */ \ + } \ + }; \ + static int INTERNAL_CATCH_UNIQUE_NAME( \ + globalRegistrar) = []() { \ + TestNameClass(); \ + return 0; \ + }(); \ + } \ + } \ + CATCH_INTERNAL_STOP_WARNINGS_SUPPRESSION \ + INTERNAL_CATCH_DEFINE_SIG_TEST_METHOD( \ + TestName, INTERNAL_CATCH_REMOVE_PARENS(Signature)) + +#ifndef CATCH_CONFIG_TRADITIONAL_MSVC_PREPROCESSOR +#define INTERNAL_CATCH_TEMPLATE_TEST_CASE_METHOD(ClassName, Name, Tags, ...) \ + INTERNAL_CATCH_TEMPLATE_TEST_CASE_METHOD_2( \ + INTERNAL_CATCH_UNIQUE_NAME( \ + C_A_T_C_H_T_E_M_P_L_A_T_E_T_E_S_T_C_L_A_S_S_), \ + INTERNAL_CATCH_UNIQUE_NAME( \ + C_A_T_C_H_T_E_M_P_L_A_T_E_T_E_S_T_), \ + ClassName, Name, Tags, typename T, __VA_ARGS__) +#else +#define INTERNAL_CATCH_TEMPLATE_TEST_CASE_METHOD(ClassName, Name, Tags, ...) \ + INTERNAL_CATCH_EXPAND_VARGS( \ + INTERNAL_CATCH_TEMPLATE_TEST_CASE_METHOD_2( \ + INTERNAL_CATCH_UNIQUE_NAME( \ + C_A_T_C_H_T_E_M_P_L_A_T_E_T_E_S_T_C_L_A_S_S_), \ + INTERNAL_CATCH_UNIQUE_NAME( \ + C_A_T_C_H_T_E_M_P_L_A_T_E_T_E_S_T_), \ + ClassName, Name, Tags, typename T, __VA_ARGS__)) +#endif + +#ifndef CATCH_CONFIG_TRADITIONAL_MSVC_PREPROCESSOR +#define INTERNAL_CATCH_TEMPLATE_TEST_CASE_METHOD_SIG(ClassName, Name, Tags, \ + Signature, ...) \ + INTERNAL_CATCH_TEMPLATE_TEST_CASE_METHOD_2( \ + INTERNAL_CATCH_UNIQUE_NAME( \ + C_A_T_C_H_T_E_M_P_L_A_T_E_T_E_S_T_C_L_A_S_S_), \ + INTERNAL_CATCH_UNIQUE_NAME( \ + C_A_T_C_H_T_E_M_P_L_A_T_E_T_E_S_T_), \ + ClassName, Name, Tags, Signature, __VA_ARGS__) +#else +#define INTERNAL_CATCH_TEMPLATE_TEST_CASE_METHOD_SIG(ClassName, Name, Tags, \ + Signature, ...) \ + INTERNAL_CATCH_EXPAND_VARGS( \ + INTERNAL_CATCH_TEMPLATE_TEST_CASE_METHOD_2( \ + INTERNAL_CATCH_UNIQUE_NAME( \ + C_A_T_C_H_T_E_M_P_L_A_T_E_T_E_S_T_C_L_A_S_S_), \ + INTERNAL_CATCH_UNIQUE_NAME( \ + C_A_T_C_H_T_E_M_P_L_A_T_E_T_E_S_T_), \ + ClassName, Name, Tags, Signature, __VA_ARGS__)) +#endif + +#define INTERNAL_CATCH_TEMPLATE_PRODUCT_TEST_CASE_METHOD_2( \ + TestNameClass, TestName, ClassName, Name, Tags, Signature, TmplTypes, \ + TypesList) \ + CATCH_INTERNAL_START_WARNINGS_SUPPRESSION \ + CATCH_INTERNAL_SUPPRESS_GLOBALS_WARNINGS \ + CATCH_INTERNAL_SUPPRESS_ZERO_VARIADIC_WARNINGS \ + CATCH_INTERNAL_SUPPRESS_UNUSED_TEMPLATE_WARNINGS \ + template \ + struct TestName : INTERNAL_CATCH_REMOVE_PARENS(ClassName) { \ + void test(); \ + }; \ + namespace { \ + namespace INTERNAL_CATCH_MAKE_NAMESPACE(TestNameClass) \ + { \ + INTERNAL_CATCH_TYPE_GEN \ + INTERNAL_CATCH_NTTP_GEN( \ + INTERNAL_CATCH_REMOVE_PARENS(Signature)) \ + template struct TestNameClass { \ + void reg_tests() \ + { \ + int index = 0; \ + using expander = int[]; \ + constexpr char const *tmpl_types[] = {CATCH_REC_LIST( \ + INTERNAL_CATCH_STRINGIZE_WITHOUT_PARENS, \ + INTERNAL_CATCH_REMOVE_PARENS( \ + TmplTypes))}; \ + constexpr char const *types_list[] = {CATCH_REC_LIST( \ + INTERNAL_CATCH_STRINGIZE_WITHOUT_PARENS, \ + INTERNAL_CATCH_REMOVE_PARENS( \ + TypesList))}; \ + constexpr auto num_types = \ + sizeof(types_list) / \ + sizeof(types_list[0]); \ + (void)expander{( \ + Catch::AutoReg( \ + Catch::makeTestInvoker( \ + &TestName::test), \ + CATCH_INTERNAL_LINEINFO, \ + #ClassName, \ + Catch::NameAndTags{ \ + Name " - " + \ + std::string( \ + tmpl_types \ + [index / \ + num_types]) + \ + "<" + \ + std::string( \ + types_list \ + [index % \ + num_types]) + \ + ">", \ + Tags}), \ + index++)...}; /* NOLINT */ \ + } \ + }; \ + static int INTERNAL_CATCH_UNIQUE_NAME( \ + globalRegistrar) = []() { \ + using TestInit = typename create< \ + TestNameClass, \ + decltype(get_wrapper< \ + INTERNAL_CATCH_REMOVE_PARENS( \ + TmplTypes)>()), \ + TypeList>::type; \ + TestInit t; \ + t.reg_tests(); \ + return 0; \ + }(); \ + } \ + } \ + CATCH_INTERNAL_STOP_WARNINGS_SUPPRESSION \ + template void TestName::test() + +#ifndef CATCH_CONFIG_TRADITIONAL_MSVC_PREPROCESSOR +#define INTERNAL_CATCH_TEMPLATE_PRODUCT_TEST_CASE_METHOD(ClassName, Name, \ + Tags, ...) \ + INTERNAL_CATCH_TEMPLATE_PRODUCT_TEST_CASE_METHOD_2( \ + INTERNAL_CATCH_UNIQUE_NAME( \ + C_A_T_C_H_T_E_M_P_L_A_T_E_T_E_S_T_), \ + INTERNAL_CATCH_UNIQUE_NAME( \ + C_A_T_C_H_T_E_M_P_L_A_T_E_T_E_S_T_F_U_N_C_), \ + ClassName, Name, Tags, typename T, __VA_ARGS__) +#else +#define INTERNAL_CATCH_TEMPLATE_PRODUCT_TEST_CASE_METHOD(ClassName, Name, \ + Tags, ...) \ + INTERNAL_CATCH_EXPAND_VARGS( \ + INTERNAL_CATCH_TEMPLATE_PRODUCT_TEST_CASE_METHOD_2( \ + INTERNAL_CATCH_UNIQUE_NAME( \ + C_A_T_C_H_T_E_M_P_L_A_T_E_T_E_S_T_), \ + INTERNAL_CATCH_UNIQUE_NAME( \ + C_A_T_C_H_T_E_M_P_L_A_T_E_T_E_S_T_F_U_N_C_), \ + ClassName, Name, Tags, typename T, __VA_ARGS__)) +#endif + +#ifndef CATCH_CONFIG_TRADITIONAL_MSVC_PREPROCESSOR +#define INTERNAL_CATCH_TEMPLATE_PRODUCT_TEST_CASE_METHOD_SIG( \ + ClassName, Name, Tags, Signature, ...) \ + INTERNAL_CATCH_TEMPLATE_PRODUCT_TEST_CASE_METHOD_2( \ + INTERNAL_CATCH_UNIQUE_NAME( \ + C_A_T_C_H_T_E_M_P_L_A_T_E_T_E_S_T_), \ + INTERNAL_CATCH_UNIQUE_NAME( \ + C_A_T_C_H_T_E_M_P_L_A_T_E_T_E_S_T_F_U_N_C_), \ + ClassName, Name, Tags, Signature, __VA_ARGS__) +#else +#define INTERNAL_CATCH_TEMPLATE_PRODUCT_TEST_CASE_METHOD_SIG( \ + ClassName, Name, Tags, Signature, ...) \ + INTERNAL_CATCH_EXPAND_VARGS( \ + INTERNAL_CATCH_TEMPLATE_PRODUCT_TEST_CASE_METHOD_2( \ + INTERNAL_CATCH_UNIQUE_NAME( \ + C_A_T_C_H_T_E_M_P_L_A_T_E_T_E_S_T_), \ + INTERNAL_CATCH_UNIQUE_NAME( \ + C_A_T_C_H_T_E_M_P_L_A_T_E_T_E_S_T_F_U_N_C_), \ + ClassName, Name, Tags, Signature, __VA_ARGS__)) +#endif + +#define INTERNAL_CATCH_TEMPLATE_LIST_TEST_CASE_METHOD_2( \ + TestNameClass, TestName, ClassName, Name, Tags, TmplList) \ + CATCH_INTERNAL_START_WARNINGS_SUPPRESSION \ + CATCH_INTERNAL_SUPPRESS_GLOBALS_WARNINGS \ + CATCH_INTERNAL_SUPPRESS_UNUSED_TEMPLATE_WARNINGS \ + template \ + struct TestName : INTERNAL_CATCH_REMOVE_PARENS(ClassName) { \ + void test(); \ + }; \ + namespace { \ + namespace INTERNAL_CATCH_MAKE_NAMESPACE(TestName) \ + { \ + INTERNAL_CATCH_TYPE_GEN \ + template struct TestNameClass { \ + void reg_tests() \ + { \ + int index = 0; \ + using expander = int[]; \ + (void)expander{( \ + Catch::AutoReg( \ + Catch::makeTestInvoker( \ + &TestName::test), \ + CATCH_INTERNAL_LINEINFO, \ + #ClassName, \ + Catch::NameAndTags{ \ + Name " - " + \ + std::string(INTERNAL_CATCH_STRINGIZE( \ + TmplList)) + \ + " - " + \ + std::to_string( \ + index), \ + Tags}), \ + index++)...}; /* NOLINT */ \ + } \ + }; \ + static int INTERNAL_CATCH_UNIQUE_NAME( \ + globalRegistrar) = []() { \ + using TestInit = typename convert::type; \ + TestInit t; \ + t.reg_tests(); \ + return 0; \ + }(); \ + } \ + } \ + CATCH_INTERNAL_STOP_WARNINGS_SUPPRESSION \ + template void TestName::test() + +#define INTERNAL_CATCH_TEMPLATE_LIST_TEST_CASE_METHOD(ClassName, Name, Tags, \ + TmplList) \ + INTERNAL_CATCH_TEMPLATE_LIST_TEST_CASE_METHOD_2( \ + INTERNAL_CATCH_UNIQUE_NAME( \ + C_A_T_C_H_T_E_M_P_L_A_T_E_T_E_S_T_), \ + INTERNAL_CATCH_UNIQUE_NAME( \ + C_A_T_C_H_T_E_M_P_L_A_T_E_T_E_S_T_F_U_N_C_), \ + ClassName, Name, Tags, TmplList) + +// end catch_test_registry.h +// start catch_capture.hpp + +// start catch_assertionhandler.h + +// start catch_assertioninfo.h + +// start catch_result_type.h + +namespace Catch { + +// ResultWas::OfType enum +struct ResultWas { + enum OfType { + Unknown = -1, + Ok = 0, + Info = 1, + Warning = 2, + + FailureBit = 0x10, + + ExpressionFailed = FailureBit | 1, + ExplicitFailure = FailureBit | 2, + + Exception = 0x100 | FailureBit, + + ThrewException = Exception | 1, + DidntThrowException = Exception | 2, + + FatalErrorCondition = 0x200 | FailureBit + + }; +}; + +bool isOk(ResultWas::OfType resultType); +bool isJustInfo(int flags); + +// ResultDisposition::Flags enum +struct ResultDisposition { + enum Flags { + Normal = 0x01, + + ContinueOnFailure = + 0x02, // Failures fail test, but execution continues + FalseTest = 0x04, // Prefix expression with ! + SuppressFail = + 0x08 // Failures are reported but do not fail the test + }; +}; + +ResultDisposition::Flags operator|(ResultDisposition::Flags lhs, + ResultDisposition::Flags rhs); + +bool shouldContinueOnFailure(int flags); +inline bool isFalseTest(int flags) +{ + return (flags & ResultDisposition::FalseTest) != 0; +} +bool shouldSuppressFailure(int flags); + +} // end namespace Catch + +// end catch_result_type.h +namespace Catch { + +struct AssertionInfo { + StringRef macroName; + SourceLineInfo lineInfo; + StringRef capturedExpression; + ResultDisposition::Flags resultDisposition; + + // We want to delete this constructor but a compiler bug in 4.8 means + // the struct is then treated as non-aggregate + //AssertionInfo() = delete; +}; + +} // end namespace Catch + +// end catch_assertioninfo.h +// start catch_decomposer.h + +// start catch_tostring.h + +#include +#include +#include +#include +// start catch_stream.h + +#include +#include +#include + +namespace Catch { + +std::ostream &cout(); +std::ostream &cerr(); +std::ostream &clog(); + +class StringRef; + +struct IStream { + virtual ~IStream(); + virtual std::ostream &stream() const = 0; +}; + +auto makeStream(StringRef const &filename) -> IStream const *; + +class ReusableStringStream : NonCopyable { + std::size_t m_index; + std::ostream *m_oss; + +public: + ReusableStringStream(); + ~ReusableStringStream(); + + auto str() const -> std::string; + + template + auto operator<<(T const &value) -> ReusableStringStream & + { + *m_oss << value; + return *this; + } + auto get() -> std::ostream & { return *m_oss; } +}; +} + +// end catch_stream.h +// start catch_interfaces_enum_values_registry.h + +#include + +namespace Catch { + +namespace Detail { +struct EnumInfo { + StringRef m_name; + std::vector> m_values; + + ~EnumInfo(); + + StringRef lookup(int value) const; +}; +} // namespace Detail + +struct IMutableEnumValuesRegistry { + virtual ~IMutableEnumValuesRegistry(); + + virtual Detail::EnumInfo const & + registerEnum(StringRef enumName, StringRef allEnums, + std::vector const &values) = 0; + + template + Detail::EnumInfo const ®isterEnum(StringRef enumName, + StringRef allEnums, + std::initializer_list values) + { + static_assert(sizeof(int) >= sizeof(E), + "Cannot serialize enum to int"); + std::vector intValues; + intValues.reserve(values.size()); + for (auto enumValue : values) + intValues.push_back(static_cast(enumValue)); + return registerEnum(enumName, allEnums, intValues); + } +}; + +} // Catch + +// end catch_interfaces_enum_values_registry.h + +#ifdef CATCH_CONFIG_CPP17_STRING_VIEW +#include +#endif + +#ifdef __OBJC__ +// start catch_objc_arc.hpp + +#import + +#ifdef __has_feature +#define CATCH_ARC_ENABLED __has_feature(objc_arc) +#else +#define CATCH_ARC_ENABLED 0 +#endif + +void arcSafeRelease(NSObject *obj); +id performOptionalSelector(id obj, SEL sel); + +#if !CATCH_ARC_ENABLED +inline void arcSafeRelease(NSObject *obj) +{ + [obj release]; +} +inline id performOptionalSelector(id obj, SEL sel) +{ + if ([obj respondsToSelector:sel]) + return [obj performSelector:sel]; + return nil; +} +#define CATCH_UNSAFE_UNRETAINED +#define CATCH_ARC_STRONG +#else +inline void arcSafeRelease(NSObject *) {} +inline id performOptionalSelector(id obj, SEL sel) +{ +#ifdef __clang__ +#pragma clang diagnostic push +#pragma clang diagnostic ignored "-Warc-performSelector-leaks" +#endif + if ([obj respondsToSelector:sel]) + return [obj performSelector:sel]; +#ifdef __clang__ +#pragma clang diagnostic pop +#endif + return nil; +} +#define CATCH_UNSAFE_UNRETAINED __unsafe_unretained +#define CATCH_ARC_STRONG __strong +#endif + +// end catch_objc_arc.hpp +#endif + +#ifdef _MSC_VER +#pragma warning(push) +#pragma warning( \ + disable : 4180) // We attempt to stream a function (address) by const&, which MSVC complains about but is harmless +#endif + +namespace Catch { +namespace Detail { + +extern const std::string unprintableString; + +std::string rawMemoryToString(const void *object, std::size_t size); + +template std::string rawMemoryToString(const T &object) +{ + return rawMemoryToString(&object, sizeof(object)); +} + +template class IsStreamInsertable { + template + static auto test(int) + -> decltype(std::declval() << std::declval(), + std::true_type()); + + template static auto test(...) -> std::false_type; + +public: + static const bool value = + decltype(test(0))::value; +}; + +template std::string convertUnknownEnumToString(E e); + +template +typename std::enable_if::value && + !std::is_base_of::value, + std::string>::type +convertUnstreamable(T const &) +{ + return Detail::unprintableString; +} +template +typename std::enable_if::value && + std::is_base_of::value, + std::string>::type +convertUnstreamable(T const &ex) +{ + return ex.what(); +} + +template +typename std::enable_if::value, std::string>::type +convertUnstreamable(T const &value) +{ + return convertUnknownEnumToString(value); +} + +#if defined(_MANAGED) +//! Convert a CLR string to a utf8 std::string +template std::string clrReferenceToString(T ^ ref) +{ + if (ref == nullptr) + return std::string("null"); + auto bytes = System::Text::Encoding::UTF8->GetBytes(ref->ToString()); + cli::pin_ptr p = &bytes[0]; + return std::string(reinterpret_cast(p), bytes->Length); +} +#endif + +} // namespace Detail + +// If we decide for C++14, change these to enable_if_ts +template struct StringMaker { + template + static typename std::enable_if< + ::Catch::Detail::IsStreamInsertable::value, + std::string>::type + convert(const Fake &value) + { + ReusableStringStream rss; + // NB: call using the function-like syntax to avoid ambiguity with + // user-defined templated operator<< under clang. + rss.operator<<(value); + return rss.str(); + } + + template + static typename std::enable_if< + !::Catch::Detail::IsStreamInsertable::value, + std::string>::type + convert(const Fake &value) + { +#if !defined(CATCH_CONFIG_FALLBACK_STRINGIFIER) + return Detail::convertUnstreamable(value); +#else + return CATCH_CONFIG_FALLBACK_STRINGIFIER(value); +#endif + } +}; + +namespace Detail { + +// This function dispatches all stringification requests inside of Catch. +// Should be preferably called fully qualified, like ::Catch::Detail::stringify +template std::string stringify(const T &e) +{ + return ::Catch::StringMaker::type>::type>::convert(e); +} + +template std::string convertUnknownEnumToString(E e) +{ + return ::Catch::Detail::stringify( + static_cast::type>(e)); +} + +#if defined(_MANAGED) +template std::string stringify(T ^ e) +{ + return ::Catch::StringMaker::convert(e); +} +#endif + +} // namespace Detail + +// Some predefined specializations + +template<> struct StringMaker { + static std::string convert(const std::string &str); +}; + +#ifdef CATCH_CONFIG_CPP17_STRING_VIEW +template<> struct StringMaker { + static std::string convert(std::string_view str); +}; +#endif + +template<> struct StringMaker { + static std::string convert(char const *str); +}; +template<> struct StringMaker { + static std::string convert(char *str); +}; + +#ifdef CATCH_CONFIG_WCHAR +template<> struct StringMaker { + static std::string convert(const std::wstring &wstr); +}; + +#ifdef CATCH_CONFIG_CPP17_STRING_VIEW +template<> struct StringMaker { + static std::string convert(std::wstring_view str); +}; +#endif + +template<> struct StringMaker { + static std::string convert(wchar_t const *str); +}; +template<> struct StringMaker { + static std::string convert(wchar_t *str); +}; +#endif + +// TBD: Should we use `strnlen` to ensure that we don't go out of the buffer, +// while keeping string semantics? +template struct StringMaker { + static std::string convert(char const *str) + { + return ::Catch::Detail::stringify(std::string{str}); + } +}; +template struct StringMaker { + static std::string convert(signed char const *str) + { + return ::Catch::Detail::stringify( + std::string{reinterpret_cast(str)}); + } +}; +template struct StringMaker { + static std::string convert(unsigned char const *str) + { + return ::Catch::Detail::stringify( + std::string{reinterpret_cast(str)}); + } +}; + +#if defined(CATCH_CONFIG_CPP17_BYTE) +template<> struct StringMaker { + static std::string convert(std::byte value); +}; +#endif // defined(CATCH_CONFIG_CPP17_BYTE) +template<> struct StringMaker { + static std::string convert(int value); +}; +template<> struct StringMaker { + static std::string convert(long value); +}; +template<> struct StringMaker { + static std::string convert(long long value); +}; +template<> struct StringMaker { + static std::string convert(unsigned int value); +}; +template<> struct StringMaker { + static std::string convert(unsigned long value); +}; +template<> struct StringMaker { + static std::string convert(unsigned long long value); +}; + +template<> struct StringMaker { + static std::string convert(bool b); +}; + +template<> struct StringMaker { + static std::string convert(char c); +}; +template<> struct StringMaker { + static std::string convert(signed char c); +}; +template<> struct StringMaker { + static std::string convert(unsigned char c); +}; + +template<> struct StringMaker { + static std::string convert(std::nullptr_t); +}; + +template<> struct StringMaker { + static std::string convert(float value); + static int precision; +}; + +template<> struct StringMaker { + static std::string convert(double value); + static int precision; +}; + +template struct StringMaker { + template static std::string convert(U *p) + { + if (p) { + return ::Catch::Detail::rawMemoryToString(p); + } else { + return "nullptr"; + } + } +}; + +template struct StringMaker { + static std::string convert(R C::*p) + { + if (p) { + return ::Catch::Detail::rawMemoryToString(p); + } else { + return "nullptr"; + } + } +}; + +#if defined(_MANAGED) +template struct StringMaker { + static std::string convert(T ^ ref) + { + return ::Catch::Detail::clrReferenceToString(ref); + } +}; +#endif + +namespace Detail { +template +std::string rangeToString(InputIterator first, Sentinel last) +{ + ReusableStringStream rss; + rss << "{ "; + if (first != last) { + rss << ::Catch::Detail::stringify(*first); + for (++first; first != last; ++first) + rss << ", " << ::Catch::Detail::stringify(*first); + } + rss << " }"; + return rss.str(); +} +} + +#ifdef __OBJC__ +template<> struct StringMaker { + static std::string convert(NSString *nsstring) + { + if (!nsstring) + return "nil"; + return std::string("@") + [nsstring UTF8String]; + } +}; +template<> struct StringMaker { + static std::string convert(NSObject *nsObject) + { + return ::Catch::Detail::stringify([nsObject description]); + } +}; +namespace Detail { +inline std::string stringify(NSString *nsstring) +{ + return StringMaker::convert(nsstring); +} + +} // namespace Detail +#endif // __OBJC__ + +} // namespace Catch + +////////////////////////////////////////////////////// +// Separate std-lib types stringification, so it can be selectively enabled +// This means that we do not bring in + +#if defined(CATCH_CONFIG_ENABLE_ALL_STRINGMAKERS) +#define CATCH_CONFIG_ENABLE_PAIR_STRINGMAKER +#define CATCH_CONFIG_ENABLE_TUPLE_STRINGMAKER +#define CATCH_CONFIG_ENABLE_VARIANT_STRINGMAKER +#define CATCH_CONFIG_ENABLE_CHRONO_STRINGMAKER +#define CATCH_CONFIG_ENABLE_OPTIONAL_STRINGMAKER +#endif + +// Separate std::pair specialization +#if defined(CATCH_CONFIG_ENABLE_PAIR_STRINGMAKER) +#include +namespace Catch { +template struct StringMaker> { + static std::string convert(const std::pair &pair) + { + ReusableStringStream rss; + rss << "{ " << ::Catch::Detail::stringify(pair.first) << ", " + << ::Catch::Detail::stringify(pair.second) << " }"; + return rss.str(); + } +}; +} +#endif // CATCH_CONFIG_ENABLE_PAIR_STRINGMAKER + +#if defined(CATCH_CONFIG_ENABLE_OPTIONAL_STRINGMAKER) && \ + defined(CATCH_CONFIG_CPP17_OPTIONAL) +#include +namespace Catch { +template struct StringMaker> { + static std::string convert(const std::optional &optional) + { + ReusableStringStream rss; + if (optional.has_value()) { + rss << ::Catch::Detail::stringify(*optional); + } else { + rss << "{ }"; + } + return rss.str(); + } +}; +} +#endif // CATCH_CONFIG_ENABLE_OPTIONAL_STRINGMAKER + +// Separate std::tuple specialization +#if defined(CATCH_CONFIG_ENABLE_TUPLE_STRINGMAKER) +#include +namespace Catch { +namespace Detail { +template::value)> +struct TupleElementPrinter { + static void print(const Tuple &tuple, std::ostream &os) + { + os << (N ? ", " : " ") + << ::Catch::Detail::stringify(std::get(tuple)); + TupleElementPrinter::print(tuple, os); + } +}; + +template +struct TupleElementPrinter { + static void print(const Tuple &, std::ostream &) {} +}; + +} + +template struct StringMaker> { + static std::string convert(const std::tuple &tuple) + { + ReusableStringStream rss; + rss << '{'; + Detail::TupleElementPrinter>::print( + tuple, rss.get()); + rss << " }"; + return rss.str(); + } +}; +} +#endif // CATCH_CONFIG_ENABLE_TUPLE_STRINGMAKER + +#if defined(CATCH_CONFIG_ENABLE_VARIANT_STRINGMAKER) && \ + defined(CATCH_CONFIG_CPP17_VARIANT) +#include +namespace Catch { +template<> struct StringMaker { + static std::string convert(const std::monostate &) { return "{ }"; } +}; + +template struct StringMaker> { + static std::string convert(const std::variant &variant) + { + if (variant.valueless_by_exception()) { + return "{valueless variant}"; + } else { + return std::visit( + [](const auto &value) { + return ::Catch::Detail::stringify( + value); + }, + variant); + } + } +}; +} +#endif // CATCH_CONFIG_ENABLE_VARIANT_STRINGMAKER + +namespace Catch { +// Import begin/ end from std here +using std::begin; +using std::end; + +namespace detail { +template struct void_type { + using type = void; +}; + +template struct is_range_impl : std::false_type { +}; + +template +struct is_range_impl< + T, typename void_type()))>::type> + : std::true_type { +}; +} // namespace detail + +template struct is_range : detail::is_range_impl { +}; + +#if defined(_MANAGED) // Managed types are never ranges +template struct is_range { + static const bool value = false; +}; +#endif + +template std::string rangeToString(Range const &range) +{ + return ::Catch::Detail::rangeToString(begin(range), end(range)); +} + +// Handle vector specially +template +std::string rangeToString(std::vector const &v) +{ + ReusableStringStream rss; + rss << "{ "; + bool first = true; + for (bool b : v) { + if (first) + first = false; + else + rss << ", "; + rss << ::Catch::Detail::stringify(b); + } + rss << " }"; + return rss.str(); +} + +template +struct StringMaker< + R, typename std::enable_if< + is_range::value && + !::Catch::Detail::IsStreamInsertable::value>::type> { + static std::string convert(R const &range) + { + return rangeToString(range); + } +}; + +template struct StringMaker { + static std::string convert(T const (&arr)[SZ]) + { + return rangeToString(arr); + } +}; + +} // namespace Catch + +// Separate std::chrono::duration specialization +#if defined(CATCH_CONFIG_ENABLE_CHRONO_STRINGMAKER) +#include +#include +#include + +namespace Catch { + +template struct ratio_string { + static std::string symbol(); +}; + +template std::string ratio_string::symbol() +{ + Catch::ReusableStringStream rss; + rss << '[' << Ratio::num << '/' << Ratio::den << ']'; + return rss.str(); +} +template<> struct ratio_string { + static std::string symbol(); +}; +template<> struct ratio_string { + static std::string symbol(); +}; +template<> struct ratio_string { + static std::string symbol(); +}; +template<> struct ratio_string { + static std::string symbol(); +}; +template<> struct ratio_string { + static std::string symbol(); +}; +template<> struct ratio_string { + static std::string symbol(); +}; + +//////////// +// std::chrono::duration specializations +template +struct StringMaker> { + static std::string + convert(std::chrono::duration const &duration) + { + ReusableStringStream rss; + rss << duration.count() << ' ' << ratio_string::symbol() + << 's'; + return rss.str(); + } +}; +template +struct StringMaker>> { + static std::string + convert(std::chrono::duration> const &duration) + { + ReusableStringStream rss; + rss << duration.count() << " s"; + return rss.str(); + } +}; +template +struct StringMaker>> { + static std::string + convert(std::chrono::duration> const &duration) + { + ReusableStringStream rss; + rss << duration.count() << " m"; + return rss.str(); + } +}; +template +struct StringMaker>> { + static std::string + convert(std::chrono::duration> const &duration) + { + ReusableStringStream rss; + rss << duration.count() << " h"; + return rss.str(); + } +}; + +//////////// +// std::chrono::time_point specialization +// Generic time_point cannot be specialized, only std::chrono::time_point +template +struct StringMaker> { + static std::string + convert(std::chrono::time_point const &time_point) + { + return ::Catch::Detail::stringify( + time_point.time_since_epoch()) + + " since epoch"; + } +}; +// std::chrono::time_point specialization +template +struct StringMaker> { + static std::string + convert(std::chrono::time_point const &time_point) + { + auto converted = + std::chrono::system_clock::to_time_t(time_point); + +#ifdef _MSC_VER + std::tm timeInfo = {}; + gmtime_s(&timeInfo, &converted); +#else + std::tm *timeInfo = std::gmtime(&converted); +#endif + + auto const timeStampSize = sizeof("2017-01-16T17:06:45Z"); + char timeStamp[timeStampSize]; + const char *const fmt = "%Y-%m-%dT%H:%M:%SZ"; + +#ifdef _MSC_VER + std::strftime(timeStamp, timeStampSize, fmt, &timeInfo); +#else + std::strftime(timeStamp, timeStampSize, fmt, timeInfo); +#endif + return std::string(timeStamp); + } +}; +} +#endif // CATCH_CONFIG_ENABLE_CHRONO_STRINGMAKER + +#define INTERNAL_CATCH_REGISTER_ENUM(enumName, ...) \ + namespace Catch { \ + template<> struct StringMaker { \ + static std::string convert(enumName value) \ + { \ + static const auto &enumInfo = \ + ::Catch::getMutableRegistryHub() \ + .getMutableEnumValuesRegistry() \ + .registerEnum(#enumName, #__VA_ARGS__, \ + {__VA_ARGS__}); \ + return static_cast( \ + enumInfo.lookup(static_cast(value))); \ + } \ + }; \ + } + +#define CATCH_REGISTER_ENUM(enumName, ...) \ + INTERNAL_CATCH_REGISTER_ENUM(enumName, __VA_ARGS__) + +#ifdef _MSC_VER +#pragma warning(pop) +#endif + +// end catch_tostring.h +#include + +#ifdef _MSC_VER +#pragma warning(push) +#pragma warning(disable : 4389) // '==' : signed/unsigned mismatch +#pragma warning(disable : 4018) // more "signed/unsigned mismatch" +#pragma warning( \ + disable : 4312) // Converting int to T* using reinterpret_cast (issue on x64 platform) +#pragma warning( \ + disable : 4180) // qualifier applied to function type has no meaning +#pragma warning(disable : 4800) // Forcing result to true or false +#endif + +namespace Catch { + +struct ITransientExpression { + auto isBinaryExpression() const -> bool { return m_isBinaryExpression; } + auto getResult() const -> bool { return m_result; } + virtual void streamReconstructedExpression(std::ostream &os) const = 0; + + ITransientExpression(bool isBinaryExpression, bool result) + : m_isBinaryExpression(isBinaryExpression), m_result(result) + { + } + + // We don't actually need a virtual destructor, but many static analysers + // complain if it's not here :-( + virtual ~ITransientExpression(); + + bool m_isBinaryExpression; + bool m_result; +}; + +void formatReconstructedExpression(std::ostream &os, std::string const &lhs, + StringRef op, std::string const &rhs); + +template +class BinaryExpr : public ITransientExpression { + LhsT m_lhs; + StringRef m_op; + RhsT m_rhs; + + void streamReconstructedExpression(std::ostream &os) const override + { + formatReconstructedExpression(os, + Catch::Detail::stringify(m_lhs), + m_op, + Catch::Detail::stringify(m_rhs)); + } + +public: + BinaryExpr(bool comparisonResult, LhsT lhs, StringRef op, RhsT rhs) + : ITransientExpression{true, comparisonResult}, + m_lhs(lhs), + m_op(op), + m_rhs(rhs) + { + } + + template + auto operator&&(T) const -> BinaryExpr const + { + static_assert( + always_false::value, + "chained comparisons are not supported inside assertions, " + "wrap the expression inside parentheses, or decompose it"); + } + + template + auto operator||(T) const -> BinaryExpr const + { + static_assert( + always_false::value, + "chained comparisons are not supported inside assertions, " + "wrap the expression inside parentheses, or decompose it"); + } + + template + auto operator==(T) const -> BinaryExpr const + { + static_assert( + always_false::value, + "chained comparisons are not supported inside assertions, " + "wrap the expression inside parentheses, or decompose it"); + } + + template + auto operator!=(T) const -> BinaryExpr const + { + static_assert( + always_false::value, + "chained comparisons are not supported inside assertions, " + "wrap the expression inside parentheses, or decompose it"); + } + + template + auto operator>(T) const -> BinaryExpr const + { + static_assert( + always_false::value, + "chained comparisons are not supported inside assertions, " + "wrap the expression inside parentheses, or decompose it"); + } + + template + auto operator<(T) const -> BinaryExpr const + { + static_assert( + always_false::value, + "chained comparisons are not supported inside assertions, " + "wrap the expression inside parentheses, or decompose it"); + } + + template + auto operator>=(T) const -> BinaryExpr const + { + static_assert( + always_false::value, + "chained comparisons are not supported inside assertions, " + "wrap the expression inside parentheses, or decompose it"); + } + + template + auto operator<=(T) const -> BinaryExpr const + { + static_assert( + always_false::value, + "chained comparisons are not supported inside assertions, " + "wrap the expression inside parentheses, or decompose it"); + } +}; + +template class UnaryExpr : public ITransientExpression { + LhsT m_lhs; + + void streamReconstructedExpression(std::ostream &os) const override + { + os << Catch::Detail::stringify(m_lhs); + } + +public: + explicit UnaryExpr(LhsT lhs) + : ITransientExpression{false, static_cast(lhs)}, + m_lhs(lhs) + { + } +}; + +// Specialised comparison functions to handle equality comparisons between ints and pointers (NULL deduces as an int) +template +auto compareEqual(LhsT const &lhs, RhsT const &rhs) -> bool +{ + return static_cast(lhs == rhs); +} +template auto compareEqual(T *const &lhs, int rhs) -> bool +{ + return lhs == reinterpret_cast(rhs); +} +template auto compareEqual(T *const &lhs, long rhs) -> bool +{ + return lhs == reinterpret_cast(rhs); +} +template auto compareEqual(int lhs, T *const &rhs) -> bool +{ + return reinterpret_cast(lhs) == rhs; +} +template auto compareEqual(long lhs, T *const &rhs) -> bool +{ + return reinterpret_cast(lhs) == rhs; +} + +template +auto compareNotEqual(LhsT const &lhs, RhsT &&rhs) -> bool +{ + return static_cast(lhs != rhs); +} +template auto compareNotEqual(T *const &lhs, int rhs) -> bool +{ + return lhs != reinterpret_cast(rhs); +} +template auto compareNotEqual(T *const &lhs, long rhs) -> bool +{ + return lhs != reinterpret_cast(rhs); +} +template auto compareNotEqual(int lhs, T *const &rhs) -> bool +{ + return reinterpret_cast(lhs) != rhs; +} +template auto compareNotEqual(long lhs, T *const &rhs) -> bool +{ + return reinterpret_cast(lhs) != rhs; +} + +template class ExprLhs { + LhsT m_lhs; + +public: + explicit ExprLhs(LhsT lhs) : m_lhs(lhs) {} + + template + auto operator==(RhsT const &rhs) -> BinaryExpr const + { + return {compareEqual(m_lhs, rhs), m_lhs, "==", rhs}; + } + auto operator==(bool rhs) -> BinaryExpr const + { + return {m_lhs == rhs, m_lhs, "==", rhs}; + } + + template + auto operator!=(RhsT const &rhs) -> BinaryExpr const + { + return {compareNotEqual(m_lhs, rhs), m_lhs, "!=", rhs}; + } + auto operator!=(bool rhs) -> BinaryExpr const + { + return {m_lhs != rhs, m_lhs, "!=", rhs}; + } + + template + auto operator>(RhsT const &rhs) -> BinaryExpr const + { + return {static_cast(m_lhs > rhs), m_lhs, ">", rhs}; + } + template + auto operator<(RhsT const &rhs) -> BinaryExpr const + { + return {static_cast(m_lhs < rhs), m_lhs, "<", rhs}; + } + template + auto operator>=(RhsT const &rhs) -> BinaryExpr const + { + return {static_cast(m_lhs >= rhs), m_lhs, ">=", rhs}; + } + template + auto operator<=(RhsT const &rhs) -> BinaryExpr const + { + return {static_cast(m_lhs <= rhs), m_lhs, "<=", rhs}; + } + template + auto operator|(RhsT const &rhs) -> BinaryExpr const + { + return {static_cast(m_lhs | rhs), m_lhs, "|", rhs}; + } + template + auto operator&(RhsT const &rhs) -> BinaryExpr const + { + return {static_cast(m_lhs & rhs), m_lhs, "&", rhs}; + } + template + auto operator^(RhsT const &rhs) -> BinaryExpr const + { + return {static_cast(m_lhs ^ rhs), m_lhs, "^", rhs}; + } + + template + auto operator&&(RhsT const &) -> BinaryExpr const + { + static_assert( + always_false::value, + "operator&& is not supported inside assertions, " + "wrap the expression inside parentheses, or decompose it"); + } + + template + auto operator||(RhsT const &) -> BinaryExpr const + { + static_assert( + always_false::value, + "operator|| is not supported inside assertions, " + "wrap the expression inside parentheses, or decompose it"); + } + + auto makeUnaryExpr() const -> UnaryExpr + { + return UnaryExpr{m_lhs}; + } +}; + +void handleExpression(ITransientExpression const &expr); + +template void handleExpression(ExprLhs const &expr) +{ + handleExpression(expr.makeUnaryExpr()); +} + +struct Decomposer { + template auto operator<=(T const &lhs) -> ExprLhs + { + return ExprLhs{lhs}; + } + + auto operator<=(bool value) -> ExprLhs + { + return ExprLhs{value}; + } +}; + +} // end namespace Catch + +#ifdef _MSC_VER +#pragma warning(pop) +#endif + +// end catch_decomposer.h +// start catch_interfaces_capture.h + +#include +#include + +namespace Catch { + +class AssertionResult; +struct AssertionInfo; +struct SectionInfo; +struct SectionEndInfo; +struct MessageInfo; +struct MessageBuilder; +struct Counts; +struct AssertionReaction; +struct SourceLineInfo; + +struct ITransientExpression; +struct IGeneratorTracker; + +#if defined(CATCH_CONFIG_ENABLE_BENCHMARKING) +struct BenchmarkInfo; +template> +struct BenchmarkStats; +#endif // CATCH_CONFIG_ENABLE_BENCHMARKING + +struct IResultCapture { + + virtual ~IResultCapture(); + + virtual bool sectionStarted(SectionInfo const §ionInfo, + Counts &assertions) = 0; + virtual void sectionEnded(SectionEndInfo const &endInfo) = 0; + virtual void sectionEndedEarly(SectionEndInfo const &endInfo) = 0; + + virtual auto acquireGeneratorTracker(StringRef generatorName, + SourceLineInfo const &lineInfo) + -> IGeneratorTracker & = 0; + +#if defined(CATCH_CONFIG_ENABLE_BENCHMARKING) + virtual void benchmarkPreparing(std::string const &name) = 0; + virtual void benchmarkStarting(BenchmarkInfo const &info) = 0; + virtual void benchmarkEnded(BenchmarkStats<> const &stats) = 0; + virtual void benchmarkFailed(std::string const &error) = 0; +#endif // CATCH_CONFIG_ENABLE_BENCHMARKING + + virtual void pushScopedMessage(MessageInfo const &message) = 0; + virtual void popScopedMessage(MessageInfo const &message) = 0; + + virtual void emplaceUnscopedMessage(MessageBuilder const &builder) = 0; + + virtual void handleFatalErrorCondition(StringRef message) = 0; + + virtual void handleExpr(AssertionInfo const &info, + ITransientExpression const &expr, + AssertionReaction &reaction) = 0; + virtual void handleMessage(AssertionInfo const &info, + ResultWas::OfType resultType, + StringRef const &message, + AssertionReaction &reaction) = 0; + virtual void + handleUnexpectedExceptionNotThrown(AssertionInfo const &info, + AssertionReaction &reaction) = 0; + virtual void + handleUnexpectedInflightException(AssertionInfo const &info, + std::string const &message, + AssertionReaction &reaction) = 0; + virtual void handleIncomplete(AssertionInfo const &info) = 0; + virtual void handleNonExpr(AssertionInfo const &info, + ResultWas::OfType resultType, + AssertionReaction &reaction) = 0; + + virtual bool lastAssertionPassed() = 0; + virtual void assertionPassed() = 0; + + // Deprecated, do not use: + virtual std::string getCurrentTestName() const = 0; + virtual const AssertionResult *getLastResult() const = 0; + virtual void exceptionEarlyReported() = 0; +}; + +IResultCapture &getResultCapture(); +} + +// end catch_interfaces_capture.h +namespace Catch { + +struct TestFailureException { +}; +struct AssertionResultData; +struct IResultCapture; +class RunContext; + +class LazyExpression { + friend class AssertionHandler; + friend struct AssertionStats; + friend class RunContext; + + ITransientExpression const *m_transientExpression = nullptr; + bool m_isNegated; + +public: + LazyExpression(bool isNegated); + LazyExpression(LazyExpression const &other); + LazyExpression &operator=(LazyExpression const &) = delete; + + explicit operator bool() const; + + friend auto operator<<(std::ostream &os, LazyExpression const &lazyExpr) + -> std::ostream &; +}; + +struct AssertionReaction { + bool shouldDebugBreak = false; + bool shouldThrow = false; +}; + +class AssertionHandler { + AssertionInfo m_assertionInfo; + AssertionReaction m_reaction; + bool m_completed = false; + IResultCapture &m_resultCapture; + +public: + AssertionHandler(StringRef const ¯oName, + SourceLineInfo const &lineInfo, + StringRef capturedExpression, + ResultDisposition::Flags resultDisposition); + ~AssertionHandler() + { + if (!m_completed) { + m_resultCapture.handleIncomplete(m_assertionInfo); + } + } + + template void handleExpr(ExprLhs const &expr) + { + handleExpr(expr.makeUnaryExpr()); + } + void handleExpr(ITransientExpression const &expr); + + void handleMessage(ResultWas::OfType resultType, + StringRef const &message); + + void handleExceptionThrownAsExpected(); + void handleUnexpectedExceptionNotThrown(); + void handleExceptionNotThrownAsExpected(); + void handleThrowingCallSkipped(); + void handleUnexpectedInflightException(); + + void complete(); + void setCompleted(); + + // query + auto allowThrows() const -> bool; +}; + +void handleExceptionMatchExpr(AssertionHandler &handler, std::string const &str, + StringRef const &matcherString); + +} // namespace Catch + +// end catch_assertionhandler.h +// start catch_message.h + +#include +#include + +namespace Catch { + +struct MessageInfo { + MessageInfo(StringRef const &_macroName, + SourceLineInfo const &_lineInfo, ResultWas::OfType _type); + + StringRef macroName; + std::string message; + SourceLineInfo lineInfo; + ResultWas::OfType type; + unsigned int sequence; + + bool operator==(MessageInfo const &other) const; + bool operator<(MessageInfo const &other) const; + +private: + static unsigned int globalCount; +}; + +struct MessageStream { + + template MessageStream &operator<<(T const &value) + { + m_stream << value; + return *this; + } + + ReusableStringStream m_stream; +}; + +struct MessageBuilder : MessageStream { + MessageBuilder(StringRef const ¯oName, + SourceLineInfo const &lineInfo, ResultWas::OfType type); + + template MessageBuilder &operator<<(T const &value) + { + m_stream << value; + return *this; + } + + MessageInfo m_info; +}; + +class ScopedMessage { +public: + explicit ScopedMessage(MessageBuilder const &builder); + ScopedMessage(ScopedMessage &duplicate) = delete; + ScopedMessage(ScopedMessage &&old); + ~ScopedMessage(); + + MessageInfo m_info; + bool m_moved; +}; + +class Capturer { + std::vector m_messages; + IResultCapture &m_resultCapture = getResultCapture(); + size_t m_captured = 0; + +public: + Capturer(StringRef macroName, SourceLineInfo const &lineInfo, + ResultWas::OfType resultType, StringRef names); + ~Capturer(); + + void captureValue(size_t index, std::string const &value); + + template void captureValues(size_t index, T const &value) + { + captureValue(index, Catch::Detail::stringify(value)); + } + + template + void captureValues(size_t index, T const &value, Ts const &...values) + { + captureValue(index, Catch::Detail::stringify(value)); + captureValues(index + 1, values...); + } +}; + +} // end namespace Catch + +// end catch_message.h +#if !defined(CATCH_CONFIG_DISABLE) + +#if !defined(CATCH_CONFIG_DISABLE_STRINGIFICATION) +#define CATCH_INTERNAL_STRINGIFY(...) #__VA_ARGS__ +#else +#define CATCH_INTERNAL_STRINGIFY(...) \ + "Disabled by CATCH_CONFIG_DISABLE_STRINGIFICATION" +#endif + +#if defined(CATCH_CONFIG_FAST_COMPILE) || \ + defined(CATCH_CONFIG_DISABLE_EXCEPTIONS) + +/////////////////////////////////////////////////////////////////////////////// +// Another way to speed-up compilation is to omit local try-catch for REQUIRE* +// macros. +#define INTERNAL_CATCH_TRY +#define INTERNAL_CATCH_CATCH(capturer) + +#else // CATCH_CONFIG_FAST_COMPILE + +#define INTERNAL_CATCH_TRY try +#define INTERNAL_CATCH_CATCH(handler) \ + catch (...) { handler.handleUnexpectedInflightException(); } + +#endif + +#define INTERNAL_CATCH_REACT(handler) handler.complete(); + +/////////////////////////////////////////////////////////////////////////////// +#define INTERNAL_CATCH_TEST(macroName, resultDisposition, ...) \ + do { \ + CATCH_INTERNAL_IGNORE_BUT_WARN(__VA_ARGS__); \ + Catch::AssertionHandler catchAssertionHandler( \ + macroName##_catch_sr, CATCH_INTERNAL_LINEINFO, \ + CATCH_INTERNAL_STRINGIFY(__VA_ARGS__), \ + resultDisposition); \ + INTERNAL_CATCH_TRY \ + { \ + CATCH_INTERNAL_START_WARNINGS_SUPPRESSION \ + CATCH_INTERNAL_SUPPRESS_PARENTHESES_WARNINGS \ + catchAssertionHandler.handleExpr( \ + Catch::Decomposer() <= __VA_ARGS__); \ + CATCH_INTERNAL_STOP_WARNINGS_SUPPRESSION \ + } \ + INTERNAL_CATCH_CATCH(catchAssertionHandler) \ + INTERNAL_CATCH_REACT(catchAssertionHandler) \ + } while ((void)0, (false) && static_cast(!!(__VA_ARGS__))) + +/////////////////////////////////////////////////////////////////////////////// +#define INTERNAL_CATCH_IF(macroName, resultDisposition, ...) \ + INTERNAL_CATCH_TEST(macroName, resultDisposition, __VA_ARGS__); \ + if (Catch::getResultCapture().lastAssertionPassed()) + +/////////////////////////////////////////////////////////////////////////////// +#define INTERNAL_CATCH_ELSE(macroName, resultDisposition, ...) \ + INTERNAL_CATCH_TEST(macroName, resultDisposition, __VA_ARGS__); \ + if (!Catch::getResultCapture().lastAssertionPassed()) + +/////////////////////////////////////////////////////////////////////////////// +#define INTERNAL_CATCH_NO_THROW(macroName, resultDisposition, ...) \ + do { \ + Catch::AssertionHandler catchAssertionHandler( \ + macroName##_catch_sr, CATCH_INTERNAL_LINEINFO, \ + CATCH_INTERNAL_STRINGIFY(__VA_ARGS__), \ + resultDisposition); \ + try { \ + static_cast(__VA_ARGS__); \ + catchAssertionHandler \ + .handleExceptionNotThrownAsExpected(); \ + } catch (...) { \ + catchAssertionHandler \ + .handleUnexpectedInflightException(); \ + } \ + INTERNAL_CATCH_REACT(catchAssertionHandler) \ + } while (false) + +/////////////////////////////////////////////////////////////////////////////// +#define INTERNAL_CATCH_THROWS(macroName, resultDisposition, ...) \ + do { \ + Catch::AssertionHandler catchAssertionHandler( \ + macroName##_catch_sr, CATCH_INTERNAL_LINEINFO, \ + CATCH_INTERNAL_STRINGIFY(__VA_ARGS__), \ + resultDisposition); \ + if (catchAssertionHandler.allowThrows()) \ + try { \ + static_cast(__VA_ARGS__); \ + catchAssertionHandler \ + .handleUnexpectedExceptionNotThrown(); \ + } catch (...) { \ + catchAssertionHandler \ + .handleExceptionThrownAsExpected(); \ + } \ + else \ + catchAssertionHandler.handleThrowingCallSkipped(); \ + INTERNAL_CATCH_REACT(catchAssertionHandler) \ + } while (false) + +/////////////////////////////////////////////////////////////////////////////// +#define INTERNAL_CATCH_THROWS_AS(macroName, exceptionType, resultDisposition, \ + expr) \ + do { \ + Catch::AssertionHandler catchAssertionHandler( \ + macroName##_catch_sr, CATCH_INTERNAL_LINEINFO, \ + CATCH_INTERNAL_STRINGIFY( \ + expr) ", " CATCH_INTERNAL_STRINGIFY(exceptionType), \ + resultDisposition); \ + if (catchAssertionHandler.allowThrows()) \ + try { \ + static_cast(expr); \ + catchAssertionHandler \ + .handleUnexpectedExceptionNotThrown(); \ + } catch (exceptionType const &) { \ + catchAssertionHandler \ + .handleExceptionThrownAsExpected(); \ + } catch (...) { \ + catchAssertionHandler \ + .handleUnexpectedInflightException(); \ + } \ + else \ + catchAssertionHandler.handleThrowingCallSkipped(); \ + INTERNAL_CATCH_REACT(catchAssertionHandler) \ + } while (false) + +/////////////////////////////////////////////////////////////////////////////// +#define INTERNAL_CATCH_MSG(macroName, messageType, resultDisposition, ...) \ + do { \ + Catch::AssertionHandler catchAssertionHandler( \ + macroName##_catch_sr, CATCH_INTERNAL_LINEINFO, \ + Catch::StringRef(), resultDisposition); \ + catchAssertionHandler.handleMessage( \ + messageType, \ + (Catch::MessageStream() \ + << __VA_ARGS__ + ::Catch::StreamEndStop()) \ + .m_stream.str()); \ + INTERNAL_CATCH_REACT(catchAssertionHandler) \ + } while (false) + +/////////////////////////////////////////////////////////////////////////////// +#define INTERNAL_CATCH_CAPTURE(varName, macroName, ...) \ + auto varName = Catch::Capturer(macroName, CATCH_INTERNAL_LINEINFO, \ + Catch::ResultWas::Info, #__VA_ARGS__); \ + varName.captureValues(0, __VA_ARGS__) + +/////////////////////////////////////////////////////////////////////////////// +#define INTERNAL_CATCH_INFO(macroName, log) \ + Catch::ScopedMessage INTERNAL_CATCH_UNIQUE_NAME(scopedMessage)( \ + Catch::MessageBuilder(macroName##_catch_sr, \ + CATCH_INTERNAL_LINEINFO, \ + Catch::ResultWas::Info) \ + << log); + +/////////////////////////////////////////////////////////////////////////////// +#define INTERNAL_CATCH_UNSCOPED_INFO(macroName, log) \ + Catch::getResultCapture().emplaceUnscopedMessage( \ + Catch::MessageBuilder(macroName##_catch_sr, \ + CATCH_INTERNAL_LINEINFO, \ + Catch::ResultWas::Info) \ + << log) + +/////////////////////////////////////////////////////////////////////////////// +// Although this is matcher-based, it can be used with just a string +#define INTERNAL_CATCH_THROWS_STR_MATCHES(macroName, resultDisposition, \ + matcher, ...) \ + do { \ + Catch::AssertionHandler catchAssertionHandler( \ + macroName##_catch_sr, CATCH_INTERNAL_LINEINFO, \ + CATCH_INTERNAL_STRINGIFY( \ + __VA_ARGS__) ", " CATCH_INTERNAL_STRINGIFY(matcher), \ + resultDisposition); \ + if (catchAssertionHandler.allowThrows()) \ + try { \ + static_cast(__VA_ARGS__); \ + catchAssertionHandler \ + .handleUnexpectedExceptionNotThrown(); \ + } catch (...) { \ + Catch::handleExceptionMatchExpr( \ + catchAssertionHandler, matcher, \ + #matcher##_catch_sr); \ + } \ + else \ + catchAssertionHandler.handleThrowingCallSkipped(); \ + INTERNAL_CATCH_REACT(catchAssertionHandler) \ + } while (false) + +#endif // CATCH_CONFIG_DISABLE + +// end catch_capture.hpp +// start catch_section.h + +// start catch_section_info.h + +// start catch_totals.h + +#include + +namespace Catch { + +struct Counts { + Counts operator-(Counts const &other) const; + Counts &operator+=(Counts const &other); + + std::size_t total() const; + bool allPassed() const; + bool allOk() const; + + std::size_t passed = 0; + std::size_t failed = 0; + std::size_t failedButOk = 0; +}; + +struct Totals { + + Totals operator-(Totals const &other) const; + Totals &operator+=(Totals const &other); + + Totals delta(Totals const &prevTotals) const; + + int error = 0; + Counts assertions; + Counts testCases; +}; +} + +// end catch_totals.h +#include + +namespace Catch { + +struct SectionInfo { + SectionInfo(SourceLineInfo const &_lineInfo, std::string const &_name); + + // Deprecated + SectionInfo(SourceLineInfo const &_lineInfo, std::string const &_name, + std::string const &) + : SectionInfo(_lineInfo, _name) + { + } + + std::string name; + std::string description; // !Deprecated: this will always be empty + SourceLineInfo lineInfo; +}; + +struct SectionEndInfo { + SectionInfo sectionInfo; + Counts prevAssertions; + double durationInSeconds; +}; + +} // end namespace Catch + +// end catch_section_info.h +// start catch_timer.h + +#include + +namespace Catch { + +auto getCurrentNanosecondsSinceEpoch() -> uint64_t; +auto getEstimatedClockResolution() -> uint64_t; + +class Timer { + uint64_t m_nanoseconds = 0; + +public: + void start(); + auto getElapsedNanoseconds() const -> uint64_t; + auto getElapsedMicroseconds() const -> uint64_t; + auto getElapsedMilliseconds() const -> unsigned int; + auto getElapsedSeconds() const -> double; +}; + +} // namespace Catch + +// end catch_timer.h +#include + +namespace Catch { + +class Section : NonCopyable { +public: + Section(SectionInfo const &info); + ~Section(); + + // This indicates whether the section should be executed or not + explicit operator bool() const; + +private: + SectionInfo m_info; + + std::string m_name; + Counts m_assertions; + bool m_sectionIncluded; + Timer m_timer; +}; + +} // end namespace Catch + +#define INTERNAL_CATCH_SECTION(...) \ + CATCH_INTERNAL_START_WARNINGS_SUPPRESSION \ + CATCH_INTERNAL_SUPPRESS_UNUSED_WARNINGS \ + if (Catch::Section const &INTERNAL_CATCH_UNIQUE_NAME( \ + catch_internal_Section) = \ + Catch::SectionInfo(CATCH_INTERNAL_LINEINFO, __VA_ARGS__)) \ + CATCH_INTERNAL_STOP_WARNINGS_SUPPRESSION + +#define INTERNAL_CATCH_DYNAMIC_SECTION(...) \ + CATCH_INTERNAL_START_WARNINGS_SUPPRESSION \ + CATCH_INTERNAL_SUPPRESS_UNUSED_WARNINGS \ + if (Catch::Section const &INTERNAL_CATCH_UNIQUE_NAME( \ + catch_internal_Section) = \ + Catch::SectionInfo(CATCH_INTERNAL_LINEINFO, \ + (Catch::ReusableStringStream() \ + << __VA_ARGS__) \ + .str())) \ + CATCH_INTERNAL_STOP_WARNINGS_SUPPRESSION + +// end catch_section.h +// start catch_interfaces_exception.h + +// start catch_interfaces_registry_hub.h + +#include +#include + +namespace Catch { + +class TestCase; +struct ITestCaseRegistry; +struct IExceptionTranslatorRegistry; +struct IExceptionTranslator; +struct IReporterRegistry; +struct IReporterFactory; +struct ITagAliasRegistry; +struct IMutableEnumValuesRegistry; + +class StartupExceptionRegistry; + +using IReporterFactoryPtr = std::shared_ptr; + +struct IRegistryHub { + virtual ~IRegistryHub(); + + virtual IReporterRegistry const &getReporterRegistry() const = 0; + virtual ITestCaseRegistry const &getTestCaseRegistry() const = 0; + virtual ITagAliasRegistry const &getTagAliasRegistry() const = 0; + virtual IExceptionTranslatorRegistry const & + getExceptionTranslatorRegistry() const = 0; + + virtual StartupExceptionRegistry const & + getStartupExceptionRegistry() const = 0; +}; + +struct IMutableRegistryHub { + virtual ~IMutableRegistryHub(); + virtual void registerReporter(std::string const &name, + IReporterFactoryPtr const &factory) = 0; + virtual void registerListener(IReporterFactoryPtr const &factory) = 0; + virtual void registerTest(TestCase const &testInfo) = 0; + virtual void + registerTranslator(const IExceptionTranslator *translator) = 0; + virtual void registerTagAlias(std::string const &alias, + std::string const &tag, + SourceLineInfo const &lineInfo) = 0; + virtual void registerStartupException() noexcept = 0; + virtual IMutableEnumValuesRegistry &getMutableEnumValuesRegistry() = 0; +}; + +IRegistryHub const &getRegistryHub(); +IMutableRegistryHub &getMutableRegistryHub(); +void cleanUp(); +std::string translateActiveException(); + +} + +// end catch_interfaces_registry_hub.h +#if defined(CATCH_CONFIG_DISABLE) +#define INTERNAL_CATCH_TRANSLATE_EXCEPTION_NO_REG(translatorName, signature) \ + static std::string translatorName(signature) +#endif + +#include +#include +#include + +namespace Catch { +using exceptionTranslateFunction = std::string (*)(); + +struct IExceptionTranslator; +using ExceptionTranslators = + std::vector>; + +struct IExceptionTranslator { + virtual ~IExceptionTranslator(); + virtual std::string + translate(ExceptionTranslators::const_iterator it, + ExceptionTranslators::const_iterator itEnd) const = 0; +}; + +struct IExceptionTranslatorRegistry { + virtual ~IExceptionTranslatorRegistry(); + + virtual std::string translateActiveException() const = 0; +}; + +class ExceptionTranslatorRegistrar { + template + class ExceptionTranslator : public IExceptionTranslator { + public: + ExceptionTranslator(std::string (*translateFunction)(T &)) + : m_translateFunction(translateFunction) + { + } + + std::string translate(ExceptionTranslators::const_iterator it, + ExceptionTranslators::const_iterator + itEnd) const override + { +#if defined(CATCH_CONFIG_DISABLE_EXCEPTIONS) + return ""; +#else + try { + if (it == itEnd) + std::rethrow_exception( + std::current_exception()); + else + return (*it)->translate(it + 1, itEnd); + } catch (T &ex) { + return m_translateFunction(ex); + } +#endif + } + + protected: + std::string (*m_translateFunction)(T &); + }; + +public: + template + ExceptionTranslatorRegistrar(std::string (*translateFunction)(T &)) + { + getMutableRegistryHub().registerTranslator( + new ExceptionTranslator(translateFunction)); + } +}; +} + +/////////////////////////////////////////////////////////////////////////////// +#define INTERNAL_CATCH_TRANSLATE_EXCEPTION2(translatorName, signature) \ + static std::string translatorName(signature); \ + CATCH_INTERNAL_START_WARNINGS_SUPPRESSION \ + CATCH_INTERNAL_SUPPRESS_GLOBALS_WARNINGS \ + namespace { \ + Catch::ExceptionTranslatorRegistrar INTERNAL_CATCH_UNIQUE_NAME( \ + catch_internal_ExceptionRegistrar)(&translatorName); \ + } \ + CATCH_INTERNAL_STOP_WARNINGS_SUPPRESSION \ + static std::string translatorName(signature) + +#define INTERNAL_CATCH_TRANSLATE_EXCEPTION(signature) \ + INTERNAL_CATCH_TRANSLATE_EXCEPTION2( \ + INTERNAL_CATCH_UNIQUE_NAME( \ + catch_internal_ExceptionTranslator), \ + signature) + +// end catch_interfaces_exception.h +// start catch_approx.h + +#include + +namespace Catch { +namespace Detail { + +class Approx { +private: + bool equalityComparisonImpl(double other) const; + // Validates the new margin (margin >= 0) + // out-of-line to avoid including stdexcept in the header + void setMargin(double margin); + // Validates the new epsilon (0 < epsilon < 1) + // out-of-line to avoid including stdexcept in the header + void setEpsilon(double epsilon); + +public: + explicit Approx(double value); + + static Approx custom(); + + Approx operator-() const; + + template::value>::type> + Approx operator()(T const &value) const + { + Approx approx(static_cast(value)); + approx.m_epsilon = m_epsilon; + approx.m_margin = m_margin; + approx.m_scale = m_scale; + return approx; + } + + template::value>::type> + explicit Approx(T const &value) : Approx(static_cast(value)) + { + } + + template::value>::type> + friend bool operator==(const T &lhs, Approx const &rhs) + { + auto lhs_v = static_cast(lhs); + return rhs.equalityComparisonImpl(lhs_v); + } + + template::value>::type> + friend bool operator==(Approx const &lhs, const T &rhs) + { + return operator==(rhs, lhs); + } + + template::value>::type> + friend bool operator!=(T const &lhs, Approx const &rhs) + { + return !operator==(lhs, rhs); + } + + template::value>::type> + friend bool operator!=(Approx const &lhs, T const &rhs) + { + return !operator==(rhs, lhs); + } + + template::value>::type> + friend bool operator<=(T const &lhs, Approx const &rhs) + { + return static_cast(lhs) < rhs.m_value || lhs == rhs; + } + + template::value>::type> + friend bool operator<=(Approx const &lhs, T const &rhs) + { + return lhs.m_value < static_cast(rhs) || lhs == rhs; + } + + template::value>::type> + friend bool operator>=(T const &lhs, Approx const &rhs) + { + return static_cast(lhs) > rhs.m_value || lhs == rhs; + } + + template::value>::type> + friend bool operator>=(Approx const &lhs, T const &rhs) + { + return lhs.m_value > static_cast(rhs) || lhs == rhs; + } + + template::value>::type> + Approx &epsilon(T const &newEpsilon) + { + double epsilonAsDouble = static_cast(newEpsilon); + setEpsilon(epsilonAsDouble); + return *this; + } + + template::value>::type> + Approx &margin(T const &newMargin) + { + double marginAsDouble = static_cast(newMargin); + setMargin(marginAsDouble); + return *this; + } + + template::value>::type> + Approx &scale(T const &newScale) + { + m_scale = static_cast(newScale); + return *this; + } + + std::string toString() const; + +private: + double m_epsilon; + double m_margin; + double m_scale; + double m_value; +}; +} // end namespace Detail + +namespace literals { +Detail::Approx operator"" _a(long double val); +Detail::Approx operator"" _a(unsigned long long val); +} // end namespace literals + +template<> struct StringMaker { + static std::string convert(Catch::Detail::Approx const &value); +}; + +} // end namespace Catch + +// end catch_approx.h +// start catch_string_manip.h + +#include +#include +#include + +namespace Catch { + +bool startsWith(std::string const &s, std::string const &prefix); +bool startsWith(std::string const &s, char prefix); +bool endsWith(std::string const &s, std::string const &suffix); +bool endsWith(std::string const &s, char suffix); +bool contains(std::string const &s, std::string const &infix); +void toLowerInPlace(std::string &s); +std::string toLower(std::string const &s); +//! Returns a new string without whitespace at the start/end +std::string trim(std::string const &str); +//! Returns a substring of the original ref without whitespace. Beware lifetimes! +StringRef trim(StringRef ref); + +// !!! Be aware, returns refs into original string - make sure original string outlives them +std::vector splitStringRef(StringRef str, char delimiter); +bool replaceInPlace(std::string &str, std::string const &replaceThis, + std::string const &withThis); + +struct pluralise { + pluralise(std::size_t count, std::string const &label); + + friend std::ostream &operator<<(std::ostream &os, + pluralise const &pluraliser); + + std::size_t m_count; + std::string m_label; +}; +} + +// end catch_string_manip.h +#ifndef CATCH_CONFIG_DISABLE_MATCHERS +// start catch_capture_matchers.h + +// start catch_matchers.h + +#include +#include + +namespace Catch { +namespace Matchers { +namespace Impl { + +template struct MatchAllOf; +template struct MatchAnyOf; +template struct MatchNotOf; + +class MatcherUntypedBase { +public: + MatcherUntypedBase() = default; + MatcherUntypedBase(MatcherUntypedBase const &) = default; + MatcherUntypedBase &operator=(MatcherUntypedBase const &) = delete; + std::string toString() const; + +protected: + virtual ~MatcherUntypedBase(); + virtual std::string describe() const = 0; + mutable std::string m_cachedToString; +}; + +#ifdef __clang__ +#pragma clang diagnostic push +#pragma clang diagnostic ignored "-Wnon-virtual-dtor" +#endif + +template struct MatcherMethod { + virtual bool match(ObjectT const &arg) const = 0; +}; + +#if defined(__OBJC__) +// Hack to fix Catch GH issue #1661. Could use id for generic Object support. +// use of const for Object pointers is very uncommon and under ARC it causes some kind of signature mismatch that breaks compilation +template<> struct MatcherMethod { + virtual bool match(NSString *arg) const = 0; +}; +#endif + +#ifdef __clang__ +#pragma clang diagnostic pop +#endif + +template struct MatcherBase : MatcherUntypedBase, MatcherMethod { + + MatchAllOf operator&&(MatcherBase const &other) const; + MatchAnyOf operator||(MatcherBase const &other) const; + MatchNotOf operator!() const; +}; + +template struct MatchAllOf : MatcherBase { + bool match(ArgT const &arg) const override + { + for (auto matcher : m_matchers) { + if (!matcher->match(arg)) + return false; + } + return true; + } + std::string describe() const override + { + std::string description; + description.reserve(4 + m_matchers.size() * 32); + description += "( "; + bool first = true; + for (auto matcher : m_matchers) { + if (first) + first = false; + else + description += " and "; + description += matcher->toString(); + } + description += " )"; + return description; + } + + MatchAllOf operator&&(MatcherBase const &other) + { + auto copy(*this); + copy.m_matchers.push_back(&other); + return copy; + } + + std::vector const *> m_matchers; +}; +template struct MatchAnyOf : MatcherBase { + + bool match(ArgT const &arg) const override + { + for (auto matcher : m_matchers) { + if (matcher->match(arg)) + return true; + } + return false; + } + std::string describe() const override + { + std::string description; + description.reserve(4 + m_matchers.size() * 32); + description += "( "; + bool first = true; + for (auto matcher : m_matchers) { + if (first) + first = false; + else + description += " or "; + description += matcher->toString(); + } + description += " )"; + return description; + } + + MatchAnyOf operator||(MatcherBase const &other) + { + auto copy(*this); + copy.m_matchers.push_back(&other); + return copy; + } + + std::vector const *> m_matchers; +}; + +template struct MatchNotOf : MatcherBase { + + MatchNotOf(MatcherBase const &underlyingMatcher) + : m_underlyingMatcher(underlyingMatcher) + { + } + + bool match(ArgT const &arg) const override + { + return !m_underlyingMatcher.match(arg); + } + + std::string describe() const override + { + return "not " + m_underlyingMatcher.toString(); + } + MatcherBase const &m_underlyingMatcher; +}; + +template +MatchAllOf MatcherBase::operator&&(MatcherBase const &other) const +{ + return MatchAllOf() && *this && other; +} +template +MatchAnyOf MatcherBase::operator||(MatcherBase const &other) const +{ + return MatchAnyOf() || *this || other; +} +template MatchNotOf MatcherBase::operator!() const +{ + return MatchNotOf(*this); +} + +} // namespace Impl + +} // namespace Matchers + +using namespace Matchers; +using Matchers::Impl::MatcherBase; + +} // namespace Catch + +// end catch_matchers.h +// start catch_matchers_exception.hpp + +namespace Catch { +namespace Matchers { +namespace Exception { + +class ExceptionMessageMatcher : public MatcherBase { + std::string m_message; + +public: + ExceptionMessageMatcher(std::string const &message) : m_message(message) + { + } + + bool match(std::exception const &ex) const override; + + std::string describe() const override; +}; + +} // namespace Exception + +Exception::ExceptionMessageMatcher Message(std::string const &message); + +} // namespace Matchers +} // namespace Catch + +// end catch_matchers_exception.hpp +// start catch_matchers_floating.h + +namespace Catch { +namespace Matchers { + +namespace Floating { + +enum class FloatingPointKind : uint8_t; + +struct WithinAbsMatcher : MatcherBase { + WithinAbsMatcher(double target, double margin); + bool match(double const &matchee) const override; + std::string describe() const override; + +private: + double m_target; + double m_margin; +}; + +struct WithinUlpsMatcher : MatcherBase { + WithinUlpsMatcher(double target, uint64_t ulps, + FloatingPointKind baseType); + bool match(double const &matchee) const override; + std::string describe() const override; + +private: + double m_target; + uint64_t m_ulps; + FloatingPointKind m_type; +}; + +// Given IEEE-754 format for floats and doubles, we can assume +// that float -> double promotion is lossless. Given this, we can +// assume that if we do the standard relative comparison of +// |lhs - rhs| <= epsilon * max(fabs(lhs), fabs(rhs)), then we get +// the same result if we do this for floats, as if we do this for +// doubles that were promoted from floats. +struct WithinRelMatcher : MatcherBase { + WithinRelMatcher(double target, double epsilon); + bool match(double const &matchee) const override; + std::string describe() const override; + +private: + double m_target; + double m_epsilon; +}; + +} // namespace Floating + +// The following functions create the actual matcher objects. +// This allows the types to be inferred +Floating::WithinUlpsMatcher WithinULP(double target, uint64_t maxUlpDiff); +Floating::WithinUlpsMatcher WithinULP(float target, uint64_t maxUlpDiff); +Floating::WithinAbsMatcher WithinAbs(double target, double margin); +Floating::WithinRelMatcher WithinRel(double target, double eps); +// defaults epsilon to 100*numeric_limits::epsilon() +Floating::WithinRelMatcher WithinRel(double target); +Floating::WithinRelMatcher WithinRel(float target, float eps); +// defaults epsilon to 100*numeric_limits::epsilon() +Floating::WithinRelMatcher WithinRel(float target); + +} // namespace Matchers +} // namespace Catch + +// end catch_matchers_floating.h +// start catch_matchers_generic.hpp + +#include +#include + +namespace Catch { +namespace Matchers { +namespace Generic { + +namespace Detail { +std::string finalizeDescription(const std::string &desc); +} + +template class PredicateMatcher : public MatcherBase { + std::function m_predicate; + std::string m_description; + +public: + PredicateMatcher(std::function const &elem, + std::string const &descr) + : m_predicate(std::move(elem)), + m_description(Detail::finalizeDescription(descr)) + { + } + + bool match(T const &item) const override { return m_predicate(item); } + + std::string describe() const override { return m_description; } +}; + +} // namespace Generic + +// The following functions create the actual matcher objects. +// The user has to explicitly specify type to the function, because +// inferring std::function is hard (but possible) and +// requires a lot of TMP. +template +Generic::PredicateMatcher +Predicate(std::function const &predicate, + std::string const &description = "") +{ + return Generic::PredicateMatcher(predicate, description); +} + +} // namespace Matchers +} // namespace Catch + +// end catch_matchers_generic.hpp +// start catch_matchers_string.h + +#include + +namespace Catch { +namespace Matchers { + +namespace StdString { + +struct CasedString { + CasedString(std::string const &str, + CaseSensitive::Choice caseSensitivity); + std::string adjustString(std::string const &str) const; + std::string caseSensitivitySuffix() const; + + CaseSensitive::Choice m_caseSensitivity; + std::string m_str; +}; + +struct StringMatcherBase : MatcherBase { + StringMatcherBase(std::string const &operation, + CasedString const &comparator); + std::string describe() const override; + + CasedString m_comparator; + std::string m_operation; +}; + +struct EqualsMatcher : StringMatcherBase { + EqualsMatcher(CasedString const &comparator); + bool match(std::string const &source) const override; +}; +struct ContainsMatcher : StringMatcherBase { + ContainsMatcher(CasedString const &comparator); + bool match(std::string const &source) const override; +}; +struct StartsWithMatcher : StringMatcherBase { + StartsWithMatcher(CasedString const &comparator); + bool match(std::string const &source) const override; +}; +struct EndsWithMatcher : StringMatcherBase { + EndsWithMatcher(CasedString const &comparator); + bool match(std::string const &source) const override; +}; + +struct RegexMatcher : MatcherBase { + RegexMatcher(std::string regex, CaseSensitive::Choice caseSensitivity); + bool match(std::string const &matchee) const override; + std::string describe() const override; + +private: + std::string m_regex; + CaseSensitive::Choice m_caseSensitivity; +}; + +} // namespace StdString + +// The following functions create the actual matcher objects. +// This allows the types to be inferred + +StdString::EqualsMatcher +Equals(std::string const &str, + CaseSensitive::Choice caseSensitivity = CaseSensitive::Yes); +StdString::ContainsMatcher +Contains(std::string const &str, + CaseSensitive::Choice caseSensitivity = CaseSensitive::Yes); +StdString::EndsWithMatcher +EndsWith(std::string const &str, + CaseSensitive::Choice caseSensitivity = CaseSensitive::Yes); +StdString::StartsWithMatcher +StartsWith(std::string const &str, + CaseSensitive::Choice caseSensitivity = CaseSensitive::Yes); +StdString::RegexMatcher +Matches(std::string const ®ex, + CaseSensitive::Choice caseSensitivity = CaseSensitive::Yes); + +} // namespace Matchers +} // namespace Catch + +// end catch_matchers_string.h +// start catch_matchers_vector.h + +#include + +namespace Catch { +namespace Matchers { + +namespace Vector { +template +struct ContainsElementMatcher : MatcherBase> { + + ContainsElementMatcher(T const &comparator) : m_comparator(comparator) + { + } + + bool match(std::vector const &v) const override + { + for (auto const &el : v) { + if (el == m_comparator) { + return true; + } + } + return false; + } + + std::string describe() const override + { + return "Contains: " + ::Catch::Detail::stringify(m_comparator); + } + + T const &m_comparator; +}; + +template +struct ContainsMatcher : MatcherBase> { + + ContainsMatcher(std::vector const &comparator) + : m_comparator(comparator) + { + } + + bool match(std::vector const &v) const override + { + // !TBD: see note in EqualsMatcher + if (m_comparator.size() > v.size()) + return false; + for (auto const &comparator : m_comparator) { + auto present = false; + for (const auto &el : v) { + if (el == comparator) { + present = true; + break; + } + } + if (!present) { + return false; + } + } + return true; + } + std::string describe() const override + { + return "Contains: " + ::Catch::Detail::stringify(m_comparator); + } + + std::vector const &m_comparator; +}; + +template +struct EqualsMatcher : MatcherBase> { + + EqualsMatcher(std::vector const &comparator) + : m_comparator(comparator) + { + } + + bool match(std::vector const &v) const override + { + // !TBD: This currently works if all elements can be compared using != + // - a more general approach would be via a compare template that defaults + // to using !=. but could be specialised for, e.g. std::vector etc + // - then just call that directly + if (m_comparator.size() != v.size()) + return false; + for (std::size_t i = 0; i < v.size(); ++i) + if (m_comparator[i] != v[i]) + return false; + return true; + } + std::string describe() const override + { + return "Equals: " + ::Catch::Detail::stringify(m_comparator); + } + std::vector const &m_comparator; +}; + +template +struct ApproxMatcher : MatcherBase> { + + ApproxMatcher(std::vector const &comparator) + : m_comparator(comparator) + { + } + + bool match(std::vector const &v) const override + { + if (m_comparator.size() != v.size()) + return false; + for (std::size_t i = 0; i < v.size(); ++i) + if (m_comparator[i] != approx(v[i])) + return false; + return true; + } + std::string describe() const override + { + return "is approx: " + ::Catch::Detail::stringify(m_comparator); + } + template::value>::type> + ApproxMatcher &epsilon(T const &newEpsilon) + { + approx.epsilon(newEpsilon); + return *this; + } + template::value>::type> + ApproxMatcher &margin(T const &newMargin) + { + approx.margin(newMargin); + return *this; + } + template::value>::type> + ApproxMatcher &scale(T const &newScale) + { + approx.scale(newScale); + return *this; + } + + std::vector const &m_comparator; + mutable Catch::Detail::Approx approx = Catch::Detail::Approx::custom(); +}; + +template +struct UnorderedEqualsMatcher : MatcherBase> { + UnorderedEqualsMatcher(std::vector const &target) + : m_target(target) + { + } + bool match(std::vector const &vec) const override + { + if (m_target.size() != vec.size()) { + return false; + } + return std::is_permutation(m_target.begin(), m_target.end(), + vec.begin()); + } + + std::string describe() const override + { + return "UnorderedEquals: " + + ::Catch::Detail::stringify(m_target); + } + +private: + std::vector const &m_target; +}; + +} // namespace Vector + +// The following functions create the actual matcher objects. +// This allows the types to be inferred + +template, + typename AllocMatch = AllocComp> +Vector::ContainsMatcher +Contains(std::vector const &comparator) +{ + return Vector::ContainsMatcher(comparator); +} + +template> +Vector::ContainsElementMatcher VectorContains(T const &comparator) +{ + return Vector::ContainsElementMatcher(comparator); +} + +template, + typename AllocMatch = AllocComp> +Vector::EqualsMatcher +Equals(std::vector const &comparator) +{ + return Vector::EqualsMatcher(comparator); +} + +template, + typename AllocMatch = AllocComp> +Vector::ApproxMatcher +Approx(std::vector const &comparator) +{ + return Vector::ApproxMatcher(comparator); +} + +template, + typename AllocMatch = AllocComp> +Vector::UnorderedEqualsMatcher +UnorderedEquals(std::vector const &target) +{ + return Vector::UnorderedEqualsMatcher(target); +} + +} // namespace Matchers +} // namespace Catch + +// end catch_matchers_vector.h +namespace Catch { + +template +class MatchExpr : public ITransientExpression { + ArgT const &m_arg; + MatcherT m_matcher; + StringRef m_matcherString; + +public: + MatchExpr(ArgT const &arg, MatcherT const &matcher, + StringRef const &matcherString) + : ITransientExpression{true, matcher.match(arg)}, + m_arg(arg), + m_matcher(matcher), + m_matcherString(matcherString) + { + } + + void streamReconstructedExpression(std::ostream &os) const override + { + auto matcherAsString = m_matcher.toString(); + os << Catch::Detail::stringify(m_arg) << ' '; + if (matcherAsString == Detail::unprintableString) + os << m_matcherString; + else + os << matcherAsString; + } +}; + +using StringMatcher = Matchers::Impl::MatcherBase; + +void handleExceptionMatchExpr(AssertionHandler &handler, + StringMatcher const &matcher, + StringRef const &matcherString); + +template +auto makeMatchExpr(ArgT const &arg, MatcherT const &matcher, + StringRef const &matcherString) -> MatchExpr +{ + return MatchExpr(arg, matcher, matcherString); +} + +} // namespace Catch + +/////////////////////////////////////////////////////////////////////////////// +#define INTERNAL_CHECK_THAT(macroName, matcher, resultDisposition, arg) \ + do { \ + Catch::AssertionHandler catchAssertionHandler( \ + macroName##_catch_sr, CATCH_INTERNAL_LINEINFO, \ + CATCH_INTERNAL_STRINGIFY( \ + arg) ", " CATCH_INTERNAL_STRINGIFY(matcher), \ + resultDisposition); \ + INTERNAL_CATCH_TRY \ + { \ + catchAssertionHandler.handleExpr(Catch::makeMatchExpr( \ + arg, matcher, #matcher##_catch_sr)); \ + } \ + INTERNAL_CATCH_CATCH(catchAssertionHandler) \ + INTERNAL_CATCH_REACT(catchAssertionHandler) \ + } while (false) + +/////////////////////////////////////////////////////////////////////////////// +#define INTERNAL_CATCH_THROWS_MATCHES(macroName, exceptionType, \ + resultDisposition, matcher, ...) \ + do { \ + Catch::AssertionHandler catchAssertionHandler( \ + macroName##_catch_sr, CATCH_INTERNAL_LINEINFO, \ + CATCH_INTERNAL_STRINGIFY(__VA_ARGS__) ", " CATCH_INTERNAL_STRINGIFY( \ + exceptionType) ", " CATCH_INTERNAL_STRINGIFY(matcher), \ + resultDisposition); \ + if (catchAssertionHandler.allowThrows()) \ + try { \ + static_cast(__VA_ARGS__); \ + catchAssertionHandler \ + .handleUnexpectedExceptionNotThrown(); \ + } catch (exceptionType const &ex) { \ + catchAssertionHandler.handleExpr( \ + Catch::makeMatchExpr( \ + ex, matcher, \ + #matcher##_catch_sr)); \ + } catch (...) { \ + catchAssertionHandler \ + .handleUnexpectedInflightException(); \ + } \ + else \ + catchAssertionHandler.handleThrowingCallSkipped(); \ + INTERNAL_CATCH_REACT(catchAssertionHandler) \ + } while (false) + +// end catch_capture_matchers.h +#endif +// start catch_generators.hpp + +// start catch_interfaces_generatortracker.h + +#include + +namespace Catch { + +namespace Generators { +class GeneratorUntypedBase { +public: + GeneratorUntypedBase() = default; + virtual ~GeneratorUntypedBase(); + // Attempts to move the generator to the next element + // + // Returns true iff the move succeeded (and a valid element + // can be retrieved). + virtual bool next() = 0; +}; +using GeneratorBasePtr = std::unique_ptr; + +} // namespace Generators + +struct IGeneratorTracker { + virtual ~IGeneratorTracker(); + virtual auto hasGenerator() const -> bool = 0; + virtual auto getGenerator() const + -> Generators::GeneratorBasePtr const & = 0; + virtual void setGenerator(Generators::GeneratorBasePtr &&generator) = 0; +}; + +} // namespace Catch + +// end catch_interfaces_generatortracker.h +// start catch_enforce.h + +#include + +namespace Catch { +#if !defined(CATCH_CONFIG_DISABLE_EXCEPTIONS) +template [[noreturn]] void throw_exception(Ex const &e) +{ + throw e; +} +#else // ^^ Exceptions are enabled // Exceptions are disabled vv +[[noreturn]] void throw_exception(std::exception const &e); +#endif + +[[noreturn]] void throw_logic_error(std::string const &msg); +[[noreturn]] void throw_domain_error(std::string const &msg); +[[noreturn]] void throw_runtime_error(std::string const &msg); + +} // namespace Catch; + +#define CATCH_MAKE_MSG(...) (Catch::ReusableStringStream() << __VA_ARGS__).str() + +#define CATCH_INTERNAL_ERROR(...) \ + Catch::throw_logic_error(CATCH_MAKE_MSG(CATCH_INTERNAL_LINEINFO \ + << ": Internal Catch2 error: " \ + << __VA_ARGS__)) + +#define CATCH_ERROR(...) Catch::throw_domain_error(CATCH_MAKE_MSG(__VA_ARGS__)) + +#define CATCH_RUNTIME_ERROR(...) \ + Catch::throw_runtime_error(CATCH_MAKE_MSG(__VA_ARGS__)) + +#define CATCH_ENFORCE(condition, ...) \ + do { \ + if (!(condition)) \ + CATCH_ERROR(__VA_ARGS__); \ + } while (false) + +// end catch_enforce.h +#include +#include +#include + +#include +#include + +namespace Catch { + +class GeneratorException : public std::exception { + const char *const m_msg = ""; + +public: + GeneratorException(const char *msg) : m_msg(msg) {} + + const char *what() const noexcept override final; +}; + +namespace Generators { + +// !TBD move this into its own location? +namespace pf { +template +std::unique_ptr make_unique(Args &&...args) +{ + return std::unique_ptr(new T(std::forward(args)...)); +} +} + +template struct IGenerator : GeneratorUntypedBase { + virtual ~IGenerator() = default; + + // Returns the current element of the generator + // + // \Precondition The generator is either freshly constructed, + // or the last call to `next()` returned true + virtual T const &get() const = 0; + using type = T; +}; + +template class SingleValueGenerator final : public IGenerator { + T m_value; + +public: + SingleValueGenerator(T &&value) : m_value(std::move(value)) {} + + T const &get() const override { return m_value; } + bool next() override { return false; } +}; + +template class FixedValuesGenerator final : public IGenerator { + static_assert( + !std::is_same::value, + "FixedValuesGenerator does not support bools because of std::vector" + "specialization, use SingleValue Generator instead."); + std::vector m_values; + size_t m_idx = 0; + +public: + FixedValuesGenerator(std::initializer_list values) : m_values(values) + { + } + + T const &get() const override { return m_values[m_idx]; } + bool next() override + { + ++m_idx; + return m_idx < m_values.size(); + } +}; + +template class GeneratorWrapper final { + std::unique_ptr> m_generator; + +public: + GeneratorWrapper(std::unique_ptr> generator) + : m_generator(std::move(generator)) + { + } + T const &get() const { return m_generator->get(); } + bool next() { return m_generator->next(); } +}; + +template GeneratorWrapper value(T &&value) +{ + return GeneratorWrapper(pf::make_unique>( + std::forward(value))); +} +template GeneratorWrapper values(std::initializer_list values) +{ + return GeneratorWrapper( + pf::make_unique>(values)); +} + +template class Generators : public IGenerator { + std::vector> m_generators; + size_t m_current = 0; + + void populate(GeneratorWrapper &&generator) + { + m_generators.emplace_back(std::move(generator)); + } + void populate(T &&val) + { + m_generators.emplace_back(value(std::forward(val))); + } + template void populate(U &&val) + { + populate(T(std::forward(val))); + } + template + void populate(U &&valueOrGenerator, Gs &&...moreGenerators) + { + populate(std::forward(valueOrGenerator)); + populate(std::forward(moreGenerators)...); + } + +public: + template Generators(Gs &&...moreGenerators) + { + m_generators.reserve(sizeof...(Gs)); + populate(std::forward(moreGenerators)...); + } + + T const &get() const override { return m_generators[m_current].get(); } + + bool next() override + { + if (m_current >= m_generators.size()) { + return false; + } + const bool current_status = m_generators[m_current].next(); + if (!current_status) { + ++m_current; + } + return m_current < m_generators.size(); + } +}; + +template +GeneratorWrapper> +table(std::initializer_list::type...>> tuples) +{ + return values>(tuples); +} + +// Tag type to signal that a generator sequence should convert arguments to a specific type +template struct as { +}; + +template +auto makeGenerators(GeneratorWrapper &&generator, Gs &&...moreGenerators) + -> Generators +{ + return Generators(std::move(generator), + std::forward(moreGenerators)...); +} +template +auto makeGenerators(GeneratorWrapper &&generator) -> Generators +{ + return Generators(std::move(generator)); +} +template +auto makeGenerators(T &&val, Gs &&...moreGenerators) -> Generators +{ + return makeGenerators(value(std::forward(val)), + std::forward(moreGenerators)...); +} +template +auto makeGenerators(as, U &&val, Gs &&...moreGenerators) -> Generators +{ + return makeGenerators(value(T(std::forward(val))), + std::forward(moreGenerators)...); +} + +auto acquireGeneratorTracker(StringRef generatorName, + SourceLineInfo const &lineInfo) + -> IGeneratorTracker &; + +template +// Note: The type after -> is weird, because VS2015 cannot parse +// the expression used in the typedef inside, when it is in +// return type. Yeah. +auto generate(StringRef generatorName, SourceLineInfo const &lineInfo, + L const &generatorExpression) + -> decltype(std::declval().get()) +{ + using UnderlyingType = typename decltype(generatorExpression())::type; + + IGeneratorTracker &tracker = + acquireGeneratorTracker(generatorName, lineInfo); + if (!tracker.hasGenerator()) { + tracker.setGenerator( + pf::make_unique>( + generatorExpression())); + } + + auto const &generator = static_cast const &>( + *tracker.getGenerator()); + return generator.get(); +} + +} // namespace Generators +} // namespace Catch + +#define GENERATE(...) \ + Catch::Generators::generate( \ + INTERNAL_CATCH_STRINGIZE( \ + INTERNAL_CATCH_UNIQUE_NAME(generator)), \ + CATCH_INTERNAL_LINEINFO, [] { \ + using namespace Catch::Generators; \ + return makeGenerators(__VA_ARGS__); \ + }) //NOLINT(google-build-using-namespace) +#define GENERATE_COPY(...) \ + Catch::Generators::generate( \ + INTERNAL_CATCH_STRINGIZE( \ + INTERNAL_CATCH_UNIQUE_NAME(generator)), \ + CATCH_INTERNAL_LINEINFO, [=] { \ + using namespace Catch::Generators; \ + return makeGenerators(__VA_ARGS__); \ + }) //NOLINT(google-build-using-namespace) +#define GENERATE_REF(...) \ + Catch::Generators::generate( \ + INTERNAL_CATCH_STRINGIZE( \ + INTERNAL_CATCH_UNIQUE_NAME(generator)), \ + CATCH_INTERNAL_LINEINFO, [&] { \ + using namespace Catch::Generators; \ + return makeGenerators(__VA_ARGS__); \ + }) //NOLINT(google-build-using-namespace) + +// end catch_generators.hpp +// start catch_generators_generic.hpp + +namespace Catch { +namespace Generators { + +template class TakeGenerator : public IGenerator { + GeneratorWrapper m_generator; + size_t m_returned = 0; + size_t m_target; + +public: + TakeGenerator(size_t target, GeneratorWrapper &&generator) + : m_generator(std::move(generator)), m_target(target) + { + assert(target != 0 && "Empty generators are not allowed"); + } + T const &get() const override { return m_generator.get(); } + bool next() override + { + ++m_returned; + if (m_returned >= m_target) { + return false; + } + + const auto success = m_generator.next(); + // If the underlying generator does not contain enough values + // then we cut short as well + if (!success) { + m_returned = m_target; + } + return success; + } +}; + +template +GeneratorWrapper take(size_t target, GeneratorWrapper &&generator) +{ + return GeneratorWrapper(pf::make_unique>( + target, std::move(generator))); +} + +template +class FilterGenerator : public IGenerator { + GeneratorWrapper m_generator; + Predicate m_predicate; + +public: + template + FilterGenerator(P &&pred, GeneratorWrapper &&generator) + : m_generator(std::move(generator)), + m_predicate(std::forward

(pred)) + { + if (!m_predicate(m_generator.get())) { + // It might happen that there are no values that pass the + // filter. In that case we throw an exception. + auto has_initial_value = nextImpl(); + if (!has_initial_value) { + Catch::throw_exception(GeneratorException( + "No valid value found in filtered generator")); + } + } + } + + T const &get() const override { return m_generator.get(); } + + bool next() override { return nextImpl(); } + +private: + bool nextImpl() + { + bool success = m_generator.next(); + if (!success) { + return false; + } + while (!m_predicate(m_generator.get()) && + (success = m_generator.next()) == true) + ; + return success; + } +}; + +template +GeneratorWrapper filter(Predicate &&pred, GeneratorWrapper &&generator) +{ + return GeneratorWrapper(std::unique_ptr>( + pf::make_unique>( + std::forward(pred), std::move(generator)))); +} + +template class RepeatGenerator : public IGenerator { + static_assert(!std::is_same::value, + "RepeatGenerator currently does not support bools" + "because of std::vector specialization"); + GeneratorWrapper m_generator; + mutable std::vector m_returned; + size_t m_target_repeats; + size_t m_current_repeat = 0; + size_t m_repeat_index = 0; + +public: + RepeatGenerator(size_t repeats, GeneratorWrapper &&generator) + : m_generator(std::move(generator)), m_target_repeats(repeats) + { + assert(m_target_repeats > 0 && + "Repeat generator must repeat at least once"); + } + + T const &get() const override + { + if (m_current_repeat == 0) { + m_returned.push_back(m_generator.get()); + return m_returned.back(); + } + return m_returned[m_repeat_index]; + } + + bool next() override + { + // There are 2 basic cases: + // 1) We are still reading the generator + // 2) We are reading our own cache + + // In the first case, we need to poke the underlying generator. + // If it happily moves, we are left in that state, otherwise it is time to start reading from our cache + if (m_current_repeat == 0) { + const auto success = m_generator.next(); + if (!success) { + ++m_current_repeat; + } + return m_current_repeat < m_target_repeats; + } + + // In the second case, we need to move indices forward and check that we haven't run up against the end + ++m_repeat_index; + if (m_repeat_index == m_returned.size()) { + m_repeat_index = 0; + ++m_current_repeat; + } + return m_current_repeat < m_target_repeats; + } +}; + +template +GeneratorWrapper repeat(size_t repeats, GeneratorWrapper &&generator) +{ + return GeneratorWrapper(pf::make_unique>( + repeats, std::move(generator))); +} + +template +class MapGenerator : public IGenerator { + // TBD: provide static assert for mapping function, for friendly error message + GeneratorWrapper m_generator; + Func m_function; + // To avoid returning dangling reference, we have to save the values + T m_cache; + +public: + template + MapGenerator(F2 &&function, GeneratorWrapper &&generator) + : m_generator(std::move(generator)), + m_function(std::forward(function)), + m_cache(m_function(m_generator.get())) + { + } + + T const &get() const override { return m_cache; } + bool next() override + { + const auto success = m_generator.next(); + if (success) { + m_cache = m_function(m_generator.get()); + } + return success; + } +}; + +template> +GeneratorWrapper map(Func &&function, GeneratorWrapper &&generator) +{ + return GeneratorWrapper(pf::make_unique>( + std::forward(function), std::move(generator))); +} + +template +GeneratorWrapper map(Func &&function, GeneratorWrapper &&generator) +{ + return GeneratorWrapper(pf::make_unique>( + std::forward(function), std::move(generator))); +} + +template +class ChunkGenerator final : public IGenerator> { + std::vector m_chunk; + size_t m_chunk_size; + GeneratorWrapper m_generator; + bool m_used_up = false; + +public: + ChunkGenerator(size_t size, GeneratorWrapper generator) + : m_chunk_size(size), m_generator(std::move(generator)) + { + m_chunk.reserve(m_chunk_size); + if (m_chunk_size != 0) { + m_chunk.push_back(m_generator.get()); + for (size_t i = 1; i < m_chunk_size; ++i) { + if (!m_generator.next()) { + Catch::throw_exception(GeneratorException( + "Not enough values to initialize the first chunk")); + } + m_chunk.push_back(m_generator.get()); + } + } + } + std::vector const &get() const override { return m_chunk; } + bool next() override + { + m_chunk.clear(); + for (size_t idx = 0; idx < m_chunk_size; ++idx) { + if (!m_generator.next()) { + return false; + } + m_chunk.push_back(m_generator.get()); + } + return true; + } +}; + +template +GeneratorWrapper> chunk(size_t size, + GeneratorWrapper &&generator) +{ + return GeneratorWrapper>( + pf::make_unique>(size, std::move(generator))); +} + +} // namespace Generators +} // namespace Catch + +// end catch_generators_generic.hpp +// start catch_generators_specific.hpp + +// start catch_context.h + +#include + +namespace Catch { + +struct IResultCapture; +struct IRunner; +struct IConfig; +struct IMutableContext; + +using IConfigPtr = std::shared_ptr; + +struct IContext { + virtual ~IContext(); + + virtual IResultCapture *getResultCapture() = 0; + virtual IRunner *getRunner() = 0; + virtual IConfigPtr const &getConfig() const = 0; +}; + +struct IMutableContext : IContext { + virtual ~IMutableContext(); + virtual void setResultCapture(IResultCapture *resultCapture) = 0; + virtual void setRunner(IRunner *runner) = 0; + virtual void setConfig(IConfigPtr const &config) = 0; + +private: + static IMutableContext *currentContext; + friend IMutableContext &getCurrentMutableContext(); + friend void cleanUpContext(); + static void createContext(); +}; + +inline IMutableContext &getCurrentMutableContext() +{ + if (!IMutableContext::currentContext) + IMutableContext::createContext(); + // NOLINTNEXTLINE(clang-analyzer-core.uninitialized.UndefReturn) + return *IMutableContext::currentContext; +} + +inline IContext &getCurrentContext() +{ + return getCurrentMutableContext(); +} + +void cleanUpContext(); + +class SimplePcg32; +SimplePcg32 &rng(); +} + +// end catch_context.h +// start catch_interfaces_config.h + +// start catch_option.hpp + +namespace Catch { + +// An optional type +template class Option { +public: + Option() : nullableValue(nullptr) {} + Option(T const &_value) : nullableValue(new (storage) T(_value)) {} + Option(Option const &_other) + : nullableValue(_other ? new (storage) T(*_other) : nullptr) + { + } + + ~Option() { reset(); } + + Option &operator=(Option const &_other) + { + if (&_other != this) { + reset(); + if (_other) + nullableValue = new (storage) T(*_other); + } + return *this; + } + Option &operator=(T const &_value) + { + reset(); + nullableValue = new (storage) T(_value); + return *this; + } + + void reset() + { + if (nullableValue) + nullableValue->~T(); + nullableValue = nullptr; + } + + T &operator*() { return *nullableValue; } + T const &operator*() const { return *nullableValue; } + T *operator->() { return nullableValue; } + const T *operator->() const { return nullableValue; } + + T valueOr(T const &defaultValue) const + { + return nullableValue ? *nullableValue : defaultValue; + } + + bool some() const { return nullableValue != nullptr; } + bool none() const { return nullableValue == nullptr; } + + bool operator!() const { return nullableValue == nullptr; } + explicit operator bool() const { return some(); } + +private: + T *nullableValue; + alignas(alignof(T)) char storage[sizeof(T)]; +}; + +} // end namespace Catch + +// end catch_option.hpp +#include +#include +#include +#include +#include + +namespace Catch { + +enum class Verbosity { Quiet = 0, Normal, High }; + +struct WarnAbout { + enum What { Nothing = 0x00, NoAssertions = 0x01, NoTests = 0x02 }; +}; + +struct ShowDurations { + enum OrNot { DefaultForReporter, Always, Never }; +}; +struct RunTests { + enum InWhatOrder { + InDeclarationOrder, + InLexicographicalOrder, + InRandomOrder + }; +}; +struct UseColour { + enum YesOrNo { Auto, Yes, No }; +}; +struct WaitForKeypress { + enum When { + Never, + BeforeStart = 1, + BeforeExit = 2, + BeforeStartAndExit = BeforeStart | BeforeExit + }; +}; + +class TestSpec; + +struct IConfig : NonCopyable { + + virtual ~IConfig(); + + virtual bool allowThrows() const = 0; + virtual std::ostream &stream() const = 0; + virtual std::string name() const = 0; + virtual bool includeSuccessfulResults() const = 0; + virtual bool shouldDebugBreak() const = 0; + virtual bool warnAboutMissingAssertions() const = 0; + virtual bool warnAboutNoTests() const = 0; + virtual int abortAfter() const = 0; + virtual bool showInvisibles() const = 0; + virtual ShowDurations::OrNot showDurations() const = 0; + virtual double minDuration() const = 0; + virtual TestSpec const &testSpec() const = 0; + virtual bool hasTestFilters() const = 0; + virtual std::vector const &getTestsOrTags() const = 0; + virtual RunTests::InWhatOrder runOrder() const = 0; + virtual unsigned int rngSeed() const = 0; + virtual UseColour::YesOrNo useColour() const = 0; + virtual std::vector const &getSectionsToRun() const = 0; + virtual Verbosity verbosity() const = 0; + + virtual bool benchmarkNoAnalysis() const = 0; + virtual int benchmarkSamples() const = 0; + virtual double benchmarkConfidenceInterval() const = 0; + virtual unsigned int benchmarkResamples() const = 0; + virtual std::chrono::milliseconds benchmarkWarmupTime() const = 0; +}; + +using IConfigPtr = std::shared_ptr; +} + +// end catch_interfaces_config.h +// start catch_random_number_generator.h + +#include + +namespace Catch { + +// This is a simple implementation of C++11 Uniform Random Number +// Generator. It does not provide all operators, because Catch2 +// does not use it, but it should behave as expected inside stdlib's +// distributions. +// The implementation is based on the PCG family (http://pcg-random.org) +class SimplePcg32 { + using state_type = std::uint64_t; + +public: + using result_type = std::uint32_t; + static constexpr result_type(min)() { return 0; } + static constexpr result_type(max)() + { + return static_cast(-1); + } + + // Provide some default initial state for the default constructor + SimplePcg32() : SimplePcg32(0xed743cc4U) {} + + explicit SimplePcg32(result_type seed_); + + void seed(result_type seed_); + void discard(uint64_t skip); + + result_type operator()(); + +private: + friend bool operator==(SimplePcg32 const &lhs, SimplePcg32 const &rhs); + friend bool operator!=(SimplePcg32 const &lhs, SimplePcg32 const &rhs); + + // In theory we also need operator<< and operator>> + // In practice we do not use them, so we will skip them for now + + std::uint64_t m_state; + // This part of the state determines which "stream" of the numbers + // is chosen -- we take it as a constant for Catch2, so we only + // need to deal with seeding the main state. + // Picked by reading 8 bytes from `/dev/random` :-) + static const std::uint64_t s_inc = (0x13ed0cc53f939476ULL << 1ULL) | + 1ULL; +}; + +} // end namespace Catch + +// end catch_random_number_generator.h +#include + +namespace Catch { +namespace Generators { + +template +class RandomFloatingGenerator final : public IGenerator { + Catch::SimplePcg32 &m_rng; + std::uniform_real_distribution m_dist; + Float m_current_number; + +public: + RandomFloatingGenerator(Float a, Float b) : m_rng(rng()), m_dist(a, b) + { + static_cast(next()); + } + + Float const &get() const override { return m_current_number; } + bool next() override + { + m_current_number = m_dist(m_rng); + return true; + } +}; + +template +class RandomIntegerGenerator final : public IGenerator { + Catch::SimplePcg32 &m_rng; + std::uniform_int_distribution m_dist; + Integer m_current_number; + +public: + RandomIntegerGenerator(Integer a, Integer b) + : m_rng(rng()), m_dist(a, b) + { + static_cast(next()); + } + + Integer const &get() const override { return m_current_number; } + bool next() override + { + m_current_number = m_dist(m_rng); + return true; + } +}; + +// TODO: Ideally this would be also constrained against the various char types, +// but I don't expect users to run into that in practice. +template +typename std::enable_if::value && + !std::is_same::value, + GeneratorWrapper>::type +random(T a, T b) +{ + return GeneratorWrapper( + pf::make_unique>(a, b)); +} + +template +typename std::enable_if::value, + GeneratorWrapper>::type +random(T a, T b) +{ + return GeneratorWrapper( + pf::make_unique>(a, b)); +} + +template class RangeGenerator final : public IGenerator { + T m_current; + T m_end; + T m_step; + bool m_positive; + +public: + RangeGenerator(T const &start, T const &end, T const &step) + : m_current(start), + m_end(end), + m_step(step), + m_positive(m_step > T(0)) + { + assert(m_current != m_end && + "Range start and end cannot be equal"); + assert(m_step != T(0) && "Step size cannot be zero"); + assert(((m_positive && m_current <= m_end) || + (!m_positive && m_current >= m_end)) && + "Step moves away from end"); + } + + RangeGenerator(T const &start, T const &end) + : RangeGenerator(start, end, (start < end) ? T(1) : T(-1)) + { + } + + T const &get() const override { return m_current; } + + bool next() override + { + m_current += m_step; + return (m_positive) ? (m_current < m_end) : (m_current > m_end); + } +}; + +template +GeneratorWrapper range(T const &start, T const &end, T const &step) +{ + static_assert(std::is_arithmetic::value && + !std::is_same::value, + "Type must be numeric"); + return GeneratorWrapper( + pf::make_unique>(start, end, step)); +} + +template GeneratorWrapper range(T const &start, T const &end) +{ + static_assert(std::is_integral::value && + !std::is_same::value, + "Type must be an integer"); + return GeneratorWrapper( + pf::make_unique>(start, end)); +} + +template class IteratorGenerator final : public IGenerator { + static_assert(!std::is_same::value, + "IteratorGenerator currently does not support bools" + "because of std::vector specialization"); + + std::vector m_elems; + size_t m_current = 0; + +public: + template + IteratorGenerator(InputIterator first, InputSentinel last) + : m_elems(first, last) + { + if (m_elems.empty()) { + Catch::throw_exception(GeneratorException( + "IteratorGenerator received no valid values")); + } + } + + T const &get() const override { return m_elems[m_current]; } + + bool next() override + { + ++m_current; + return m_current != m_elems.size(); + } +}; + +template::value_type> +GeneratorWrapper from_range(InputIterator from, InputSentinel to) +{ + return GeneratorWrapper( + pf::make_unique>(from, to)); +} + +template +GeneratorWrapper from_range(Container const &cnt) +{ + return GeneratorWrapper( + pf::make_unique>(cnt.begin(), + cnt.end())); +} + +} // namespace Generators +} // namespace Catch + +// end catch_generators_specific.hpp + +// These files are included here so the single_include script doesn't put them +// in the conditionally compiled sections +// start catch_test_case_info.h + +#include +#include +#include + +#ifdef __clang__ +#pragma clang diagnostic push +#pragma clang diagnostic ignored "-Wpadded" +#endif + +namespace Catch { + +struct ITestInvoker; + +struct TestCaseInfo { + enum SpecialProperties { + None = 0, + IsHidden = 1 << 1, + ShouldFail = 1 << 2, + MayFail = 1 << 3, + Throws = 1 << 4, + NonPortable = 1 << 5, + Benchmark = 1 << 6 + }; + + TestCaseInfo(std::string const &_name, std::string const &_className, + std::string const &_description, + std::vector const &_tags, + SourceLineInfo const &_lineInfo); + + friend void setTags(TestCaseInfo &testCaseInfo, + std::vector tags); + + bool isHidden() const; + bool throws() const; + bool okToFail() const; + bool expectedToFail() const; + + std::string tagsAsString() const; + + std::string name; + std::string className; + std::string description; + std::vector tags; + std::vector lcaseTags; + SourceLineInfo lineInfo; + SpecialProperties properties; +}; + +class TestCase : public TestCaseInfo { +public: + TestCase(ITestInvoker *testCase, TestCaseInfo &&info); + + TestCase withName(std::string const &_newName) const; + + void invoke() const; + + TestCaseInfo const &getTestCaseInfo() const; + + bool operator==(TestCase const &other) const; + bool operator<(TestCase const &other) const; + +private: + std::shared_ptr test; +}; + +TestCase makeTestCase(ITestInvoker *testCase, std::string const &className, + NameAndTags const &nameAndTags, + SourceLineInfo const &lineInfo); +} + +#ifdef __clang__ +#pragma clang diagnostic pop +#endif + +// end catch_test_case_info.h +// start catch_interfaces_runner.h + +namespace Catch { + +struct IRunner { + virtual ~IRunner(); + virtual bool aborting() const = 0; +}; +} + +// end catch_interfaces_runner.h + +#ifdef __OBJC__ +// start catch_objc.hpp + +#import + +#include + +// NB. Any general catch headers included here must be included +// in catch.hpp first to make sure they are included by the single +// header for non obj-usage + +/////////////////////////////////////////////////////////////////////////////// +// This protocol is really only here for (self) documenting purposes, since +// all its methods are optional. +@protocol OcFixture + +@optional + +- (void)setUp; +- (void)tearDown; + +@end + +namespace Catch { + +class OcMethod : public ITestInvoker { + +public: + OcMethod(Class cls, SEL sel) : m_cls(cls), m_sel(sel) {} + + virtual void invoke() const + { + id obj = [[m_cls alloc] init]; + + performOptionalSelector(obj, @selector(setUp)); + performOptionalSelector(obj, m_sel); + performOptionalSelector(obj, @selector(tearDown)); + + arcSafeRelease(obj); + } + +private: + virtual ~OcMethod() {} + + Class m_cls; + SEL m_sel; +}; + +namespace Detail { + +inline std::string getAnnotation(Class cls, std::string const &annotationName, + std::string const &testCaseName) +{ + NSString *selStr = [[NSString alloc] + initWithFormat:@"Catch_%s_%s", annotationName.c_str(), + testCaseName.c_str()]; + SEL sel = NSSelectorFromString(selStr); + arcSafeRelease(selStr); + id value = performOptionalSelector(cls, sel); + if (value) + return [(NSString *)value UTF8String]; + return ""; +} +} + +inline std::size_t registerTestMethods() +{ + std::size_t noTestMethods = 0; + int noClasses = objc_getClassList(nullptr, 0); + + Class *classes = (CATCH_UNSAFE_UNRETAINED Class *)malloc(sizeof(Class) * + noClasses); + objc_getClassList(classes, noClasses); + + for (int c = 0; c < noClasses; c++) { + Class cls = classes[c]; + { + u_int count; + Method *methods = class_copyMethodList(cls, &count); + for (u_int m = 0; m < count; m++) { + SEL selector = method_getName(methods[m]); + std::string methodName = sel_getName(selector); + if (startsWith(methodName, "Catch_TestCase_")) { + std::string testCaseName = + methodName.substr(15); + std::string name = Detail::getAnnotation( + cls, "Name", testCaseName); + std::string desc = + Detail::getAnnotation( + cls, "Description", + testCaseName); + const char *className = + class_getName(cls); + + getMutableRegistryHub().registerTest( + makeTestCase( + new OcMethod(cls, + selector), + className, + NameAndTags( + name.c_str(), + desc.c_str()), + SourceLineInfo("", 0))); + noTestMethods++; + } + } + free(methods); + } + } + return noTestMethods; +} + +#if !defined(CATCH_CONFIG_DISABLE_MATCHERS) + +namespace Matchers { +namespace Impl { +namespace NSStringMatchers { + +struct StringHolder : MatcherBase { + StringHolder(NSString *substr) : m_substr([substr copy]) {} + StringHolder(StringHolder const &other) + : m_substr([other.m_substr copy]) + { + } + StringHolder() { arcSafeRelease(m_substr); } + + bool match(NSString *str) const override { return false; } + + NSString *CATCH_ARC_STRONG m_substr; +}; + +struct Equals : StringHolder { + Equals(NSString *substr) : StringHolder(substr) {} + + bool match(NSString *str) const override + { + return (str != nil || m_substr == nil) && + [str isEqualToString:m_substr]; + } + + std::string describe() const override + { + return "equals string: " + Catch::Detail::stringify(m_substr); + } +}; + +struct Contains : StringHolder { + Contains(NSString *substr) : StringHolder(substr) {} + + bool match(NSString *str) const override + { + return (str != nil || m_substr == nil) && + [str rangeOfString:m_substr].location != NSNotFound; + } + + std::string describe() const override + { + return "contains string: " + Catch::Detail::stringify(m_substr); + } +}; + +struct StartsWith : StringHolder { + StartsWith(NSString *substr) : StringHolder(substr) {} + + bool match(NSString *str) const override + { + return (str != nil || m_substr == nil) && + [str rangeOfString:m_substr].location == 0; + } + + std::string describe() const override + { + return "starts with: " + Catch::Detail::stringify(m_substr); + } +}; +struct EndsWith : StringHolder { + EndsWith(NSString *substr) : StringHolder(substr) {} + + bool match(NSString *str) const override + { + return (str != nil || m_substr == nil) && + [str rangeOfString:m_substr].location == + [str length] - [m_substr length]; + } + + std::string describe() const override + { + return "ends with: " + Catch::Detail::stringify(m_substr); + } +}; + +} // namespace NSStringMatchers +} // namespace Impl + +inline Impl::NSStringMatchers::Equals Equals(NSString *substr) +{ + return Impl::NSStringMatchers::Equals(substr); +} + +inline Impl::NSStringMatchers::Contains Contains(NSString *substr) +{ + return Impl::NSStringMatchers::Contains(substr); +} + +inline Impl::NSStringMatchers::StartsWith StartsWith(NSString *substr) +{ + return Impl::NSStringMatchers::StartsWith(substr); +} + +inline Impl::NSStringMatchers::EndsWith EndsWith(NSString *substr) +{ + return Impl::NSStringMatchers::EndsWith(substr); +} + +} // namespace Matchers + +using namespace Matchers; + +#endif // CATCH_CONFIG_DISABLE_MATCHERS + +} // namespace Catch + +/////////////////////////////////////////////////////////////////////////////// +#define OC_MAKE_UNIQUE_NAME(root, uniqueSuffix) root##uniqueSuffix +#define OC_TEST_CASE2(name, desc, uniqueSuffix) \ + +(NSString *)OC_MAKE_UNIQUE_NAME(Catch_Name_test_, uniqueSuffix) \ + { \ + return @name; \ + } \ + +(NSString *)OC_MAKE_UNIQUE_NAME(Catch_Description_test_, \ + uniqueSuffix) \ + { \ + return @desc; \ + } \ + -(void)OC_MAKE_UNIQUE_NAME(Catch_TestCase_test_, uniqueSuffix) + +#define OC_TEST_CASE(name, desc) OC_TEST_CASE2(name, desc, __LINE__) + +// end catch_objc.hpp +#endif + +// Benchmarking needs the externally-facing parts of reporters to work +#if defined(CATCH_CONFIG_EXTERNAL_INTERFACES) || \ + defined(CATCH_CONFIG_ENABLE_BENCHMARKING) +// start catch_external_interfaces.h + +// start catch_reporter_bases.hpp + +// start catch_interfaces_reporter.h + +// start catch_config.hpp + +// start catch_test_spec_parser.h + +#ifdef __clang__ +#pragma clang diagnostic push +#pragma clang diagnostic ignored "-Wpadded" +#endif + +// start catch_test_spec.h + +#ifdef __clang__ +#pragma clang diagnostic push +#pragma clang diagnostic ignored "-Wpadded" +#endif + +// start catch_wildcard_pattern.h + +namespace Catch { +class WildcardPattern { + enum WildcardPosition { + NoWildcard = 0, + WildcardAtStart = 1, + WildcardAtEnd = 2, + WildcardAtBothEnds = WildcardAtStart | WildcardAtEnd + }; + +public: + WildcardPattern(std::string const &pattern, + CaseSensitive::Choice caseSensitivity); + virtual ~WildcardPattern() = default; + virtual bool matches(std::string const &str) const; + +private: + std::string normaliseString(std::string const &str) const; + CaseSensitive::Choice m_caseSensitivity; + WildcardPosition m_wildcard = NoWildcard; + std::string m_pattern; +}; +} + +// end catch_wildcard_pattern.h +#include +#include +#include + +namespace Catch { + +struct IConfig; + +class TestSpec { + class Pattern { + public: + explicit Pattern(std::string const &name); + virtual ~Pattern(); + virtual bool matches(TestCaseInfo const &testCase) const = 0; + std::string const &name() const; + + private: + std::string const m_name; + }; + using PatternPtr = std::shared_ptr; + + class NamePattern : public Pattern { + public: + explicit NamePattern(std::string const &name, + std::string const &filterString); + bool matches(TestCaseInfo const &testCase) const override; + + private: + WildcardPattern m_wildcardPattern; + }; + + class TagPattern : public Pattern { + public: + explicit TagPattern(std::string const &tag, + std::string const &filterString); + bool matches(TestCaseInfo const &testCase) const override; + + private: + std::string m_tag; + }; + + class ExcludedPattern : public Pattern { + public: + explicit ExcludedPattern(PatternPtr const &underlyingPattern); + bool matches(TestCaseInfo const &testCase) const override; + + private: + PatternPtr m_underlyingPattern; + }; + + struct Filter { + std::vector m_patterns; + + bool matches(TestCaseInfo const &testCase) const; + std::string name() const; + }; + +public: + struct FilterMatch { + std::string name; + std::vector tests; + }; + using Matches = std::vector; + using vectorStrings = std::vector; + + bool hasFilters() const; + bool matches(TestCaseInfo const &testCase) const; + Matches matchesByFilter(std::vector const &testCases, + IConfig const &config) const; + const vectorStrings &getInvalidArgs() const; + +private: + std::vector m_filters; + std::vector m_invalidArgs; + friend class TestSpecParser; +}; +} + +#ifdef __clang__ +#pragma clang diagnostic pop +#endif + +// end catch_test_spec.h +// start catch_interfaces_tag_alias_registry.h + +#include + +namespace Catch { + +struct TagAlias; + +struct ITagAliasRegistry { + virtual ~ITagAliasRegistry(); + // Nullptr if not present + virtual TagAlias const *find(std::string const &alias) const = 0; + virtual std::string + expandAliases(std::string const &unexpandedTestSpec) const = 0; + + static ITagAliasRegistry const &get(); +}; + +} // end namespace Catch + +// end catch_interfaces_tag_alias_registry.h +namespace Catch { + +class TestSpecParser { + enum Mode { None, Name, QuotedName, Tag, EscapedName }; + Mode m_mode = None; + Mode lastMode = None; + bool m_exclusion = false; + std::size_t m_pos = 0; + std::size_t m_realPatternPos = 0; + std::string m_arg; + std::string m_substring; + std::string m_patternName; + std::vector m_escapeChars; + TestSpec::Filter m_currentFilter; + TestSpec m_testSpec; + ITagAliasRegistry const *m_tagAliases = nullptr; + +public: + TestSpecParser(ITagAliasRegistry const &tagAliases); + + TestSpecParser &parse(std::string const &arg); + TestSpec testSpec(); + +private: + bool visitChar(char c); + void startNewMode(Mode mode); + bool processNoneChar(char c); + void processNameChar(char c); + bool processOtherChar(char c); + void endMode(); + void escape(); + bool isControlChar(char c) const; + void saveLastMode(); + void revertBackToLastMode(); + void addFilter(); + bool separate(); + + // Handles common preprocessing of the pattern for name/tag patterns + std::string preprocessPattern(); + // Adds the current pattern as a test name + void addNamePattern(); + // Adds the current pattern as a tag + void addTagPattern(); + + inline void addCharToPattern(char c) + { + m_substring += c; + m_patternName += c; + m_realPatternPos++; + } +}; +TestSpec parseTestSpec(std::string const &arg); + +} // namespace Catch + +#ifdef __clang__ +#pragma clang diagnostic pop +#endif + +// end catch_test_spec_parser.h +// Libstdc++ doesn't like incomplete classes for unique_ptr + +#include +#include +#include + +#ifndef CATCH_CONFIG_CONSOLE_WIDTH +#define CATCH_CONFIG_CONSOLE_WIDTH 80 +#endif + +namespace Catch { + +struct IStream; + +struct ConfigData { + bool listTests = false; + bool listTags = false; + bool listReporters = false; + bool listTestNamesOnly = false; + + bool showSuccessfulTests = false; + bool shouldDebugBreak = false; + bool noThrow = false; + bool showHelp = false; + bool showInvisibles = false; + bool filenamesAsTags = false; + bool libIdentify = false; + + int abortAfter = -1; + unsigned int rngSeed = 0; + + bool benchmarkNoAnalysis = false; + unsigned int benchmarkSamples = 100; + double benchmarkConfidenceInterval = 0.95; + unsigned int benchmarkResamples = 100000; + std::chrono::milliseconds::rep benchmarkWarmupTime = 100; + + Verbosity verbosity = Verbosity::Normal; + WarnAbout::What warnings = WarnAbout::Nothing; + ShowDurations::OrNot showDurations = ShowDurations::DefaultForReporter; + double minDuration = -1; + RunTests::InWhatOrder runOrder = RunTests::InDeclarationOrder; + UseColour::YesOrNo useColour = UseColour::Auto; + WaitForKeypress::When waitForKeypress = WaitForKeypress::Never; + + std::string outputFilename; + std::string name; + std::string processName; +#ifndef CATCH_CONFIG_DEFAULT_REPORTER +#define CATCH_CONFIG_DEFAULT_REPORTER "console" +#endif + std::string reporterName = CATCH_CONFIG_DEFAULT_REPORTER; +#undef CATCH_CONFIG_DEFAULT_REPORTER + + std::vector testsOrTags; + std::vector sectionsToRun; +}; + +class Config : public IConfig { +public: + Config() = default; + Config(ConfigData const &data); + virtual ~Config() = default; + + std::string const &getFilename() const; + + bool listTests() const; + bool listTestNamesOnly() const; + bool listTags() const; + bool listReporters() const; + + std::string getProcessName() const; + std::string const &getReporterName() const; + + std::vector const &getTestsOrTags() const override; + std::vector const &getSectionsToRun() const override; + + TestSpec const &testSpec() const override; + bool hasTestFilters() const override; + + bool showHelp() const; + + // IConfig interface + bool allowThrows() const override; + std::ostream &stream() const override; + std::string name() const override; + bool includeSuccessfulResults() const override; + bool warnAboutMissingAssertions() const override; + bool warnAboutNoTests() const override; + ShowDurations::OrNot showDurations() const override; + double minDuration() const override; + RunTests::InWhatOrder runOrder() const override; + unsigned int rngSeed() const override; + UseColour::YesOrNo useColour() const override; + bool shouldDebugBreak() const override; + int abortAfter() const override; + bool showInvisibles() const override; + Verbosity verbosity() const override; + bool benchmarkNoAnalysis() const override; + int benchmarkSamples() const override; + double benchmarkConfidenceInterval() const override; + unsigned int benchmarkResamples() const override; + std::chrono::milliseconds benchmarkWarmupTime() const override; + +private: + IStream const *openStream(); + ConfigData m_data; + + std::unique_ptr m_stream; + TestSpec m_testSpec; + bool m_hasTestFilters = false; +}; + +} // end namespace Catch + +// end catch_config.hpp +// start catch_assertionresult.h + +#include + +namespace Catch { + +struct AssertionResultData { + AssertionResultData() = delete; + + AssertionResultData(ResultWas::OfType _resultType, + LazyExpression const &_lazyExpression); + + std::string message; + mutable std::string reconstructedExpression; + LazyExpression lazyExpression; + ResultWas::OfType resultType; + + std::string reconstructExpression() const; +}; + +class AssertionResult { +public: + AssertionResult() = delete; + AssertionResult(AssertionInfo const &info, + AssertionResultData const &data); + + bool isOk() const; + bool succeeded() const; + ResultWas::OfType getResultType() const; + bool hasExpression() const; + bool hasMessage() const; + std::string getExpression() const; + std::string getExpressionInMacro() const; + bool hasExpandedExpression() const; + std::string getExpandedExpression() const; + std::string getMessage() const; + SourceLineInfo getSourceInfo() const; + StringRef getTestMacroName() const; + + //protected: + AssertionInfo m_info; + AssertionResultData m_resultData; +}; + +} // end namespace Catch + +// end catch_assertionresult.h +#if defined(CATCH_CONFIG_ENABLE_BENCHMARKING) +// start catch_estimate.hpp + +// Statistics estimates + +namespace Catch { +namespace Benchmark { +template struct Estimate { + Duration point; + Duration lower_bound; + Duration upper_bound; + double confidence_interval; + + template operator Estimate() const + { + return {point, lower_bound, upper_bound, confidence_interval}; + } +}; +} // namespace Benchmark +} // namespace Catch + +// end catch_estimate.hpp +// start catch_outlier_classification.hpp + +// Outlier information + +namespace Catch { +namespace Benchmark { +struct OutlierClassification { + int samples_seen = 0; + int low_severe = 0; // more than 3 times IQR below Q1 + int low_mild = 0; // 1.5 to 3 times IQR below Q1 + int high_mild = 0; // 1.5 to 3 times IQR above Q3 + int high_severe = 0; // more than 3 times IQR above Q3 + + int total() const + { + return low_severe + low_mild + high_mild + high_severe; + } +}; +} // namespace Benchmark +} // namespace Catch + +// end catch_outlier_classification.hpp + +#include +#endif // CATCH_CONFIG_ENABLE_BENCHMARKING + +#include +#include +#include +#include +#include +#include + +namespace Catch { + +struct ReporterConfig { + explicit ReporterConfig(IConfigPtr const &_fullConfig); + + ReporterConfig(IConfigPtr const &_fullConfig, std::ostream &_stream); + + std::ostream &stream() const; + IConfigPtr fullConfig() const; + +private: + std::ostream *m_stream; + IConfigPtr m_fullConfig; +}; + +struct ReporterPreferences { + bool shouldRedirectStdOut = false; + bool shouldReportAllAssertions = false; +}; + +template struct LazyStat : Option { + LazyStat &operator=(T const &_value) + { + Option::operator=(_value); + used = false; + return *this; + } + void reset() + { + Option::reset(); + used = false; + } + bool used = false; +}; + +struct TestRunInfo { + TestRunInfo(std::string const &_name); + std::string name; +}; +struct GroupInfo { + GroupInfo(std::string const &_name, std::size_t _groupIndex, + std::size_t _groupsCount); + + std::string name; + std::size_t groupIndex; + std::size_t groupsCounts; +}; + +struct AssertionStats { + AssertionStats(AssertionResult const &_assertionResult, + std::vector const &_infoMessages, + Totals const &_totals); + + AssertionStats(AssertionStats const &) = default; + AssertionStats(AssertionStats &&) = default; + AssertionStats &operator=(AssertionStats const &) = delete; + AssertionStats &operator=(AssertionStats &&) = delete; + virtual ~AssertionStats(); + + AssertionResult assertionResult; + std::vector infoMessages; + Totals totals; +}; + +struct SectionStats { + SectionStats(SectionInfo const &_sectionInfo, Counts const &_assertions, + double _durationInSeconds, bool _missingAssertions); + SectionStats(SectionStats const &) = default; + SectionStats(SectionStats &&) = default; + SectionStats &operator=(SectionStats const &) = default; + SectionStats &operator=(SectionStats &&) = default; + virtual ~SectionStats(); + + SectionInfo sectionInfo; + Counts assertions; + double durationInSeconds; + bool missingAssertions; +}; + +struct TestCaseStats { + TestCaseStats(TestCaseInfo const &_testInfo, Totals const &_totals, + std::string const &_stdOut, std::string const &_stdErr, + bool _aborting); + + TestCaseStats(TestCaseStats const &) = default; + TestCaseStats(TestCaseStats &&) = default; + TestCaseStats &operator=(TestCaseStats const &) = default; + TestCaseStats &operator=(TestCaseStats &&) = default; + virtual ~TestCaseStats(); + + TestCaseInfo testInfo; + Totals totals; + std::string stdOut; + std::string stdErr; + bool aborting; +}; + +struct TestGroupStats { + TestGroupStats(GroupInfo const &_groupInfo, Totals const &_totals, + bool _aborting); + TestGroupStats(GroupInfo const &_groupInfo); + + TestGroupStats(TestGroupStats const &) = default; + TestGroupStats(TestGroupStats &&) = default; + TestGroupStats &operator=(TestGroupStats const &) = default; + TestGroupStats &operator=(TestGroupStats &&) = default; + virtual ~TestGroupStats(); + + GroupInfo groupInfo; + Totals totals; + bool aborting; +}; + +struct TestRunStats { + TestRunStats(TestRunInfo const &_runInfo, Totals const &_totals, + bool _aborting); + + TestRunStats(TestRunStats const &) = default; + TestRunStats(TestRunStats &&) = default; + TestRunStats &operator=(TestRunStats const &) = default; + TestRunStats &operator=(TestRunStats &&) = default; + virtual ~TestRunStats(); + + TestRunInfo runInfo; + Totals totals; + bool aborting; +}; + +#if defined(CATCH_CONFIG_ENABLE_BENCHMARKING) +struct BenchmarkInfo { + std::string name; + double estimatedDuration; + int iterations; + int samples; + unsigned int resamples; + double clockResolution; + double clockCost; +}; + +template struct BenchmarkStats { + BenchmarkInfo info; + + std::vector samples; + Benchmark::Estimate mean; + Benchmark::Estimate standardDeviation; + Benchmark::OutlierClassification outliers; + double outlierVariance; + + template operator BenchmarkStats() const + { + std::vector samples2; + samples2.reserve(samples.size()); + std::transform(samples.begin(), samples.end(), + std::back_inserter(samples2), + [](Duration d) { return Duration2(d); }); + return { + info, std::move(samples2), mean, standardDeviation, + outliers, outlierVariance, + }; + } +}; +#endif // CATCH_CONFIG_ENABLE_BENCHMARKING + +struct IStreamingReporter { + virtual ~IStreamingReporter() = default; + + // Implementing class must also provide the following static methods: + // static std::string getDescription(); + // static std::set getSupportedVerbosities() + + virtual ReporterPreferences getPreferences() const = 0; + + virtual void noMatchingTestCases(std::string const &spec) = 0; + + virtual void reportInvalidArguments(std::string const &) {} + + virtual void testRunStarting(TestRunInfo const &testRunInfo) = 0; + virtual void testGroupStarting(GroupInfo const &groupInfo) = 0; + + virtual void testCaseStarting(TestCaseInfo const &testInfo) = 0; + virtual void sectionStarting(SectionInfo const §ionInfo) = 0; + +#if defined(CATCH_CONFIG_ENABLE_BENCHMARKING) + virtual void benchmarkPreparing(std::string const &) {} + virtual void benchmarkStarting(BenchmarkInfo const &) {} + virtual void benchmarkEnded(BenchmarkStats<> const &) {} + virtual void benchmarkFailed(std::string const &) {} +#endif // CATCH_CONFIG_ENABLE_BENCHMARKING + + virtual void assertionStarting(AssertionInfo const &assertionInfo) = 0; + + // The return value indicates if the messages buffer should be cleared: + virtual bool assertionEnded(AssertionStats const &assertionStats) = 0; + + virtual void sectionEnded(SectionStats const §ionStats) = 0; + virtual void testCaseEnded(TestCaseStats const &testCaseStats) = 0; + virtual void testGroupEnded(TestGroupStats const &testGroupStats) = 0; + virtual void testRunEnded(TestRunStats const &testRunStats) = 0; + + virtual void skipTest(TestCaseInfo const &testInfo) = 0; + + // Default empty implementation provided + virtual void fatalErrorEncountered(StringRef name); + + virtual bool isMulti() const; +}; +using IStreamingReporterPtr = std::unique_ptr; + +struct IReporterFactory { + virtual ~IReporterFactory(); + virtual IStreamingReporterPtr + create(ReporterConfig const &config) const = 0; + virtual std::string getDescription() const = 0; +}; +using IReporterFactoryPtr = std::shared_ptr; + +struct IReporterRegistry { + using FactoryMap = std::map; + using Listeners = std::vector; + + virtual ~IReporterRegistry(); + virtual IStreamingReporterPtr + create(std::string const &name, IConfigPtr const &config) const = 0; + virtual FactoryMap const &getFactories() const = 0; + virtual Listeners const &getListeners() const = 0; +}; + +} // end namespace Catch + +// end catch_interfaces_reporter.h +#include +#include +#include +#include +#include +#include +#include + +namespace Catch { +void prepareExpandedExpression(AssertionResult &result); + +// Returns double formatted as %.3f (format expected on output) +std::string getFormattedDuration(double duration); + +//! Should the reporter show +bool shouldShowDuration(IConfig const &config, double duration); + +std::string serializeFilters(std::vector const &container); + +template struct StreamingReporterBase : IStreamingReporter { + + StreamingReporterBase(ReporterConfig const &_config) + : m_config(_config.fullConfig()), stream(_config.stream()) + { + m_reporterPrefs.shouldRedirectStdOut = false; + if (!DerivedT::getSupportedVerbosities().count( + m_config->verbosity())) + CATCH_ERROR( + "Verbosity level not supported by this reporter"); + } + + ReporterPreferences getPreferences() const override + { + return m_reporterPrefs; + } + + static std::set getSupportedVerbosities() + { + return {Verbosity::Normal}; + } + + ~StreamingReporterBase() override = default; + + void noMatchingTestCases(std::string const &) override {} + + void reportInvalidArguments(std::string const &) override {} + + void testRunStarting(TestRunInfo const &_testRunInfo) override + { + currentTestRunInfo = _testRunInfo; + } + + void testGroupStarting(GroupInfo const &_groupInfo) override + { + currentGroupInfo = _groupInfo; + } + + void testCaseStarting(TestCaseInfo const &_testInfo) override + { + currentTestCaseInfo = _testInfo; + } + void sectionStarting(SectionInfo const &_sectionInfo) override + { + m_sectionStack.push_back(_sectionInfo); + } + + void sectionEnded(SectionStats const & /* _sectionStats */) override + { + m_sectionStack.pop_back(); + } + void testCaseEnded(TestCaseStats const & /* _testCaseStats */) override + { + currentTestCaseInfo.reset(); + } + void + testGroupEnded(TestGroupStats const & /* _testGroupStats */) override + { + currentGroupInfo.reset(); + } + void testRunEnded(TestRunStats const & /* _testRunStats */) override + { + currentTestCaseInfo.reset(); + currentGroupInfo.reset(); + currentTestRunInfo.reset(); + } + + void skipTest(TestCaseInfo const &) override + { + // Don't do anything with this by default. + // It can optionally be overridden in the derived class. + } + + IConfigPtr m_config; + std::ostream &stream; + + LazyStat currentTestRunInfo; + LazyStat currentGroupInfo; + LazyStat currentTestCaseInfo; + + std::vector m_sectionStack; + ReporterPreferences m_reporterPrefs; +}; + +template struct CumulativeReporterBase : IStreamingReporter { + template struct Node { + explicit Node(T const &_value) : value(_value) {} + virtual ~Node() {} + + using ChildNodes = std::vector>; + T value; + ChildNodes children; + }; + struct SectionNode { + explicit SectionNode(SectionStats const &_stats) : stats(_stats) + { + } + virtual ~SectionNode() = default; + + bool operator==(SectionNode const &other) const + { + return stats.sectionInfo.lineInfo == + other.stats.sectionInfo.lineInfo; + } + bool operator==(std::shared_ptr const &other) const + { + return operator==(*other); + } + + SectionStats stats; + using ChildSections = std::vector>; + using Assertions = std::vector; + ChildSections childSections; + Assertions assertions; + std::string stdOut; + std::string stdErr; + }; + + struct BySectionInfo { + BySectionInfo(SectionInfo const &other) : m_other(other) {} + BySectionInfo(BySectionInfo const &other) + : m_other(other.m_other) + { + } + bool operator()(std::shared_ptr const &node) const + { + return ((node->stats.sectionInfo.name == + m_other.name) && + (node->stats.sectionInfo.lineInfo == + m_other.lineInfo)); + } + void operator=(BySectionInfo const &) = delete; + + private: + SectionInfo const &m_other; + }; + + using TestCaseNode = Node; + using TestGroupNode = Node; + using TestRunNode = Node; + + CumulativeReporterBase(ReporterConfig const &_config) + : m_config(_config.fullConfig()), stream(_config.stream()) + { + m_reporterPrefs.shouldRedirectStdOut = false; + if (!DerivedT::getSupportedVerbosities().count( + m_config->verbosity())) + CATCH_ERROR( + "Verbosity level not supported by this reporter"); + } + ~CumulativeReporterBase() override = default; + + ReporterPreferences getPreferences() const override + { + return m_reporterPrefs; + } + + static std::set getSupportedVerbosities() + { + return {Verbosity::Normal}; + } + + void testRunStarting(TestRunInfo const &) override {} + void testGroupStarting(GroupInfo const &) override {} + + void testCaseStarting(TestCaseInfo const &) override {} + + void sectionStarting(SectionInfo const §ionInfo) override + { + SectionStats incompleteStats(sectionInfo, Counts(), 0, false); + std::shared_ptr node; + if (m_sectionStack.empty()) { + if (!m_rootSection) + m_rootSection = std::make_shared( + incompleteStats); + node = m_rootSection; + } else { + SectionNode &parentNode = *m_sectionStack.back(); + auto it = std::find_if(parentNode.childSections.begin(), + parentNode.childSections.end(), + BySectionInfo(sectionInfo)); + if (it == parentNode.childSections.end()) { + node = std::make_shared( + incompleteStats); + parentNode.childSections.push_back(node); + } else + node = *it; + } + m_sectionStack.push_back(node); + m_deepestSection = std::move(node); + } + + void assertionStarting(AssertionInfo const &) override {} + + bool assertionEnded(AssertionStats const &assertionStats) override + { + assert(!m_sectionStack.empty()); + // AssertionResult holds a pointer to a temporary DecomposedExpression, + // which getExpandedExpression() calls to build the expression string. + // Our section stack copy of the assertionResult will likely outlive the + // temporary, so it must be expanded or discarded now to avoid calling + // a destroyed object later. + prepareExpandedExpression(const_cast( + assertionStats.assertionResult)); + SectionNode §ionNode = *m_sectionStack.back(); + sectionNode.assertions.push_back(assertionStats); + return true; + } + void sectionEnded(SectionStats const §ionStats) override + { + assert(!m_sectionStack.empty()); + SectionNode &node = *m_sectionStack.back(); + node.stats = sectionStats; + m_sectionStack.pop_back(); + } + void testCaseEnded(TestCaseStats const &testCaseStats) override + { + auto node = std::make_shared(testCaseStats); + assert(m_sectionStack.size() == 0); + node->children.push_back(m_rootSection); + m_testCases.push_back(node); + m_rootSection.reset(); + + assert(m_deepestSection); + m_deepestSection->stdOut = testCaseStats.stdOut; + m_deepestSection->stdErr = testCaseStats.stdErr; + } + void testGroupEnded(TestGroupStats const &testGroupStats) override + { + auto node = std::make_shared(testGroupStats); + node->children.swap(m_testCases); + m_testGroups.push_back(node); + } + void testRunEnded(TestRunStats const &testRunStats) override + { + auto node = std::make_shared(testRunStats); + node->children.swap(m_testGroups); + m_testRuns.push_back(node); + testRunEndedCumulative(); + } + virtual void testRunEndedCumulative() = 0; + + void skipTest(TestCaseInfo const &) override {} + + IConfigPtr m_config; + std::ostream &stream; + std::vector m_assertions; + std::vector>> m_sections; + std::vector> m_testCases; + std::vector> m_testGroups; + + std::vector> m_testRuns; + + std::shared_ptr m_rootSection; + std::shared_ptr m_deepestSection; + std::vector> m_sectionStack; + ReporterPreferences m_reporterPrefs; +}; + +template char const *getLineOfChars() +{ + static char line[CATCH_CONFIG_CONSOLE_WIDTH] = {0}; + if (!*line) { + std::memset(line, C, CATCH_CONFIG_CONSOLE_WIDTH - 1); + line[CATCH_CONFIG_CONSOLE_WIDTH - 1] = 0; + } + return line; +} + +struct TestEventListenerBase : StreamingReporterBase { + TestEventListenerBase(ReporterConfig const &_config); + + static std::set getSupportedVerbosities(); + + void assertionStarting(AssertionInfo const &) override; + bool assertionEnded(AssertionStats const &) override; +}; + +} // end namespace Catch + +// end catch_reporter_bases.hpp +// start catch_console_colour.h + +namespace Catch { + +struct Colour { + 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, + BrightYellow = Bright | Yellow, + + // By intention + FileName = LightGrey, + Warning = BrightYellow, + ResultError = BrightRed, + ResultSuccess = BrightGreen, + ResultExpectedFailure = Warning, + + Error = BrightRed, + Success = Green, + + OriginalExpression = Cyan, + ReconstructedExpression = BrightYellow, + + SecondaryText = LightGrey, + Headers = White + }; + + // Use constructed object for RAII guard + Colour(Code _colourCode); + Colour(Colour &&other) noexcept; + Colour &operator=(Colour &&other) noexcept; + ~Colour(); + + // Use static method for one-shot changes + static void use(Code _colourCode); + +private: + bool m_moved = false; +}; + +std::ostream &operator<<(std::ostream &os, Colour const &); + +} // end namespace Catch + +// end catch_console_colour.h +// start catch_reporter_registrars.hpp + +namespace Catch { + +template class ReporterRegistrar { + + class ReporterFactory : public IReporterFactory { + + IStreamingReporterPtr + create(ReporterConfig const &config) const override + { + return std::unique_ptr(new T(config)); + } + + std::string getDescription() const override + { + return T::getDescription(); + } + }; + +public: + explicit ReporterRegistrar(std::string const &name) + { + getMutableRegistryHub().registerReporter( + name, std::make_shared()); + } +}; + +template class ListenerRegistrar { + + class ListenerFactory : public IReporterFactory { + + IStreamingReporterPtr + create(ReporterConfig const &config) const override + { + return std::unique_ptr(new T(config)); + } + std::string getDescription() const override + { + return std::string(); + } + }; + +public: + ListenerRegistrar() + { + getMutableRegistryHub().registerListener( + std::make_shared()); + } +}; +} + +#if !defined(CATCH_CONFIG_DISABLE) + +#define CATCH_REGISTER_REPORTER(name, reporterType) \ + CATCH_INTERNAL_START_WARNINGS_SUPPRESSION \ + CATCH_INTERNAL_SUPPRESS_GLOBALS_WARNINGS \ + namespace { \ + Catch::ReporterRegistrar \ + catch_internal_RegistrarFor##reporterType(name); \ + } \ + CATCH_INTERNAL_STOP_WARNINGS_SUPPRESSION + +#define CATCH_REGISTER_LISTENER(listenerType) \ + CATCH_INTERNAL_START_WARNINGS_SUPPRESSION \ + CATCH_INTERNAL_SUPPRESS_GLOBALS_WARNINGS \ + namespace { \ + Catch::ListenerRegistrar \ + catch_internal_RegistrarFor##listenerType; \ + } \ + CATCH_INTERNAL_STOP_WARNINGS_SUPPRESSION +#else // CATCH_CONFIG_DISABLE + +#define CATCH_REGISTER_REPORTER(name, reporterType) +#define CATCH_REGISTER_LISTENER(listenerType) + +#endif // CATCH_CONFIG_DISABLE + +// end catch_reporter_registrars.hpp +// Allow users to base their work off existing reporters +// start catch_reporter_compact.h + +namespace Catch { + +struct CompactReporter : StreamingReporterBase { + + using StreamingReporterBase::StreamingReporterBase; + + ~CompactReporter() override; + + static std::string getDescription(); + + void noMatchingTestCases(std::string const &spec) override; + + void assertionStarting(AssertionInfo const &) override; + + bool assertionEnded(AssertionStats const &_assertionStats) override; + + void sectionEnded(SectionStats const &_sectionStats) override; + + void testRunEnded(TestRunStats const &_testRunStats) override; +}; + +} // end namespace Catch + +// end catch_reporter_compact.h +// start catch_reporter_console.h + +#if defined(_MSC_VER) +#pragma warning(push) +#pragma warning( \ + disable : 4061) // Not all labels are EXPLICITLY handled in switch \ + // Note that 4062 (not all labels are handled \ + // and default is missing) is enabled +#endif + +namespace Catch { +// Fwd decls +struct SummaryColumn; +class TablePrinter; + +struct ConsoleReporter : StreamingReporterBase { + std::unique_ptr m_tablePrinter; + + ConsoleReporter(ReporterConfig const &config); + ~ConsoleReporter() override; + static std::string getDescription(); + + void noMatchingTestCases(std::string const &spec) override; + + void reportInvalidArguments(std::string const &arg) override; + + void assertionStarting(AssertionInfo const &) override; + + bool assertionEnded(AssertionStats const &_assertionStats) override; + + void sectionStarting(SectionInfo const &_sectionInfo) override; + void sectionEnded(SectionStats const &_sectionStats) override; + +#if defined(CATCH_CONFIG_ENABLE_BENCHMARKING) + void benchmarkPreparing(std::string const &name) override; + void benchmarkStarting(BenchmarkInfo const &info) override; + void benchmarkEnded(BenchmarkStats<> const &stats) override; + void benchmarkFailed(std::string const &error) override; +#endif // CATCH_CONFIG_ENABLE_BENCHMARKING + + void testCaseEnded(TestCaseStats const &_testCaseStats) override; + void testGroupEnded(TestGroupStats const &_testGroupStats) override; + void testRunEnded(TestRunStats const &_testRunStats) override; + void testRunStarting(TestRunInfo const &_testRunInfo) override; + +private: + void lazyPrint(); + + void lazyPrintWithoutClosingBenchmarkTable(); + void lazyPrintRunInfo(); + void lazyPrintGroupInfo(); + void printTestCaseAndSectionHeader(); + + void printClosedHeader(std::string const &_name); + void printOpenHeader(std::string const &_name); + + // if string has a : in first line will set indent to follow it on + // subsequent lines + void printHeaderString(std::string const &_string, + std::size_t indent = 0); + + void printTotals(Totals const &totals); + void printSummaryRow(std::string const &label, + std::vector const &cols, + std::size_t row); + + void printTotalsDivider(Totals const &totals); + void printSummaryDivider(); + void printTestFilters(); + +private: + bool m_headerPrinted = false; +}; + +} // end namespace Catch + +#if defined(_MSC_VER) +#pragma warning(pop) +#endif + +// end catch_reporter_console.h +// start catch_reporter_junit.h + +// start catch_xmlwriter.h + +#include + +namespace Catch { +enum class XmlFormatting { + None = 0x00, + Indent = 0x01, + Newline = 0x02, +}; + +XmlFormatting operator|(XmlFormatting lhs, XmlFormatting rhs); +XmlFormatting operator&(XmlFormatting lhs, XmlFormatting rhs); + +class XmlEncode { +public: + enum ForWhat { ForTextNodes, ForAttributes }; + + XmlEncode(std::string const &str, ForWhat forWhat = ForTextNodes); + + void encodeTo(std::ostream &os) const; + + friend std::ostream &operator<<(std::ostream &os, + XmlEncode const &xmlEncode); + +private: + std::string m_str; + ForWhat m_forWhat; +}; + +class XmlWriter { +public: + class ScopedElement { + public: + ScopedElement(XmlWriter *writer, XmlFormatting fmt); + + ScopedElement(ScopedElement &&other) noexcept; + ScopedElement &operator=(ScopedElement &&other) noexcept; + + ~ScopedElement(); + + ScopedElement & + writeText(std::string const &text, + XmlFormatting fmt = XmlFormatting::Newline | + XmlFormatting::Indent); + + template + ScopedElement &writeAttribute(std::string const &name, + T const &attribute) + { + m_writer->writeAttribute(name, attribute); + return *this; + } + + private: + mutable XmlWriter *m_writer = nullptr; + XmlFormatting m_fmt; + }; + + XmlWriter(std::ostream &os = Catch::cout()); + ~XmlWriter(); + + XmlWriter(XmlWriter const &) = delete; + XmlWriter &operator=(XmlWriter const &) = delete; + + XmlWriter &startElement(std::string const &name, + XmlFormatting fmt = XmlFormatting::Newline | + XmlFormatting::Indent); + + ScopedElement scopedElement(std::string const &name, + XmlFormatting fmt = XmlFormatting::Newline | + XmlFormatting::Indent); + + XmlWriter &endElement(XmlFormatting fmt = XmlFormatting::Newline | + XmlFormatting::Indent); + + XmlWriter &writeAttribute(std::string const &name, + std::string const &attribute); + + XmlWriter &writeAttribute(std::string const &name, bool attribute); + + template + XmlWriter &writeAttribute(std::string const &name, T const &attribute) + { + ReusableStringStream rss; + rss << attribute; + return writeAttribute(name, rss.str()); + } + + XmlWriter &writeText(std::string const &text, + XmlFormatting fmt = XmlFormatting::Newline | + XmlFormatting::Indent); + + XmlWriter &writeComment(std::string const &text, + XmlFormatting fmt = XmlFormatting::Newline | + XmlFormatting::Indent); + + void writeStylesheetRef(std::string const &url); + + XmlWriter &writeBlankLine(); + + void ensureTagClosed(); + +private: + void applyFormatting(XmlFormatting fmt); + + void writeDeclaration(); + + void newlineIfNecessary(); + + bool m_tagIsOpen = false; + bool m_needsNewline = false; + std::vector m_tags; + std::string m_indent; + std::ostream &m_os; +}; + +} + +// end catch_xmlwriter.h +namespace Catch { + +class JunitReporter : public CumulativeReporterBase { +public: + JunitReporter(ReporterConfig const &_config); + + ~JunitReporter() override; + + static std::string getDescription(); + + void noMatchingTestCases(std::string const & /*spec*/) override; + + void testRunStarting(TestRunInfo const &runInfo) override; + + void testGroupStarting(GroupInfo const &groupInfo) override; + + void testCaseStarting(TestCaseInfo const &testCaseInfo) override; + bool assertionEnded(AssertionStats const &assertionStats) override; + + void testCaseEnded(TestCaseStats const &testCaseStats) override; + + void testGroupEnded(TestGroupStats const &testGroupStats) override; + + void testRunEndedCumulative() override; + + void writeGroup(TestGroupNode const &groupNode, double suiteTime); + + void writeTestCase(TestCaseNode const &testCaseNode); + + void writeSection(std::string const &className, + std::string const &rootName, + SectionNode const §ionNode, bool testOkToFail); + + void writeAssertions(SectionNode const §ionNode); + void writeAssertion(AssertionStats const &stats); + + XmlWriter xml; + Timer suiteTimer; + std::string stdOutForSuite; + std::string stdErrForSuite; + unsigned int unexpectedExceptions = 0; + bool m_okToFail = false; +}; + +} // end namespace Catch + +// end catch_reporter_junit.h +// start catch_reporter_xml.h + +namespace Catch { +class XmlReporter : public StreamingReporterBase { +public: + XmlReporter(ReporterConfig const &_config); + + ~XmlReporter() override; + + static std::string getDescription(); + + virtual std::string getStylesheetRef() const; + + void writeSourceInfo(SourceLineInfo const &sourceInfo); + +public: // StreamingReporterBase + void noMatchingTestCases(std::string const &s) override; + + void testRunStarting(TestRunInfo const &testInfo) override; + + void testGroupStarting(GroupInfo const &groupInfo) override; + + void testCaseStarting(TestCaseInfo const &testInfo) override; + + void sectionStarting(SectionInfo const §ionInfo) override; + + void assertionStarting(AssertionInfo const &) override; + + bool assertionEnded(AssertionStats const &assertionStats) override; + + void sectionEnded(SectionStats const §ionStats) override; + + void testCaseEnded(TestCaseStats const &testCaseStats) override; + + void testGroupEnded(TestGroupStats const &testGroupStats) override; + + void testRunEnded(TestRunStats const &testRunStats) override; + +#if defined(CATCH_CONFIG_ENABLE_BENCHMARKING) + void benchmarkPreparing(std::string const &name) override; + void benchmarkStarting(BenchmarkInfo const &) override; + void benchmarkEnded(BenchmarkStats<> const &) override; + void benchmarkFailed(std::string const &) override; +#endif // CATCH_CONFIG_ENABLE_BENCHMARKING + +private: + Timer m_testCaseTimer; + XmlWriter m_xml; + int m_sectionDepth = 0; +}; + +} // end namespace Catch + +// end catch_reporter_xml.h + +// end catch_external_interfaces.h +#endif + +#if defined(CATCH_CONFIG_ENABLE_BENCHMARKING) +// start catch_benchmarking_all.hpp + +// A proxy header that includes all of the benchmarking headers to allow +// concise include of the benchmarking features. You should prefer the +// individual includes in standard use. + +// start catch_benchmark.hpp + +// Benchmark + +// start catch_chronometer.hpp + +// User-facing chronometer + +// start catch_clock.hpp + +// Clocks + +#include +#include + +namespace Catch { +namespace Benchmark { +template using ClockDuration = typename Clock::duration; +template +using FloatDuration = std::chrono::duration; + +template using TimePoint = typename Clock::time_point; + +using default_clock = std::chrono::steady_clock; + +template struct now { + TimePoint operator()() const { return Clock::now(); } +}; + +using fp_seconds = std::chrono::duration>; +} // namespace Benchmark +} // namespace Catch + +// end catch_clock.hpp +// start catch_optimizer.hpp + +// Hinting the optimizer + +#if defined(_MSC_VER) +#include // atomic_thread_fence +#endif + +namespace Catch { +namespace Benchmark { +#if defined(__GNUC__) || defined(__clang__) +template inline void keep_memory(T *p) +{ + asm volatile("" : : "g"(p) : "memory"); +} +inline void keep_memory() +{ + asm volatile("" : : : "memory"); +} + +namespace Detail { +inline void optimizer_barrier() +{ + keep_memory(); +} +} // namespace Detail +#elif defined(_MSC_VER) + +#pragma optimize("", off) +template inline void keep_memory(T *p) +{ + // thanks @milleniumbug + *reinterpret_cast(p) = + *reinterpret_cast(p); +} +// TODO equivalent keep_memory() +#pragma optimize("", on) + +namespace Detail { +inline void optimizer_barrier() +{ + std::atomic_thread_fence(std::memory_order_seq_cst); +} +} // namespace Detail + +#endif + +template inline void deoptimize_value(T &&x) +{ + keep_memory(&x); +} + +template +inline auto invoke_deoptimized(Fn &&fn, Args &&...args) -> + typename std::enable_if< + !std::is_same::value>::type +{ + deoptimize_value(std::forward(fn)(std::forward(args...))); +} + +template +inline auto invoke_deoptimized(Fn &&fn, Args &&...args) -> + typename std::enable_if< + std::is_same::value>::type +{ + std::forward(fn)(std::forward(args...)); +} +} // namespace Benchmark +} // namespace Catch + +// end catch_optimizer.hpp +// start catch_complete_invoke.hpp + +// Invoke with a special case for void + +#include +#include + +namespace Catch { +namespace Benchmark { +namespace Detail { +template struct CompleteType { + using type = T; +}; +template<> struct CompleteType { + struct type { + }; +}; + +template using CompleteType_t = typename CompleteType::type; + +template struct CompleteInvoker { + template + static Result invoke(Fun &&fun, Args &&...args) + { + return std::forward(fun)(std::forward(args)...); + } +}; +template<> struct CompleteInvoker { + template + static CompleteType_t invoke(Fun &&fun, Args &&...args) + { + std::forward(fun)(std::forward(args)...); + return {}; + } +}; + +// invoke and not return void :( +template +CompleteType_t> complete_invoke(Fun &&fun, + Args &&...args) +{ + return CompleteInvoker>::invoke( + std::forward(fun), std::forward(args)...); +} + +const std::string benchmarkErrorMsg = "a benchmark failed to run successfully"; +} // namespace Detail + +template +Detail::CompleteType_t> user_code(Fun &&fun) +{ + CATCH_TRY { return Detail::complete_invoke(std::forward(fun)); } + CATCH_CATCH_ALL + { + getResultCapture().benchmarkFailed(translateActiveException()); + CATCH_RUNTIME_ERROR(Detail::benchmarkErrorMsg); + } +} +} // namespace Benchmark +} // namespace Catch + +// end catch_complete_invoke.hpp +namespace Catch { +namespace Benchmark { +namespace Detail { +struct ChronometerConcept { + virtual void start() = 0; + virtual void finish() = 0; + virtual ~ChronometerConcept() = default; +}; +template +struct ChronometerModel final : public ChronometerConcept { + void start() override { started = Clock::now(); } + void finish() override { finished = Clock::now(); } + + ClockDuration elapsed() const { return finished - started; } + + TimePoint started; + TimePoint finished; +}; +} // namespace Detail + +struct Chronometer { +public: + template void measure(Fun &&fun) + { + measure(std::forward(fun), is_callable()); + } + + int runs() const { return k; } + + Chronometer(Detail::ChronometerConcept &meter, int k) + : impl(&meter), k(k) + { + } + +private: + template void measure(Fun &&fun, std::false_type) + { + measure([&fun](int) { return fun(); }, std::true_type()); + } + + template void measure(Fun &&fun, std::true_type) + { + Detail::optimizer_barrier(); + impl->start(); + for (int i = 0; i < k; ++i) + invoke_deoptimized(fun, i); + impl->finish(); + Detail::optimizer_barrier(); + } + + Detail::ChronometerConcept *impl; + int k; +}; +} // namespace Benchmark +} // namespace Catch + +// end catch_chronometer.hpp +// start catch_environment.hpp + +// Environment information + +namespace Catch { +namespace Benchmark { +template struct EnvironmentEstimate { + Duration mean; + OutlierClassification outliers; + + template + operator EnvironmentEstimate() const + { + return {mean, outliers}; + } +}; +template struct Environment { + using clock_type = Clock; + EnvironmentEstimate> clock_resolution; + EnvironmentEstimate> clock_cost; +}; +} // namespace Benchmark +} // namespace Catch + +// end catch_environment.hpp +// start catch_execution_plan.hpp + +// Execution plan + +// start catch_benchmark_function.hpp + +// Dumb std::function implementation for consistent call overhead + +#include +#include +#include +#include + +namespace Catch { +namespace Benchmark { +namespace Detail { +template using Decay = typename std::decay::type; +template +struct is_related : std::is_same, Decay> { +}; + +/// We need to reinvent std::function because every piece of code that might add overhead +/// in a measurement context needs to have consistent performance characteristics so that we +/// can account for it in the measurement. +/// Implementations of std::function with optimizations that aren't always applicable, like +/// small buffer optimizations, are not uncommon. +/// This is effectively an implementation of std::function without any such optimizations; +/// it may be slow, but it is consistently slow. +struct BenchmarkFunction { +private: + struct callable { + virtual void call(Chronometer meter) const = 0; + virtual callable *clone() const = 0; + virtual ~callable() = default; + }; + template struct model : public callable { + model(Fun &&fun) : fun(std::move(fun)) {} + model(Fun const &fun) : fun(fun) {} + + model *clone() const override + { + return new model(*this); + } + + void call(Chronometer meter) const override + { + call(meter, is_callable()); + } + void call(Chronometer meter, std::true_type) const + { + fun(meter); + } + void call(Chronometer meter, std::false_type) const + { + meter.measure(fun); + } + + Fun fun; + }; + + struct do_nothing { + void operator()() const {} + }; + + template BenchmarkFunction(model *c) : f(c) {} + +public: + BenchmarkFunction() : f(new model{{}}) {} + + template::value, int>::type = + 0> + BenchmarkFunction(Fun &&fun) + : f(new model::type>( + std::forward(fun))) + { + } + + BenchmarkFunction(BenchmarkFunction &&that) : f(std::move(that.f)) {} + + BenchmarkFunction(BenchmarkFunction const &that) : f(that.f->clone()) {} + + BenchmarkFunction &operator=(BenchmarkFunction &&that) + { + f = std::move(that.f); + return *this; + } + + BenchmarkFunction &operator=(BenchmarkFunction const &that) + { + f.reset(that.f->clone()); + return *this; + } + + void operator()(Chronometer meter) const { f->call(meter); } + +private: + std::unique_ptr f; +}; +} // namespace Detail +} // namespace Benchmark +} // namespace Catch + +// end catch_benchmark_function.hpp +// start catch_repeat.hpp + +// repeat algorithm + +#include +#include + +namespace Catch { +namespace Benchmark { +namespace Detail { +template struct repeater { + void operator()(int k) const + { + for (int i = 0; i < k; ++i) { + fun(); + } + } + Fun fun; +}; +template +repeater::type> repeat(Fun &&fun) +{ + return {std::forward(fun)}; +} +} // namespace Detail +} // namespace Benchmark +} // namespace Catch + +// end catch_repeat.hpp +// start catch_run_for_at_least.hpp + +// Run a function for a minimum amount of time + +// start catch_measure.hpp + +// Measure + +// start catch_timing.hpp + +// Timing + +#include +#include + +namespace Catch { +namespace Benchmark { +template struct Timing { + Duration elapsed; + Result result; + int iterations; +}; +template +using TimingOf = + Timing, + Detail::CompleteType_t>>; +} // namespace Benchmark +} // namespace Catch + +// end catch_timing.hpp +#include + +namespace Catch { +namespace Benchmark { +namespace Detail { +template +TimingOf measure(Fun &&fun, Args &&...args) +{ + auto start = Clock::now(); + auto &&r = Detail::complete_invoke(fun, std::forward(args)...); + auto end = Clock::now(); + auto delta = end - start; + return {delta, std::forward(r), 1}; +} +} // namespace Detail +} // namespace Benchmark +} // namespace Catch + +// end catch_measure.hpp +#include +#include + +namespace Catch { +namespace Benchmark { +namespace Detail { +template +TimingOf measure_one(Fun &&fun, int iters, std::false_type) +{ + return Detail::measure(fun, iters); +} +template +TimingOf measure_one(Fun &&fun, int iters, + std::true_type) +{ + Detail::ChronometerModel meter; + auto &&result = Detail::complete_invoke(fun, Chronometer(meter, iters)); + + return {meter.elapsed(), std::move(result), iters}; +} + +template +using run_for_at_least_argument_t = + typename std::conditional::value, + Chronometer, int>::type; + +struct optimized_away_error : std::exception { + const char *what() const noexcept override + { + return "could not measure benchmark, maybe it was optimized away"; + } +}; + +template +TimingOf> +run_for_at_least(ClockDuration how_long, int seed, Fun &&fun) +{ + auto iters = seed; + while (iters < (1 << 30)) { + auto &&Timing = measure_one( + fun, iters, is_callable()); + + if (Timing.elapsed >= how_long) { + return {Timing.elapsed, std::move(Timing.result), + iters}; + } + iters *= 2; + } + Catch::throw_exception(optimized_away_error{}); +} +} // namespace Detail +} // namespace Benchmark +} // namespace Catch + +// end catch_run_for_at_least.hpp +#include +#include + +namespace Catch { +namespace Benchmark { +template struct ExecutionPlan { + int iterations_per_sample; + Duration estimated_duration; + Detail::BenchmarkFunction benchmark; + Duration warmup_time; + int warmup_iterations; + + template operator ExecutionPlan() const + { + return {iterations_per_sample, estimated_duration, benchmark, + warmup_time, warmup_iterations}; + } + + template + std::vector> + run(const IConfig &cfg, Environment> env) const + { + // warmup a bit + Detail::run_for_at_least( + std::chrono::duration_cast>( + warmup_time), + warmup_iterations, Detail::repeat(now{})); + + std::vector> times; + times.reserve(cfg.benchmarkSamples()); + std::generate_n( + std::back_inserter(times), cfg.benchmarkSamples(), + [this, env] { + Detail::ChronometerModel model; + this->benchmark(Chronometer( + model, iterations_per_sample)); + auto sample_time = + model.elapsed() - env.clock_cost.mean; + if (sample_time < FloatDuration::zero()) + sample_time = + FloatDuration::zero(); + return sample_time / iterations_per_sample; + }); + return times; + } +}; +} // namespace Benchmark +} // namespace Catch + +// end catch_execution_plan.hpp +// start catch_estimate_clock.hpp + +// Environment measurement + +// start catch_stats.hpp + +// Statistical analysis tools + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +namespace Catch { +namespace Benchmark { +namespace Detail { +using sample = std::vector; + +double weighted_average_quantile(int k, int q, + std::vector::iterator first, + std::vector::iterator last); + +template +OutlierClassification classify_outliers(Iterator first, Iterator last) +{ + std::vector copy(first, last); + + auto q1 = weighted_average_quantile(1, 4, copy.begin(), copy.end()); + auto q3 = weighted_average_quantile(3, 4, copy.begin(), copy.end()); + auto iqr = q3 - q1; + auto los = q1 - (iqr * 3.); + auto lom = q1 - (iqr * 1.5); + auto him = q3 + (iqr * 1.5); + auto his = q3 + (iqr * 3.); + + OutlierClassification o; + for (; first != last; ++first) { + auto &&t = *first; + if (t < los) + ++o.low_severe; + else if (t < lom) + ++o.low_mild; + else if (t > his) + ++o.high_severe; + else if (t > him) + ++o.high_mild; + ++o.samples_seen; + } + return o; +} + +template double mean(Iterator first, Iterator last) +{ + auto count = last - first; + double sum = std::accumulate(first, last, 0.); + return sum / count; +} + +template +sample resample(URng &rng, int resamples, Iterator first, Iterator last, + Estimator &estimator) +{ + auto n = last - first; + std::uniform_int_distribution dist(0, n - 1); + + sample out; + out.reserve(resamples); + std::generate_n(std::back_inserter(out), resamples, + [n, first, &estimator, &dist, &rng] { + std::vector resampled; + resampled.reserve(n); + std::generate_n(std::back_inserter(resampled), + n, [first, &dist, &rng] { + return first[dist(rng)]; + }); + return estimator(resampled.begin(), + resampled.end()); + }); + std::sort(out.begin(), out.end()); + return out; +} + +template +sample jackknife(Estimator &&estimator, Iterator first, Iterator last) +{ + auto n = last - first; + auto second = std::next(first); + sample results; + results.reserve(n); + + for (auto it = first; it != last; ++it) { + std::iter_swap(it, first); + results.push_back(estimator(second, last)); + } + + return results; +} + +inline double normal_cdf(double x) +{ + return std::erfc(-x / std::sqrt(2.0)) / 2.0; +} + +double erfc_inv(double x); + +double normal_quantile(double p); + +template +Estimate bootstrap(double confidence_level, Iterator first, + Iterator last, sample const &resample, + Estimator &&estimator) +{ + auto n_samples = last - first; + + double point = estimator(first, last); + // Degenerate case with a single sample + if (n_samples == 1) + return {point, point, point, confidence_level}; + + sample jack = jackknife(estimator, first, last); + double jack_mean = mean(jack.begin(), jack.end()); + double sum_squares, sum_cubes; + std::tie(sum_squares, sum_cubes) = std::accumulate( + jack.begin(), jack.end(), std::make_pair(0., 0.), + [jack_mean](std::pair sqcb, + double x) -> std::pair { + auto d = jack_mean - x; + auto d2 = d * d; + auto d3 = d2 * d; + return {sqcb.first + d2, sqcb.second + d3}; + }); + + double accel = sum_cubes / (6 * std::pow(sum_squares, 1.5)); + int n = static_cast(resample.size()); + double prob_n = std::count_if(resample.begin(), resample.end(), + [point](double x) { return x < point; }) / + (double)n; + // degenerate case with uniform samples + if (prob_n == 0) + return {point, point, point, confidence_level}; + + double bias = normal_quantile(prob_n); + double z1 = normal_quantile((1. - confidence_level) / 2.); + + auto cumn = [n](double x) -> int { + return std::lround(normal_cdf(x) * n); + }; + auto a = [bias, accel](double b) { + return bias + b / (1. - accel * b); + }; + double b1 = bias + z1; + double b2 = bias - z1; + double a1 = a(b1); + double a2 = a(b2); + auto lo = (std::max)(cumn(a1), 0); + auto hi = (std::min)(cumn(a2), n - 1); + + return {point, resample[lo], resample[hi], confidence_level}; +} + +double outlier_variance(Estimate mean, Estimate stddev, int n); + +struct bootstrap_analysis { + Estimate mean; + Estimate standard_deviation; + double outlier_variance; +}; + +bootstrap_analysis analyse_samples(double confidence_level, int n_resamples, + std::vector::iterator first, + std::vector::iterator last); +} // namespace Detail +} // namespace Benchmark +} // namespace Catch + +// end catch_stats.hpp +#include +#include +#include +#include +#include + +namespace Catch { +namespace Benchmark { +namespace Detail { +template std::vector resolution(int k) +{ + std::vector> times; + times.reserve(k + 1); + std::generate_n(std::back_inserter(times), k + 1, now{}); + + std::vector deltas; + deltas.reserve(k); + std::transform(std::next(times.begin()), times.end(), times.begin(), + std::back_inserter(deltas), + [](TimePoint a, TimePoint b) { + return static_cast((a - b).count()); + }); + + return deltas; +} + +const auto warmup_iterations = 10000; +const auto warmup_time = std::chrono::milliseconds(100); +const auto minimum_ticks = 1000; +const auto warmup_seed = 10000; +const auto clock_resolution_estimation_time = std::chrono::milliseconds(500); +const auto clock_cost_estimation_time_limit = std::chrono::seconds(1); +const auto clock_cost_estimation_tick_limit = 100000; +const auto clock_cost_estimation_time = std::chrono::milliseconds(10); +const auto clock_cost_estimation_iterations = 10000; + +template int warmup() +{ + return run_for_at_least( + std::chrono::duration_cast>( + warmup_time), + warmup_seed, &resolution) + .iterations; +} +template +EnvironmentEstimate> +estimate_clock_resolution(int iterations) +{ + auto r = run_for_at_least( + std::chrono::duration_cast>( + clock_resolution_estimation_time), + iterations, &resolution) + .result; + return { + FloatDuration(mean(r.begin(), r.end())), + classify_outliers(r.begin(), r.end()), + }; +} +template +EnvironmentEstimate> +estimate_clock_cost(FloatDuration resolution) +{ + auto time_limit = (std::min)( + resolution * clock_cost_estimation_tick_limit, + FloatDuration(clock_cost_estimation_time_limit)); + auto time_clock = [](int k) { + return Detail::measure([k] { + for (int i = 0; i < k; ++i) { + volatile auto ignored = Clock::now(); + (void)ignored; + } + }) + .elapsed; + }; + time_clock(1); + int iters = clock_cost_estimation_iterations; + auto &&r = run_for_at_least( + std::chrono::duration_cast>( + clock_cost_estimation_time), + iters, time_clock); + std::vector times; + int nsamples = static_cast(std::ceil(time_limit / r.elapsed)); + times.reserve(nsamples); + std::generate_n(std::back_inserter(times), nsamples, [time_clock, &r] { + return static_cast( + (time_clock(r.iterations) / r.iterations).count()); + }); + return { + FloatDuration(mean(times.begin(), times.end())), + classify_outliers(times.begin(), times.end()), + }; +} + +template Environment> measure_environment() +{ + static Environment> *env = nullptr; + if (env) { + return *env; + } + + auto iters = Detail::warmup(); + auto resolution = Detail::estimate_clock_resolution(iters); + auto cost = Detail::estimate_clock_cost(resolution.mean); + + env = new Environment>{resolution, cost}; + return *env; +} +} // namespace Detail +} // namespace Benchmark +} // namespace Catch + +// end catch_estimate_clock.hpp +// start catch_analyse.hpp + +// Run and analyse one benchmark + +// start catch_sample_analysis.hpp + +// Benchmark results + +#include +#include +#include +#include + +namespace Catch { +namespace Benchmark { +template struct SampleAnalysis { + std::vector samples; + Estimate mean; + Estimate standard_deviation; + OutlierClassification outliers; + double outlier_variance; + + template operator SampleAnalysis() const + { + std::vector samples2; + samples2.reserve(samples.size()); + std::transform(samples.begin(), samples.end(), + std::back_inserter(samples2), + [](Duration d) { return Duration2(d); }); + return { + std::move(samples2), mean, standard_deviation, outliers, + outlier_variance, + }; + } +}; +} // namespace Benchmark +} // namespace Catch + +// end catch_sample_analysis.hpp +#include +#include +#include + +namespace Catch { +namespace Benchmark { +namespace Detail { +template +SampleAnalysis analyse(const IConfig &cfg, Environment, + Iterator first, Iterator last) +{ + if (!cfg.benchmarkNoAnalysis()) { + std::vector samples; + samples.reserve(last - first); + std::transform(first, last, std::back_inserter(samples), + [](Duration d) { return d.count(); }); + + auto analysis = Catch::Benchmark::Detail::analyse_samples( + cfg.benchmarkConfidenceInterval(), + cfg.benchmarkResamples(), samples.begin(), + samples.end()); + auto outliers = Catch::Benchmark::Detail::classify_outliers( + samples.begin(), samples.end()); + + auto wrap_estimate = [](Estimate e) { + return Estimate{ + Duration(e.point), + Duration(e.lower_bound), + Duration(e.upper_bound), + e.confidence_interval, + }; + }; + std::vector samples2; + samples2.reserve(samples.size()); + std::transform(samples.begin(), samples.end(), + std::back_inserter(samples2), + [](double d) { return Duration(d); }); + return { + std::move(samples2), + wrap_estimate(analysis.mean), + wrap_estimate(analysis.standard_deviation), + outliers, + analysis.outlier_variance, + }; + } else { + std::vector samples; + samples.reserve(last - first); + + Duration mean = Duration(0); + int i = 0; + for (auto it = first; it < last; ++it, ++i) { + samples.push_back(Duration(*it)); + mean += Duration(*it); + } + mean /= i; + + return {std::move(samples), + Estimate{mean, mean, mean, 0.0}, + Estimate{Duration(0), Duration(0), + Duration(0), 0.0}, + OutlierClassification{}, 0.0}; + } +} +} // namespace Detail +} // namespace Benchmark +} // namespace Catch + +// end catch_analyse.hpp +#include +#include +#include +#include +#include + +namespace Catch { +namespace Benchmark { +struct Benchmark { + Benchmark(std::string &&name) : name(std::move(name)) {} + + template + Benchmark(std::string &&name, FUN &&func) + : fun(std::move(func)), name(std::move(name)) + { + } + + template + ExecutionPlan> + prepare(const IConfig &cfg, Environment> env) const + { + auto min_time = + env.clock_resolution.mean * Detail::minimum_ticks; + auto run_time = + std::max(min_time, + std::chrono::duration_cast( + cfg.benchmarkWarmupTime())); + auto &&test = Detail::run_for_at_least( + std::chrono::duration_cast>( + run_time), + 1, fun); + int new_iters = static_cast( + std::ceil(min_time * test.iterations / test.elapsed)); + return {new_iters, + test.elapsed / test.iterations * new_iters * + cfg.benchmarkSamples(), + fun, + std::chrono::duration_cast>( + cfg.benchmarkWarmupTime()), + Detail::warmup_iterations}; + } + + template void run() + { + IConfigPtr cfg = getCurrentContext().getConfig(); + + auto env = Detail::measure_environment(); + + getResultCapture().benchmarkPreparing(name); + CATCH_TRY + { + auto plan = user_code( + [&] { return prepare(*cfg, env); }); + + BenchmarkInfo info{name, + plan.estimated_duration.count(), + plan.iterations_per_sample, + cfg->benchmarkSamples(), + cfg->benchmarkResamples(), + env.clock_resolution.mean.count(), + env.clock_cost.mean.count()}; + + getResultCapture().benchmarkStarting(info); + + auto samples = user_code([&] { + return plan.template run(*cfg, env); + }); + + auto analysis = Detail::analyse( + *cfg, env, samples.begin(), samples.end()); + BenchmarkStats> stats{ + info, + analysis.samples, + analysis.mean, + analysis.standard_deviation, + analysis.outliers, + analysis.outlier_variance}; + getResultCapture().benchmarkEnded(stats); + } + CATCH_CATCH_ALL + { + if (translateActiveException() != + Detail::benchmarkErrorMsg) // benchmark errors have been reported, otherwise rethrow. + std::rethrow_exception( + std::current_exception()); + } + } + + // sets lambda to be used in fun *and* executes benchmark! + template::value, int>::type = + 0> + Benchmark &operator=(Fun func) + { + fun = Detail::BenchmarkFunction(func); + run(); + return *this; + } + + explicit operator bool() { return true; } + +private: + Detail::BenchmarkFunction fun; + std::string name; +}; +} +} // namespace Catch + +#define INTERNAL_CATCH_GET_1_ARG(arg1, arg2, ...) arg1 +#define INTERNAL_CATCH_GET_2_ARG(arg1, arg2, ...) arg2 + +#define INTERNAL_CATCH_BENCHMARK(BenchmarkName, name, benchmarkIndex) \ + if (Catch::Benchmark::Benchmark BenchmarkName{name}) \ + BenchmarkName = [&](int benchmarkIndex) + +#define INTERNAL_CATCH_BENCHMARK_ADVANCED(BenchmarkName, name) \ + if (Catch::Benchmark::Benchmark BenchmarkName{name}) \ + BenchmarkName = [&] + +// end catch_benchmark.hpp +// start catch_constructor.hpp + +// Constructor and destructor helpers + +#include + +namespace Catch { +namespace Benchmark { +namespace Detail { +template struct ObjectStorage { + using TStorage = typename std::aligned_storage< + sizeof(T), std::alignment_of::value>::type; + + ObjectStorage() : data() {} + + ObjectStorage(const ObjectStorage &other) + { + new (&data) T(other.stored_object()); + } + + ObjectStorage(ObjectStorage &&other) + { + new (&data) T(std::move(other.stored_object())); + } + + ~ObjectStorage() { destruct_on_exit(); } + + template void construct(Args &&...args) + { + new (&data) T(std::forward(args)...); + } + + template + typename std::enable_if::type destruct() + { + stored_object().~T(); + } + +private: + // If this is a constructor benchmark, destruct the underlying object + template + void destruct_on_exit(typename std::enable_if::type * = 0) + { + destruct(); + } + // Otherwise, don't + template + void destruct_on_exit(typename std::enable_if::type * = 0) + { + } + + T &stored_object() + { + return *static_cast(static_cast(&data)); + } + + T const &stored_object() const + { + return *static_cast(static_cast(&data)); + } + + TStorage data; +}; +} + +template using storage_for = Detail::ObjectStorage; + +template +using destructable_object = Detail::ObjectStorage; +} +} + +// end catch_constructor.hpp +// end catch_benchmarking_all.hpp +#endif + +#endif // ! CATCH_CONFIG_IMPL_ONLY + +#ifdef CATCH_IMPL +// start catch_impl.hpp + +#ifdef __clang__ +#pragma clang diagnostic push +#pragma clang diagnostic ignored "-Wweak-vtables" +#endif + +// Keep these here for external reporters +// start catch_test_case_tracker.h + +#include +#include +#include + +namespace Catch { +namespace TestCaseTracking { + +struct NameAndLocation { + std::string name; + SourceLineInfo location; + + NameAndLocation(std::string const &_name, + SourceLineInfo const &_location); + friend bool operator==(NameAndLocation const &lhs, + NameAndLocation const &rhs) + { + return lhs.name == rhs.name && lhs.location == rhs.location; + } +}; + +class ITracker; + +using ITrackerPtr = std::shared_ptr; + +class ITracker { + NameAndLocation m_nameAndLocation; + +public: + ITracker(NameAndLocation const &nameAndLoc) + : m_nameAndLocation(nameAndLoc) + { + } + + // static queries + NameAndLocation const &nameAndLocation() const + { + return m_nameAndLocation; + } + + virtual ~ITracker(); + + // dynamic queries + virtual bool isComplete() const = 0; // Successfully completed or failed + virtual bool isSuccessfullyCompleted() const = 0; + virtual bool isOpen() const = 0; // Started but not complete + virtual bool hasChildren() const = 0; + virtual bool hasStarted() const = 0; + + virtual ITracker &parent() = 0; + + // actions + virtual void close() = 0; // Successfully complete + virtual void fail() = 0; + virtual void markAsNeedingAnotherRun() = 0; + + virtual void addChild(ITrackerPtr const &child) = 0; + virtual ITrackerPtr + findChild(NameAndLocation const &nameAndLocation) = 0; + virtual void openChild() = 0; + + // Debug/ checking + virtual bool isSectionTracker() const = 0; + virtual bool isGeneratorTracker() const = 0; +}; + +class TrackerContext { + + enum RunState { NotStarted, Executing, CompletedCycle }; + + ITrackerPtr m_rootTracker; + ITracker *m_currentTracker = nullptr; + RunState m_runState = NotStarted; + +public: + ITracker &startRun(); + void endRun(); + + void startCycle(); + void completeCycle(); + + bool completedCycle() const; + ITracker ¤tTracker(); + void setCurrentTracker(ITracker *tracker); +}; + +class TrackerBase : public ITracker { +protected: + enum CycleState { + NotStarted, + Executing, + ExecutingChildren, + NeedsAnotherRun, + CompletedSuccessfully, + Failed + }; + + using Children = std::vector; + TrackerContext &m_ctx; + ITracker *m_parent; + Children m_children; + CycleState m_runState = NotStarted; + +public: + TrackerBase(NameAndLocation const &nameAndLocation, TrackerContext &ctx, + ITracker *parent); + + bool isComplete() const override; + bool isSuccessfullyCompleted() const override; + bool isOpen() const override; + bool hasChildren() const override; + bool hasStarted() const override { return m_runState != NotStarted; } + + void addChild(ITrackerPtr const &child) override; + + ITrackerPtr findChild(NameAndLocation const &nameAndLocation) override; + ITracker &parent() override; + + void openChild() override; + + bool isSectionTracker() const override; + bool isGeneratorTracker() const override; + + void open(); + + void close() override; + void fail() override; + void markAsNeedingAnotherRun() override; + +private: + void moveToParent(); + void moveToThis(); +}; + +class SectionTracker : public TrackerBase { + std::vector m_filters; + std::string m_trimmed_name; + +public: + SectionTracker(NameAndLocation const &nameAndLocation, + TrackerContext &ctx, ITracker *parent); + + bool isSectionTracker() const override; + + bool isComplete() const override; + + static SectionTracker &acquire(TrackerContext &ctx, + NameAndLocation const &nameAndLocation); + + void tryOpen(); + + void addInitialFilters(std::vector const &filters); + void addNextFilters(std::vector const &filters); + //! Returns filters active in this tracker + std::vector const &getFilters() const; + //! Returns whitespace-trimmed name of the tracked section + std::string const &trimmedName() const; +}; + +} // namespace TestCaseTracking + +using TestCaseTracking::ITracker; +using TestCaseTracking::TrackerContext; +using TestCaseTracking::SectionTracker; + +} // namespace Catch + +// end catch_test_case_tracker.h + +// start catch_leak_detector.h + +namespace Catch { + +struct LeakDetector { + LeakDetector(); + ~LeakDetector(); +}; + +} +// end catch_leak_detector.h +// Cpp files will be included in the single-header file here +// start catch_stats.cpp + +// Statistical analysis tools + +#if defined(CATCH_CONFIG_ENABLE_BENCHMARKING) + +#include +#include + +#if defined(CATCH_CONFIG_USE_ASYNC) +#include +#endif + +namespace { +double erf_inv(double x) +{ + // Code accompanying the article "Approximating the erfinv function" in GPU Computing Gems, Volume 2 + double w, p; + + w = -log((1.0 - x) * (1.0 + x)); + + if (w < 6.250000) { + w = w - 3.125000; + p = -3.6444120640178196996e-21; + p = -1.685059138182016589e-19 + p * w; + p = 1.2858480715256400167e-18 + p * w; + p = 1.115787767802518096e-17 + p * w; + p = -1.333171662854620906e-16 + p * w; + p = 2.0972767875968561637e-17 + p * w; + p = 6.6376381343583238325e-15 + p * w; + p = -4.0545662729752068639e-14 + p * w; + p = -8.1519341976054721522e-14 + p * w; + p = 2.6335093153082322977e-12 + p * w; + p = -1.2975133253453532498e-11 + p * w; + p = -5.4154120542946279317e-11 + p * w; + p = 1.051212273321532285e-09 + p * w; + p = -4.1126339803469836976e-09 + p * w; + p = -2.9070369957882005086e-08 + p * w; + p = 4.2347877827932403518e-07 + p * w; + p = -1.3654692000834678645e-06 + p * w; + p = -1.3882523362786468719e-05 + p * w; + p = 0.0001867342080340571352 + p * w; + p = -0.00074070253416626697512 + p * w; + p = -0.0060336708714301490533 + p * w; + p = 0.24015818242558961693 + p * w; + p = 1.6536545626831027356 + p * w; + } else if (w < 16.000000) { + w = sqrt(w) - 3.250000; + p = 2.2137376921775787049e-09; + p = 9.0756561938885390979e-08 + p * w; + p = -2.7517406297064545428e-07 + p * w; + p = 1.8239629214389227755e-08 + p * w; + p = 1.5027403968909827627e-06 + p * w; + p = -4.013867526981545969e-06 + p * w; + p = 2.9234449089955446044e-06 + p * w; + p = 1.2475304481671778723e-05 + p * w; + p = -4.7318229009055733981e-05 + p * w; + p = 6.8284851459573175448e-05 + p * w; + p = 2.4031110387097893999e-05 + p * w; + p = -0.0003550375203628474796 + p * w; + p = 0.00095328937973738049703 + p * w; + p = -0.0016882755560235047313 + p * w; + p = 0.0024914420961078508066 + p * w; + p = -0.0037512085075692412107 + p * w; + p = 0.005370914553590063617 + p * w; + p = 1.0052589676941592334 + p * w; + p = 3.0838856104922207635 + p * w; + } else { + w = sqrt(w) - 5.000000; + p = -2.7109920616438573243e-11; + p = -2.5556418169965252055e-10 + p * w; + p = 1.5076572693500548083e-09 + p * w; + p = -3.7894654401267369937e-09 + p * w; + p = 7.6157012080783393804e-09 + p * w; + p = -1.4960026627149240478e-08 + p * w; + p = 2.9147953450901080826e-08 + p * w; + p = -6.7711997758452339498e-08 + p * w; + p = 2.2900482228026654717e-07 + p * w; + p = -9.9298272942317002539e-07 + p * w; + p = 4.5260625972231537039e-06 + p * w; + p = -1.9681778105531670567e-05 + p * w; + p = 7.5995277030017761139e-05 + p * w; + p = -0.00021503011930044477347 + p * w; + p = -0.00013871931833623122026 + p * w; + p = 1.0103004648645343977 + p * w; + p = 4.8499064014085844221 + p * w; + } + return p * x; +} + +double standard_deviation(std::vector::iterator first, + std::vector::iterator last) +{ + auto m = Catch::Benchmark::Detail::mean(first, last); + double variance = std::accumulate(first, last, 0., + [m](double a, double b) { + double diff = b - m; + return a + diff * diff; + }) / + (last - first); + return std::sqrt(variance); +} + +} + +namespace Catch { +namespace Benchmark { +namespace Detail { + +double weighted_average_quantile(int k, int q, + std::vector::iterator first, + std::vector::iterator last) +{ + auto count = last - first; + double idx = (count - 1) * k / static_cast(q); + int j = static_cast(idx); + double g = idx - j; + std::nth_element(first, first + j, last); + auto xj = first[j]; + if (g == 0) + return xj; + + auto xj1 = *std::min_element(first + (j + 1), last); + return xj + g * (xj1 - xj); +} + +double erfc_inv(double x) +{ + return erf_inv(1.0 - x); +} + +double normal_quantile(double p) +{ + static const double ROOT_TWO = std::sqrt(2.0); + + double result = 0.0; + assert(p >= 0 && p <= 1); + if (p < 0 || p > 1) { + return result; + } + + result = -erfc_inv(2.0 * p); + // result *= normal distribution standard deviation (1.0) * sqrt(2) + result *= /*sd * */ ROOT_TWO; + // result += normal disttribution mean (0) + return result; +} + +double outlier_variance(Estimate mean, Estimate stddev, int n) +{ + double sb = stddev.point; + double mn = mean.point / n; + double mg_min = mn / 2.; + double sg = (std::min)(mg_min / 4., sb / std::sqrt(n)); + double sg2 = sg * sg; + double sb2 = sb * sb; + + auto c_max = [n, mn, sb2, sg2](double x) -> double { + double k = mn - x; + double d = k * k; + double nd = n * d; + double k0 = -n * nd; + double k1 = sb2 - n * sg2 + nd; + double det = k1 * k1 - 4 * sg2 * k0; + return (int)(-2. * k0 / (k1 + std::sqrt(det))); + }; + + auto var_out = [n, sb2, sg2](double c) { + double nc = n - c; + return (nc / n) * (sb2 - nc * sg2); + }; + + return (std::min)(var_out(1), + var_out((std::min)(c_max(0.), c_max(mg_min)))) / + sb2; +} + +bootstrap_analysis analyse_samples(double confidence_level, int n_resamples, + std::vector::iterator first, + std::vector::iterator last) +{ + CATCH_INTERNAL_START_WARNINGS_SUPPRESSION + CATCH_INTERNAL_SUPPRESS_GLOBALS_WARNINGS + static std::random_device entropy; + CATCH_INTERNAL_STOP_WARNINGS_SUPPRESSION + + auto n = static_cast( + last - + first); // seriously, one can't use integral types without hell in C++ + + auto mean = &Detail::mean::iterator>; + auto stddev = &standard_deviation; + +#if defined(CATCH_CONFIG_USE_ASYNC) + auto Estimate = [=](double (*f)(std::vector::iterator, + std::vector::iterator)) { + auto seed = entropy(); + return std::async(std::launch::async, [=] { + std::mt19937 rng(seed); + auto resampled = + resample(rng, n_resamples, first, last, f); + return bootstrap(confidence_level, first, last, + resampled, f); + }); + }; + + auto mean_future = Estimate(mean); + auto stddev_future = Estimate(stddev); + + auto mean_estimate = mean_future.get(); + auto stddev_estimate = stddev_future.get(); +#else + auto Estimate = [=](double (*f)(std::vector::iterator, + std::vector::iterator)) { + auto seed = entropy(); + std::mt19937 rng(seed); + auto resampled = resample(rng, n_resamples, first, last, f); + return bootstrap(confidence_level, first, last, resampled, f); + }; + + auto mean_estimate = Estimate(mean); + auto stddev_estimate = Estimate(stddev); +#endif // CATCH_USE_ASYNC + + double outlier_variance = + Detail::outlier_variance(mean_estimate, stddev_estimate, n); + + return {mean_estimate, stddev_estimate, outlier_variance}; +} +} // namespace Detail +} // namespace Benchmark +} // namespace Catch + +#endif // CATCH_CONFIG_ENABLE_BENCHMARKING +// end catch_stats.cpp +// start catch_approx.cpp + +#include +#include + +namespace { + +// Performs equivalent check of std::fabs(lhs - rhs) <= margin +// But without the subtraction to allow for INFINITY in comparison +bool marginComparison(double lhs, double rhs, double margin) +{ + return (lhs + margin >= rhs) && (rhs + margin >= lhs); +} + +} + +namespace Catch { +namespace Detail { + +Approx::Approx(double value) + : m_epsilon(std::numeric_limits::epsilon() * 100), + m_margin(0.0), + m_scale(0.0), + m_value(value) +{ +} + +Approx Approx::custom() +{ + return Approx(0); +} + +Approx Approx::operator-() const +{ + auto temp(*this); + temp.m_value = -temp.m_value; + return temp; +} + +std::string Approx::toString() const +{ + ReusableStringStream rss; + rss << "Approx( " << ::Catch::Detail::stringify(m_value) << " )"; + return rss.str(); +} + +bool Approx::equalityComparisonImpl(const double other) const +{ + // First try with fixed margin, then compute margin based on epsilon, scale and Approx's value + // Thanks to Richard Harris for his help refining the scaled margin value + return marginComparison(m_value, other, m_margin) || + marginComparison( + m_value, other, + m_epsilon * + (m_scale + + std::fabs(std::isinf(m_value) ? 0 : m_value))); +} + +void Approx::setMargin(double newMargin) +{ + CATCH_ENFORCE(newMargin >= 0, + "Invalid Approx::margin: " + << newMargin << '.' + << " Approx::Margin has to be non-negative."); + m_margin = newMargin; +} + +void Approx::setEpsilon(double newEpsilon) +{ + CATCH_ENFORCE(newEpsilon >= 0 && newEpsilon <= 1.0, + "Invalid Approx::epsilon: " + << newEpsilon << '.' + << " Approx::epsilon has to be in [0, 1]"); + m_epsilon = newEpsilon; +} + +} // end namespace Detail + +namespace literals { +Detail::Approx operator"" _a(long double val) +{ + return Detail::Approx(val); +} +Detail::Approx operator"" _a(unsigned long long val) +{ + return Detail::Approx(val); +} +} // end namespace literals + +std::string +StringMaker::convert(Catch::Detail::Approx const &value) +{ + return value.toString(); +} + +} // end namespace Catch +// end catch_approx.cpp +// start catch_assertionhandler.cpp + +// start catch_debugger.h + +namespace Catch { +bool isDebuggerActive(); +} + +#ifdef CATCH_PLATFORM_MAC + +#if defined(__i386__) || defined(__x86_64__) +#define CATCH_TRAP() __asm__("int $3\n" : :) /* NOLINT */ +#elif defined(__aarch64__) +#define CATCH_TRAP() __asm__(".inst 0xd4200000") +#endif + +#elif defined(CATCH_PLATFORM_IPHONE) + +// use inline assembler +#if defined(__i386__) || defined(__x86_64__) +#define CATCH_TRAP() __asm__("int $3") +#elif defined(__aarch64__) +#define CATCH_TRAP() __asm__(".inst 0xd4200000") +#elif defined(__arm__) && !defined(__thumb__) +#define CATCH_TRAP() __asm__(".inst 0xe7f001f0") +#elif defined(__arm__) && defined(__thumb__) +#define CATCH_TRAP() __asm__(".inst 0xde01") +#endif + +#elif defined(CATCH_PLATFORM_LINUX) +// If we can use inline assembler, do it because this allows us to break +// directly at the location of the failing check instead of breaking inside +// raise() called from it, i.e. one stack frame below. +#if defined(__GNUC__) && (defined(__i386) || defined(__x86_64)) +#define CATCH_TRAP() asm volatile("int $3") /* NOLINT */ +#else // Fall back to the generic way. +#include + +#define CATCH_TRAP() raise(SIGTRAP) +#endif +#elif defined(_MSC_VER) +#define CATCH_TRAP() __debugbreak() +#elif defined(__MINGW32__) +extern "C" __declspec(dllimport) void __stdcall DebugBreak(); +#define CATCH_TRAP() DebugBreak() +#endif + +#ifndef CATCH_BREAK_INTO_DEBUGGER +#ifdef CATCH_TRAP +#define CATCH_BREAK_INTO_DEBUGGER() \ + [] { \ + if (Catch::isDebuggerActive()) { \ + CATCH_TRAP(); \ + } \ + }() +#else +#define CATCH_BREAK_INTO_DEBUGGER() [] {}() +#endif +#endif + +// end catch_debugger.h +// start catch_run_context.h + +// start catch_fatal_condition.h + +#include + +namespace Catch { + +// Wrapper for platform-specific fatal error (signals/SEH) handlers +// +// Tries to be cooperative with other handlers, and not step over +// other handlers. This means that unknown structured exceptions +// are passed on, previous signal handlers are called, and so on. +// +// Can only be instantiated once, and assumes that once a signal +// is caught, the binary will end up terminating. Thus, there +class FatalConditionHandler { + bool m_started = false; + + // Install/disengage implementation for specific platform. + // Should be if-defed to work on current platform, can assume + // engage-disengage 1:1 pairing. + void engage_platform(); + void disengage_platform(); + +public: + // Should also have platform-specific implementations as needed + FatalConditionHandler(); + ~FatalConditionHandler(); + + void engage() + { + assert(!m_started && "Handler cannot be installed twice."); + m_started = true; + engage_platform(); + } + + void disengage() + { + assert(m_started && + "Handler cannot be uninstalled without being installed first"); + m_started = false; + disengage_platform(); + } +}; + +//! Simple RAII guard for (dis)engaging the FatalConditionHandler +class FatalConditionHandlerGuard { + FatalConditionHandler *m_handler; + +public: + FatalConditionHandlerGuard(FatalConditionHandler *handler) + : m_handler(handler) + { + m_handler->engage(); + } + ~FatalConditionHandlerGuard() { m_handler->disengage(); } +}; + +} // end namespace Catch + +// end catch_fatal_condition.h +#include + +namespace Catch { + +struct IMutableContext; + +/////////////////////////////////////////////////////////////////////////// + +class RunContext : public IResultCapture, public IRunner { + +public: + RunContext(RunContext const &) = delete; + RunContext &operator=(RunContext const &) = delete; + + explicit RunContext(IConfigPtr const &_config, + IStreamingReporterPtr &&reporter); + + ~RunContext() override; + + void testGroupStarting(std::string const &testSpec, + std::size_t groupIndex, std::size_t groupsCount); + void testGroupEnded(std::string const &testSpec, Totals const &totals, + std::size_t groupIndex, std::size_t groupsCount); + + Totals runTest(TestCase const &testCase); + + IConfigPtr config() const; + IStreamingReporter &reporter() const; + +public: // IResultCapture + // Assertion handlers + void handleExpr(AssertionInfo const &info, + ITransientExpression const &expr, + AssertionReaction &reaction) override; + void handleMessage(AssertionInfo const &info, + ResultWas::OfType resultType, + StringRef const &message, + AssertionReaction &reaction) override; + void handleUnexpectedExceptionNotThrown( + AssertionInfo const &info, + AssertionReaction &reaction) override; + void + handleUnexpectedInflightException(AssertionInfo const &info, + std::string const &message, + AssertionReaction &reaction) override; + void handleIncomplete(AssertionInfo const &info) override; + void handleNonExpr(AssertionInfo const &info, + ResultWas::OfType resultType, + AssertionReaction &reaction) override; + + bool sectionStarted(SectionInfo const §ionInfo, + Counts &assertions) override; + + void sectionEnded(SectionEndInfo const &endInfo) override; + void sectionEndedEarly(SectionEndInfo const &endInfo) override; + + auto acquireGeneratorTracker(StringRef generatorName, + SourceLineInfo const &lineInfo) + -> IGeneratorTracker & override; + +#if defined(CATCH_CONFIG_ENABLE_BENCHMARKING) + void benchmarkPreparing(std::string const &name) override; + void benchmarkStarting(BenchmarkInfo const &info) override; + void benchmarkEnded(BenchmarkStats<> const &stats) override; + void benchmarkFailed(std::string const &error) override; +#endif // CATCH_CONFIG_ENABLE_BENCHMARKING + + void pushScopedMessage(MessageInfo const &message) override; + void popScopedMessage(MessageInfo const &message) override; + + void emplaceUnscopedMessage(MessageBuilder const &builder) override; + + std::string getCurrentTestName() const override; + + const AssertionResult *getLastResult() const override; + + void exceptionEarlyReported() override; + + void handleFatalErrorCondition(StringRef message) override; + + bool lastAssertionPassed() override; + + void assertionPassed() override; + +public: + // !TBD We need to do this another way! + bool aborting() const final; + +private: + void runCurrentTest(std::string &redirectedCout, + std::string &redirectedCerr); + void invokeActiveTestCase(); + + void resetAssertionInfo(); + bool testForMissingAssertions(Counts &assertions); + + void assertionEnded(AssertionResult const &result); + void reportExpr(AssertionInfo const &info, ResultWas::OfType resultType, + ITransientExpression const *expr, bool negated); + + void populateReaction(AssertionReaction &reaction); + +private: + void handleUnfinishedSections(); + + TestRunInfo m_runInfo; + IMutableContext &m_context; + TestCase const *m_activeTestCase = nullptr; + ITracker *m_testCaseTracker = nullptr; + Option m_lastResult; + + IConfigPtr m_config; + Totals m_totals; + IStreamingReporterPtr m_reporter; + std::vector m_messages; + std::vector + m_messageScopes; /* Keeps owners of so-called unscoped messages. */ + AssertionInfo m_lastAssertionInfo; + std::vector m_unfinishedSections; + std::vector m_activeSections; + TrackerContext m_trackerContext; + FatalConditionHandler m_fatalConditionhandler; + bool m_lastAssertionPassed = false; + bool m_shouldReportUnexpected = true; + bool m_includeSuccessfulResults; +}; + +void seedRng(IConfig const &config); +unsigned int rngSeed(); +} // end namespace Catch + +// end catch_run_context.h +namespace Catch { + +namespace { +auto operator<<(std::ostream &os, ITransientExpression const &expr) + -> std::ostream & +{ + expr.streamReconstructedExpression(os); + return os; +} +} + +LazyExpression::LazyExpression(bool isNegated) : m_isNegated(isNegated) {} + +LazyExpression::LazyExpression(LazyExpression const &other) + : m_isNegated(other.m_isNegated) +{ +} + +LazyExpression::operator bool() const +{ + return m_transientExpression != nullptr; +} + +auto operator<<(std::ostream &os, LazyExpression const &lazyExpr) + -> std::ostream & +{ + if (lazyExpr.m_isNegated) + os << "!"; + + if (lazyExpr) { + if (lazyExpr.m_isNegated && + lazyExpr.m_transientExpression->isBinaryExpression()) + os << "(" << *lazyExpr.m_transientExpression << ")"; + else + os << *lazyExpr.m_transientExpression; + } else { + os << "{** error - unchecked empty expression requested **}"; + } + return os; +} + +AssertionHandler::AssertionHandler(StringRef const ¯oName, + SourceLineInfo const &lineInfo, + StringRef capturedExpression, + ResultDisposition::Flags resultDisposition) + : m_assertionInfo{macroName, lineInfo, capturedExpression, + resultDisposition}, + m_resultCapture(getResultCapture()) +{ +} + +void AssertionHandler::handleExpr(ITransientExpression const &expr) +{ + m_resultCapture.handleExpr(m_assertionInfo, expr, m_reaction); +} +void AssertionHandler::handleMessage(ResultWas::OfType resultType, + StringRef const &message) +{ + m_resultCapture.handleMessage(m_assertionInfo, resultType, message, + m_reaction); +} + +auto AssertionHandler::allowThrows() const -> bool +{ + return getCurrentContext().getConfig()->allowThrows(); +} + +void AssertionHandler::complete() +{ + setCompleted(); + if (m_reaction.shouldDebugBreak) { + + // If you find your debugger stopping you here then go one level up on the + // call-stack for the code that caused it (typically a failed assertion) + + // (To go back to the test and change execution, jump over the throw, next) + CATCH_BREAK_INTO_DEBUGGER(); + } + if (m_reaction.shouldThrow) { +#if !defined(CATCH_CONFIG_DISABLE_EXCEPTIONS) + throw Catch::TestFailureException(); +#else + CATCH_ERROR("Test failure requires aborting test!"); +#endif + } +} +void AssertionHandler::setCompleted() +{ + m_completed = true; +} + +void AssertionHandler::handleUnexpectedInflightException() +{ + m_resultCapture.handleUnexpectedInflightException( + m_assertionInfo, Catch::translateActiveException(), m_reaction); +} + +void AssertionHandler::handleExceptionThrownAsExpected() +{ + m_resultCapture.handleNonExpr(m_assertionInfo, ResultWas::Ok, + m_reaction); +} +void AssertionHandler::handleExceptionNotThrownAsExpected() +{ + m_resultCapture.handleNonExpr(m_assertionInfo, ResultWas::Ok, + m_reaction); +} + +void AssertionHandler::handleUnexpectedExceptionNotThrown() +{ + m_resultCapture.handleUnexpectedExceptionNotThrown(m_assertionInfo, + m_reaction); +} + +void AssertionHandler::handleThrowingCallSkipped() +{ + m_resultCapture.handleNonExpr(m_assertionInfo, ResultWas::Ok, + m_reaction); +} + +// This is the overload that takes a string and infers the Equals matcher from it +// The more general overload, that takes any string matcher, is in catch_capture_matchers.cpp +void handleExceptionMatchExpr(AssertionHandler &handler, std::string const &str, + StringRef const &matcherString) +{ + handleExceptionMatchExpr(handler, Matchers::Equals(str), matcherString); +} + +} // namespace Catch +// end catch_assertionhandler.cpp +// start catch_assertionresult.cpp + +namespace Catch { +AssertionResultData::AssertionResultData(ResultWas::OfType _resultType, + LazyExpression const &_lazyExpression) + : lazyExpression(_lazyExpression), resultType(_resultType) +{ +} + +std::string AssertionResultData::reconstructExpression() const +{ + + if (reconstructedExpression.empty()) { + if (lazyExpression) { + ReusableStringStream rss; + rss << lazyExpression; + reconstructedExpression = rss.str(); + } + } + return reconstructedExpression; +} + +AssertionResult::AssertionResult(AssertionInfo const &info, + AssertionResultData const &data) + : m_info(info), m_resultData(data) +{ +} + +// Result was a success +bool AssertionResult::succeeded() const +{ + return Catch::isOk(m_resultData.resultType); +} + +// Result was a success, or failure is suppressed +bool AssertionResult::isOk() const +{ + return Catch::isOk(m_resultData.resultType) || + shouldSuppressFailure(m_info.resultDisposition); +} + +ResultWas::OfType AssertionResult::getResultType() const +{ + return m_resultData.resultType; +} + +bool AssertionResult::hasExpression() const +{ + return !m_info.capturedExpression.empty(); +} + +bool AssertionResult::hasMessage() const +{ + return !m_resultData.message.empty(); +} + +std::string AssertionResult::getExpression() const +{ + // Possibly overallocating by 3 characters should be basically free + std::string expr; + expr.reserve(m_info.capturedExpression.size() + 3); + if (isFalseTest(m_info.resultDisposition)) { + expr += "!("; + } + expr += m_info.capturedExpression; + if (isFalseTest(m_info.resultDisposition)) { + expr += ')'; + } + return expr; +} + +std::string AssertionResult::getExpressionInMacro() const +{ + std::string expr; + if (m_info.macroName.empty()) + expr = static_cast(m_info.capturedExpression); + else { + expr.reserve(m_info.macroName.size() + + m_info.capturedExpression.size() + 4); + expr += m_info.macroName; + expr += "( "; + expr += m_info.capturedExpression; + expr += " )"; + } + return expr; +} + +bool AssertionResult::hasExpandedExpression() const +{ + return hasExpression() && getExpandedExpression() != getExpression(); +} + +std::string AssertionResult::getExpandedExpression() const +{ + std::string expr = m_resultData.reconstructExpression(); + return expr.empty() ? getExpression() : expr; +} + +std::string AssertionResult::getMessage() const +{ + return m_resultData.message; +} +SourceLineInfo AssertionResult::getSourceInfo() const +{ + return m_info.lineInfo; +} + +StringRef AssertionResult::getTestMacroName() const +{ + return m_info.macroName; +} + +} // end namespace Catch +// end catch_assertionresult.cpp +// start catch_capture_matchers.cpp + +namespace Catch { + +using StringMatcher = Matchers::Impl::MatcherBase; + +// This is the general overload that takes a any string matcher +// There is another overload, in catch_assertionhandler.h/.cpp, that only takes a string and infers +// the Equals matcher (so the header does not mention matchers) +void handleExceptionMatchExpr(AssertionHandler &handler, + StringMatcher const &matcher, + StringRef const &matcherString) +{ + std::string exceptionMessage = Catch::translateActiveException(); + MatchExpr expr( + exceptionMessage, matcher, matcherString); + handler.handleExpr(expr); +} + +} // namespace Catch +// end catch_capture_matchers.cpp +// start catch_commandline.cpp + +// start catch_commandline.h + +// start catch_clara.h + +// Use Catch's value for console width (store Clara's off to the side, if present) +#ifdef CLARA_CONFIG_CONSOLE_WIDTH +#define CATCH_TEMP_CLARA_CONFIG_CONSOLE_WIDTH \ + CATCH_CLARA_TEXTFLOW_CONFIG_CONSOLE_WIDTH +#undef CATCH_CLARA_TEXTFLOW_CONFIG_CONSOLE_WIDTH +#endif +#define CATCH_CLARA_TEXTFLOW_CONFIG_CONSOLE_WIDTH CATCH_CONFIG_CONSOLE_WIDTH - 1 + +#ifdef __clang__ +#pragma clang diagnostic push +#pragma clang diagnostic ignored "-Wweak-vtables" +#pragma clang diagnostic ignored "-Wexit-time-destructors" +#pragma clang diagnostic ignored "-Wshadow" +#endif + +// start clara.hpp +// Copyright 2017 Two Blue Cubes Ltd. All rights reserved. +// +// Distributed under the Boost Software License, Version 1.0. (See accompanying +// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) +// +// See https://github.com/philsquared/Clara for more details + +// Clara v1.1.5 + +#ifndef CATCH_CLARA_CONFIG_CONSOLE_WIDTH +#define CATCH_CLARA_CONFIG_CONSOLE_WIDTH 80 +#endif + +#ifndef CATCH_CLARA_TEXTFLOW_CONFIG_CONSOLE_WIDTH +#define CATCH_CLARA_TEXTFLOW_CONFIG_CONSOLE_WIDTH \ + CATCH_CLARA_CONFIG_CONSOLE_WIDTH +#endif + +#ifndef CLARA_CONFIG_OPTIONAL_TYPE +#ifdef __has_include +#if __has_include() && __cplusplus >= 201703L +#include +#define CLARA_CONFIG_OPTIONAL_TYPE std::optional +#endif +#endif +#endif + +// ----------- #included from clara_textflow.hpp ----------- + +// TextFlowCpp +// +// A single-header library for wrapping and laying out basic text, by Phil Nash +// +// Distributed under the Boost Software License, Version 1.0. (See accompanying +// file LICENSE.txt or copy at http://www.boost.org/LICENSE_1_0.txt) +// +// This project is hosted at https://github.com/philsquared/textflowcpp + +#include +#include +#include +#include + +#ifndef CATCH_CLARA_TEXTFLOW_CONFIG_CONSOLE_WIDTH +#define CATCH_CLARA_TEXTFLOW_CONFIG_CONSOLE_WIDTH 80 +#endif + +namespace Catch { +namespace clara { +namespace TextFlow { + +inline auto isWhitespace(char c) -> bool +{ + static std::string chars = " \t\n\r"; + return chars.find(c) != std::string::npos; +} +inline auto isBreakableBefore(char c) -> bool +{ + static std::string chars = "[({<|"; + return chars.find(c) != std::string::npos; +} +inline auto isBreakableAfter(char c) -> bool +{ + static std::string chars = "])}>.,:;*+-=&/\\"; + return chars.find(c) != std::string::npos; +} + +class Columns; + +class Column { + std::vector m_strings; + size_t m_width = CATCH_CLARA_TEXTFLOW_CONFIG_CONSOLE_WIDTH; + size_t m_indent = 0; + size_t m_initialIndent = std::string::npos; + +public: + class iterator { + friend Column; + + Column const &m_column; + size_t m_stringIndex = 0; + size_t m_pos = 0; + + size_t m_len = 0; + size_t m_end = 0; + bool m_suffix = false; + + iterator(Column const &column, size_t stringIndex) + : m_column(column), m_stringIndex(stringIndex) + { + } + + auto line() const -> std::string const & + { + return m_column.m_strings[m_stringIndex]; + } + + auto isBoundary(size_t at) const -> bool + { + assert(at > 0); + assert(at <= line().size()); + + return at == line().size() || + (isWhitespace(line()[at]) && + !isWhitespace(line()[at - 1])) || + isBreakableBefore(line()[at]) || + isBreakableAfter(line()[at - 1]); + } + + void calcLength() + { + assert(m_stringIndex < m_column.m_strings.size()); + + m_suffix = false; + auto width = m_column.m_width - indent(); + m_end = m_pos; + if (line()[m_pos] == '\n') { + ++m_end; + } + while (m_end < line().size() && line()[m_end] != '\n') + ++m_end; + + if (m_end < m_pos + width) { + m_len = m_end - m_pos; + } else { + size_t len = width; + while (len > 0 && !isBoundary(m_pos + len)) + --len; + while (len > 0 && + isWhitespace(line()[m_pos + len - 1])) + --len; + + if (len > 0) { + m_len = len; + } else { + m_suffix = true; + m_len = width - 1; + } + } + } + + auto indent() const -> size_t + { + auto initial = m_pos == 0 && m_stringIndex == 0 + ? m_column.m_initialIndent + : std::string::npos; + return initial == std::string::npos ? m_column.m_indent + : initial; + } + + auto addIndentAndSuffix(std::string const &plain) const + -> std::string + { + return std::string(indent(), ' ') + + (m_suffix ? plain + "-" : plain); + } + + public: + using difference_type = std::ptrdiff_t; + using value_type = std::string; + using pointer = value_type *; + using reference = value_type &; + using iterator_category = std::forward_iterator_tag; + + explicit iterator(Column const &column) : m_column(column) + { + assert(m_column.m_width > m_column.m_indent); + assert(m_column.m_initialIndent == std::string::npos || + m_column.m_width > m_column.m_initialIndent); + calcLength(); + if (m_len == 0) + m_stringIndex++; // Empty string + } + + auto operator*() const -> std::string + { + assert(m_stringIndex < m_column.m_strings.size()); + assert(m_pos <= m_end); + return addIndentAndSuffix(line().substr(m_pos, m_len)); + } + + auto operator++() -> iterator & + { + m_pos += m_len; + if (m_pos < line().size() && line()[m_pos] == '\n') + m_pos += 1; + else + while (m_pos < line().size() && + isWhitespace(line()[m_pos])) + ++m_pos; + + if (m_pos == line().size()) { + m_pos = 0; + ++m_stringIndex; + } + if (m_stringIndex < m_column.m_strings.size()) + calcLength(); + return *this; + } + auto operator++(int) -> iterator + { + iterator prev(*this); + operator++(); + return prev; + } + + auto operator==(iterator const &other) const -> bool + { + return m_pos == other.m_pos && + m_stringIndex == other.m_stringIndex && + &m_column == &other.m_column; + } + auto operator!=(iterator const &other) const -> bool + { + return !operator==(other); + } + }; + using const_iterator = iterator; + + explicit Column(std::string const &text) { m_strings.push_back(text); } + + auto width(size_t newWidth) -> Column & + { + assert(newWidth > 0); + m_width = newWidth; + return *this; + } + auto indent(size_t newIndent) -> Column & + { + m_indent = newIndent; + return *this; + } + auto initialIndent(size_t newIndent) -> Column & + { + m_initialIndent = newIndent; + return *this; + } + + auto width() const -> size_t { return m_width; } + auto begin() const -> iterator { return iterator(*this); } + auto end() const -> iterator { return {*this, m_strings.size()}; } + + inline friend std::ostream &operator<<(std::ostream &os, + Column const &col) + { + bool first = true; + for (auto line : col) { + if (first) + first = false; + else + os << "\n"; + os << line; + } + return os; + } + + auto operator+(Column const &other) -> Columns; + + auto toString() const -> std::string + { + std::ostringstream oss; + oss << *this; + return oss.str(); + } +}; + +class Spacer : public Column { + +public: + explicit Spacer(size_t spaceWidth) : Column("") { width(spaceWidth); } +}; + +class Columns { + std::vector m_columns; + +public: + class iterator { + friend Columns; + struct EndTag { + }; + + std::vector const &m_columns; + std::vector m_iterators; + size_t m_activeIterators; + + iterator(Columns const &columns, EndTag) + : m_columns(columns.m_columns), m_activeIterators(0) + { + m_iterators.reserve(m_columns.size()); + + for (auto const &col : m_columns) + m_iterators.push_back(col.end()); + } + + public: + using difference_type = std::ptrdiff_t; + using value_type = std::string; + using pointer = value_type *; + using reference = value_type &; + using iterator_category = std::forward_iterator_tag; + + explicit iterator(Columns const &columns) + : m_columns(columns.m_columns), + m_activeIterators(m_columns.size()) + { + m_iterators.reserve(m_columns.size()); + + for (auto const &col : m_columns) + m_iterators.push_back(col.begin()); + } + + auto operator==(iterator const &other) const -> bool + { + return m_iterators == other.m_iterators; + } + auto operator!=(iterator const &other) const -> bool + { + return m_iterators != other.m_iterators; + } + auto operator*() const -> std::string + { + std::string row, padding; + + for (size_t i = 0; i < m_columns.size(); ++i) { + auto width = m_columns[i].width(); + if (m_iterators[i] != m_columns[i].end()) { + std::string col = *m_iterators[i]; + row += padding + col; + if (col.size() < width) + padding = std::string( + width - col.size(), + ' '); + else + padding = ""; + } else { + padding += std::string(width, ' '); + } + } + return row; + } + auto operator++() -> iterator & + { + for (size_t i = 0; i < m_columns.size(); ++i) { + if (m_iterators[i] != m_columns[i].end()) + ++m_iterators[i]; + } + return *this; + } + auto operator++(int) -> iterator + { + iterator prev(*this); + operator++(); + return prev; + } + }; + using const_iterator = iterator; + + auto begin() const -> iterator { return iterator(*this); } + auto end() const -> iterator { return {*this, iterator::EndTag()}; } + + auto operator+=(Column const &col) -> Columns & + { + m_columns.push_back(col); + return *this; + } + auto operator+(Column const &col) -> Columns + { + Columns combined = *this; + combined += col; + return combined; + } + + inline friend std::ostream &operator<<(std::ostream &os, + Columns const &cols) + { + + bool first = true; + for (auto line : cols) { + if (first) + first = false; + else + os << "\n"; + os << line; + } + return os; + } + + auto toString() const -> std::string + { + std::ostringstream oss; + oss << *this; + return oss.str(); + } +}; + +inline auto Column::operator+(Column const &other) -> Columns +{ + Columns cols; + cols += *this; + cols += other; + return cols; +} +} + +} +} + +// ----------- end of #include from clara_textflow.hpp ----------- +// ........... back in clara.hpp + +#include +#include +#include +#include +#include + +#if !defined(CATCH_PLATFORM_WINDOWS) && \ + (defined(WIN32) || defined(__WIN32__) || defined(_WIN32) || \ + defined(_MSC_VER)) +#define CATCH_PLATFORM_WINDOWS +#endif + +namespace Catch { +namespace clara { +namespace detail { + +// Traits for extracting arg and return type of lambdas (for single argument lambdas) +template +struct UnaryLambdaTraits : UnaryLambdaTraits { +}; + +template +struct UnaryLambdaTraits { + static const bool isValid = false; +}; + +template +struct UnaryLambdaTraits { + static const bool isValid = true; + using ArgType = typename std::remove_const< + typename std::remove_reference::type>::type; + using ReturnType = ReturnT; +}; + +class TokenStream; + +// Transport for raw args (copied from main args, or supplied via init list for testing) +class Args { + friend TokenStream; + std::string m_exeName; + std::vector m_args; + +public: + Args(int argc, char const *const *argv) + : m_exeName(argv[0]), m_args(argv + 1, argv + argc) + { + } + + Args(std::initializer_list args) + : m_exeName(*args.begin()), m_args(args.begin() + 1, args.end()) + { + } + + auto exeName() const -> std::string { return m_exeName; } +}; + +// Wraps a token coming from a token stream. These may not directly correspond to strings as a single string +// may encode an option + its argument if the : or = form is used +enum class TokenType { Option, Argument }; +struct Token { + TokenType type; + std::string token; +}; + +inline auto isOptPrefix(char c) -> bool +{ + return c == '-' +#ifdef CATCH_PLATFORM_WINDOWS + || c == '/' +#endif + ; +} + +// Abstracts iterators into args as a stream of tokens, with option arguments uniformly handled +class TokenStream { + using Iterator = std::vector::const_iterator; + Iterator it; + Iterator itEnd; + std::vector m_tokenBuffer; + + void loadBuffer() + { + m_tokenBuffer.resize(0); + + // Skip any empty strings + while (it != itEnd && it->empty()) + ++it; + + if (it != itEnd) { + auto const &next = *it; + if (isOptPrefix(next[0])) { + auto delimiterPos = next.find_first_of(" :="); + if (delimiterPos != std::string::npos) { + m_tokenBuffer.push_back( + {TokenType::Option, + next.substr(0, delimiterPos)}); + m_tokenBuffer.push_back( + {TokenType::Argument, + next.substr(delimiterPos + 1)}); + } else { + if (next[1] != '-' && next.size() > 2) { + std::string opt = "- "; + for (size_t i = 1; + i < next.size(); ++i) { + opt[1] = next[i]; + m_tokenBuffer.push_back( + {TokenType::Option, + opt}); + } + } else { + m_tokenBuffer.push_back( + {TokenType::Option, + next}); + } + } + } else { + m_tokenBuffer.push_back( + {TokenType::Argument, next}); + } + } + } + +public: + explicit TokenStream(Args const &args) + : TokenStream(args.m_args.begin(), args.m_args.end()) + { + } + + TokenStream(Iterator it, Iterator itEnd) : it(it), itEnd(itEnd) + { + loadBuffer(); + } + + explicit operator bool() const + { + return !m_tokenBuffer.empty() || it != itEnd; + } + + auto count() const -> size_t + { + return m_tokenBuffer.size() + (itEnd - it); + } + + auto operator*() const -> Token + { + assert(!m_tokenBuffer.empty()); + return m_tokenBuffer.front(); + } + + auto operator->() const -> Token const * + { + assert(!m_tokenBuffer.empty()); + return &m_tokenBuffer.front(); + } + + auto operator++() -> TokenStream & + { + if (m_tokenBuffer.size() >= 2) { + m_tokenBuffer.erase(m_tokenBuffer.begin()); + } else { + if (it != itEnd) + ++it; + loadBuffer(); + } + return *this; + } +}; + +class ResultBase { +public: + enum Type { Ok, LogicError, RuntimeError }; + +protected: + ResultBase(Type type) : m_type(type) {} + virtual ~ResultBase() = default; + + virtual void enforceOk() const = 0; + + Type m_type; +}; + +template class ResultValueBase : public ResultBase { +public: + auto value() const -> T const & + { + enforceOk(); + return m_value; + } + +protected: + ResultValueBase(Type type) : ResultBase(type) {} + + ResultValueBase(ResultValueBase const &other) : ResultBase(other) + { + if (m_type == ResultBase::Ok) + new (&m_value) T(other.m_value); + } + + ResultValueBase(Type, T const &value) : ResultBase(Ok) + { + new (&m_value) T(value); + } + + auto operator=(ResultValueBase const &other) -> ResultValueBase & + { + if (m_type == ResultBase::Ok) + m_value.~T(); + ResultBase::operator=(other); + if (m_type == ResultBase::Ok) + new (&m_value) T(other.m_value); + return *this; + } + + ~ResultValueBase() override + { + if (m_type == Ok) + m_value.~T(); + } + + union { + T m_value; + }; +}; + +template<> class ResultValueBase : public ResultBase { +protected: + using ResultBase::ResultBase; +}; + +template class BasicResult : public ResultValueBase { +public: + template + explicit BasicResult(BasicResult const &other) + : ResultValueBase(other.type()), + m_errorMessage(other.errorMessage()) + { + assert(type() != ResultBase::Ok); + } + + template static auto ok(U const &value) -> BasicResult + { + return {ResultBase::Ok, value}; + } + static auto ok() -> BasicResult { return {ResultBase::Ok}; } + static auto logicError(std::string const &message) -> BasicResult + { + return {ResultBase::LogicError, message}; + } + static auto runtimeError(std::string const &message) -> BasicResult + { + return {ResultBase::RuntimeError, message}; + } + + explicit operator bool() const { return m_type == ResultBase::Ok; } + auto type() const -> ResultBase::Type { return m_type; } + auto errorMessage() const -> std::string { return m_errorMessage; } + +protected: + void enforceOk() const override + { + + // Errors shouldn't reach this point, but if they do + // the actual error message will be in m_errorMessage + assert(m_type != ResultBase::LogicError); + assert(m_type != ResultBase::RuntimeError); + if (m_type != ResultBase::Ok) + std::abort(); + } + + std::string m_errorMessage; // Only populated if resultType is an error + + BasicResult(ResultBase::Type type, std::string const &message) + : ResultValueBase(type), m_errorMessage(message) + { + assert(m_type != ResultBase::Ok); + } + + using ResultValueBase::ResultValueBase; + using ResultBase::m_type; +}; + +enum class ParseResultType { + Matched, + NoMatch, + ShortCircuitAll, + ShortCircuitSame +}; + +class ParseState { +public: + ParseState(ParseResultType type, TokenStream const &remainingTokens) + : m_type(type), m_remainingTokens(remainingTokens) + { + } + + auto type() const -> ParseResultType { return m_type; } + auto remainingTokens() const -> TokenStream + { + return m_remainingTokens; + } + +private: + ParseResultType m_type; + TokenStream m_remainingTokens; +}; + +using Result = BasicResult; +using ParserResult = BasicResult; +using InternalParseResult = BasicResult; + +struct HelpColumns { + std::string left; + std::string right; +}; + +template +inline auto convertInto(std::string const &source, T &target) -> ParserResult +{ + std::stringstream ss; + ss << source; + ss >> target; + if (ss.fail()) + return ParserResult::runtimeError("Unable to convert '" + + source + + "' to destination type"); + else + return ParserResult::ok(ParseResultType::Matched); +} +inline auto convertInto(std::string const &source, std::string &target) + -> ParserResult +{ + target = source; + return ParserResult::ok(ParseResultType::Matched); +} +inline auto convertInto(std::string const &source, bool &target) -> ParserResult +{ + std::string srcLC = source; + std::transform(srcLC.begin(), srcLC.end(), srcLC.begin(), + [](unsigned char c) { + return static_cast(std::tolower(c)); + }); + if (srcLC == "y" || srcLC == "1" || srcLC == "true" || srcLC == "yes" || + srcLC == "on") + target = true; + else if (srcLC == "n" || srcLC == "0" || srcLC == "false" || + srcLC == "no" || srcLC == "off") + target = false; + else + return ParserResult::runtimeError( + "Expected a boolean value but did not recognise: '" + + source + "'"); + return ParserResult::ok(ParseResultType::Matched); +} +#ifdef CLARA_CONFIG_OPTIONAL_TYPE +template +inline auto convertInto(std::string const &source, + CLARA_CONFIG_OPTIONAL_TYPE &target) -> ParserResult +{ + T temp; + auto result = convertInto(source, temp); + if (result) + target = std::move(temp); + return result; +} +#endif // CLARA_CONFIG_OPTIONAL_TYPE + +struct NonCopyable { + NonCopyable() = default; + NonCopyable(NonCopyable const &) = delete; + NonCopyable(NonCopyable &&) = delete; + NonCopyable &operator=(NonCopyable const &) = delete; + NonCopyable &operator=(NonCopyable &&) = delete; +}; + +struct BoundRef : NonCopyable { + virtual ~BoundRef() = default; + virtual auto isContainer() const -> bool { return false; } + virtual auto isFlag() const -> bool { return false; } +}; +struct BoundValueRefBase : BoundRef { + virtual auto setValue(std::string const &arg) -> ParserResult = 0; +}; +struct BoundFlagRefBase : BoundRef { + virtual auto setFlag(bool flag) -> ParserResult = 0; + virtual auto isFlag() const -> bool { return true; } +}; + +template struct BoundValueRef : BoundValueRefBase { + T &m_ref; + + explicit BoundValueRef(T &ref) : m_ref(ref) {} + + auto setValue(std::string const &arg) -> ParserResult override + { + return convertInto(arg, m_ref); + } +}; + +template struct BoundValueRef> : BoundValueRefBase { + std::vector &m_ref; + + explicit BoundValueRef(std::vector &ref) : m_ref(ref) {} + + auto isContainer() const -> bool override { return true; } + + auto setValue(std::string const &arg) -> ParserResult override + { + T temp; + auto result = convertInto(arg, temp); + if (result) + m_ref.push_back(temp); + return result; + } +}; + +struct BoundFlagRef : BoundFlagRefBase { + bool &m_ref; + + explicit BoundFlagRef(bool &ref) : m_ref(ref) {} + + auto setFlag(bool flag) -> ParserResult override + { + m_ref = flag; + return ParserResult::ok(ParseResultType::Matched); + } +}; + +template struct LambdaInvoker { + static_assert(std::is_same::value, + "Lambda must return void or clara::ParserResult"); + + template + static auto invoke(L const &lambda, ArgType const &arg) -> ParserResult + { + return lambda(arg); + } +}; + +template<> struct LambdaInvoker { + template + static auto invoke(L const &lambda, ArgType const &arg) -> ParserResult + { + lambda(arg); + return ParserResult::ok(ParseResultType::Matched); + } +}; + +template +inline auto invokeLambda(L const &lambda, std::string const &arg) + -> ParserResult +{ + ArgType temp{}; + auto result = convertInto(arg, temp); + return !result ? result + : LambdaInvoker::ReturnType>::invoke(lambda, temp); +} + +template struct BoundLambda : BoundValueRefBase { + L m_lambda; + + static_assert(UnaryLambdaTraits::isValid, + "Supplied lambda must take exactly one argument"); + explicit BoundLambda(L const &lambda) : m_lambda(lambda) {} + + auto setValue(std::string const &arg) -> ParserResult override + { + return invokeLambda::ArgType>( + m_lambda, arg); + } +}; + +template struct BoundFlagLambda : BoundFlagRefBase { + L m_lambda; + + static_assert(UnaryLambdaTraits::isValid, + "Supplied lambda must take exactly one argument"); + static_assert(std::is_same::ArgType, + bool>::value, + "flags must be boolean"); + + explicit BoundFlagLambda(L const &lambda) : m_lambda(lambda) {} + + auto setFlag(bool flag) -> ParserResult override + { + return LambdaInvoker::ReturnType>::invoke(m_lambda, flag); + } +}; + +enum class Optionality { Optional, Required }; + +struct Parser; + +class ParserBase { +public: + virtual ~ParserBase() = default; + virtual auto validate() const -> Result { return Result::ok(); } + virtual auto parse(std::string const &exeName, + TokenStream const &tokens) const + -> InternalParseResult = 0; + virtual auto cardinality() const -> size_t { return 1; } + + auto parse(Args const &args) const -> InternalParseResult + { + return parse(args.exeName(), TokenStream(args)); + } +}; + +template class ComposableParserImpl : public ParserBase { +public: + template auto operator|(T const &other) const -> Parser; + + template auto operator+(T const &other) const -> Parser; +}; + +// Common code and state for Args and Opts +template +class ParserRefImpl : public ComposableParserImpl { +protected: + Optionality m_optionality = Optionality::Optional; + std::shared_ptr m_ref; + std::string m_hint; + std::string m_description; + + explicit ParserRefImpl(std::shared_ptr const &ref) + : m_ref(ref) + { + } + +public: + template + ParserRefImpl(T &ref, std::string const &hint) + : m_ref(std::make_shared>(ref)), m_hint(hint) + { + } + + template + ParserRefImpl(LambdaT const &ref, std::string const &hint) + : m_ref(std::make_shared>(ref)), + m_hint(hint) + { + } + + auto operator()(std::string const &description) -> DerivedT & + { + m_description = description; + return static_cast(*this); + } + + auto optional() -> DerivedT & + { + m_optionality = Optionality::Optional; + return static_cast(*this); + }; + + auto required() -> DerivedT & + { + m_optionality = Optionality::Required; + return static_cast(*this); + }; + + auto isOptional() const -> bool + { + return m_optionality == Optionality::Optional; + } + + auto cardinality() const -> size_t override + { + if (m_ref->isContainer()) + return 0; + else + return 1; + } + + auto hint() const -> std::string { return m_hint; } +}; + +class ExeName : public ComposableParserImpl { + std::shared_ptr m_name; + std::shared_ptr m_ref; + + template + static auto makeRef(LambdaT const &lambda) + -> std::shared_ptr + { + return std::make_shared>(lambda); + } + +public: + ExeName() : m_name(std::make_shared("")) {} + + explicit ExeName(std::string &ref) : ExeName() + { + m_ref = std::make_shared>(ref); + } + + template + explicit ExeName(LambdaT const &lambda) : ExeName() + { + m_ref = std::make_shared>(lambda); + } + + // The exe name is not parsed out of the normal tokens, but is handled specially + auto parse(std::string const &, TokenStream const &tokens) const + -> InternalParseResult override + { + return InternalParseResult::ok( + ParseState(ParseResultType::NoMatch, tokens)); + } + + auto name() const -> std::string { return *m_name; } + auto set(std::string const &newName) -> ParserResult + { + + auto lastSlash = newName.find_last_of("\\/"); + auto filename = (lastSlash == std::string::npos) + ? newName + : newName.substr(lastSlash + 1); + + *m_name = filename; + if (m_ref) + return m_ref->setValue(filename); + else + return ParserResult::ok(ParseResultType::Matched); + } +}; + +class Arg : public ParserRefImpl { +public: + using ParserRefImpl::ParserRefImpl; + + auto parse(std::string const &, TokenStream const &tokens) const + -> InternalParseResult override + { + auto validationResult = validate(); + if (!validationResult) + return InternalParseResult(validationResult); + + auto remainingTokens = tokens; + auto const &token = *remainingTokens; + if (token.type != TokenType::Argument) + return InternalParseResult::ok(ParseState( + ParseResultType::NoMatch, remainingTokens)); + + assert(!m_ref->isFlag()); + auto valueRef = + static_cast(m_ref.get()); + + auto result = valueRef->setValue(remainingTokens->token); + if (!result) + return InternalParseResult(result); + else + return InternalParseResult::ok(ParseState( + ParseResultType::Matched, ++remainingTokens)); + } +}; + +inline auto normaliseOpt(std::string const &optName) -> std::string +{ +#ifdef CATCH_PLATFORM_WINDOWS + if (optName[0] == '/') + return "-" + optName.substr(1); + else +#endif + return optName; +} + +class Opt : public ParserRefImpl { +protected: + std::vector m_optNames; + +public: + template + explicit Opt(LambdaT const &ref) + : ParserRefImpl(std::make_shared>(ref)) + { + } + + explicit Opt(bool &ref) + : ParserRefImpl(std::make_shared(ref)) + { + } + + template + Opt(LambdaT const &ref, std::string const &hint) + : ParserRefImpl(ref, hint) + { + } + + template + Opt(T &ref, std::string const &hint) : ParserRefImpl(ref, hint) + { + } + + auto operator[](std::string const &optName) -> Opt & + { + m_optNames.push_back(optName); + return *this; + } + + auto getHelpColumns() const -> std::vector + { + std::ostringstream oss; + bool first = true; + for (auto const &opt : m_optNames) { + if (first) + first = false; + else + oss << ", "; + oss << opt; + } + if (!m_hint.empty()) + oss << " <" << m_hint << ">"; + return {{oss.str(), m_description}}; + } + + auto isMatch(std::string const &optToken) const -> bool + { + auto normalisedToken = normaliseOpt(optToken); + for (auto const &name : m_optNames) { + if (normaliseOpt(name) == normalisedToken) + return true; + } + return false; + } + + using ParserBase::parse; + + auto parse(std::string const &, TokenStream const &tokens) const + -> InternalParseResult override + { + auto validationResult = validate(); + if (!validationResult) + return InternalParseResult(validationResult); + + auto remainingTokens = tokens; + if (remainingTokens && + remainingTokens->type == TokenType::Option) { + auto const &token = *remainingTokens; + if (isMatch(token.token)) { + if (m_ref->isFlag()) { + auto flagRef = static_cast< + detail::BoundFlagRefBase *>( + m_ref.get()); + auto result = flagRef->setFlag(true); + if (!result) + return InternalParseResult( + result); + if (result.value() == + ParseResultType::ShortCircuitAll) + return InternalParseResult::ok( + ParseState( + result.value(), + remainingTokens)); + } else { + auto valueRef = static_cast< + detail::BoundValueRefBase *>( + m_ref.get()); + ++remainingTokens; + if (!remainingTokens) + return InternalParseResult::runtimeError( + "Expected argument following " + + token.token); + auto const &argToken = *remainingTokens; + if (argToken.type != + TokenType::Argument) + return InternalParseResult::runtimeError( + "Expected argument following " + + token.token); + auto result = valueRef->setValue( + argToken.token); + if (!result) + return InternalParseResult( + result); + if (result.value() == + ParseResultType::ShortCircuitAll) + return InternalParseResult::ok( + ParseState( + result.value(), + remainingTokens)); + } + return InternalParseResult::ok( + ParseState(ParseResultType::Matched, + ++remainingTokens)); + } + } + return InternalParseResult::ok( + ParseState(ParseResultType::NoMatch, remainingTokens)); + } + + auto validate() const -> Result override + { + if (m_optNames.empty()) + return Result::logicError("No options supplied to Opt"); + for (auto const &name : m_optNames) { + if (name.empty()) + return Result::logicError( + "Option name cannot be empty"); +#ifdef CATCH_PLATFORM_WINDOWS + if (name[0] != '-' && name[0] != '/') + return Result::logicError( + "Option name must begin with '-' or '/'"); +#else + if (name[0] != '-') + return Result::logicError( + "Option name must begin with '-'"); +#endif + } + return ParserRefImpl::validate(); + } +}; + +struct Help : Opt { + Help(bool &showHelpFlag) + : Opt([&](bool flag) { + showHelpFlag = flag; + return ParserResult::ok( + ParseResultType::ShortCircuitAll); + }) + { + static_cast (*this)( + "display usage information")["-?"]["-h"]["--help"] + .optional(); + } +}; + +struct Parser : ParserBase { + + mutable ExeName m_exeName; + std::vector m_options; + std::vector m_args; + + auto operator|=(ExeName const &exeName) -> Parser & + { + m_exeName = exeName; + return *this; + } + + auto operator|=(Arg const &arg) -> Parser & + { + m_args.push_back(arg); + return *this; + } + + auto operator|=(Opt const &opt) -> Parser & + { + m_options.push_back(opt); + return *this; + } + + auto operator|=(Parser const &other) -> Parser & + { + m_options.insert(m_options.end(), other.m_options.begin(), + other.m_options.end()); + m_args.insert(m_args.end(), other.m_args.begin(), + other.m_args.end()); + return *this; + } + + template auto operator|(T const &other) const -> Parser + { + return Parser(*this) |= other; + } + + // Forward deprecated interface with '+' instead of '|' + template auto operator+=(T const &other) -> Parser & + { + return operator|=(other); + } + template auto operator+(T const &other) const -> Parser + { + return operator|(other); + } + + auto getHelpColumns() const -> std::vector + { + std::vector cols; + for (auto const &o : m_options) { + auto childCols = o.getHelpColumns(); + cols.insert(cols.end(), childCols.begin(), + childCols.end()); + } + return cols; + } + + void writeToStream(std::ostream &os) const + { + if (!m_exeName.name().empty()) { + os << "usage:\n" + << " " << m_exeName.name() << " "; + bool required = true, first = true; + for (auto const &arg : m_args) { + if (first) + first = false; + else + os << " "; + if (arg.isOptional() && required) { + os << "["; + required = false; + } + os << "<" << arg.hint() << ">"; + if (arg.cardinality() == 0) + os << " ... "; + } + if (!required) + os << "]"; + if (!m_options.empty()) + os << " options"; + os << "\n\nwhere options are:" << std::endl; + } + + auto rows = getHelpColumns(); + size_t consoleWidth = CATCH_CLARA_CONFIG_CONSOLE_WIDTH; + size_t optWidth = 0; + for (auto const &cols : rows) + optWidth = (std::max)(optWidth, cols.left.size() + 2); + + optWidth = (std::min)(optWidth, consoleWidth / 2); + + for (auto const &cols : rows) { + auto row = TextFlow::Column(cols.left) + .width(optWidth) + .indent(2) + + TextFlow::Spacer(4) + + TextFlow::Column(cols.right) + .width(consoleWidth - 7 - optWidth); + os << row << std::endl; + } + } + + friend auto operator<<(std::ostream &os, Parser const &parser) + -> std::ostream & + { + parser.writeToStream(os); + return os; + } + + auto validate() const -> Result override + { + for (auto const &opt : m_options) { + auto result = opt.validate(); + if (!result) + return result; + } + for (auto const &arg : m_args) { + auto result = arg.validate(); + if (!result) + return result; + } + return Result::ok(); + } + + using ParserBase::parse; + + auto parse(std::string const &exeName, TokenStream const &tokens) const + -> InternalParseResult override + { + + struct ParserInfo { + ParserBase const *parser = nullptr; + size_t count = 0; + }; + const size_t totalParsers = m_options.size() + m_args.size(); + assert(totalParsers < 512); + // ParserInfo parseInfos[totalParsers]; // <-- this is what we really want to do + ParserInfo parseInfos[512]; + + { + size_t i = 0; + for (auto const &opt : m_options) + parseInfos[i++].parser = &opt; + for (auto const &arg : m_args) + parseInfos[i++].parser = &arg; + } + + m_exeName.set(exeName); + + auto result = InternalParseResult::ok( + ParseState(ParseResultType::NoMatch, tokens)); + while (result.value().remainingTokens()) { + bool tokenParsed = false; + + for (size_t i = 0; i < totalParsers; ++i) { + auto &parseInfo = parseInfos[i]; + if (parseInfo.parser->cardinality() == 0 || + parseInfo.count < + parseInfo.parser->cardinality()) { + result = parseInfo.parser->parse( + exeName, + result.value() + .remainingTokens()); + if (!result) + return result; + if (result.value().type() != + ParseResultType::NoMatch) { + tokenParsed = true; + ++parseInfo.count; + break; + } + } + } + + if (result.value().type() == + ParseResultType::ShortCircuitAll) + return result; + if (!tokenParsed) + return InternalParseResult::runtimeError( + "Unrecognised token: " + + result.value().remainingTokens()->token); + } + // !TBD Check missing required options + return result; + } +}; + +template +template +auto ComposableParserImpl::operator|(T const &other) const -> Parser +{ + return Parser() | static_cast(*this) | other; +} +} // namespace detail + +// A Combined parser +using detail::Parser; + +// A parser for options +using detail::Opt; + +// A parser for arguments +using detail::Arg; + +// Wrapper for argc, argv from main() +using detail::Args; + +// Specifies the name of the executable +using detail::ExeName; + +// Convenience wrapper for option parser that specifies the help option +using detail::Help; + +// enum of result types from a parse +using detail::ParseResultType; + +// Result type for parser operation +using detail::ParserResult; + +} +} // namespace Catch::clara + +// end clara.hpp +#ifdef __clang__ +#pragma clang diagnostic pop +#endif + +// Restore Clara's value for console width, if present +#ifdef CATCH_TEMP_CLARA_CONFIG_CONSOLE_WIDTH +#define CATCH_CLARA_TEXTFLOW_CONFIG_CONSOLE_WIDTH \ + CATCH_TEMP_CLARA_CONFIG_CONSOLE_WIDTH +#undef CATCH_TEMP_CLARA_CONFIG_CONSOLE_WIDTH +#endif + +// end catch_clara.h +namespace Catch { + +clara::Parser makeCommandLineParser(ConfigData &config); + +} // end namespace Catch + +// end catch_commandline.h +#include +#include + +namespace Catch { + +clara::Parser makeCommandLineParser(ConfigData &config) +{ + + using namespace clara; + + auto const setWarning = [&](std::string const &warning) { + auto warningSet = [&]() { + if (warning == "NoAssertions") + return WarnAbout::NoAssertions; + + if (warning == "NoTests") + return WarnAbout::NoTests; + + return WarnAbout::Nothing; + }(); + + if (warningSet == WarnAbout::Nothing) + return ParserResult::runtimeError( + "Unrecognised warning: '" + warning + "'"); + config.warnings = static_cast(config.warnings | + warningSet); + return ParserResult::ok(ParseResultType::Matched); + }; + auto const loadTestNamesFromFile = [&](std::string const &filename) { + std::ifstream f(filename.c_str()); + if (!f.is_open()) + return ParserResult::runtimeError( + "Unable to load input file: '" + filename + + "'"); + + std::string line; + while (std::getline(f, line)) { + line = trim(line); + if (!line.empty() && !startsWith(line, '#')) { + if (!startsWith(line, '"')) + line = '"' + line + '"'; + config.testsOrTags.push_back(line); + config.testsOrTags.emplace_back(","); + } + } + //Remove comma in the end + if (!config.testsOrTags.empty()) + config.testsOrTags.erase(config.testsOrTags.end() - 1); + + return ParserResult::ok(ParseResultType::Matched); + }; + auto const setTestOrder = [&](std::string const &order) { + if (startsWith("declared", order)) + config.runOrder = RunTests::InDeclarationOrder; + else if (startsWith("lexical", order)) + config.runOrder = RunTests::InLexicographicalOrder; + else if (startsWith("random", order)) + config.runOrder = RunTests::InRandomOrder; + else + return clara::ParserResult::runtimeError( + "Unrecognised ordering: '" + order + "'"); + return ParserResult::ok(ParseResultType::Matched); + }; + auto const setRngSeed = [&](std::string const &seed) { + if (seed != "time") + return clara::detail::convertInto(seed, config.rngSeed); + config.rngSeed = static_cast(std::time(nullptr)); + return ParserResult::ok(ParseResultType::Matched); + }; + auto const setColourUsage = [&](std::string const &useColour) { + auto mode = toLower(useColour); + + if (mode == "yes") + config.useColour = UseColour::Yes; + else if (mode == "no") + config.useColour = UseColour::No; + else if (mode == "auto") + config.useColour = UseColour::Auto; + else + return ParserResult::runtimeError( + "colour mode must be one of: auto, yes or no. '" + + useColour + "' not recognised"); + return ParserResult::ok(ParseResultType::Matched); + }; + auto const setWaitForKeypress = [&](std::string const &keypress) { + auto keypressLc = toLower(keypress); + if (keypressLc == "never") + config.waitForKeypress = WaitForKeypress::Never; + else if (keypressLc == "start") + config.waitForKeypress = WaitForKeypress::BeforeStart; + else if (keypressLc == "exit") + config.waitForKeypress = WaitForKeypress::BeforeExit; + else if (keypressLc == "both") + config.waitForKeypress = + WaitForKeypress::BeforeStartAndExit; + else + return ParserResult::runtimeError( + "keypress argument must be one of: never, start, exit or both. '" + + keypress + "' not recognised"); + return ParserResult::ok(ParseResultType::Matched); + }; + auto const setVerbosity = [&](std::string const &verbosity) { + auto lcVerbosity = toLower(verbosity); + if (lcVerbosity == "quiet") + config.verbosity = Verbosity::Quiet; + else if (lcVerbosity == "normal") + config.verbosity = Verbosity::Normal; + else if (lcVerbosity == "high") + config.verbosity = Verbosity::High; + else + return ParserResult::runtimeError( + "Unrecognised verbosity, '" + verbosity + "'"); + return ParserResult::ok(ParseResultType::Matched); + }; + auto const setReporter = [&](std::string const &reporter) { + IReporterRegistry::FactoryMap const &factories = + getRegistryHub().getReporterRegistry().getFactories(); + + auto lcReporter = toLower(reporter); + auto result = factories.find(lcReporter); + + if (factories.end() != result) + config.reporterName = lcReporter; + else + return ParserResult::runtimeError( + "Unrecognized reporter, '" + reporter + + "'. Check available with --list-reporters"); + return ParserResult::ok(ParseResultType::Matched); + }; + + auto cli = + ExeName(config.processName) | Help(config.showHelp) | + Opt(config.listTests)["-l"]["--list-tests"]( + "list all/matching test cases") | + Opt(config.listTags)["-t"]["--list-tags"]( + "list all/matching tags") | + Opt(config.showSuccessfulTests)["-s"]["--success"]( + "include successful tests in output") | + Opt(config.shouldDebugBreak)["-b"]["--break"]( + "break into debugger on failure") | + Opt(config.noThrow)["-e"]["--nothrow"]("skip exception tests") | + Opt(config.showInvisibles)["-i"]["--invisibles"]( + "show invisibles (tabs, newlines)") | + Opt(config.outputFilename, + "filename")["-o"]["--out"]("output filename") | + Opt(setReporter, "name")["-r"]["--reporter"]( + "reporter to use (defaults to console)") | + Opt(config.name, "name")["-n"]["--name"]("suite name") | + Opt([&](bool) { config.abortAfter = 1; })["-a"]["--abort"]( + "abort at first failure") | + Opt([&](int x) { config.abortAfter = x; }, + "no. failures")["-x"]["--abortx"]( + "abort after x failures") | + Opt(setWarning, + "warning name")["-w"]["--warn"]("enable warnings") | + Opt( + [&](bool flag) { + config.showDurations = + flag ? ShowDurations::Always + : ShowDurations::Never; + }, + "yes|no")["-d"]["--durations"]("show test durations") | + Opt(config.minDuration, "seconds")["-D"]["--min-duration"]( + "show test durations for tests taking at least the given number of seconds") | + Opt(loadTestNamesFromFile, "filename")["-f"]["--input-file"]( + "load test names to run from a file") | + Opt(config.filenamesAsTags)["-#"]["--filenames-as-tags"]( + "adds a tag for the filename") | + Opt(config.sectionsToRun, "section name")["-c"]["--section"]( + "specify section to run") | + Opt(setVerbosity, "quiet|normal|high")["-v"]["--verbosity"]( + "set output verbosity") | + Opt(config.listTestNamesOnly)["--list-test-names-only"]( + "list all/matching test cases names only") | + Opt(config.listReporters)["--list-reporters"]( + "list all reporters") | + Opt(setTestOrder, "decl|lex|rand")["--order"]( + "test case order (defaults to decl)") | + Opt(setRngSeed, "'time'|number")["--rng-seed"]( + "set a specific seed for random numbers") | + Opt(setColourUsage, + "yes|no")["--use-colour"]("should output be colourised") | + Opt(config.libIdentify)["--libidentify"]( + "report name and version according to libidentify standard") | + Opt(setWaitForKeypress, + "never|start|exit|both")["--wait-for-keypress"]( + "waits for a keypress before exiting") | + Opt(config.benchmarkSamples, "samples")["--benchmark-samples"]( + "number of samples to collect (default: 100)") | + Opt(config.benchmarkResamples, + "resamples")["--benchmark-resamples"]( + "number of resamples for the bootstrap (default: 100000)") | + Opt(config.benchmarkConfidenceInterval, + "confidence interval")["--benchmark-confidence-interval"]( + "confidence interval for the bootstrap (between 0 and 1, default: 0.95)") | + Opt(config.benchmarkNoAnalysis)["--benchmark-no-analysis"]( + "perform only measurements; do not perform any analysis") | + Opt(config.benchmarkWarmupTime, + "benchmarkWarmupTime")["--benchmark-warmup-time"]( + "amount of time in milliseconds spent on warming up each test (default: 100)") | + Arg(config.testsOrTags, + "test name|pattern|tags")("which test or tests to use"); + + return cli; +} + +} // end namespace Catch +// end catch_commandline.cpp +// start catch_common.cpp + +#include +#include + +namespace Catch { + +bool SourceLineInfo::operator==(SourceLineInfo const &other) const noexcept +{ + return line == other.line && + (file == other.file || std::strcmp(file, other.file) == 0); +} +bool SourceLineInfo::operator<(SourceLineInfo const &other) const noexcept +{ + // We can assume that the same file will usually have the same pointer. + // Thus, if the pointers are the same, there is no point in calling the strcmp + return line < other.line || (line == other.line && file != other.file && + (std::strcmp(file, other.file) < 0)); +} + +std::ostream &operator<<(std::ostream &os, SourceLineInfo const &info) +{ +#ifndef __GNUG__ + os << info.file << '(' << info.line << ')'; +#else + os << info.file << ':' << info.line; +#endif + return os; +} + +std::string StreamEndStop::operator+() const +{ + return std::string(); +} + +NonCopyable::NonCopyable() = default; +NonCopyable::~NonCopyable() = default; + +} +// end catch_common.cpp +// start catch_config.cpp + +namespace Catch { + +Config::Config(ConfigData const &data) : m_data(data), m_stream(openStream()) +{ + // We need to trim filter specs to avoid trouble with superfluous + // whitespace (esp. important for bdd macros, as those are manually + // aligned with whitespace). + + for (auto &elem : m_data.testsOrTags) { + elem = trim(elem); + } + for (auto &elem : m_data.sectionsToRun) { + elem = trim(elem); + } + + TestSpecParser parser(ITagAliasRegistry::get()); + if (!m_data.testsOrTags.empty()) { + m_hasTestFilters = true; + for (auto const &testOrTags : m_data.testsOrTags) { + parser.parse(testOrTags); + } + } + m_testSpec = parser.testSpec(); +} + +std::string const &Config::getFilename() const +{ + return m_data.outputFilename; +} + +bool Config::listTests() const +{ + return m_data.listTests; +} +bool Config::listTestNamesOnly() const +{ + return m_data.listTestNamesOnly; +} +bool Config::listTags() const +{ + return m_data.listTags; +} +bool Config::listReporters() const +{ + return m_data.listReporters; +} + +std::string Config::getProcessName() const +{ + return m_data.processName; +} +std::string const &Config::getReporterName() const +{ + return m_data.reporterName; +} + +std::vector const &Config::getTestsOrTags() const +{ + return m_data.testsOrTags; +} +std::vector const &Config::getSectionsToRun() const +{ + return m_data.sectionsToRun; +} + +TestSpec const &Config::testSpec() const +{ + return m_testSpec; +} +bool Config::hasTestFilters() const +{ + return m_hasTestFilters; +} + +bool Config::showHelp() const +{ + return m_data.showHelp; +} + +// IConfig interface +bool Config::allowThrows() const +{ + return !m_data.noThrow; +} +std::ostream &Config::stream() const +{ + return m_stream->stream(); +} +std::string Config::name() const +{ + return m_data.name.empty() ? m_data.processName : m_data.name; +} +bool Config::includeSuccessfulResults() const +{ + return m_data.showSuccessfulTests; +} +bool Config::warnAboutMissingAssertions() const +{ + return !!(m_data.warnings & WarnAbout::NoAssertions); +} +bool Config::warnAboutNoTests() const +{ + return !!(m_data.warnings & WarnAbout::NoTests); +} +ShowDurations::OrNot Config::showDurations() const +{ + return m_data.showDurations; +} +double Config::minDuration() const +{ + return m_data.minDuration; +} +RunTests::InWhatOrder Config::runOrder() const +{ + return m_data.runOrder; +} +unsigned int Config::rngSeed() const +{ + return m_data.rngSeed; +} +UseColour::YesOrNo Config::useColour() const +{ + return m_data.useColour; +} +bool Config::shouldDebugBreak() const +{ + return m_data.shouldDebugBreak; +} +int Config::abortAfter() const +{ + return m_data.abortAfter; +} +bool Config::showInvisibles() const +{ + return m_data.showInvisibles; +} +Verbosity Config::verbosity() const +{ + return m_data.verbosity; +} + +bool Config::benchmarkNoAnalysis() const +{ + return m_data.benchmarkNoAnalysis; +} +int Config::benchmarkSamples() const +{ + return m_data.benchmarkSamples; +} +double Config::benchmarkConfidenceInterval() const +{ + return m_data.benchmarkConfidenceInterval; +} +unsigned int Config::benchmarkResamples() const +{ + return m_data.benchmarkResamples; +} +std::chrono::milliseconds Config::benchmarkWarmupTime() const +{ + return std::chrono::milliseconds(m_data.benchmarkWarmupTime); +} + +IStream const *Config::openStream() +{ + return Catch::makeStream(m_data.outputFilename); +} + +} // end namespace Catch +// end catch_config.cpp +// start catch_console_colour.cpp + +#if defined(__clang__) +#pragma clang diagnostic push +#pragma clang diagnostic ignored "-Wexit-time-destructors" +#endif + +// start catch_errno_guard.h + +namespace Catch { + +class ErrnoGuard { +public: + ErrnoGuard(); + ~ErrnoGuard(); + +private: + int m_oldErrno; +}; + +} + +// end catch_errno_guard.h +// start catch_windows_h_proxy.h + +#if defined(CATCH_PLATFORM_WINDOWS) + +#if !defined(NOMINMAX) && !defined(CATCH_CONFIG_NO_NOMINMAX) +#define CATCH_DEFINED_NOMINMAX +#define NOMINMAX +#endif +#if !defined(WIN32_LEAN_AND_MEAN) && \ + !defined(CATCH_CONFIG_NO_WIN32_LEAN_AND_MEAN) +#define CATCH_DEFINED_WIN32_LEAN_AND_MEAN +#define WIN32_LEAN_AND_MEAN +#endif + +#ifdef __AFXDLL +#include +#else +#include +#endif + +#ifdef CATCH_DEFINED_NOMINMAX +#undef NOMINMAX +#endif +#ifdef CATCH_DEFINED_WIN32_LEAN_AND_MEAN +#undef WIN32_LEAN_AND_MEAN +#endif + +#endif // defined(CATCH_PLATFORM_WINDOWS) + +// end catch_windows_h_proxy.h +#include + +namespace Catch { +namespace { + +struct IColourImpl { + virtual ~IColourImpl() = default; + virtual void use(Colour::Code _colourCode) = 0; +}; + +struct NoColourImpl : IColourImpl { + void use(Colour::Code) override {} + + static IColourImpl *instance() + { + static NoColourImpl s_instance; + return &s_instance; + } +}; + +} // anon namespace +} // namespace Catch + +#if !defined(CATCH_CONFIG_COLOUR_NONE) && \ + !defined(CATCH_CONFIG_COLOUR_WINDOWS) && \ + !defined(CATCH_CONFIG_COLOUR_ANSI) +#ifdef CATCH_PLATFORM_WINDOWS +#define CATCH_CONFIG_COLOUR_WINDOWS +#else +#define CATCH_CONFIG_COLOUR_ANSI +#endif +#endif + +#if defined(CATCH_CONFIG_COLOUR_WINDOWS) ///////////////////////////////////////// + +namespace Catch { +namespace { + +class Win32ColourImpl : public IColourImpl { +public: + Win32ColourImpl() : stdoutHandle(GetStdHandle(STD_OUTPUT_HANDLE)) + { + 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); + } + + void use(Colour::Code _colourCode) override + { + switch (_colourCode) { + case Colour::None: + return setTextAttribute(originalForegroundAttributes); + case Colour::White: + return setTextAttribute(FOREGROUND_GREEN | + FOREGROUND_RED | + FOREGROUND_BLUE); + case Colour::Red: + return setTextAttribute(FOREGROUND_RED); + case Colour::Green: + return setTextAttribute(FOREGROUND_GREEN); + case Colour::Blue: + return setTextAttribute(FOREGROUND_BLUE); + case Colour::Cyan: + return setTextAttribute(FOREGROUND_BLUE | + FOREGROUND_GREEN); + case Colour::Yellow: + return setTextAttribute(FOREGROUND_RED | + FOREGROUND_GREEN); + case Colour::Grey: + return setTextAttribute(0); + + case Colour::LightGrey: + return setTextAttribute(FOREGROUND_INTENSITY); + case Colour::BrightRed: + return setTextAttribute(FOREGROUND_INTENSITY | + FOREGROUND_RED); + case Colour::BrightGreen: + return setTextAttribute(FOREGROUND_INTENSITY | + FOREGROUND_GREEN); + case Colour::BrightWhite: + return setTextAttribute( + FOREGROUND_INTENSITY | FOREGROUND_GREEN | + FOREGROUND_RED | FOREGROUND_BLUE); + case Colour::BrightYellow: + return setTextAttribute(FOREGROUND_INTENSITY | + FOREGROUND_RED | + FOREGROUND_GREEN); + + case Colour::Bright: + CATCH_INTERNAL_ERROR("not a colour"); + + default: + CATCH_ERROR("Unknown colour requested"); + } + } + +private: + void setTextAttribute(WORD _textAttribute) + { + SetConsoleTextAttribute(stdoutHandle, + _textAttribute | + originalBackgroundAttributes); + } + HANDLE stdoutHandle; + WORD originalForegroundAttributes; + WORD originalBackgroundAttributes; +}; + +IColourImpl *platformColourInstance() +{ + static Win32ColourImpl s_instance; + + IConfigPtr config = getCurrentContext().getConfig(); + UseColour::YesOrNo colourMode = config ? config->useColour() + : UseColour::Auto; + if (colourMode == UseColour::Auto) + colourMode = UseColour::Yes; + return colourMode == UseColour::Yes ? &s_instance + : NoColourImpl::instance(); +} + +} // end anon namespace +} // end namespace Catch + +#elif defined(CATCH_CONFIG_COLOUR_ANSI) ////////////////////////////////////// + +#include + +namespace Catch { +namespace { + +// use POSIX/ ANSI console terminal codes +// Thanks to Adam Strzelecki for original contribution +// (http://github.com/nanoant) +// https://github.com/philsquared/Catch/pull/131 +class PosixColourImpl : public IColourImpl { +public: + void use(Colour::Code _colourCode) override + { + switch (_colourCode) { + case Colour::None: + case Colour::White: + return setColour("[0m"); + case Colour::Red: + return setColour("[0;31m"); + case Colour::Green: + return setColour("[0;32m"); + case Colour::Blue: + return setColour("[0;34m"); + case Colour::Cyan: + return setColour("[0;36m"); + case Colour::Yellow: + return setColour("[0;33m"); + case Colour::Grey: + return setColour("[1;30m"); + + case Colour::LightGrey: + return setColour("[0;37m"); + case Colour::BrightRed: + return setColour("[1;31m"); + case Colour::BrightGreen: + return setColour("[1;32m"); + case Colour::BrightWhite: + return setColour("[1;37m"); + case Colour::BrightYellow: + return setColour("[1;33m"); + + case Colour::Bright: + CATCH_INTERNAL_ERROR("not a colour"); + default: + CATCH_INTERNAL_ERROR("Unknown colour requested"); + } + } + static IColourImpl *instance() + { + static PosixColourImpl s_instance; + return &s_instance; + } + +private: + void setColour(const char *_escapeCode) + { + getCurrentContext().getConfig()->stream() + << '\033' << _escapeCode; + } +}; + +bool useColourOnPlatform() +{ + return +#if defined(CATCH_PLATFORM_MAC) || defined(CATCH_PLATFORM_IPHONE) + !isDebuggerActive() && +#endif +#if !(defined(__DJGPP__) && defined(__STRICT_ANSI__)) + isatty(STDOUT_FILENO) +#else + false +#endif + ; +} +IColourImpl *platformColourInstance() +{ + ErrnoGuard guard; + IConfigPtr config = getCurrentContext().getConfig(); + UseColour::YesOrNo colourMode = config ? config->useColour() + : UseColour::Auto; + if (colourMode == UseColour::Auto) + colourMode = useColourOnPlatform() ? UseColour::Yes + : UseColour::No; + return colourMode == UseColour::Yes ? PosixColourImpl::instance() + : NoColourImpl::instance(); +} + +} // end anon namespace +} // end namespace Catch + +#else // not Windows or ANSI /////////////////////////////////////////////// + +namespace Catch { + +static IColourImpl *platformColourInstance() +{ + return NoColourImpl::instance(); +} + +} // end namespace Catch + +#endif // Windows/ ANSI/ None + +namespace Catch { + +Colour::Colour(Code _colourCode) +{ + use(_colourCode); +} +Colour::Colour(Colour &&other) noexcept +{ + m_moved = other.m_moved; + other.m_moved = true; +} +Colour &Colour::operator=(Colour &&other) noexcept +{ + m_moved = other.m_moved; + other.m_moved = true; + return *this; +} + +Colour::~Colour() +{ + if (!m_moved) + use(None); +} + +void Colour::use(Code _colourCode) +{ + static IColourImpl *impl = platformColourInstance(); + // Strictly speaking, this cannot possibly happen. + // However, under some conditions it does happen (see #1626), + // and this change is small enough that we can let practicality + // triumph over purity in this case. + if (impl != nullptr) { + impl->use(_colourCode); + } +} + +std::ostream &operator<<(std::ostream &os, Colour const &) +{ + return os; +} + +} // end namespace Catch + +#if defined(__clang__) +#pragma clang diagnostic pop +#endif + +// end catch_console_colour.cpp +// start catch_context.cpp + +namespace Catch { + +class Context : public IMutableContext, NonCopyable { + +public: // IContext + IResultCapture *getResultCapture() override { return m_resultCapture; } + IRunner *getRunner() override { return m_runner; } + + IConfigPtr const &getConfig() const override { return m_config; } + + ~Context() override; + +public: // IMutableContext + void setResultCapture(IResultCapture *resultCapture) override + { + m_resultCapture = resultCapture; + } + void setRunner(IRunner *runner) override { m_runner = runner; } + void setConfig(IConfigPtr const &config) override { m_config = config; } + + friend IMutableContext &getCurrentMutableContext(); + +private: + IConfigPtr m_config; + IRunner *m_runner = nullptr; + IResultCapture *m_resultCapture = nullptr; +}; + +IMutableContext *IMutableContext::currentContext = nullptr; + +void IMutableContext::createContext() +{ + currentContext = new Context(); +} + +void cleanUpContext() +{ + delete IMutableContext::currentContext; + IMutableContext::currentContext = nullptr; +} +IContext::~IContext() = default; +IMutableContext::~IMutableContext() = default; +Context::~Context() = default; + +SimplePcg32 &rng() +{ + static SimplePcg32 s_rng; + return s_rng; +} + +} +// end catch_context.cpp +// start catch_debug_console.cpp + +// start catch_debug_console.h + +#include + +namespace Catch { +void writeToDebugConsole(std::string const &text); +} + +// end catch_debug_console.h +#if defined(CATCH_CONFIG_ANDROID_LOGWRITE) +#include + +namespace Catch { +void writeToDebugConsole(std::string const &text) +{ + __android_log_write(ANDROID_LOG_DEBUG, "Catch", text.c_str()); +} +} + +#elif defined(CATCH_PLATFORM_WINDOWS) + +namespace Catch { +void writeToDebugConsole(std::string const &text) +{ + ::OutputDebugStringA(text.c_str()); +} +} + +#else + +namespace Catch { +void writeToDebugConsole(std::string const &text) +{ + // !TBD: Need a version for Mac/ XCode and other IDEs + Catch::cout() << text; +} +} + +#endif // Platform +// end catch_debug_console.cpp +// start catch_debugger.cpp + +#if defined(CATCH_PLATFORM_MAC) || defined(CATCH_PLATFORM_IPHONE) + +#include +#include +#include +#include +#include + +#ifdef __apple_build_version__ +// These headers will only compile with AppleClang (XCode) +// For other compilers (Clang, GCC, ... ) we need to exclude them +#include +#endif + +namespace Catch { +#ifdef __apple_build_version__ +// The following function is taken directly from the following technical note: +// https://developer.apple.com/library/archive/qa/qa1361/_index.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; + std::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, nullptr, 0) != + 0) { + Catch::cerr() + << "\n** Call to sysctl failed - unable to determine if debugger is active **\n" + << std::endl; + return false; + } + + // We're being debugged if the P_TRACED flag is set. + + return ((info.kp_proc.p_flag & P_TRACED) != 0); +} +#else +bool isDebuggerActive() +{ + // We need to find another way to determine this for non-appleclang compilers on macOS + return false; +} +#endif +} // namespace Catch + +#elif defined(CATCH_PLATFORM_LINUX) +#include +#include + +namespace Catch { +// The standard POSIX way of detecting a debugger is to attempt to +// ptrace() the process, but this needs to be done from a child and not +// this process itself to still allow attaching to this process later +// if wanted, so is rather heavy. Under Linux we have the PID of the +// "debugger" (which doesn't need to be gdb, of course, it could also +// be strace, for example) in /proc/$PID/status, so just get it from +// there instead. +bool isDebuggerActive() +{ + // Libstdc++ has a bug, where std::ifstream sets errno to 0 + // This way our users can properly assert over errno values + ErrnoGuard guard; + std::ifstream in("/proc/self/status"); + for (std::string line; std::getline(in, line);) { + static const int PREFIX_LEN = 11; + if (line.compare(0, PREFIX_LEN, "TracerPid:\t") == 0) { + // We're traced if the PID is not 0 and no other PID starts + // with 0 digit, so it's enough to check for just a single + // character. + return line.length() > PREFIX_LEN && + line[PREFIX_LEN] != '0'; + } + } + + return false; +} +} // namespace Catch +#elif defined(_MSC_VER) +extern "C" __declspec(dllimport) int __stdcall IsDebuggerPresent(); +namespace Catch { +bool isDebuggerActive() +{ + return IsDebuggerPresent() != 0; +} +} +#elif defined(__MINGW32__) +extern "C" __declspec(dllimport) int __stdcall IsDebuggerPresent(); +namespace Catch { +bool isDebuggerActive() +{ + return IsDebuggerPresent() != 0; +} +} +#else +namespace Catch { +bool isDebuggerActive() +{ + return false; +} +} +#endif // Platform +// end catch_debugger.cpp +// start catch_decomposer.cpp + +namespace Catch { + +ITransientExpression::~ITransientExpression() = default; + +void formatReconstructedExpression(std::ostream &os, std::string const &lhs, + StringRef op, std::string const &rhs) +{ + if (lhs.size() + rhs.size() < 40 && + lhs.find('\n') == std::string::npos && + rhs.find('\n') == std::string::npos) + os << lhs << " " << op << " " << rhs; + else + os << lhs << "\n" << op << "\n" << rhs; +} +} +// end catch_decomposer.cpp +// start catch_enforce.cpp + +#include + +namespace Catch { +#if defined(CATCH_CONFIG_DISABLE_EXCEPTIONS) && \ + !defined(CATCH_CONFIG_DISABLE_EXCEPTIONS_CUSTOM_HANDLER) +[[noreturn]] void throw_exception(std::exception const &e) +{ + Catch::cerr() + << "Catch will terminate because it needed to throw an exception.\n" + << "The message was: " << e.what() << '\n'; + std::terminate(); +} +#endif + +[[noreturn]] void throw_logic_error(std::string const &msg) +{ + throw_exception(std::logic_error(msg)); +} + +[[noreturn]] void throw_domain_error(std::string const &msg) +{ + throw_exception(std::domain_error(msg)); +} + +[[noreturn]] void throw_runtime_error(std::string const &msg) +{ + throw_exception(std::runtime_error(msg)); +} + +} // namespace Catch; +// end catch_enforce.cpp +// start catch_enum_values_registry.cpp +// start catch_enum_values_registry.h + +#include +#include + +namespace Catch { + +namespace Detail { + +std::unique_ptr makeEnumInfo(StringRef enumName, + StringRef allValueNames, + std::vector const &values); + +class EnumValuesRegistry : public IMutableEnumValuesRegistry { + + std::vector> m_enumInfos; + + EnumInfo const ®isterEnum(StringRef enumName, StringRef allEnums, + std::vector const &values) override; +}; + +std::vector parseEnums(StringRef enums); + +} // Detail + +} // Catch + +// end catch_enum_values_registry.h + +#include +#include + +namespace Catch { + +IMutableEnumValuesRegistry::~IMutableEnumValuesRegistry() {} + +namespace Detail { + +namespace { +// Extracts the actual name part of an enum instance +// In other words, it returns the Blue part of Bikeshed::Colour::Blue +StringRef extractInstanceName(StringRef enumInstance) +{ + // Find last occurrence of ":" + size_t name_start = enumInstance.size(); + while (name_start > 0 && enumInstance[name_start - 1] != ':') { + --name_start; + } + return enumInstance.substr(name_start, + enumInstance.size() - name_start); +} +} + +std::vector parseEnums(StringRef enums) +{ + auto enumValues = splitStringRef(enums, ','); + std::vector parsed; + parsed.reserve(enumValues.size()); + for (auto const &enumValue : enumValues) { + parsed.push_back(trim(extractInstanceName(enumValue))); + } + return parsed; +} + +EnumInfo::~EnumInfo() {} + +StringRef EnumInfo::lookup(int value) const +{ + for (auto const &valueToName : m_values) { + if (valueToName.first == value) + return valueToName.second; + } + return "{** unexpected enum value **}"_sr; +} + +std::unique_ptr makeEnumInfo(StringRef enumName, + StringRef allValueNames, + std::vector const &values) +{ + std::unique_ptr enumInfo(new EnumInfo); + enumInfo->m_name = enumName; + enumInfo->m_values.reserve(values.size()); + + const auto valueNames = Catch::Detail::parseEnums(allValueNames); + assert(valueNames.size() == values.size()); + std::size_t i = 0; + for (auto value : values) + enumInfo->m_values.emplace_back(value, valueNames[i++]); + + return enumInfo; +} + +EnumInfo const &EnumValuesRegistry::registerEnum(StringRef enumName, + StringRef allValueNames, + std::vector const &values) +{ + m_enumInfos.push_back(makeEnumInfo(enumName, allValueNames, values)); + return *m_enumInfos.back(); +} + +} // Detail +} // Catch + +// end catch_enum_values_registry.cpp +// start catch_errno_guard.cpp + +#include + +namespace Catch { +ErrnoGuard::ErrnoGuard() : m_oldErrno(errno) {} +ErrnoGuard::~ErrnoGuard() +{ + errno = m_oldErrno; +} +} +// end catch_errno_guard.cpp +// start catch_exception_translator_registry.cpp + +// start catch_exception_translator_registry.h + +#include +#include +#include + +namespace Catch { + +class ExceptionTranslatorRegistry : public IExceptionTranslatorRegistry { +public: + ~ExceptionTranslatorRegistry(); + virtual void registerTranslator(const IExceptionTranslator *translator); + std::string translateActiveException() const override; + std::string tryTranslators() const; + +private: + std::vector> m_translators; +}; +} + +// end catch_exception_translator_registry.h +#ifdef __OBJC__ +#import "Foundation/Foundation.h" +#endif + +namespace Catch { + +ExceptionTranslatorRegistry::~ExceptionTranslatorRegistry() {} + +void ExceptionTranslatorRegistry::registerTranslator( + const IExceptionTranslator *translator) +{ + m_translators.push_back( + std::unique_ptr(translator)); +} + +#if !defined(CATCH_CONFIG_DISABLE_EXCEPTIONS) +std::string ExceptionTranslatorRegistry::translateActiveException() const +{ + try { +#ifdef __OBJC__ + // In Objective-C try objective-c exceptions first + @try { + return tryTranslators(); + } @catch (NSException *exception) { + return Catch::Detail::stringify( + [exception description]); + } +#else + // Compiling a mixed mode project with MSVC means that CLR + // exceptions will be caught in (...) as well. However, these + // do not fill-in std::current_exception and thus lead to crash + // when attempting rethrow. + // /EHa switch also causes structured exceptions to be caught + // here, but they fill-in current_exception properly, so + // at worst the output should be a little weird, instead of + // causing a crash. + if (std::current_exception() == nullptr) { + return "Non C++ exception. Possibly a CLR exception."; + } + return tryTranslators(); +#endif + } catch (TestFailureException &) { + std::rethrow_exception(std::current_exception()); + } catch (std::exception &ex) { + return ex.what(); + } catch (std::string &msg) { + return msg; + } catch (const char *msg) { + return msg; + } catch (...) { + return "Unknown exception"; + } +} + +std::string ExceptionTranslatorRegistry::tryTranslators() const +{ + if (m_translators.empty()) { + std::rethrow_exception(std::current_exception()); + } else { + return m_translators[0]->translate(m_translators.begin() + 1, + m_translators.end()); + } +} + +#else // ^^ Exceptions are enabled // Exceptions are disabled vv +std::string ExceptionTranslatorRegistry::translateActiveException() const +{ + CATCH_INTERNAL_ERROR( + "Attempted to translate active exception under CATCH_CONFIG_DISABLE_EXCEPTIONS!"); +} + +std::string ExceptionTranslatorRegistry::tryTranslators() const +{ + CATCH_INTERNAL_ERROR( + "Attempted to use exception translators under CATCH_CONFIG_DISABLE_EXCEPTIONS!"); +} +#endif + +} +// end catch_exception_translator_registry.cpp +// start catch_fatal_condition.cpp + +#include + +#if !defined(CATCH_CONFIG_WINDOWS_SEH) && !defined(CATCH_CONFIG_POSIX_SIGNALS) + +namespace Catch { + +// If neither SEH nor signal handling is required, the handler impls +// do not have to do anything, and can be empty. +void FatalConditionHandler::engage_platform() {} +void FatalConditionHandler::disengage_platform() {} +FatalConditionHandler::FatalConditionHandler() = default; +FatalConditionHandler::~FatalConditionHandler() = default; + +} // end namespace Catch + +#endif // !CATCH_CONFIG_WINDOWS_SEH && !CATCH_CONFIG_POSIX_SIGNALS + +#if defined(CATCH_CONFIG_WINDOWS_SEH) && defined(CATCH_CONFIG_POSIX_SIGNALS) +#error "Inconsistent configuration: Windows' SEH handling and POSIX signals cannot be enabled at the same time" +#endif // CATCH_CONFIG_WINDOWS_SEH && CATCH_CONFIG_POSIX_SIGNALS + +#if defined(CATCH_CONFIG_WINDOWS_SEH) || defined(CATCH_CONFIG_POSIX_SIGNALS) + +namespace { +//! Signals fatal error message to the run context +void reportFatal(char const *const message) +{ + Catch::getCurrentContext().getResultCapture()->handleFatalErrorCondition( + message); +} + +//! Minimal size Catch2 needs for its own fatal error handling. +//! Picked anecdotally, so it might not be sufficient on all +//! platforms, and for all configurations. +constexpr std::size_t minStackSizeForErrors = 32 * 1024; +} // end unnamed namespace + +#endif // CATCH_CONFIG_WINDOWS_SEH || CATCH_CONFIG_POSIX_SIGNALS + +#if defined(CATCH_CONFIG_WINDOWS_SEH) + +namespace Catch { + +struct SignalDefs { + DWORD id; + const char *name; +}; + +// There is no 1-1 mapping between signals and windows exceptions. +// Windows can easily distinguish between SO and SigSegV, +// but SigInt, SigTerm, etc are handled differently. +static SignalDefs signalDefs[] = { + {static_cast(EXCEPTION_ILLEGAL_INSTRUCTION), + "SIGILL - Illegal instruction signal"}, + {static_cast(EXCEPTION_STACK_OVERFLOW), + "SIGSEGV - Stack overflow"}, + {static_cast(EXCEPTION_ACCESS_VIOLATION), + "SIGSEGV - Segmentation violation signal"}, + {static_cast(EXCEPTION_INT_DIVIDE_BY_ZERO), + "Divide by zero error"}, +}; + +static LONG CALLBACK handleVectoredException(PEXCEPTION_POINTERS ExceptionInfo) +{ + for (auto const &def : signalDefs) { + if (ExceptionInfo->ExceptionRecord->ExceptionCode == def.id) { + reportFatal(def.name); + } + } + // If its not an exception we care about, pass it along. + // This stops us from eating debugger breaks etc. + return EXCEPTION_CONTINUE_SEARCH; +} + +// Since we do not support multiple instantiations, we put these +// into global variables and rely on cleaning them up in outlined +// constructors/destructors +static PVOID exceptionHandlerHandle = nullptr; + +// For MSVC, we reserve part of the stack memory for handling +// memory overflow structured exception. +FatalConditionHandler::FatalConditionHandler() +{ + ULONG guaranteeSize = static_cast(minStackSizeForErrors); + if (!SetThreadStackGuarantee(&guaranteeSize)) { + // We do not want to fully error out, because needing + // the stack reserve should be rare enough anyway. + Catch::cerr() + << "Failed to reserve piece of stack." + << " Stack overflows will not be reported successfully."; + } +} + +// We do not attempt to unset the stack guarantee, because +// Windows does not support lowering the stack size guarantee. +FatalConditionHandler::~FatalConditionHandler() = default; + +void FatalConditionHandler::engage_platform() +{ + // Register as first handler in current chain + exceptionHandlerHandle = + AddVectoredExceptionHandler(1, handleVectoredException); + if (!exceptionHandlerHandle) { + CATCH_RUNTIME_ERROR( + "Could not register vectored exception handler"); + } +} + +void FatalConditionHandler::disengage_platform() +{ + if (!RemoveVectoredExceptionHandler(exceptionHandlerHandle)) { + CATCH_RUNTIME_ERROR( + "Could not unregister vectored exception handler"); + } + exceptionHandlerHandle = nullptr; +} + +} // end namespace Catch + +#endif // CATCH_CONFIG_WINDOWS_SEH + +#if defined(CATCH_CONFIG_POSIX_SIGNALS) + +#include + +namespace Catch { + +struct SignalDefs { + int id; + const char *name; +}; + +static SignalDefs signalDefs[] = { + {SIGINT, "SIGINT - Terminal interrupt signal"}, + {SIGILL, "SIGILL - Illegal instruction signal"}, + {SIGFPE, "SIGFPE - Floating point error signal"}, + {SIGSEGV, "SIGSEGV - Segmentation violation signal"}, + {SIGTERM, "SIGTERM - Termination request signal"}, + {SIGABRT, "SIGABRT - Abort (abnormal termination) signal"}}; + +// Older GCCs trigger -Wmissing-field-initializers for T foo = {} +// which is zero initialization, but not explicit. We want to avoid +// that. +#if defined(__GNUC__) +#pragma GCC diagnostic push +#pragma GCC diagnostic ignored "-Wmissing-field-initializers" +#endif + +static char *altStackMem = nullptr; +static std::size_t altStackSize = 0; +static stack_t oldSigStack{}; +static struct sigaction oldSigActions[sizeof(signalDefs) / sizeof(SignalDefs)]{}; + +static void restorePreviousSignalHandlers() +{ + // We set signal handlers back to the previous ones. Hopefully + // nobody overwrote them in the meantime, and doesn't expect + // their signal handlers to live past ours given that they + // installed them after ours.. + for (std::size_t i = 0; i < sizeof(signalDefs) / sizeof(SignalDefs); + ++i) { + sigaction(signalDefs[i].id, &oldSigActions[i], nullptr); + } + // Return the old stack + sigaltstack(&oldSigStack, nullptr); +} + +static void handleSignal(int sig) +{ + char const *name = ""; + for (auto const &def : signalDefs) { + if (sig == def.id) { + name = def.name; + break; + } + } + // We need to restore previous signal handlers and let them do + // their thing, so that the users can have the debugger break + // when a signal is raised, and so on. + restorePreviousSignalHandlers(); + reportFatal(name); + raise(sig); +} + +FatalConditionHandler::FatalConditionHandler() +{ + assert(!altStackMem && + "Cannot initialize POSIX signal handler when one already exists"); + if (altStackSize == 0) { + altStackSize = std::max(static_cast(SIGSTKSZ), + minStackSizeForErrors); + } + altStackMem = new char[altStackSize](); +} + +FatalConditionHandler::~FatalConditionHandler() +{ + delete[] altStackMem; + // We signal that another instance can be constructed by zeroing + // out the pointer. + altStackMem = nullptr; +} + +void FatalConditionHandler::engage_platform() +{ + stack_t sigStack; + sigStack.ss_sp = altStackMem; + sigStack.ss_size = altStackSize; + sigStack.ss_flags = 0; + sigaltstack(&sigStack, &oldSigStack); + struct sigaction sa = {}; + + sa.sa_handler = handleSignal; + sa.sa_flags = SA_ONSTACK; + for (std::size_t i = 0; i < sizeof(signalDefs) / sizeof(SignalDefs); + ++i) { + sigaction(signalDefs[i].id, &sa, &oldSigActions[i]); + } +} + +#if defined(__GNUC__) +#pragma GCC diagnostic pop +#endif + +void FatalConditionHandler::disengage_platform() +{ + restorePreviousSignalHandlers(); +} + +} // end namespace Catch + +#endif // CATCH_CONFIG_POSIX_SIGNALS +// end catch_fatal_condition.cpp +// start catch_generators.cpp + +#include +#include + +namespace Catch { + +IGeneratorTracker::~IGeneratorTracker() {} + +const char *GeneratorException::what() const noexcept +{ + return m_msg; +} + +namespace Generators { + +GeneratorUntypedBase::~GeneratorUntypedBase() {} + +auto acquireGeneratorTracker(StringRef generatorName, + SourceLineInfo const &lineInfo) + -> IGeneratorTracker & +{ + return getResultCapture().acquireGeneratorTracker(generatorName, + lineInfo); +} + +} // namespace Generators +} // namespace Catch +// end catch_generators.cpp +// start catch_interfaces_capture.cpp + +namespace Catch { +IResultCapture::~IResultCapture() = default; +} +// end catch_interfaces_capture.cpp +// start catch_interfaces_config.cpp + +namespace Catch { +IConfig::~IConfig() = default; +} +// end catch_interfaces_config.cpp +// start catch_interfaces_exception.cpp + +namespace Catch { +IExceptionTranslator::~IExceptionTranslator() = default; +IExceptionTranslatorRegistry::~IExceptionTranslatorRegistry() = default; +} +// end catch_interfaces_exception.cpp +// start catch_interfaces_registry_hub.cpp + +namespace Catch { +IRegistryHub::~IRegistryHub() = default; +IMutableRegistryHub::~IMutableRegistryHub() = default; +} +// end catch_interfaces_registry_hub.cpp +// start catch_interfaces_reporter.cpp + +// start catch_reporter_listening.h + +namespace Catch { + +class ListeningReporter : public IStreamingReporter { + using Reporters = std::vector; + Reporters m_listeners; + IStreamingReporterPtr m_reporter = nullptr; + ReporterPreferences m_preferences; + +public: + ListeningReporter(); + + void addListener(IStreamingReporterPtr &&listener); + void addReporter(IStreamingReporterPtr &&reporter); + +public: // IStreamingReporter + ReporterPreferences getPreferences() const override; + + void noMatchingTestCases(std::string const &spec) override; + + void reportInvalidArguments(std::string const &arg) override; + + static std::set getSupportedVerbosities(); + +#if defined(CATCH_CONFIG_ENABLE_BENCHMARKING) + void benchmarkPreparing(std::string const &name) override; + void benchmarkStarting(BenchmarkInfo const &benchmarkInfo) override; + void benchmarkEnded(BenchmarkStats<> const &benchmarkStats) override; + void benchmarkFailed(std::string const &) override; +#endif // CATCH_CONFIG_ENABLE_BENCHMARKING + + void testRunStarting(TestRunInfo const &testRunInfo) override; + void testGroupStarting(GroupInfo const &groupInfo) override; + void testCaseStarting(TestCaseInfo const &testInfo) override; + void sectionStarting(SectionInfo const §ionInfo) override; + void assertionStarting(AssertionInfo const &assertionInfo) override; + + // The return value indicates if the messages buffer should be cleared: + bool assertionEnded(AssertionStats const &assertionStats) override; + void sectionEnded(SectionStats const §ionStats) override; + void testCaseEnded(TestCaseStats const &testCaseStats) override; + void testGroupEnded(TestGroupStats const &testGroupStats) override; + void testRunEnded(TestRunStats const &testRunStats) override; + + void skipTest(TestCaseInfo const &testInfo) override; + bool isMulti() const override; +}; + +} // end namespace Catch + +// end catch_reporter_listening.h +namespace Catch { + +ReporterConfig::ReporterConfig(IConfigPtr const &_fullConfig) + : m_stream(&_fullConfig->stream()), m_fullConfig(_fullConfig) +{ +} + +ReporterConfig::ReporterConfig(IConfigPtr const &_fullConfig, + std::ostream &_stream) + : m_stream(&_stream), m_fullConfig(_fullConfig) +{ +} + +std::ostream &ReporterConfig::stream() const +{ + return *m_stream; +} +IConfigPtr ReporterConfig::fullConfig() const +{ + return m_fullConfig; +} + +TestRunInfo::TestRunInfo(std::string const &_name) : name(_name) {} + +GroupInfo::GroupInfo(std::string const &_name, std::size_t _groupIndex, + std::size_t _groupsCount) + : name(_name), groupIndex(_groupIndex), groupsCounts(_groupsCount) +{ +} + +AssertionStats::AssertionStats(AssertionResult const &_assertionResult, + std::vector const &_infoMessages, + Totals const &_totals) + : assertionResult(_assertionResult), + infoMessages(_infoMessages), + totals(_totals) +{ + assertionResult.m_resultData.lazyExpression.m_transientExpression = + _assertionResult.m_resultData.lazyExpression + .m_transientExpression; + + if (assertionResult.hasMessage()) { + // Copy message into messages list. + // !TBD This should have been done earlier, somewhere + MessageBuilder builder(assertionResult.getTestMacroName(), + assertionResult.getSourceInfo(), + assertionResult.getResultType()); + builder << assertionResult.getMessage(); + builder.m_info.message = builder.m_stream.str(); + + infoMessages.push_back(builder.m_info); + } +} + +AssertionStats::~AssertionStats() = default; + +SectionStats::SectionStats(SectionInfo const &_sectionInfo, + Counts const &_assertions, double _durationInSeconds, + bool _missingAssertions) + : sectionInfo(_sectionInfo), + assertions(_assertions), + durationInSeconds(_durationInSeconds), + missingAssertions(_missingAssertions) +{ +} + +SectionStats::~SectionStats() = default; + +TestCaseStats::TestCaseStats(TestCaseInfo const &_testInfo, + Totals const &_totals, std::string const &_stdOut, + std::string const &_stdErr, bool _aborting) + : testInfo(_testInfo), + totals(_totals), + stdOut(_stdOut), + stdErr(_stdErr), + aborting(_aborting) +{ +} + +TestCaseStats::~TestCaseStats() = default; + +TestGroupStats::TestGroupStats(GroupInfo const &_groupInfo, + Totals const &_totals, bool _aborting) + : groupInfo(_groupInfo), totals(_totals), aborting(_aborting) +{ +} + +TestGroupStats::TestGroupStats(GroupInfo const &_groupInfo) + : groupInfo(_groupInfo), aborting(false) +{ +} + +TestGroupStats::~TestGroupStats() = default; + +TestRunStats::TestRunStats(TestRunInfo const &_runInfo, Totals const &_totals, + bool _aborting) + : runInfo(_runInfo), totals(_totals), aborting(_aborting) +{ +} + +TestRunStats::~TestRunStats() = default; + +void IStreamingReporter::fatalErrorEncountered(StringRef) {} +bool IStreamingReporter::isMulti() const +{ + return false; +} + +IReporterFactory::~IReporterFactory() = default; +IReporterRegistry::~IReporterRegistry() = default; + +} // end namespace Catch +// end catch_interfaces_reporter.cpp +// start catch_interfaces_runner.cpp + +namespace Catch { +IRunner::~IRunner() = default; +} +// end catch_interfaces_runner.cpp +// start catch_interfaces_testcase.cpp + +namespace Catch { +ITestInvoker::~ITestInvoker() = default; +ITestCaseRegistry::~ITestCaseRegistry() = default; +} +// end catch_interfaces_testcase.cpp +// start catch_leak_detector.cpp + +#ifdef CATCH_CONFIG_WINDOWS_CRTDBG +#include + +namespace Catch { + +LeakDetector::LeakDetector() +{ + int flag = _CrtSetDbgFlag(_CRTDBG_REPORT_FLAG); + flag |= _CRTDBG_LEAK_CHECK_DF; + flag |= _CRTDBG_ALLOC_MEM_DF; + _CrtSetDbgFlag(flag); + _CrtSetReportMode(_CRT_WARN, _CRTDBG_MODE_FILE | _CRTDBG_MODE_DEBUG); + _CrtSetReportFile(_CRT_WARN, _CRTDBG_FILE_STDERR); + // Change this to leaking allocation's number to break there + _CrtSetBreakAlloc(-1); +} +} + +#else + +Catch::LeakDetector::LeakDetector() {} + +#endif + +Catch::LeakDetector::~LeakDetector() +{ + Catch::cleanUp(); +} +// end catch_leak_detector.cpp +// start catch_list.cpp + +// start catch_list.h + +#include + +namespace Catch { + +std::size_t listTests(Config const &config); + +std::size_t listTestsNamesOnly(Config const &config); + +struct TagInfo { + void add(std::string const &spelling); + std::string all() const; + + std::set spellings; + std::size_t count = 0; +}; + +std::size_t listTags(Config const &config); + +std::size_t listReporters(); + +Option list(std::shared_ptr const &config); + +} // end namespace Catch + +// end catch_list.h +// start catch_text.h + +namespace Catch { +using namespace clara::TextFlow; +} + +// end catch_text.h +#include +#include +#include + +namespace Catch { + +std::size_t listTests(Config const &config) +{ + TestSpec const &testSpec = config.testSpec(); + if (config.hasTestFilters()) + Catch::cout() << "Matching test cases:\n"; + else { + Catch::cout() << "All available test cases:\n"; + } + + auto matchedTestCases = + filterTests(getAllTestCasesSorted(config), testSpec, config); + for (auto const &testCaseInfo : matchedTestCases) { + Colour::Code colour = testCaseInfo.isHidden() + ? Colour::SecondaryText + : Colour::None; + Colour colourGuard(colour); + + Catch::cout() + << Column(testCaseInfo.name).initialIndent(2).indent(4) + << "\n"; + if (config.verbosity() >= Verbosity::High) { + Catch::cout() << Column(Catch::Detail::stringify( + testCaseInfo.lineInfo)) + .indent(4) + << std::endl; + std::string description = testCaseInfo.description; + if (description.empty()) + description = "(NO DESCRIPTION)"; + Catch::cout() + << Column(description).indent(4) << std::endl; + } + if (!testCaseInfo.tags.empty()) + Catch::cout() + << Column(testCaseInfo.tagsAsString()).indent(6) + << "\n"; + } + + if (!config.hasTestFilters()) + Catch::cout() << pluralise(matchedTestCases.size(), "test case") + << '\n' + << std::endl; + else + Catch::cout() << pluralise(matchedTestCases.size(), + "matching test case") + << '\n' + << std::endl; + return matchedTestCases.size(); +} + +std::size_t listTestsNamesOnly(Config const &config) +{ + TestSpec const &testSpec = config.testSpec(); + std::size_t matchedTests = 0; + std::vector matchedTestCases = + filterTests(getAllTestCasesSorted(config), testSpec, config); + for (auto const &testCaseInfo : matchedTestCases) { + matchedTests++; + if (startsWith(testCaseInfo.name, '#')) + Catch::cout() << '"' << testCaseInfo.name << '"'; + else + Catch::cout() << testCaseInfo.name; + if (config.verbosity() >= Verbosity::High) + Catch::cout() << "\t@" << testCaseInfo.lineInfo; + Catch::cout() << std::endl; + } + return matchedTests; +} + +void TagInfo::add(std::string const &spelling) +{ + ++count; + spellings.insert(spelling); +} + +std::string TagInfo::all() const +{ + size_t size = 0; + for (auto const &spelling : spellings) { + // Add 2 for the brackes + size += spelling.size() + 2; + } + + std::string out; + out.reserve(size); + for (auto const &spelling : spellings) { + out += '['; + out += spelling; + out += ']'; + } + return out; +} + +std::size_t listTags(Config const &config) +{ + TestSpec const &testSpec = config.testSpec(); + if (config.hasTestFilters()) + Catch::cout() << "Tags for matching test cases:\n"; + else { + Catch::cout() << "All available tags:\n"; + } + + std::map tagCounts; + + std::vector matchedTestCases = + filterTests(getAllTestCasesSorted(config), testSpec, config); + for (auto const &testCase : matchedTestCases) { + for (auto const &tagName : testCase.getTestCaseInfo().tags) { + std::string lcaseTagName = toLower(tagName); + auto countIt = tagCounts.find(lcaseTagName); + if (countIt == tagCounts.end()) + countIt = tagCounts + .insert(std::make_pair( + lcaseTagName, + TagInfo())) + .first; + countIt->second.add(tagName); + } + } + + for (auto const &tagCount : tagCounts) { + ReusableStringStream rss; + rss << " " << std::setw(2) << tagCount.second.count << " "; + auto str = rss.str(); + auto wrapper = Column(tagCount.second.all()) + .initialIndent(0) + .indent(str.size()) + .width(CATCH_CONFIG_CONSOLE_WIDTH - 10); + Catch::cout() << str << wrapper << '\n'; + } + Catch::cout() << pluralise(tagCounts.size(), "tag") << '\n' + << std::endl; + return tagCounts.size(); +} + +std::size_t listReporters() +{ + Catch::cout() << "Available reporters:\n"; + IReporterRegistry::FactoryMap const &factories = + getRegistryHub().getReporterRegistry().getFactories(); + std::size_t maxNameLen = 0; + for (auto const &factoryKvp : factories) + maxNameLen = (std::max)(maxNameLen, factoryKvp.first.size()); + + for (auto const &factoryKvp : factories) { + Catch::cout() + << Column(factoryKvp.first + ":") + .indent(2) + .width(5 + maxNameLen) + + Column(factoryKvp.second->getDescription()) + .initialIndent(0) + .indent(2) + .width(CATCH_CONFIG_CONSOLE_WIDTH - + maxNameLen - 8) + << "\n"; + } + Catch::cout() << std::endl; + return factories.size(); +} + +Option list(std::shared_ptr const &config) +{ + Option listedCount; + getCurrentMutableContext().setConfig(config); + if (config->listTests()) + listedCount = listedCount.valueOr(0) + listTests(*config); + if (config->listTestNamesOnly()) + listedCount = + listedCount.valueOr(0) + listTestsNamesOnly(*config); + if (config->listTags()) + listedCount = listedCount.valueOr(0) + listTags(*config); + if (config->listReporters()) + listedCount = listedCount.valueOr(0) + listReporters(); + return listedCount; +} + +} // end namespace Catch +// end catch_list.cpp +// start catch_matchers.cpp + +namespace Catch { +namespace Matchers { +namespace Impl { + +std::string MatcherUntypedBase::toString() const +{ + if (m_cachedToString.empty()) + m_cachedToString = describe(); + return m_cachedToString; +} + +MatcherUntypedBase::~MatcherUntypedBase() = default; + +} // namespace Impl +} // namespace Matchers + +using namespace Matchers; +using Matchers::Impl::MatcherBase; + +} // namespace Catch +// end catch_matchers.cpp +// start catch_matchers_exception.cpp + +namespace Catch { +namespace Matchers { +namespace Exception { + +bool ExceptionMessageMatcher::match(std::exception const &ex) const +{ + return ex.what() == m_message; +} + +std::string ExceptionMessageMatcher::describe() const +{ + return "exception message matches \"" + m_message + "\""; +} + +} +Exception::ExceptionMessageMatcher Message(std::string const &message) +{ + return Exception::ExceptionMessageMatcher(message); +} + +// namespace Exception +} // namespace Matchers +} // namespace Catch +// end catch_matchers_exception.cpp +// start catch_matchers_floating.cpp + +// start catch_polyfills.hpp + +namespace Catch { +bool isnan(float f); +bool isnan(double d); +} + +// end catch_polyfills.hpp +// start catch_to_string.hpp + +#include + +namespace Catch { +template std::string to_string(T const &t) +{ +#if defined(CATCH_CONFIG_CPP11_TO_STRING) + return std::to_string(t); +#else + ReusableStringStream rss; + rss << t; + return rss.str(); +#endif +} +} // end namespace Catch + +// end catch_to_string.hpp +#include +#include +#include +#include +#include +#include +#include +#include +#include + +namespace Catch { +namespace { + +int32_t convert(float f) +{ + static_assert(sizeof(float) == sizeof(int32_t), + "Important ULP matcher assumption violated"); + int32_t i; + std::memcpy(&i, &f, sizeof(f)); + return i; +} + +int64_t convert(double d) +{ + static_assert(sizeof(double) == sizeof(int64_t), + "Important ULP matcher assumption violated"); + int64_t i; + std::memcpy(&i, &d, sizeof(d)); + return i; +} + +template bool almostEqualUlps(FP lhs, FP rhs, uint64_t maxUlpDiff) +{ + // Comparison with NaN should always be false. + // This way we can rule it out before getting into the ugly details + if (Catch::isnan(lhs) || Catch::isnan(rhs)) { + return false; + } + + auto lc = convert(lhs); + auto rc = convert(rhs); + + if ((lc < 0) != (rc < 0)) { + // Potentially we can have +0 and -0 + return lhs == rhs; + } + + // static cast as a workaround for IBM XLC + auto ulpDiff = std::abs(static_cast(lc - rc)); + return static_cast(ulpDiff) <= maxUlpDiff; +} + +#if defined(CATCH_CONFIG_GLOBAL_NEXTAFTER) + +float nextafter(float x, float y) +{ + return ::nextafterf(x, y); +} + +double nextafter(double x, double y) +{ + return ::nextafter(x, y); +} + +#endif // ^^^ CATCH_CONFIG_GLOBAL_NEXTAFTER ^^^ + +template FP step(FP start, FP direction, uint64_t steps) +{ + for (uint64_t i = 0; i < steps; ++i) { +#if defined(CATCH_CONFIG_GLOBAL_NEXTAFTER) + start = Catch::nextafter(start, direction); +#else + start = std::nextafter(start, direction); +#endif + } + return start; +} + +// Performs equivalent check of std::fabs(lhs - rhs) <= margin +// But without the subtraction to allow for INFINITY in comparison +bool marginComparison(double lhs, double rhs, double margin) +{ + return (lhs + margin >= rhs) && (rhs + margin >= lhs); +} + +template +void write(std::ostream &out, FloatingPoint num) +{ + out << std::scientific + << std::setprecision( + std::numeric_limits::max_digits10 - 1) + << num; +} + +} // end anonymous namespace + +namespace Matchers { +namespace Floating { + +enum class FloatingPointKind : uint8_t { Float, Double }; + +WithinAbsMatcher::WithinAbsMatcher(double target, double margin) + : m_target{target}, m_margin{margin} +{ + CATCH_ENFORCE(margin >= 0, + "Invalid margin: " << margin << '.' + << " Margin has to be non-negative."); +} + +// Performs equivalent check of std::fabs(lhs - rhs) <= margin +// But without the subtraction to allow for INFINITY in comparison +bool WithinAbsMatcher::match(double const &matchee) const +{ + return (matchee + m_margin >= m_target) && + (m_target + m_margin >= matchee); +} + +std::string WithinAbsMatcher::describe() const +{ + return "is within " + ::Catch::Detail::stringify(m_margin) + " of " + + ::Catch::Detail::stringify(m_target); +} + +WithinUlpsMatcher::WithinUlpsMatcher(double target, uint64_t ulps, + FloatingPointKind baseType) + : m_target{target}, m_ulps{ulps}, m_type{baseType} +{ + CATCH_ENFORCE( + m_type == FloatingPointKind::Double || + m_ulps < (std::numeric_limits::max)(), + "Provided ULP is impossibly large for a float comparison."); +} + +#if defined(__clang__) +#pragma clang diagnostic push +// Clang <3.5 reports on the default branch in the switch below +#pragma clang diagnostic ignored "-Wunreachable-code" +#endif + +bool WithinUlpsMatcher::match(double const &matchee) const +{ + switch (m_type) { + case FloatingPointKind::Float: + return almostEqualUlps(static_cast(matchee), + static_cast(m_target), + m_ulps); + case FloatingPointKind::Double: + return almostEqualUlps(matchee, m_target, m_ulps); + default: + CATCH_INTERNAL_ERROR("Unknown FloatingPointKind value"); + } +} + +#if defined(__clang__) +#pragma clang diagnostic pop +#endif + +std::string WithinUlpsMatcher::describe() const +{ + std::stringstream ret; + + ret << "is within " << m_ulps << " ULPs of "; + + if (m_type == FloatingPointKind::Float) { + write(ret, static_cast(m_target)); + ret << 'f'; + } else { + write(ret, m_target); + } + + ret << " (["; + if (m_type == FloatingPointKind::Double) { + write(ret, + step(m_target, static_cast(-INFINITY), m_ulps)); + ret << ", "; + write(ret, + step(m_target, static_cast(INFINITY), m_ulps)); + } else { + // We have to cast INFINITY to float because of MinGW, see #1782 + write(ret, step(static_cast(m_target), + static_cast(-INFINITY), m_ulps)); + ret << ", "; + write(ret, step(static_cast(m_target), + static_cast(INFINITY), m_ulps)); + } + ret << "])"; + + return ret.str(); +} + +WithinRelMatcher::WithinRelMatcher(double target, double epsilon) + : m_target(target), m_epsilon(epsilon) +{ + CATCH_ENFORCE( + m_epsilon >= 0., + "Relative comparison with epsilon < 0 does not make sense."); + CATCH_ENFORCE( + m_epsilon < 1., + "Relative comparison with epsilon >= 1 does not make sense."); +} + +bool WithinRelMatcher::match(double const &matchee) const +{ + const auto relMargin = + m_epsilon * (std::max)(std::fabs(matchee), std::fabs(m_target)); + return marginComparison(matchee, m_target, + std::isinf(relMargin) ? 0 : relMargin); +} + +std::string WithinRelMatcher::describe() const +{ + Catch::ReusableStringStream sstr; + sstr << "and " << m_target << " are within " << m_epsilon * 100. + << "% of each other"; + return sstr.str(); +} + +} // namespace Floating + +Floating::WithinUlpsMatcher WithinULP(double target, uint64_t maxUlpDiff) +{ + return Floating::WithinUlpsMatcher(target, maxUlpDiff, + Floating::FloatingPointKind::Double); +} + +Floating::WithinUlpsMatcher WithinULP(float target, uint64_t maxUlpDiff) +{ + return Floating::WithinUlpsMatcher(target, maxUlpDiff, + Floating::FloatingPointKind::Float); +} + +Floating::WithinAbsMatcher WithinAbs(double target, double margin) +{ + return Floating::WithinAbsMatcher(target, margin); +} + +Floating::WithinRelMatcher WithinRel(double target, double eps) +{ + return Floating::WithinRelMatcher(target, eps); +} + +Floating::WithinRelMatcher WithinRel(double target) +{ + return Floating::WithinRelMatcher( + target, std::numeric_limits::epsilon() * 100); +} + +Floating::WithinRelMatcher WithinRel(float target, float eps) +{ + return Floating::WithinRelMatcher(target, eps); +} + +Floating::WithinRelMatcher WithinRel(float target) +{ + return Floating::WithinRelMatcher( + target, std::numeric_limits::epsilon() * 100); +} + +} // namespace Matchers +} // namespace Catch +// end catch_matchers_floating.cpp +// start catch_matchers_generic.cpp + +std::string +Catch::Matchers::Generic::Detail::finalizeDescription(const std::string &desc) +{ + if (desc.empty()) { + return "matches undescribed predicate"; + } else { + return "matches predicate: \"" + desc + '"'; + } +} +// end catch_matchers_generic.cpp +// start catch_matchers_string.cpp + +#include + +namespace Catch { +namespace Matchers { + +namespace StdString { + +CasedString::CasedString(std::string const &str, + CaseSensitive::Choice caseSensitivity) + : m_caseSensitivity(caseSensitivity), m_str(adjustString(str)) +{ +} +std::string CasedString::adjustString(std::string const &str) const +{ + return m_caseSensitivity == CaseSensitive::No ? toLower(str) : str; +} +std::string CasedString::caseSensitivitySuffix() const +{ + return m_caseSensitivity == CaseSensitive::No ? " (case insensitive)" + : std::string(); +} + +StringMatcherBase::StringMatcherBase(std::string const &operation, + CasedString const &comparator) + : m_comparator(comparator), m_operation(operation) +{ +} + +std::string StringMatcherBase::describe() const +{ + std::string description; + description.reserve(5 + m_operation.size() + m_comparator.m_str.size() + + m_comparator.caseSensitivitySuffix().size()); + description += m_operation; + description += ": \""; + description += m_comparator.m_str; + description += "\""; + description += m_comparator.caseSensitivitySuffix(); + return description; +} + +EqualsMatcher::EqualsMatcher(CasedString const &comparator) + : StringMatcherBase("equals", comparator) +{ +} + +bool EqualsMatcher::match(std::string const &source) const +{ + return m_comparator.adjustString(source) == m_comparator.m_str; +} + +ContainsMatcher::ContainsMatcher(CasedString const &comparator) + : StringMatcherBase("contains", comparator) +{ +} + +bool ContainsMatcher::match(std::string const &source) const +{ + return contains(m_comparator.adjustString(source), m_comparator.m_str); +} + +StartsWithMatcher::StartsWithMatcher(CasedString const &comparator) + : StringMatcherBase("starts with", comparator) +{ +} + +bool StartsWithMatcher::match(std::string const &source) const +{ + return startsWith(m_comparator.adjustString(source), + m_comparator.m_str); +} + +EndsWithMatcher::EndsWithMatcher(CasedString const &comparator) + : StringMatcherBase("ends with", comparator) +{ +} + +bool EndsWithMatcher::match(std::string const &source) const +{ + return endsWith(m_comparator.adjustString(source), m_comparator.m_str); +} + +RegexMatcher::RegexMatcher(std::string regex, + CaseSensitive::Choice caseSensitivity) + : m_regex(std::move(regex)), m_caseSensitivity(caseSensitivity) +{ +} + +bool RegexMatcher::match(std::string const &matchee) const +{ + auto flags = std::regex:: + ECMAScript; // ECMAScript is the default syntax option anyway + if (m_caseSensitivity == CaseSensitive::Choice::No) { + flags |= std::regex::icase; + } + auto reg = std::regex(m_regex, flags); + return std::regex_match(matchee, reg); +} + +std::string RegexMatcher::describe() const +{ + return "matches " + ::Catch::Detail::stringify(m_regex) + + ((m_caseSensitivity == CaseSensitive::Choice::Yes) + ? " case sensitively" + : " case insensitively"); +} + +} // namespace StdString + +StdString::EqualsMatcher Equals(std::string const &str, + CaseSensitive::Choice caseSensitivity) +{ + return StdString::EqualsMatcher( + StdString::CasedString(str, caseSensitivity)); +} +StdString::ContainsMatcher Contains(std::string const &str, + CaseSensitive::Choice caseSensitivity) +{ + return StdString::ContainsMatcher( + StdString::CasedString(str, caseSensitivity)); +} +StdString::EndsWithMatcher EndsWith(std::string const &str, + CaseSensitive::Choice caseSensitivity) +{ + return StdString::EndsWithMatcher( + StdString::CasedString(str, caseSensitivity)); +} +StdString::StartsWithMatcher StartsWith(std::string const &str, + CaseSensitive::Choice caseSensitivity) +{ + return StdString::StartsWithMatcher( + StdString::CasedString(str, caseSensitivity)); +} + +StdString::RegexMatcher Matches(std::string const ®ex, + CaseSensitive::Choice caseSensitivity) +{ + return StdString::RegexMatcher(regex, caseSensitivity); +} + +} // namespace Matchers +} // namespace Catch +// end catch_matchers_string.cpp +// start catch_message.cpp + +// start catch_uncaught_exceptions.h + +namespace Catch { +bool uncaught_exceptions(); +} // end namespace Catch + +// end catch_uncaught_exceptions.h +#include +#include + +namespace Catch { + +MessageInfo::MessageInfo(StringRef const &_macroName, + SourceLineInfo const &_lineInfo, + ResultWas::OfType _type) + : macroName(_macroName), + lineInfo(_lineInfo), + type(_type), + sequence(++globalCount) +{ +} + +bool MessageInfo::operator==(MessageInfo const &other) const +{ + return sequence == other.sequence; +} + +bool MessageInfo::operator<(MessageInfo const &other) const +{ + return sequence < other.sequence; +} + +// This may need protecting if threading support is added +unsigned int MessageInfo::globalCount = 0; + +//////////////////////////////////////////////////////////////////////////// + +Catch::MessageBuilder::MessageBuilder(StringRef const ¯oName, + SourceLineInfo const &lineInfo, + ResultWas::OfType type) + : m_info(macroName, lineInfo, type) +{ +} + +//////////////////////////////////////////////////////////////////////////// + +ScopedMessage::ScopedMessage(MessageBuilder const &builder) + : m_info(builder.m_info), m_moved() +{ + m_info.message = builder.m_stream.str(); + getResultCapture().pushScopedMessage(m_info); +} + +ScopedMessage::ScopedMessage(ScopedMessage &&old) + : m_info(old.m_info), m_moved() +{ + old.m_moved = true; +} + +ScopedMessage::~ScopedMessage() +{ + if (!uncaught_exceptions() && !m_moved) { + getResultCapture().popScopedMessage(m_info); + } +} + +Capturer::Capturer(StringRef macroName, SourceLineInfo const &lineInfo, + ResultWas::OfType resultType, StringRef names) +{ + auto trimmed = [&](size_t start, size_t end) { + while (names[start] == ',' || + isspace(static_cast(names[start]))) { + ++start; + } + while (names[end] == ',' || + isspace(static_cast(names[end]))) { + --end; + } + return names.substr(start, end - start + 1); + }; + auto skipq = [&](size_t start, char quote) { + for (auto i = start + 1; i < names.size(); ++i) { + if (names[i] == quote) + return i; + if (names[i] == '\\') + ++i; + } + CATCH_INTERNAL_ERROR( + "CAPTURE parsing encountered unmatched quote"); + }; + + size_t start = 0; + std::stack openings; + for (size_t pos = 0; pos < names.size(); ++pos) { + char c = names[pos]; + switch (c) { + case '[': + case '{': + case '(': + // It is basically impossible to disambiguate between + // comparison and start of template args in this context + // case '<': + openings.push(c); + break; + case ']': + case '}': + case ')': + // case '>': + openings.pop(); + break; + case '"': + case '\'': + pos = skipq(pos, c); + break; + case ',': + if (start != pos && openings.empty()) { + m_messages.emplace_back(macroName, lineInfo, + resultType); + m_messages.back().message = + static_cast( + trimmed(start, pos)); + m_messages.back().message += " := "; + start = pos; + } + } + } + assert(openings.empty() && "Mismatched openings"); + m_messages.emplace_back(macroName, lineInfo, resultType); + m_messages.back().message = + static_cast(trimmed(start, names.size() - 1)); + m_messages.back().message += " := "; +} +Capturer::~Capturer() +{ + if (!uncaught_exceptions()) { + assert(m_captured == m_messages.size()); + for (size_t i = 0; i < m_captured; ++i) + m_resultCapture.popScopedMessage(m_messages[i]); + } +} + +void Capturer::captureValue(size_t index, std::string const &value) +{ + assert(index < m_messages.size()); + m_messages[index].message += value; + m_resultCapture.pushScopedMessage(m_messages[index]); + m_captured++; +} + +} // end namespace Catch +// end catch_message.cpp +// start catch_output_redirect.cpp + +// start catch_output_redirect.h +#ifndef TWOBLUECUBES_CATCH_OUTPUT_REDIRECT_H +#define TWOBLUECUBES_CATCH_OUTPUT_REDIRECT_H + +#include +#include +#include + +namespace Catch { + +class RedirectedStream { + std::ostream &m_originalStream; + std::ostream &m_redirectionStream; + std::streambuf *m_prevBuf; + +public: + RedirectedStream(std::ostream &originalStream, + std::ostream &redirectionStream); + ~RedirectedStream(); +}; + +class RedirectedStdOut { + ReusableStringStream m_rss; + RedirectedStream m_cout; + +public: + RedirectedStdOut(); + auto str() const -> std::string; +}; + +// StdErr has two constituent streams in C++, std::cerr and std::clog +// This means that we need to redirect 2 streams into 1 to keep proper +// order of writes +class RedirectedStdErr { + ReusableStringStream m_rss; + RedirectedStream m_cerr; + RedirectedStream m_clog; + +public: + RedirectedStdErr(); + auto str() const -> std::string; +}; + +class RedirectedStreams { +public: + RedirectedStreams(RedirectedStreams const &) = delete; + RedirectedStreams &operator=(RedirectedStreams const &) = delete; + RedirectedStreams(RedirectedStreams &&) = delete; + RedirectedStreams &operator=(RedirectedStreams &&) = delete; + + RedirectedStreams(std::string &redirectedCout, + std::string &redirectedCerr); + ~RedirectedStreams(); + +private: + std::string &m_redirectedCout; + std::string &m_redirectedCerr; + RedirectedStdOut m_redirectedStdOut; + RedirectedStdErr m_redirectedStdErr; +}; + +#if defined(CATCH_CONFIG_NEW_CAPTURE) + +// Windows's implementation of std::tmpfile is terrible (it tries +// to create a file inside system folder, thus requiring elevated +// privileges for the binary), so we have to use tmpnam(_s) and +// create the file ourselves there. +class TempFile { +public: + TempFile(TempFile const &) = delete; + TempFile &operator=(TempFile const &) = delete; + TempFile(TempFile &&) = delete; + TempFile &operator=(TempFile &&) = delete; + + TempFile(); + ~TempFile(); + + std::FILE *getFile(); + std::string getContents(); + +private: + std::FILE *m_file = nullptr; +#if defined(_MSC_VER) + char m_buffer[L_tmpnam] = {0}; +#endif +}; + +class OutputRedirect { +public: + OutputRedirect(OutputRedirect const &) = delete; + OutputRedirect &operator=(OutputRedirect const &) = delete; + OutputRedirect(OutputRedirect &&) = delete; + OutputRedirect &operator=(OutputRedirect &&) = delete; + + OutputRedirect(std::string &stdout_dest, std::string &stderr_dest); + ~OutputRedirect(); + +private: + int m_originalStdout = -1; + int m_originalStderr = -1; + TempFile m_stdoutFile; + TempFile m_stderrFile; + std::string &m_stdoutDest; + std::string &m_stderrDest; +}; + +#endif + +} // end namespace Catch + +#endif // TWOBLUECUBES_CATCH_OUTPUT_REDIRECT_H +// end catch_output_redirect.h +#include +#include +#include +#include +#include + +#if defined(CATCH_CONFIG_NEW_CAPTURE) +#if defined(_MSC_VER) +#include //_dup and _dup2 +#define dup _dup +#define dup2 _dup2 +#define fileno _fileno +#else +#include // dup and dup2 +#endif +#endif + +namespace Catch { + +RedirectedStream::RedirectedStream(std::ostream &originalStream, + std::ostream &redirectionStream) + : m_originalStream(originalStream), + m_redirectionStream(redirectionStream), + m_prevBuf(m_originalStream.rdbuf()) +{ + m_originalStream.rdbuf(m_redirectionStream.rdbuf()); +} + +RedirectedStream::~RedirectedStream() +{ + m_originalStream.rdbuf(m_prevBuf); +} + +RedirectedStdOut::RedirectedStdOut() : m_cout(Catch::cout(), m_rss.get()) {} +auto RedirectedStdOut::str() const -> std::string +{ + return m_rss.str(); +} + +RedirectedStdErr::RedirectedStdErr() + : m_cerr(Catch::cerr(), m_rss.get()), m_clog(Catch::clog(), m_rss.get()) +{ +} +auto RedirectedStdErr::str() const -> std::string +{ + return m_rss.str(); +} + +RedirectedStreams::RedirectedStreams(std::string &redirectedCout, + std::string &redirectedCerr) + : m_redirectedCout(redirectedCout), m_redirectedCerr(redirectedCerr) +{ +} + +RedirectedStreams::~RedirectedStreams() +{ + m_redirectedCout += m_redirectedStdOut.str(); + m_redirectedCerr += m_redirectedStdErr.str(); +} + +#if defined(CATCH_CONFIG_NEW_CAPTURE) + +#if defined(_MSC_VER) +TempFile::TempFile() +{ + if (tmpnam_s(m_buffer)) { + CATCH_RUNTIME_ERROR("Could not get a temp filename"); + } + if (fopen_s(&m_file, m_buffer, "w+")) { + char buffer[100]; + if (strerror_s(buffer, errno)) { + CATCH_RUNTIME_ERROR( + "Could not translate errno to a string"); + } + CATCH_RUNTIME_ERROR("Could not open the temp file: '" + << m_buffer << "' because: " << buffer); + } +} +#else +TempFile::TempFile() +{ + m_file = std::tmpfile(); + if (!m_file) { + CATCH_RUNTIME_ERROR("Could not create a temp file."); + } +} + +#endif + +TempFile::~TempFile() +{ + // TBD: What to do about errors here? + std::fclose(m_file); + // We manually create the file on Windows only, on Linux + // it will be autodeleted +#if defined(_MSC_VER) + std::remove(m_buffer); +#endif +} + +FILE *TempFile::getFile() +{ + return m_file; +} + +std::string TempFile::getContents() +{ + std::stringstream sstr; + char buffer[100] = {}; + std::rewind(m_file); + while (std::fgets(buffer, sizeof(buffer), m_file)) { + sstr << buffer; + } + return sstr.str(); +} + +OutputRedirect::OutputRedirect(std::string &stdout_dest, + std::string &stderr_dest) + : m_originalStdout(dup(1)), + m_originalStderr(dup(2)), + m_stdoutDest(stdout_dest), + m_stderrDest(stderr_dest) +{ + dup2(fileno(m_stdoutFile.getFile()), 1); + dup2(fileno(m_stderrFile.getFile()), 2); +} + +OutputRedirect::~OutputRedirect() +{ + Catch::cout() << std::flush; + fflush(stdout); + // Since we support overriding these streams, we flush cerr + // even though std::cerr is unbuffered + Catch::cerr() << std::flush; + Catch::clog() << std::flush; + fflush(stderr); + + dup2(m_originalStdout, 1); + dup2(m_originalStderr, 2); + + m_stdoutDest += m_stdoutFile.getContents(); + m_stderrDest += m_stderrFile.getContents(); +} + +#endif // CATCH_CONFIG_NEW_CAPTURE + +} // namespace Catch + +#if defined(CATCH_CONFIG_NEW_CAPTURE) +#if defined(_MSC_VER) +#undef dup +#undef dup2 +#undef fileno +#endif +#endif +// end catch_output_redirect.cpp +// start catch_polyfills.cpp + +#include + +namespace Catch { + +#if !defined(CATCH_CONFIG_POLYFILL_ISNAN) +bool isnan(float f) +{ + return std::isnan(f); +} +bool isnan(double d) +{ + return std::isnan(d); +} +#else +// For now we only use this for embarcadero +bool isnan(float f) +{ + return std::_isnan(f); +} +bool isnan(double d) +{ + return std::_isnan(d); +} +#endif + +} // end namespace Catch +// end catch_polyfills.cpp +// start catch_random_number_generator.cpp + +namespace Catch { + +namespace { + +#if defined(_MSC_VER) +#pragma warning(push) +#pragma warning(disable : 4146) // we negate uint32 during the rotate +#endif +// Safe rotr implementation thanks to John Regehr +uint32_t rotate_right(uint32_t val, uint32_t count) +{ + const uint32_t mask = 31; + count &= mask; + return (val >> count) | (val << (-count & mask)); +} + +#if defined(_MSC_VER) +#pragma warning(pop) +#endif + +} + +SimplePcg32::SimplePcg32(result_type seed_) +{ + seed(seed_); +} + +void SimplePcg32::seed(result_type seed_) +{ + m_state = 0; + (*this)(); + m_state += seed_; + (*this)(); +} + +void SimplePcg32::discard(uint64_t skip) +{ + // We could implement this to run in O(log n) steps, but this + // should suffice for our use case. + for (uint64_t s = 0; s < skip; ++s) { + static_cast((*this)()); + } +} + +SimplePcg32::result_type SimplePcg32::operator()() +{ + // prepare the output value + const uint32_t xorshifted = + static_cast(((m_state >> 18u) ^ m_state) >> 27u); + const auto output = rotate_right(xorshifted, m_state >> 59u); + + // advance state + m_state = m_state * 6364136223846793005ULL + s_inc; + + return output; +} + +bool operator==(SimplePcg32 const &lhs, SimplePcg32 const &rhs) +{ + return lhs.m_state == rhs.m_state; +} + +bool operator!=(SimplePcg32 const &lhs, SimplePcg32 const &rhs) +{ + return lhs.m_state != rhs.m_state; +} +} +// end catch_random_number_generator.cpp +// start catch_registry_hub.cpp + +// start catch_test_case_registry_impl.h + +#include +#include +#include +#include + +namespace Catch { + +class TestCase; +struct IConfig; + +std::vector sortTests(IConfig const &config, + std::vector const &unsortedTestCases); + +bool isThrowSafe(TestCase const &testCase, IConfig const &config); +bool matchTest(TestCase const &testCase, TestSpec const &testSpec, + IConfig const &config); + +void enforceNoDuplicateTestCases(std::vector const &functions); + +std::vector filterTests(std::vector const &testCases, + TestSpec const &testSpec, + IConfig const &config); +std::vector const &getAllTestCasesSorted(IConfig const &config); + +class TestRegistry : public ITestCaseRegistry { +public: + virtual ~TestRegistry() = default; + + virtual void registerTest(TestCase const &testCase); + + std::vector const &getAllTests() const override; + std::vector const & + getAllTestsSorted(IConfig const &config) const override; + +private: + std::vector m_functions; + mutable RunTests::InWhatOrder m_currentSortOrder = + RunTests::InDeclarationOrder; + mutable std::vector m_sortedFunctions; + std::size_t m_unnamedCount = 0; + std::ios_base::Init m_ostreamInit; // Forces cout/ cerr to be initialised +}; + +/////////////////////////////////////////////////////////////////////////// + +class TestInvokerAsFunction : public ITestInvoker { + void (*m_testAsFunction)(); + +public: + TestInvokerAsFunction(void (*testAsFunction)()) noexcept; + + void invoke() const override; +}; + +std::string extractClassName(StringRef const &classOrQualifiedMethodName); + +/////////////////////////////////////////////////////////////////////////// + +} // end namespace Catch + +// end catch_test_case_registry_impl.h +// start catch_reporter_registry.h + +#include + +namespace Catch { + +class ReporterRegistry : public IReporterRegistry { + +public: + ~ReporterRegistry() override; + + IStreamingReporterPtr create(std::string const &name, + IConfigPtr const &config) const override; + + void registerReporter(std::string const &name, + IReporterFactoryPtr const &factory); + void registerListener(IReporterFactoryPtr const &factory); + + FactoryMap const &getFactories() const override; + Listeners const &getListeners() const override; + +private: + FactoryMap m_factories; + Listeners m_listeners; +}; +} + +// end catch_reporter_registry.h +// start catch_tag_alias_registry.h + +// start catch_tag_alias.h + +#include + +namespace Catch { + +struct TagAlias { + TagAlias(std::string const &_tag, SourceLineInfo _lineInfo); + + std::string tag; + SourceLineInfo lineInfo; +}; + +} // end namespace Catch + +// end catch_tag_alias.h +#include + +namespace Catch { + +class TagAliasRegistry : public ITagAliasRegistry { +public: + ~TagAliasRegistry() override; + TagAlias const *find(std::string const &alias) const override; + std::string + expandAliases(std::string const &unexpandedTestSpec) const override; + void add(std::string const &alias, std::string const &tag, + SourceLineInfo const &lineInfo); + +private: + std::map m_registry; +}; + +} // end namespace Catch + +// end catch_tag_alias_registry.h +// start catch_startup_exception_registry.h + +#include +#include + +namespace Catch { + +class StartupExceptionRegistry { +#if !defined(CATCH_CONFIG_DISABLE_EXCEPTIONS) +public: + void add(std::exception_ptr const &exception) noexcept; + std::vector const &getExceptions() const noexcept; + +private: + std::vector m_exceptions; +#endif +}; + +} // end namespace Catch + +// end catch_startup_exception_registry.h +// start catch_singletons.hpp + +namespace Catch { + +struct ISingleton { + virtual ~ISingleton(); +}; + +void addSingleton(ISingleton *singleton); +void cleanupSingletons(); + +template +class Singleton : SingletonImplT, public ISingleton { + + static auto getInternal() -> Singleton * + { + static Singleton *s_instance = nullptr; + if (!s_instance) { + s_instance = new Singleton; + addSingleton(s_instance); + } + return s_instance; + } + +public: + static auto get() -> InterfaceT const & { return *getInternal(); } + static auto getMutable() -> MutableInterfaceT & + { + return *getInternal(); + } +}; + +} // namespace Catch + +// end catch_singletons.hpp +namespace Catch { + +namespace { + +class RegistryHub : public IRegistryHub, + public IMutableRegistryHub, + private NonCopyable { + +public: // IRegistryHub + RegistryHub() = default; + IReporterRegistry const &getReporterRegistry() const override + { + return m_reporterRegistry; + } + ITestCaseRegistry const &getTestCaseRegistry() const override + { + return m_testCaseRegistry; + } + IExceptionTranslatorRegistry const & + getExceptionTranslatorRegistry() const override + { + return m_exceptionTranslatorRegistry; + } + ITagAliasRegistry const &getTagAliasRegistry() const override + { + return m_tagAliasRegistry; + } + StartupExceptionRegistry const & + getStartupExceptionRegistry() const override + { + return m_exceptionRegistry; + } + +public: // IMutableRegistryHub + void registerReporter(std::string const &name, + IReporterFactoryPtr const &factory) override + { + m_reporterRegistry.registerReporter(name, factory); + } + void registerListener(IReporterFactoryPtr const &factory) override + { + m_reporterRegistry.registerListener(factory); + } + void registerTest(TestCase const &testInfo) override + { + m_testCaseRegistry.registerTest(testInfo); + } + void registerTranslator(const IExceptionTranslator *translator) override + { + m_exceptionTranslatorRegistry.registerTranslator(translator); + } + void registerTagAlias(std::string const &alias, std::string const &tag, + SourceLineInfo const &lineInfo) override + { + m_tagAliasRegistry.add(alias, tag, lineInfo); + } + void registerStartupException() noexcept override + { +#if !defined(CATCH_CONFIG_DISABLE_EXCEPTIONS) + m_exceptionRegistry.add(std::current_exception()); +#else + CATCH_INTERNAL_ERROR( + "Attempted to register active exception under CATCH_CONFIG_DISABLE_EXCEPTIONS!"); +#endif + } + IMutableEnumValuesRegistry &getMutableEnumValuesRegistry() override + { + return m_enumValuesRegistry; + } + +private: + TestRegistry m_testCaseRegistry; + ReporterRegistry m_reporterRegistry; + ExceptionTranslatorRegistry m_exceptionTranslatorRegistry; + TagAliasRegistry m_tagAliasRegistry; + StartupExceptionRegistry m_exceptionRegistry; + Detail::EnumValuesRegistry m_enumValuesRegistry; +}; +} + +using RegistryHubSingleton = + Singleton; + +IRegistryHub const &getRegistryHub() +{ + return RegistryHubSingleton::get(); +} +IMutableRegistryHub &getMutableRegistryHub() +{ + return RegistryHubSingleton::getMutable(); +} +void cleanUp() +{ + cleanupSingletons(); + cleanUpContext(); +} +std::string translateActiveException() +{ + return getRegistryHub() + .getExceptionTranslatorRegistry() + .translateActiveException(); +} + +} // end namespace Catch +// end catch_registry_hub.cpp +// start catch_reporter_registry.cpp + +namespace Catch { + +ReporterRegistry::~ReporterRegistry() = default; + +IStreamingReporterPtr ReporterRegistry::create(std::string const &name, + IConfigPtr const &config) const +{ + auto it = m_factories.find(name); + if (it == m_factories.end()) + return nullptr; + return it->second->create(ReporterConfig(config)); +} + +void ReporterRegistry::registerReporter(std::string const &name, + IReporterFactoryPtr const &factory) +{ + m_factories.emplace(name, factory); +} +void ReporterRegistry::registerListener(IReporterFactoryPtr const &factory) +{ + m_listeners.push_back(factory); +} + +IReporterRegistry::FactoryMap const &ReporterRegistry::getFactories() const +{ + return m_factories; +} +IReporterRegistry::Listeners const &ReporterRegistry::getListeners() const +{ + return m_listeners; +} + +} +// end catch_reporter_registry.cpp +// start catch_result_type.cpp + +namespace Catch { + +bool isOk(ResultWas::OfType resultType) +{ + return (resultType & ResultWas::FailureBit) == 0; +} +bool isJustInfo(int flags) +{ + return flags == ResultWas::Info; +} + +ResultDisposition::Flags operator|(ResultDisposition::Flags lhs, + ResultDisposition::Flags rhs) +{ + return static_cast(static_cast(lhs) | + static_cast(rhs)); +} + +bool shouldContinueOnFailure(int flags) +{ + return (flags & ResultDisposition::ContinueOnFailure) != 0; +} +bool shouldSuppressFailure(int flags) +{ + return (flags & ResultDisposition::SuppressFail) != 0; +} + +} // end namespace Catch +// end catch_result_type.cpp +// start catch_run_context.cpp + +#include +#include +#include + +namespace Catch { + +namespace Generators { +struct GeneratorTracker : TestCaseTracking::TrackerBase, IGeneratorTracker { + GeneratorBasePtr m_generator; + + GeneratorTracker( + TestCaseTracking::NameAndLocation const &nameAndLocation, + TrackerContext &ctx, ITracker *parent) + : TrackerBase(nameAndLocation, ctx, parent) + { + } + ~GeneratorTracker(); + + static GeneratorTracker & + acquire(TrackerContext &ctx, + TestCaseTracking::NameAndLocation const &nameAndLocation) + { + std::shared_ptr tracker; + + ITracker ¤tTracker = ctx.currentTracker(); + // Under specific circumstances, the generator we want + // to acquire is also the current tracker. If this is + // the case, we have to avoid looking through current + // tracker's children, and instead return the current + // tracker. + // A case where this check is important is e.g. + // for (int i = 0; i < 5; ++i) { + // int n = GENERATE(1, 2); + // } + // + // without it, the code above creates 5 nested generators. + if (currentTracker.nameAndLocation() == nameAndLocation) { + auto thisTracker = currentTracker.parent().findChild( + nameAndLocation); + assert(thisTracker); + assert(thisTracker->isGeneratorTracker()); + tracker = std::static_pointer_cast( + thisTracker); + } else if (TestCaseTracking::ITrackerPtr childTracker = + currentTracker.findChild(nameAndLocation)) { + assert(childTracker); + assert(childTracker->isGeneratorTracker()); + tracker = std::static_pointer_cast( + childTracker); + } else { + tracker = std::make_shared( + nameAndLocation, ctx, ¤tTracker); + currentTracker.addChild(tracker); + } + + if (!tracker->isComplete()) { + tracker->open(); + } + + return *tracker; + } + + // TrackerBase interface + bool isGeneratorTracker() const override { return true; } + auto hasGenerator() const -> bool override { return !!m_generator; } + void close() override + { + TrackerBase::close(); + // If a generator has a child (it is followed by a section) + // and none of its children have started, then we must wait + // until later to start consuming its values. + // This catches cases where `GENERATE` is placed between two + // `SECTION`s. + // **The check for m_children.empty cannot be removed**. + // doing so would break `GENERATE` _not_ followed by `SECTION`s. + const bool should_wait_for_child = [&]() { + // No children -> nobody to wait for + if (m_children.empty()) { + return false; + } + // If at least one child started executing, don't wait + if (std::find_if( + m_children.begin(), m_children.end(), + [](TestCaseTracking::ITrackerPtr tracker) { + return tracker->hasStarted(); + }) != m_children.end()) { + return false; + } + + // No children have started. We need to check if they _can_ + // start, and thus we should wait for them, or they cannot + // start (due to filters), and we shouldn't wait for them + auto *parent = m_parent; + // This is safe: there is always at least one section + // tracker in a test case tracking tree + while (!parent->isSectionTracker()) { + parent = &(parent->parent()); + } + assert(parent && + "Missing root (test case) level section"); + + auto const &parentSection = + static_cast(*parent); + auto const &filters = parentSection.getFilters(); + // No filters -> no restrictions on running sections + if (filters.empty()) { + return true; + } + + for (auto const &child : m_children) { + if (child->isSectionTracker() && + std::find(filters.begin(), filters.end(), + static_cast( + *child) + .trimmedName()) != + filters.end()) { + return true; + } + } + return false; + }(); + + // This check is a bit tricky, because m_generator->next() + // has a side-effect, where it consumes generator's current + // value, but we do not want to invoke the side-effect if + // this generator is still waiting for any child to start. + if (should_wait_for_child || + (m_runState == CompletedSuccessfully && + m_generator->next())) { + m_children.clear(); + m_runState = Executing; + } + } + + // IGeneratorTracker interface + auto getGenerator() const -> GeneratorBasePtr const & override + { + return m_generator; + } + void setGenerator(GeneratorBasePtr &&generator) override + { + m_generator = std::move(generator); + } +}; +GeneratorTracker::~GeneratorTracker() {} +} + +RunContext::RunContext(IConfigPtr const &_config, + IStreamingReporterPtr &&reporter) + : m_runInfo(_config->name()), + m_context(getCurrentMutableContext()), + m_config(_config), + m_reporter(std::move(reporter)), + m_lastAssertionInfo{StringRef(), SourceLineInfo("", 0), StringRef(), + ResultDisposition::Normal}, + m_includeSuccessfulResults( + m_config->includeSuccessfulResults() || + m_reporter->getPreferences().shouldReportAllAssertions) +{ + m_context.setRunner(this); + m_context.setConfig(m_config); + m_context.setResultCapture(this); + m_reporter->testRunStarting(m_runInfo); +} + +RunContext::~RunContext() +{ + m_reporter->testRunEnded(TestRunStats(m_runInfo, m_totals, aborting())); +} + +void RunContext::testGroupStarting(std::string const &testSpec, + std::size_t groupIndex, + std::size_t groupsCount) +{ + m_reporter->testGroupStarting( + GroupInfo(testSpec, groupIndex, groupsCount)); +} + +void RunContext::testGroupEnded(std::string const &testSpec, + Totals const &totals, std::size_t groupIndex, + std::size_t groupsCount) +{ + m_reporter->testGroupEnded( + TestGroupStats(GroupInfo(testSpec, groupIndex, groupsCount), + totals, aborting())); +} + +Totals RunContext::runTest(TestCase const &testCase) +{ + Totals prevTotals = m_totals; + + std::string redirectedCout; + std::string redirectedCerr; + + auto const &testInfo = testCase.getTestCaseInfo(); + + m_reporter->testCaseStarting(testInfo); + + m_activeTestCase = &testCase; + + ITracker &rootTracker = m_trackerContext.startRun(); + assert(rootTracker.isSectionTracker()); + static_cast(rootTracker) + .addInitialFilters(m_config->getSectionsToRun()); + do { + m_trackerContext.startCycle(); + m_testCaseTracker = &SectionTracker::acquire( + m_trackerContext, + TestCaseTracking::NameAndLocation(testInfo.name, + testInfo.lineInfo)); + runCurrentTest(redirectedCout, redirectedCerr); + } while (!m_testCaseTracker->isSuccessfullyCompleted() && !aborting()); + + Totals deltaTotals = m_totals.delta(prevTotals); + if (testInfo.expectedToFail() && deltaTotals.testCases.passed > 0) { + deltaTotals.assertions.failed++; + deltaTotals.testCases.passed--; + deltaTotals.testCases.failed++; + } + m_totals.testCases += deltaTotals.testCases; + m_reporter->testCaseEnded(TestCaseStats(testInfo, deltaTotals, + redirectedCout, redirectedCerr, + aborting())); + + m_activeTestCase = nullptr; + m_testCaseTracker = nullptr; + + return deltaTotals; +} + +IConfigPtr RunContext::config() const +{ + return m_config; +} + +IStreamingReporter &RunContext::reporter() const +{ + return *m_reporter; +} + +void RunContext::assertionEnded(AssertionResult const &result) +{ + if (result.getResultType() == ResultWas::Ok) { + m_totals.assertions.passed++; + m_lastAssertionPassed = true; + } else if (!result.isOk()) { + m_lastAssertionPassed = false; + if (m_activeTestCase->getTestCaseInfo().okToFail()) + m_totals.assertions.failedButOk++; + else + m_totals.assertions.failed++; + } else { + m_lastAssertionPassed = true; + } + + // We have no use for the return value (whether messages should be cleared), because messages were made scoped + // and should be let to clear themselves out. + static_cast(m_reporter->assertionEnded( + AssertionStats(result, m_messages, m_totals))); + + if (result.getResultType() != ResultWas::Warning) + m_messageScopes.clear(); + + // Reset working state + resetAssertionInfo(); + m_lastResult = result; +} +void RunContext::resetAssertionInfo() +{ + m_lastAssertionInfo.macroName = StringRef(); + m_lastAssertionInfo.capturedExpression = + "{Unknown expression after the reported line}"_sr; +} + +bool RunContext::sectionStarted(SectionInfo const §ionInfo, + Counts &assertions) +{ + ITracker §ionTracker = SectionTracker::acquire( + m_trackerContext, + TestCaseTracking::NameAndLocation(sectionInfo.name, + sectionInfo.lineInfo)); + if (!sectionTracker.isOpen()) + return false; + m_activeSections.push_back(§ionTracker); + + m_lastAssertionInfo.lineInfo = sectionInfo.lineInfo; + + m_reporter->sectionStarting(sectionInfo); + + assertions = m_totals.assertions; + + return true; +} +auto RunContext::acquireGeneratorTracker(StringRef generatorName, + SourceLineInfo const &lineInfo) + -> IGeneratorTracker & +{ + using namespace Generators; + GeneratorTracker &tracker = GeneratorTracker::acquire( + m_trackerContext, + TestCaseTracking::NameAndLocation( + static_cast(generatorName), lineInfo)); + m_lastAssertionInfo.lineInfo = lineInfo; + return tracker; +} + +bool RunContext::testForMissingAssertions(Counts &assertions) +{ + if (assertions.total() != 0) + return false; + if (!m_config->warnAboutMissingAssertions()) + return false; + if (m_trackerContext.currentTracker().hasChildren()) + return false; + m_totals.assertions.failed++; + assertions.failed++; + return true; +} + +void RunContext::sectionEnded(SectionEndInfo const &endInfo) +{ + Counts assertions = m_totals.assertions - endInfo.prevAssertions; + bool missingAssertions = testForMissingAssertions(assertions); + + if (!m_activeSections.empty()) { + m_activeSections.back()->close(); + m_activeSections.pop_back(); + } + + m_reporter->sectionEnded(SectionStats(endInfo.sectionInfo, assertions, + endInfo.durationInSeconds, + missingAssertions)); + m_messages.clear(); + m_messageScopes.clear(); +} + +void RunContext::sectionEndedEarly(SectionEndInfo const &endInfo) +{ + if (m_unfinishedSections.empty()) + m_activeSections.back()->fail(); + else + m_activeSections.back()->close(); + m_activeSections.pop_back(); + + m_unfinishedSections.push_back(endInfo); +} + +#if defined(CATCH_CONFIG_ENABLE_BENCHMARKING) +void RunContext::benchmarkPreparing(std::string const &name) +{ + m_reporter->benchmarkPreparing(name); +} +void RunContext::benchmarkStarting(BenchmarkInfo const &info) +{ + m_reporter->benchmarkStarting(info); +} +void RunContext::benchmarkEnded(BenchmarkStats<> const &stats) +{ + m_reporter->benchmarkEnded(stats); +} +void RunContext::benchmarkFailed(std::string const &error) +{ + m_reporter->benchmarkFailed(error); +} +#endif // CATCH_CONFIG_ENABLE_BENCHMARKING + +void RunContext::pushScopedMessage(MessageInfo const &message) +{ + m_messages.push_back(message); +} + +void RunContext::popScopedMessage(MessageInfo const &message) +{ + m_messages.erase(std::remove(m_messages.begin(), m_messages.end(), + message), + m_messages.end()); +} + +void RunContext::emplaceUnscopedMessage(MessageBuilder const &builder) +{ + m_messageScopes.emplace_back(builder); +} + +std::string RunContext::getCurrentTestName() const +{ + return m_activeTestCase ? m_activeTestCase->getTestCaseInfo().name + : std::string(); +} + +const AssertionResult *RunContext::getLastResult() const +{ + return &(*m_lastResult); +} + +void RunContext::exceptionEarlyReported() +{ + m_shouldReportUnexpected = false; +} + +void RunContext::handleFatalErrorCondition(StringRef message) +{ + // First notify reporter that bad things happened + m_reporter->fatalErrorEncountered(message); + + // Don't rebuild the result -- the stringification itself can cause more fatal errors + // Instead, fake a result data. + AssertionResultData tempResult(ResultWas::FatalErrorCondition, {false}); + tempResult.message = static_cast(message); + AssertionResult result(m_lastAssertionInfo, tempResult); + + assertionEnded(result); + + handleUnfinishedSections(); + + // Recreate section for test case (as we will lose the one that was in scope) + auto const &testCaseInfo = m_activeTestCase->getTestCaseInfo(); + SectionInfo testCaseSection(testCaseInfo.lineInfo, testCaseInfo.name); + + Counts assertions; + assertions.failed = 1; + SectionStats testCaseSectionStats(testCaseSection, assertions, 0, + false); + m_reporter->sectionEnded(testCaseSectionStats); + + auto const &testInfo = m_activeTestCase->getTestCaseInfo(); + + Totals deltaTotals; + deltaTotals.testCases.failed = 1; + deltaTotals.assertions.failed = 1; + m_reporter->testCaseEnded(TestCaseStats( + testInfo, deltaTotals, std::string(), std::string(), false)); + m_totals.testCases.failed++; + testGroupEnded(std::string(), m_totals, 1, 1); + m_reporter->testRunEnded(TestRunStats(m_runInfo, m_totals, false)); +} + +bool RunContext::lastAssertionPassed() +{ + return m_lastAssertionPassed; +} + +void RunContext::assertionPassed() +{ + m_lastAssertionPassed = true; + ++m_totals.assertions.passed; + resetAssertionInfo(); + m_messageScopes.clear(); +} + +bool RunContext::aborting() const +{ + return m_totals.assertions.failed >= + static_cast(m_config->abortAfter()); +} + +void RunContext::runCurrentTest(std::string &redirectedCout, + std::string &redirectedCerr) +{ + auto const &testCaseInfo = m_activeTestCase->getTestCaseInfo(); + SectionInfo testCaseSection(testCaseInfo.lineInfo, testCaseInfo.name); + m_reporter->sectionStarting(testCaseSection); + Counts prevAssertions = m_totals.assertions; + double duration = 0; + m_shouldReportUnexpected = true; + m_lastAssertionInfo = {"TEST_CASE"_sr, testCaseInfo.lineInfo, + StringRef(), ResultDisposition::Normal}; + + seedRng(*m_config); + + Timer timer; + CATCH_TRY + { + if (m_reporter->getPreferences().shouldRedirectStdOut) { +#if !defined(CATCH_CONFIG_EXPERIMENTAL_REDIRECT) + RedirectedStreams redirectedStreams(redirectedCout, + redirectedCerr); + + timer.start(); + invokeActiveTestCase(); +#else + OutputRedirect r(redirectedCout, redirectedCerr); + timer.start(); + invokeActiveTestCase(); +#endif + } else { + timer.start(); + invokeActiveTestCase(); + } + duration = timer.getElapsedSeconds(); + } + CATCH_CATCH_ANON(TestFailureException &) + { + // This just means the test was aborted due to failure + } + CATCH_CATCH_ALL + { + // Under CATCH_CONFIG_FAST_COMPILE, unexpected exceptions under REQUIRE assertions + // are reported without translation at the point of origin. + if (m_shouldReportUnexpected) { + AssertionReaction dummyReaction; + handleUnexpectedInflightException( + m_lastAssertionInfo, translateActiveException(), + dummyReaction); + } + } + Counts assertions = m_totals.assertions - prevAssertions; + bool missingAssertions = testForMissingAssertions(assertions); + + m_testCaseTracker->close(); + handleUnfinishedSections(); + m_messages.clear(); + m_messageScopes.clear(); + + SectionStats testCaseSectionStats(testCaseSection, assertions, duration, + missingAssertions); + m_reporter->sectionEnded(testCaseSectionStats); +} + +void RunContext::invokeActiveTestCase() +{ + FatalConditionHandlerGuard _(&m_fatalConditionhandler); + m_activeTestCase->invoke(); +} + +void RunContext::handleUnfinishedSections() +{ + // If sections ended prematurely due to an exception we stored their + // infos here so we can tear them down outside the unwind process. + for (auto it = m_unfinishedSections.rbegin(), + itEnd = m_unfinishedSections.rend(); + it != itEnd; ++it) + sectionEnded(*it); + m_unfinishedSections.clear(); +} + +void RunContext::handleExpr(AssertionInfo const &info, + ITransientExpression const &expr, + AssertionReaction &reaction) +{ + m_reporter->assertionStarting(info); + + bool negated = isFalseTest(info.resultDisposition); + bool result = expr.getResult() != negated; + + if (result) { + if (!m_includeSuccessfulResults) { + assertionPassed(); + } else { + reportExpr(info, ResultWas::Ok, &expr, negated); + } + } else { + reportExpr(info, ResultWas::ExpressionFailed, &expr, negated); + populateReaction(reaction); + } +} +void RunContext::reportExpr(AssertionInfo const &info, + ResultWas::OfType resultType, + ITransientExpression const *expr, bool negated) +{ + + m_lastAssertionInfo = info; + AssertionResultData data(resultType, LazyExpression(negated)); + + AssertionResult assertionResult{info, data}; + assertionResult.m_resultData.lazyExpression.m_transientExpression = + expr; + + assertionEnded(assertionResult); +} + +void RunContext::handleMessage(AssertionInfo const &info, + ResultWas::OfType resultType, + StringRef const &message, + AssertionReaction &reaction) +{ + m_reporter->assertionStarting(info); + + m_lastAssertionInfo = info; + + AssertionResultData data(resultType, LazyExpression(false)); + data.message = static_cast(message); + AssertionResult assertionResult{m_lastAssertionInfo, data}; + assertionEnded(assertionResult); + if (!assertionResult.isOk()) + populateReaction(reaction); +} +void RunContext::handleUnexpectedExceptionNotThrown(AssertionInfo const &info, + AssertionReaction &reaction) +{ + handleNonExpr(info, Catch::ResultWas::DidntThrowException, reaction); +} + +void RunContext::handleUnexpectedInflightException(AssertionInfo const &info, + std::string const &message, + AssertionReaction &reaction) +{ + m_lastAssertionInfo = info; + + AssertionResultData data(ResultWas::ThrewException, + LazyExpression(false)); + data.message = message; + AssertionResult assertionResult{info, data}; + assertionEnded(assertionResult); + populateReaction(reaction); +} + +void RunContext::populateReaction(AssertionReaction &reaction) +{ + reaction.shouldDebugBreak = m_config->shouldDebugBreak(); + reaction.shouldThrow = aborting() || + (m_lastAssertionInfo.resultDisposition & + ResultDisposition::Normal); +} + +void RunContext::handleIncomplete(AssertionInfo const &info) +{ + m_lastAssertionInfo = info; + + AssertionResultData data(ResultWas::ThrewException, + LazyExpression(false)); + data.message = + "Exception translation was disabled by CATCH_CONFIG_FAST_COMPILE"; + AssertionResult assertionResult{info, data}; + assertionEnded(assertionResult); +} +void RunContext::handleNonExpr(AssertionInfo const &info, + ResultWas::OfType resultType, + AssertionReaction &reaction) +{ + m_lastAssertionInfo = info; + + AssertionResultData data(resultType, LazyExpression(false)); + AssertionResult assertionResult{info, data}; + assertionEnded(assertionResult); + + if (!assertionResult.isOk()) + populateReaction(reaction); +} + +IResultCapture &getResultCapture() +{ + if (auto *capture = getCurrentContext().getResultCapture()) + return *capture; + else + CATCH_INTERNAL_ERROR("No result capture instance"); +} + +void seedRng(IConfig const &config) +{ + if (config.rngSeed() != 0) { + std::srand(config.rngSeed()); + rng().seed(config.rngSeed()); + } +} + +unsigned int rngSeed() +{ + return getCurrentContext().getConfig()->rngSeed(); +} + +} +// end catch_run_context.cpp +// start catch_section.cpp + +namespace Catch { + +Section::Section(SectionInfo const &info) + : m_info(info), + m_sectionIncluded( + getResultCapture().sectionStarted(m_info, m_assertions)) +{ + m_timer.start(); +} + +Section::~Section() +{ + if (m_sectionIncluded) { + SectionEndInfo endInfo{m_info, m_assertions, + m_timer.getElapsedSeconds()}; + if (uncaught_exceptions()) + getResultCapture().sectionEndedEarly(endInfo); + else + getResultCapture().sectionEnded(endInfo); + } +} + +// This indicates whether the section should be executed or not +Section::operator bool() const +{ + return m_sectionIncluded; +} + +} // end namespace Catch +// end catch_section.cpp +// start catch_section_info.cpp + +namespace Catch { + +SectionInfo::SectionInfo(SourceLineInfo const &_lineInfo, + std::string const &_name) + : name(_name), lineInfo(_lineInfo) +{ +} + +} // end namespace Catch +// end catch_section_info.cpp +// start catch_session.cpp + +// start catch_session.h + +#include + +namespace Catch { + +class Session : NonCopyable { +public: + Session(); + ~Session() override; + + void showHelp() const; + void libIdentify(); + + int applyCommandLine(int argc, char const *const *argv); +#if defined(CATCH_CONFIG_WCHAR) && defined(_WIN32) && defined(UNICODE) + int applyCommandLine(int argc, wchar_t const *const *argv); +#endif + + void useConfigData(ConfigData const &configData); + + template int run(int argc, CharT const *const argv[]) + { + if (m_startupExceptions) + return 1; + int returnCode = applyCommandLine(argc, argv); + if (returnCode == 0) + returnCode = run(); + return returnCode; + } + + int run(); + + clara::Parser const &cli() const; + void cli(clara::Parser const &newParser); + ConfigData &configData(); + Config &config(); + +private: + int runInternal(); + + clara::Parser m_cli; + ConfigData m_configData; + std::shared_ptr m_config; + bool m_startupExceptions = false; +}; + +} // end namespace Catch + +// end catch_session.h +// start catch_version.h + +#include + +namespace Catch { + +// Versioning information +struct Version { + Version(Version const &) = delete; + Version &operator=(Version const &) = delete; + Version(unsigned int _majorVersion, unsigned int _minorVersion, + unsigned int _patchNumber, char const *const _branchName, + unsigned int _buildNumber); + + unsigned int const majorVersion; + unsigned int const minorVersion; + unsigned int const patchNumber; + + // buildNumber is only used if branchName is not null + char const *const branchName; + unsigned int const buildNumber; + + friend std::ostream &operator<<(std::ostream &os, + Version const &version); +}; + +Version const &libraryVersion(); +} + +// end catch_version.h +#include +#include +#include +#include + +namespace Catch { + +namespace { +const int MaxExitCode = 255; + +IStreamingReporterPtr createReporter(std::string const &reporterName, + IConfigPtr const &config) +{ + auto reporter = Catch::getRegistryHub().getReporterRegistry().create( + reporterName, config); + CATCH_ENFORCE(reporter, "No reporter registered with name: '" + << reporterName << "'"); + + return reporter; +} + +IStreamingReporterPtr makeReporter(std::shared_ptr const &config) +{ + if (Catch::getRegistryHub() + .getReporterRegistry() + .getListeners() + .empty()) { + return createReporter(config->getReporterName(), config); + } + + // On older platforms, returning std::unique_ptr + // when the return type is std::unique_ptr + // doesn't compile without a std::move call. However, this causes + // a warning on newer platforms. Thus, we have to work around + // it a bit and downcast the pointer manually. + auto ret = std::unique_ptr(new ListeningReporter); + auto &multi = static_cast(*ret); + auto const &listeners = + Catch::getRegistryHub().getReporterRegistry().getListeners(); + for (auto const &listener : listeners) { + multi.addListener( + listener->create(Catch::ReporterConfig(config))); + } + multi.addReporter(createReporter(config->getReporterName(), config)); + return ret; +} + +class TestGroup { +public: + explicit TestGroup(std::shared_ptr const &config) + : m_config{config}, m_context{config, makeReporter(config)} + { + auto const &allTestCases = getAllTestCasesSorted(*m_config); + m_matches = m_config->testSpec().matchesByFilter(allTestCases, + *m_config); + auto const &invalidArgs = m_config->testSpec().getInvalidArgs(); + + if (m_matches.empty() && invalidArgs.empty()) { + for (auto const &test : allTestCases) + if (!test.isHidden()) + m_tests.emplace(&test); + } else { + for (auto const &match : m_matches) + m_tests.insert(match.tests.begin(), + match.tests.end()); + } + } + + Totals execute() + { + auto const &invalidArgs = m_config->testSpec().getInvalidArgs(); + Totals totals; + m_context.testGroupStarting(m_config->name(), 1, 1); + for (auto const &testCase : m_tests) { + if (!m_context.aborting()) + totals += m_context.runTest(*testCase); + else + m_context.reporter().skipTest(*testCase); + } + + for (auto const &match : m_matches) { + if (match.tests.empty()) { + m_context.reporter().noMatchingTestCases( + match.name); + totals.error = -1; + } + } + + if (!invalidArgs.empty()) { + for (auto const &invalidArg : invalidArgs) + m_context.reporter().reportInvalidArguments( + invalidArg); + } + + m_context.testGroupEnded(m_config->name(), totals, 1, 1); + return totals; + } + +private: + using Tests = std::set; + + std::shared_ptr m_config; + RunContext m_context; + Tests m_tests; + TestSpec::Matches m_matches; +}; + +void applyFilenamesAsTags(Catch::IConfig const &config) +{ + auto &tests = const_cast &>( + getAllTestCasesSorted(config)); + for (auto &testCase : tests) { + auto tags = testCase.tags; + + std::string filename = testCase.lineInfo.file; + auto lastSlash = filename.find_last_of("\\/"); + if (lastSlash != std::string::npos) { + filename.erase(0, lastSlash); + filename[0] = '#'; + } + + auto lastDot = filename.find_last_of('.'); + if (lastDot != std::string::npos) { + filename.erase(lastDot); + } + + tags.push_back(std::move(filename)); + setTags(testCase, tags); + } +} + +} // anon namespace + +Session::Session() +{ + static bool alreadyInstantiated = false; + if (alreadyInstantiated) { + CATCH_TRY + { + CATCH_INTERNAL_ERROR( + "Only one instance of Catch::Session can ever be used"); + } + CATCH_CATCH_ALL + { + getMutableRegistryHub().registerStartupException(); + } + } + + // There cannot be exceptions at startup in no-exception mode. +#if !defined(CATCH_CONFIG_DISABLE_EXCEPTIONS) + const auto &exceptions = + getRegistryHub().getStartupExceptionRegistry().getExceptions(); + if (!exceptions.empty()) { + config(); + getCurrentMutableContext().setConfig(m_config); + + m_startupExceptions = true; + Colour colourGuard(Colour::Red); + Catch::cerr() << "Errors occurred during startup!" << '\n'; + // iterate over all exceptions and notify user + for (const auto &ex_ptr : exceptions) { + try { + std::rethrow_exception(ex_ptr); + } catch (std::exception const &ex) { + Catch::cerr() + << Column(ex.what()).indent(2) << '\n'; + } + } + } +#endif + + alreadyInstantiated = true; + m_cli = makeCommandLineParser(m_configData); +} +Session::~Session() +{ + Catch::cleanUp(); +} + +void Session::showHelp() const +{ + Catch::cout() << "\nCatch v" << libraryVersion() << "\n" + << m_cli << std::endl + << "For more detailed usage please see the project docs\n" + << std::endl; +} +void Session::libIdentify() +{ + Catch::cout() << std::left << std::setw(16) << "description: " + << "A Catch2 test executable\n" + << std::left << std::setw(16) << "category: " + << "testframework\n" + << std::left << std::setw(16) << "framework: " + << "Catch Test\n" + << std::left << std::setw(16) + << "version: " << libraryVersion() << std::endl; +} + +int Session::applyCommandLine(int argc, char const *const *argv) +{ + if (m_startupExceptions) + return 1; + + auto result = m_cli.parse(clara::Args(argc, argv)); + if (!result) { + config(); + getCurrentMutableContext().setConfig(m_config); + Catch::cerr() + << Colour(Colour::Red) << "\nError(s) in input:\n" + << Column(result.errorMessage()).indent(2) << "\n\n"; + Catch::cerr() << "Run with -? for usage\n" << std::endl; + return MaxExitCode; + } + + if (m_configData.showHelp) + showHelp(); + if (m_configData.libIdentify) + libIdentify(); + m_config.reset(); + return 0; +} + +#if defined(CATCH_CONFIG_WCHAR) && defined(_WIN32) && defined(UNICODE) +int Session::applyCommandLine(int argc, wchar_t const *const *argv) +{ + + char **utf8Argv = new char *[argc]; + + for (int i = 0; i < argc; ++i) { + int bufSize = WideCharToMultiByte(CP_UTF8, 0, argv[i], -1, + nullptr, 0, nullptr, nullptr); + + utf8Argv[i] = new char[bufSize]; + + WideCharToMultiByte(CP_UTF8, 0, argv[i], -1, utf8Argv[i], + bufSize, nullptr, nullptr); + } + + int returnCode = applyCommandLine(argc, utf8Argv); + + for (int i = 0; i < argc; ++i) + delete[] utf8Argv[i]; + + delete[] utf8Argv; + + return returnCode; +} +#endif + +void Session::useConfigData(ConfigData const &configData) +{ + m_configData = configData; + m_config.reset(); +} + +int Session::run() +{ + if ((m_configData.waitForKeypress & WaitForKeypress::BeforeStart) != + 0) { + Catch::cout() << "...waiting for enter/ return before starting" + << std::endl; + static_cast(std::getchar()); + } + int exitCode = runInternal(); + if ((m_configData.waitForKeypress & WaitForKeypress::BeforeExit) != 0) { + Catch::cout() + << "...waiting for enter/ return before exiting, with code: " + << exitCode << std::endl; + static_cast(std::getchar()); + } + return exitCode; +} + +clara::Parser const &Session::cli() const +{ + return m_cli; +} +void Session::cli(clara::Parser const &newParser) +{ + m_cli = newParser; +} +ConfigData &Session::configData() +{ + return m_configData; +} +Config &Session::config() +{ + if (!m_config) + m_config = std::make_shared(m_configData); + return *m_config; +} + +int Session::runInternal() +{ + if (m_startupExceptions) + return 1; + + if (m_configData.showHelp || m_configData.libIdentify) { + return 0; + } + + CATCH_TRY + { + config(); // Force config to be constructed + + seedRng(*m_config); + + if (m_configData.filenamesAsTags) + applyFilenamesAsTags(*m_config); + + // Handle list request + if (Option listed = list(m_config)) + return static_cast(*listed); + + TestGroup tests{m_config}; + auto const totals = tests.execute(); + + if (m_config->warnAboutNoTests() && totals.error == -1) + return 2; + + // Note that on unices only the lower 8 bits are usually used, clamping + // the return value to 255 prevents false negative when some multiple + // of 256 tests has failed + return (std::min)( + MaxExitCode, + (std::max)(totals.error, + static_cast(totals.assertions.failed))); + } +#if !defined(CATCH_CONFIG_DISABLE_EXCEPTIONS) + catch (std::exception &ex) + { + Catch::cerr() << ex.what() << std::endl; + return MaxExitCode; + } +#endif +} + +} // end namespace Catch +// end catch_session.cpp +// start catch_singletons.cpp + +#include + +namespace Catch { + +namespace { +static auto getSingletons() -> std::vector *& +{ + static std::vector *g_singletons = nullptr; + if (!g_singletons) + g_singletons = new std::vector(); + return g_singletons; +} +} + +ISingleton::~ISingleton() {} + +void addSingleton(ISingleton *singleton) +{ + getSingletons()->push_back(singleton); +} +void cleanupSingletons() +{ + auto &singletons = getSingletons(); + for (auto singleton : *singletons) + delete singleton; + delete singletons; + singletons = nullptr; +} + +} // namespace Catch +// end catch_singletons.cpp +// start catch_startup_exception_registry.cpp + +#if !defined(CATCH_CONFIG_DISABLE_EXCEPTIONS) +namespace Catch { +void StartupExceptionRegistry::add(std::exception_ptr const &exception) noexcept +{ + CATCH_TRY { m_exceptions.push_back(exception); } + CATCH_CATCH_ALL + { + // If we run out of memory during start-up there's really not a lot more we can do about it + std::terminate(); + } +} + +std::vector const & +StartupExceptionRegistry::getExceptions() const noexcept +{ + return m_exceptions; +} + +} // end namespace Catch +#endif +// end catch_startup_exception_registry.cpp +// start catch_stream.cpp + +#include +#include +#include +#include +#include +#include + +namespace Catch { + +Catch::IStream::~IStream() = default; + +namespace Detail { +namespace { +template +class StreamBufImpl : public std::streambuf { + char data[bufferSize]; + WriterF m_writer; + +public: + StreamBufImpl() { setp(data, data + sizeof(data)); } + + ~StreamBufImpl() noexcept { StreamBufImpl::sync(); } + +private: + int overflow(int c) override + { + sync(); + + if (c != EOF) { + if (pbase() == epptr()) + m_writer(std::string(1, static_cast(c))); + else + sputc(static_cast(c)); + } + return 0; + } + + int sync() override + { + if (pbase() != pptr()) { + m_writer(std::string( + pbase(), static_cast( + pptr() - pbase()))); + setp(pbase(), epptr()); + } + return 0; + } +}; + +/////////////////////////////////////////////////////////////////////////// + +struct OutputDebugWriter { + + void operator()(std::string const &str) { writeToDebugConsole(str); } +}; + +/////////////////////////////////////////////////////////////////////////// + +class FileStream : public IStream { + mutable std::ofstream m_ofs; + +public: + FileStream(StringRef filename) + { + m_ofs.open(filename.c_str()); + CATCH_ENFORCE(!m_ofs.fail(), + "Unable to open file: '" << filename << "'"); + } + ~FileStream() override = default; + +public: // IStream + std::ostream &stream() const override { return m_ofs; } +}; + +/////////////////////////////////////////////////////////////////////////// + +class CoutStream : public IStream { + mutable std::ostream m_os; + +public: + // Store the streambuf from cout up-front because + // cout may get redirected when running tests + CoutStream() : m_os(Catch::cout().rdbuf()) {} + ~CoutStream() override = default; + +public: // IStream + std::ostream &stream() const override { return m_os; } +}; + +/////////////////////////////////////////////////////////////////////////// + +class DebugOutStream : public IStream { + std::unique_ptr> m_streamBuf; + mutable std::ostream m_os; + +public: + DebugOutStream() + : m_streamBuf(new StreamBufImpl()), + m_os(m_streamBuf.get()) + { + } + + ~DebugOutStream() override = default; + +public: // IStream + std::ostream &stream() const override { return m_os; } +}; + +} +} // namespace anon::detail + +/////////////////////////////////////////////////////////////////////////// + +auto makeStream(StringRef const &filename) -> IStream const * +{ + if (filename.empty()) + return new Detail::CoutStream(); + else if (filename[0] == '%') { + if (filename == "%debug") + return new Detail::DebugOutStream(); + else + CATCH_ERROR("Unrecognised stream: '" << filename + << "'"); + } else + return new Detail::FileStream(filename); +} + +// This class encapsulates the idea of a pool of ostringstreams that can be reused. +struct StringStreams { + std::vector> m_streams; + std::vector m_unused; + std::ostringstream m_referenceStream; // Used for copy state/ flags from + + auto add() -> std::size_t + { + if (m_unused.empty()) { + m_streams.push_back(std::unique_ptr( + new std::ostringstream)); + return m_streams.size() - 1; + } else { + auto index = m_unused.back(); + m_unused.pop_back(); + return index; + } + } + + void release(std::size_t index) + { + m_streams[index]->copyfmt( + m_referenceStream); // Restore initial flags and other state + m_unused.push_back(index); + } +}; + +ReusableStringStream::ReusableStringStream() + : m_index(Singleton::getMutable().add()), + m_oss(Singleton::getMutable().m_streams[m_index].get()) +{ +} + +ReusableStringStream::~ReusableStringStream() +{ + static_cast(m_oss)->str(""); + m_oss->clear(); + Singleton::getMutable().release(m_index); +} + +auto ReusableStringStream::str() const -> std::string +{ + return static_cast(m_oss)->str(); +} + +/////////////////////////////////////////////////////////////////////////// + +#ifndef CATCH_CONFIG_NOSTDOUT // If you #define this you must implement these functions +std::ostream &cout() +{ + return std::cout; +} +std::ostream &cerr() +{ + return std::cerr; +} +std::ostream &clog() +{ + return std::clog; +} +#endif +} +// end catch_stream.cpp +// start catch_string_manip.cpp + +#include +#include +#include +#include +#include + +namespace Catch { + +namespace { +char toLowerCh(char c) +{ + return static_cast(std::tolower(static_cast(c))); +} +} + +bool startsWith(std::string const &s, std::string const &prefix) +{ + return s.size() >= prefix.size() && + std::equal(prefix.begin(), prefix.end(), s.begin()); +} +bool startsWith(std::string const &s, char prefix) +{ + return !s.empty() && s[0] == prefix; +} +bool endsWith(std::string const &s, std::string const &suffix) +{ + return s.size() >= suffix.size() && + std::equal(suffix.rbegin(), suffix.rend(), s.rbegin()); +} +bool endsWith(std::string const &s, char suffix) +{ + return !s.empty() && s[s.size() - 1] == suffix; +} +bool contains(std::string const &s, std::string const &infix) +{ + return s.find(infix) != std::string::npos; +} +void toLowerInPlace(std::string &s) +{ + std::transform(s.begin(), s.end(), s.begin(), toLowerCh); +} +std::string toLower(std::string const &s) +{ + std::string lc = s; + toLowerInPlace(lc); + return lc; +} +std::string trim(std::string const &str) +{ + static char const *whitespaceChars = "\n\r\t "; + std::string::size_type start = str.find_first_not_of(whitespaceChars); + std::string::size_type end = str.find_last_not_of(whitespaceChars); + + return start != std::string::npos ? str.substr(start, 1 + end - start) + : std::string(); +} + +StringRef trim(StringRef ref) +{ + const auto is_ws = [](char c) { + return c == ' ' || c == '\t' || c == '\n' || c == '\r'; + }; + size_t real_begin = 0; + while (real_begin < ref.size() && is_ws(ref[real_begin])) { + ++real_begin; + } + size_t real_end = ref.size(); + while (real_end > real_begin && is_ws(ref[real_end - 1])) { + --real_end; + } + + return ref.substr(real_begin, real_end - real_begin); +} + +bool replaceInPlace(std::string &str, std::string const &replaceThis, + std::string const &withThis) +{ + bool replaced = false; + std::size_t i = str.find(replaceThis); + while (i != std::string::npos) { + replaced = true; + str = str.substr(0, i) + withThis + + str.substr(i + replaceThis.size()); + if (i < str.size() - withThis.size()) + i = str.find(replaceThis, i + withThis.size()); + else + i = std::string::npos; + } + return replaced; +} + +std::vector splitStringRef(StringRef str, char delimiter) +{ + std::vector subStrings; + std::size_t start = 0; + for (std::size_t pos = 0; pos < str.size(); ++pos) { + if (str[pos] == delimiter) { + if (pos - start > 1) + subStrings.push_back( + str.substr(start, pos - start)); + start = pos + 1; + } + } + if (start < str.size()) + subStrings.push_back(str.substr(start, str.size() - start)); + return subStrings; +} + +pluralise::pluralise(std::size_t count, std::string const &label) + : m_count(count), m_label(label) +{ +} + +std::ostream &operator<<(std::ostream &os, pluralise const &pluraliser) +{ + os << pluraliser.m_count << ' ' << pluraliser.m_label; + if (pluraliser.m_count != 1) + os << 's'; + return os; +} + +} +// end catch_string_manip.cpp +// start catch_stringref.cpp + +#include +#include +#include +#include + +namespace Catch { +StringRef::StringRef(char const *rawChars) noexcept + : StringRef(rawChars, + static_cast(std::strlen(rawChars))) +{ +} + +auto StringRef::c_str() const -> char const * +{ + CATCH_ENFORCE( + isNullTerminated(), + "Called StringRef::c_str() on a non-null-terminated instance"); + return m_start; +} +auto StringRef::data() const noexcept -> char const * +{ + return m_start; +} + +auto StringRef::substr(size_type start, size_type size) const noexcept + -> StringRef +{ + if (start < m_size) { + return StringRef(m_start + start, + (std::min)(m_size - start, size)); + } else { + return StringRef(); + } +} +auto StringRef::operator==(StringRef const &other) const noexcept -> bool +{ + return m_size == other.m_size && + (std::memcmp(m_start, other.m_start, m_size) == 0); +} + +auto operator<<(std::ostream &os, StringRef const &str) -> std::ostream & +{ + return os.write(str.data(), str.size()); +} + +auto operator+=(std::string &lhs, StringRef const &rhs) -> std::string & +{ + lhs.append(rhs.data(), rhs.size()); + return lhs; +} + +} // namespace Catch +// end catch_stringref.cpp +// start catch_tag_alias.cpp + +namespace Catch { +TagAlias::TagAlias(std::string const &_tag, SourceLineInfo _lineInfo) + : tag(_tag), lineInfo(_lineInfo) +{ +} +} +// end catch_tag_alias.cpp +// start catch_tag_alias_autoregistrar.cpp + +namespace Catch { + +RegistrarForTagAliases::RegistrarForTagAliases(char const *alias, + char const *tag, + SourceLineInfo const &lineInfo) +{ + CATCH_TRY + { + getMutableRegistryHub().registerTagAlias(alias, tag, lineInfo); + } + CATCH_CATCH_ALL + { + // Do not throw when constructing global objects, instead register the exception to be processed later + getMutableRegistryHub().registerStartupException(); + } +} + +} +// end catch_tag_alias_autoregistrar.cpp +// start catch_tag_alias_registry.cpp + +#include + +namespace Catch { + +TagAliasRegistry::~TagAliasRegistry() {} + +TagAlias const *TagAliasRegistry::find(std::string const &alias) const +{ + auto it = m_registry.find(alias); + if (it != m_registry.end()) + return &(it->second); + else + return nullptr; +} + +std::string +TagAliasRegistry::expandAliases(std::string const &unexpandedTestSpec) const +{ + std::string expandedTestSpec = unexpandedTestSpec; + for (auto const ®istryKvp : m_registry) { + std::size_t pos = expandedTestSpec.find(registryKvp.first); + if (pos != std::string::npos) { + expandedTestSpec = + expandedTestSpec.substr(0, pos) + + registryKvp.second.tag + + expandedTestSpec.substr( + pos + registryKvp.first.size()); + } + } + return expandedTestSpec; +} + +void TagAliasRegistry::add(std::string const &alias, std::string const &tag, + SourceLineInfo const &lineInfo) +{ + CATCH_ENFORCE(startsWith(alias, "[@") && endsWith(alias, ']'), + "error: tag alias, '" + << alias + << "' is not of the form [@alias name].\n" + << lineInfo); + + CATCH_ENFORCE( + m_registry + .insert(std::make_pair(alias, TagAlias(tag, lineInfo))) + .second, + "error: tag alias, '" + << alias << "' already registered.\n" + << "\tFirst seen at: " << find(alias)->lineInfo << "\n" + << "\tRedefined at: " << lineInfo); +} + +ITagAliasRegistry::~ITagAliasRegistry() {} + +ITagAliasRegistry const &ITagAliasRegistry::get() +{ + return getRegistryHub().getTagAliasRegistry(); +} + +} // end namespace Catch +// end catch_tag_alias_registry.cpp +// start catch_test_case_info.cpp + +#include +#include +#include +#include + +namespace Catch { + +namespace { +TestCaseInfo::SpecialProperties parseSpecialTag(std::string const &tag) +{ + if (startsWith(tag, '.') || tag == "!hide") + return TestCaseInfo::IsHidden; + else if (tag == "!throws") + return TestCaseInfo::Throws; + else if (tag == "!shouldfail") + return TestCaseInfo::ShouldFail; + else if (tag == "!mayfail") + return TestCaseInfo::MayFail; + else if (tag == "!nonportable") + return TestCaseInfo::NonPortable; + else if (tag == "!benchmark") + return static_cast( + TestCaseInfo::Benchmark | TestCaseInfo::IsHidden); + else + return TestCaseInfo::None; +} +bool isReservedTag(std::string const &tag) +{ + return parseSpecialTag(tag) == TestCaseInfo::None && tag.size() > 0 && + !std::isalnum(static_cast(tag[0])); +} +void enforceNotReservedTag(std::string const &tag, + SourceLineInfo const &_lineInfo) +{ + CATCH_ENFORCE( + !isReservedTag(tag), + "Tag name: [" + << tag << "] is not allowed.\n" + << "Tag names starting with non alphanumeric characters are reserved\n" + << _lineInfo); +} +} + +TestCase makeTestCase(ITestInvoker *_testCase, std::string const &_className, + NameAndTags const &nameAndTags, + SourceLineInfo const &_lineInfo) +{ + bool isHidden = false; + + // Parse out tags + std::vector tags; + std::string desc, tag; + bool inTag = false; + for (char c : nameAndTags.tags) { + if (!inTag) { + if (c == '[') + inTag = true; + else + desc += c; + } else { + if (c == ']') { + TestCaseInfo::SpecialProperties prop = + parseSpecialTag(tag); + if ((prop & TestCaseInfo::IsHidden) != 0) + isHidden = true; + else if (prop == TestCaseInfo::None) + enforceNotReservedTag(tag, _lineInfo); + + // Merged hide tags like `[.approvals]` should be added as + // `[.][approvals]`. The `[.]` is added at later point, so + // we only strip the prefix + if (startsWith(tag, '.') && tag.size() > 1) { + tag.erase(0, 1); + } + tags.push_back(tag); + tag.clear(); + inTag = false; + } else + tag += c; + } + } + if (isHidden) { + // Add all "hidden" tags to make them behave identically + tags.insert(tags.end(), {".", "!hide"}); + } + + TestCaseInfo info(static_cast(nameAndTags.name), + _className, desc, tags, _lineInfo); + return TestCase(_testCase, std::move(info)); +} + +void setTags(TestCaseInfo &testCaseInfo, std::vector tags) +{ + std::sort(begin(tags), end(tags)); + tags.erase(std::unique(begin(tags), end(tags)), end(tags)); + testCaseInfo.lcaseTags.clear(); + + for (auto const &tag : tags) { + std::string lcaseTag = toLower(tag); + testCaseInfo.properties = + static_cast( + testCaseInfo.properties | + parseSpecialTag(lcaseTag)); + testCaseInfo.lcaseTags.push_back(lcaseTag); + } + testCaseInfo.tags = std::move(tags); +} + +TestCaseInfo::TestCaseInfo(std::string const &_name, + std::string const &_className, + std::string const &_description, + std::vector const &_tags, + SourceLineInfo const &_lineInfo) + : name(_name), + className(_className), + description(_description), + lineInfo(_lineInfo), + properties(None) +{ + setTags(*this, _tags); +} + +bool TestCaseInfo::isHidden() const +{ + return (properties & IsHidden) != 0; +} +bool TestCaseInfo::throws() const +{ + return (properties & Throws) != 0; +} +bool TestCaseInfo::okToFail() const +{ + return (properties & (ShouldFail | MayFail)) != 0; +} +bool TestCaseInfo::expectedToFail() const +{ + return (properties & (ShouldFail)) != 0; +} + +std::string TestCaseInfo::tagsAsString() const +{ + std::string ret; + // '[' and ']' per tag + std::size_t full_size = 2 * tags.size(); + for (const auto &tag : tags) { + full_size += tag.size(); + } + ret.reserve(full_size); + for (const auto &tag : tags) { + ret.push_back('['); + ret.append(tag); + ret.push_back(']'); + } + + return ret; +} + +TestCase::TestCase(ITestInvoker *testCase, TestCaseInfo &&info) + : TestCaseInfo(std::move(info)), test(testCase) +{ +} + +TestCase TestCase::withName(std::string const &_newName) const +{ + TestCase other(*this); + other.name = _newName; + return other; +} + +void TestCase::invoke() const +{ + test->invoke(); +} + +bool TestCase::operator==(TestCase const &other) const +{ + return test.get() == other.test.get() && name == other.name && + className == other.className; +} + +bool TestCase::operator<(TestCase const &other) const +{ + return name < other.name; +} + +TestCaseInfo const &TestCase::getTestCaseInfo() const +{ + return *this; +} + +} // end namespace Catch +// end catch_test_case_info.cpp +// start catch_test_case_registry_impl.cpp + +#include +#include + +namespace Catch { + +namespace { +struct TestHasher { + using hash_t = uint64_t; + + explicit TestHasher(hash_t hashSuffix) : m_hashSuffix{hashSuffix} {} + + uint32_t operator()(TestCase const &t) const + { + // FNV-1a hash with multiplication fold. + const hash_t prime = 1099511628211u; + hash_t hash = 14695981039346656037u; + for (const char c : t.name) { + hash ^= c; + hash *= prime; + } + hash ^= m_hashSuffix; + hash *= prime; + const uint32_t low{static_cast(hash)}; + const uint32_t high{static_cast(hash >> 32)}; + return low * high; + } + +private: + hash_t m_hashSuffix; +}; +} // end unnamed namespace + +std::vector sortTests(IConfig const &config, + std::vector const &unsortedTestCases) +{ + switch (config.runOrder()) { + case RunTests::InDeclarationOrder: + // already in declaration order + break; + + case RunTests::InLexicographicalOrder: { + std::vector sorted = unsortedTestCases; + std::sort(sorted.begin(), sorted.end()); + return sorted; + } + + case RunTests::InRandomOrder: { + seedRng(config); + TestHasher h{config.rngSeed()}; + + using hashedTest = + std::pair; + std::vector indexed_tests; + indexed_tests.reserve(unsortedTestCases.size()); + + for (auto const &testCase : unsortedTestCases) { + indexed_tests.emplace_back(h(testCase), &testCase); + } + + std::sort(indexed_tests.begin(), indexed_tests.end(), + [](hashedTest const &lhs, hashedTest const &rhs) { + if (lhs.first == rhs.first) { + return lhs.second->name < + rhs.second->name; + } + return lhs.first < rhs.first; + }); + + std::vector sorted; + sorted.reserve(indexed_tests.size()); + + for (auto const &hashed : indexed_tests) { + sorted.emplace_back(*hashed.second); + } + + return sorted; + } + } + return unsortedTestCases; +} + +bool isThrowSafe(TestCase const &testCase, IConfig const &config) +{ + return !testCase.throws() || config.allowThrows(); +} + +bool matchTest(TestCase const &testCase, TestSpec const &testSpec, + IConfig const &config) +{ + return testSpec.matches(testCase) && isThrowSafe(testCase, config); +} + +void enforceNoDuplicateTestCases(std::vector const &functions) +{ + std::set seenFunctions; + for (auto const &function : functions) { + auto prev = seenFunctions.insert(function); + CATCH_ENFORCE(prev.second, + "error: TEST_CASE( \"" + << function.name + << "\" ) already defined.\n" + << "\tFirst seen at " + << prev.first->getTestCaseInfo().lineInfo + << "\n" + << "\tRedefined at " + << function.getTestCaseInfo().lineInfo); + } +} + +std::vector filterTests(std::vector const &testCases, + TestSpec const &testSpec, + IConfig const &config) +{ + std::vector filtered; + filtered.reserve(testCases.size()); + for (auto const &testCase : testCases) { + if ((!testSpec.hasFilters() && !testCase.isHidden()) || + (testSpec.hasFilters() && + matchTest(testCase, testSpec, config))) { + filtered.push_back(testCase); + } + } + return filtered; +} +std::vector const &getAllTestCasesSorted(IConfig const &config) +{ + return getRegistryHub().getTestCaseRegistry().getAllTestsSorted(config); +} + +void TestRegistry::registerTest(TestCase const &testCase) +{ + std::string name = testCase.getTestCaseInfo().name; + if (name.empty()) { + ReusableStringStream rss; + rss << "Anonymous test case " << ++m_unnamedCount; + return registerTest(testCase.withName(rss.str())); + } + m_functions.push_back(testCase); +} + +std::vector const &TestRegistry::getAllTests() const +{ + return m_functions; +} +std::vector const & +TestRegistry::getAllTestsSorted(IConfig const &config) const +{ + if (m_sortedFunctions.empty()) + enforceNoDuplicateTestCases(m_functions); + + if (m_currentSortOrder != config.runOrder() || + m_sortedFunctions.empty()) { + m_sortedFunctions = sortTests(config, m_functions); + m_currentSortOrder = config.runOrder(); + } + return m_sortedFunctions; +} + +/////////////////////////////////////////////////////////////////////////// +TestInvokerAsFunction::TestInvokerAsFunction(void (*testAsFunction)()) noexcept + : m_testAsFunction(testAsFunction) +{ +} + +void TestInvokerAsFunction::invoke() const +{ + m_testAsFunction(); +} + +std::string extractClassName(StringRef const &classOrQualifiedMethodName) +{ + std::string className(classOrQualifiedMethodName); + if (startsWith(className, '&')) { + std::size_t lastColons = className.rfind("::"); + std::size_t penultimateColons = + className.rfind("::", lastColons - 1); + if (penultimateColons == std::string::npos) + penultimateColons = 1; + className = className.substr(penultimateColons, + lastColons - penultimateColons); + } + return className; +} + +} // end namespace Catch +// end catch_test_case_registry_impl.cpp +// start catch_test_case_tracker.cpp + +#include +#include +#include +#include +#include + +#if defined(__clang__) +#pragma clang diagnostic push +#pragma clang diagnostic ignored "-Wexit-time-destructors" +#endif + +namespace Catch { +namespace TestCaseTracking { + +NameAndLocation::NameAndLocation(std::string const &_name, + SourceLineInfo const &_location) + : name(_name), location(_location) +{ +} + +ITracker::~ITracker() = default; + +ITracker &TrackerContext::startRun() +{ + m_rootTracker = std::make_shared( + NameAndLocation("{root}", CATCH_INTERNAL_LINEINFO), *this, + nullptr); + m_currentTracker = nullptr; + m_runState = Executing; + return *m_rootTracker; +} + +void TrackerContext::endRun() +{ + m_rootTracker.reset(); + m_currentTracker = nullptr; + m_runState = NotStarted; +} + +void TrackerContext::startCycle() +{ + m_currentTracker = m_rootTracker.get(); + m_runState = Executing; +} +void TrackerContext::completeCycle() +{ + m_runState = CompletedCycle; +} + +bool TrackerContext::completedCycle() const +{ + return m_runState == CompletedCycle; +} +ITracker &TrackerContext::currentTracker() +{ + return *m_currentTracker; +} +void TrackerContext::setCurrentTracker(ITracker *tracker) +{ + m_currentTracker = tracker; +} + +TrackerBase::TrackerBase(NameAndLocation const &nameAndLocation, + TrackerContext &ctx, ITracker *parent) + : ITracker(nameAndLocation), m_ctx(ctx), m_parent(parent) +{ +} + +bool TrackerBase::isComplete() const +{ + return m_runState == CompletedSuccessfully || m_runState == Failed; +} +bool TrackerBase::isSuccessfullyCompleted() const +{ + return m_runState == CompletedSuccessfully; +} +bool TrackerBase::isOpen() const +{ + return m_runState != NotStarted && !isComplete(); +} +bool TrackerBase::hasChildren() const +{ + return !m_children.empty(); +} + +void TrackerBase::addChild(ITrackerPtr const &child) +{ + m_children.push_back(child); +} + +ITrackerPtr TrackerBase::findChild(NameAndLocation const &nameAndLocation) +{ + auto it = std::find_if( + m_children.begin(), m_children.end(), + [&nameAndLocation](ITrackerPtr const &tracker) { + return tracker->nameAndLocation().location == + nameAndLocation.location && + tracker->nameAndLocation().name == + nameAndLocation.name; + }); + return (it != m_children.end()) ? *it : nullptr; +} +ITracker &TrackerBase::parent() +{ + assert(m_parent); // Should always be non-null except for root + return *m_parent; +} + +void TrackerBase::openChild() +{ + if (m_runState != ExecutingChildren) { + m_runState = ExecutingChildren; + if (m_parent) + m_parent->openChild(); + } +} + +bool TrackerBase::isSectionTracker() const +{ + return false; +} +bool TrackerBase::isGeneratorTracker() const +{ + return false; +} + +void TrackerBase::open() +{ + m_runState = Executing; + moveToThis(); + if (m_parent) + m_parent->openChild(); +} + +void TrackerBase::close() +{ + + // Close any still open children (e.g. generators) + while (&m_ctx.currentTracker() != this) + m_ctx.currentTracker().close(); + + switch (m_runState) { + case NeedsAnotherRun: + break; + + case Executing: + m_runState = CompletedSuccessfully; + break; + case ExecutingChildren: + if (std::all_of(m_children.begin(), m_children.end(), + [](ITrackerPtr const &t) { + return t->isComplete(); + })) + m_runState = CompletedSuccessfully; + break; + + case NotStarted: + case CompletedSuccessfully: + case Failed: + CATCH_INTERNAL_ERROR("Illogical state: " << m_runState); + + default: + CATCH_INTERNAL_ERROR("Unknown state: " << m_runState); + } + moveToParent(); + m_ctx.completeCycle(); +} +void TrackerBase::fail() +{ + m_runState = Failed; + if (m_parent) + m_parent->markAsNeedingAnotherRun(); + moveToParent(); + m_ctx.completeCycle(); +} +void TrackerBase::markAsNeedingAnotherRun() +{ + m_runState = NeedsAnotherRun; +} + +void TrackerBase::moveToParent() +{ + assert(m_parent); + m_ctx.setCurrentTracker(m_parent); +} +void TrackerBase::moveToThis() +{ + m_ctx.setCurrentTracker(this); +} + +SectionTracker::SectionTracker(NameAndLocation const &nameAndLocation, + TrackerContext &ctx, ITracker *parent) + : TrackerBase(nameAndLocation, ctx, parent), + m_trimmed_name(trim(nameAndLocation.name)) +{ + if (parent) { + while (!parent->isSectionTracker()) + parent = &parent->parent(); + + SectionTracker &parentSection = + static_cast(*parent); + addNextFilters(parentSection.m_filters); + } +} + +bool SectionTracker::isComplete() const +{ + bool complete = true; + + if (m_filters.empty() || m_filters[0] == "" || + std::find(m_filters.begin(), m_filters.end(), m_trimmed_name) != + m_filters.end()) { + complete = TrackerBase::isComplete(); + } + return complete; +} + +bool SectionTracker::isSectionTracker() const +{ + return true; +} + +SectionTracker &SectionTracker::acquire(TrackerContext &ctx, + NameAndLocation const &nameAndLocation) +{ + std::shared_ptr section; + + ITracker ¤tTracker = ctx.currentTracker(); + if (ITrackerPtr childTracker = + currentTracker.findChild(nameAndLocation)) { + assert(childTracker); + assert(childTracker->isSectionTracker()); + section = + std::static_pointer_cast(childTracker); + } else { + section = std::make_shared(nameAndLocation, ctx, + ¤tTracker); + currentTracker.addChild(section); + } + if (!ctx.completedCycle()) + section->tryOpen(); + return *section; +} + +void SectionTracker::tryOpen() +{ + if (!isComplete()) + open(); +} + +void SectionTracker::addInitialFilters(std::vector const &filters) +{ + if (!filters.empty()) { + m_filters.reserve(m_filters.size() + filters.size() + 2); + m_filters.emplace_back(""); // Root - should never be consulted + m_filters.emplace_back(""); // Test Case - not a section filter + m_filters.insert(m_filters.end(), filters.begin(), + filters.end()); + } +} +void SectionTracker::addNextFilters(std::vector const &filters) +{ + if (filters.size() > 1) + m_filters.insert(m_filters.end(), filters.begin() + 1, + filters.end()); +} + +std::vector const &SectionTracker::getFilters() const +{ + return m_filters; +} + +std::string const &SectionTracker::trimmedName() const +{ + return m_trimmed_name; +} + +} // namespace TestCaseTracking + +using TestCaseTracking::ITracker; +using TestCaseTracking::TrackerContext; +using TestCaseTracking::SectionTracker; + +} // namespace Catch + +#if defined(__clang__) +#pragma clang diagnostic pop +#endif +// end catch_test_case_tracker.cpp +// start catch_test_registry.cpp + +namespace Catch { + +auto makeTestInvoker(void (*testAsFunction)()) noexcept -> ITestInvoker * +{ + return new (std::nothrow) TestInvokerAsFunction(testAsFunction); +} + +NameAndTags::NameAndTags(StringRef const &name_, + StringRef const &tags_) noexcept + : name(name_), tags(tags_) +{ +} + +AutoReg::AutoReg(ITestInvoker *invoker, SourceLineInfo const &lineInfo, + StringRef const &classOrMethod, + NameAndTags const &nameAndTags) noexcept +{ + CATCH_TRY + { + getMutableRegistryHub().registerTest( + makeTestCase(invoker, extractClassName(classOrMethod), + nameAndTags, lineInfo)); + } + CATCH_CATCH_ALL + { + // Do not throw when constructing global objects, instead register the exception to be processed later + getMutableRegistryHub().registerStartupException(); + } +} + +AutoReg::~AutoReg() = default; +} +// end catch_test_registry.cpp +// start catch_test_spec.cpp + +#include +#include +#include +#include + +namespace Catch { + +TestSpec::Pattern::Pattern(std::string const &name) : m_name(name) {} + +TestSpec::Pattern::~Pattern() = default; + +std::string const &TestSpec::Pattern::name() const +{ + return m_name; +} + +TestSpec::NamePattern::NamePattern(std::string const &name, + std::string const &filterString) + : Pattern(filterString), + m_wildcardPattern(toLower(name), CaseSensitive::No) +{ +} + +bool TestSpec::NamePattern::matches(TestCaseInfo const &testCase) const +{ + return m_wildcardPattern.matches(testCase.name); +} + +TestSpec::TagPattern::TagPattern(std::string const &tag, + std::string const &filterString) + : Pattern(filterString), m_tag(toLower(tag)) +{ +} + +bool TestSpec::TagPattern::matches(TestCaseInfo const &testCase) const +{ + return std::find(begin(testCase.lcaseTags), end(testCase.lcaseTags), + m_tag) != end(testCase.lcaseTags); +} + +TestSpec::ExcludedPattern::ExcludedPattern(PatternPtr const &underlyingPattern) + : Pattern(underlyingPattern->name()), + m_underlyingPattern(underlyingPattern) +{ +} + +bool TestSpec::ExcludedPattern::matches(TestCaseInfo const &testCase) const +{ + return !m_underlyingPattern->matches(testCase); +} + +bool TestSpec::Filter::matches(TestCaseInfo const &testCase) const +{ + return std::all_of(m_patterns.begin(), m_patterns.end(), + [&](PatternPtr const &p) { + return p->matches(testCase); + }); +} + +std::string TestSpec::Filter::name() const +{ + std::string name; + for (auto const &p : m_patterns) + name += p->name(); + return name; +} + +bool TestSpec::hasFilters() const +{ + return !m_filters.empty(); +} + +bool TestSpec::matches(TestCaseInfo const &testCase) const +{ + return std::any_of(m_filters.begin(), m_filters.end(), + [&](Filter const &f) { + return f.matches(testCase); + }); +} + +TestSpec::Matches +TestSpec::matchesByFilter(std::vector const &testCases, + IConfig const &config) const +{ + Matches matches(m_filters.size()); + std::transform( + m_filters.begin(), m_filters.end(), matches.begin(), + [&](Filter const &filter) { + std::vector currentMatches; + for (auto const &test : testCases) + if (isThrowSafe(test, config) && + filter.matches(test)) + currentMatches.emplace_back(&test); + return FilterMatch{filter.name(), currentMatches}; + }); + return matches; +} + +const TestSpec::vectorStrings &TestSpec::getInvalidArgs() const +{ + return (m_invalidArgs); +} + +} +// end catch_test_spec.cpp +// start catch_test_spec_parser.cpp + +namespace Catch { + +TestSpecParser::TestSpecParser(ITagAliasRegistry const &tagAliases) + : m_tagAliases(&tagAliases) +{ +} + +TestSpecParser &TestSpecParser::parse(std::string const &arg) +{ + m_mode = None; + m_exclusion = false; + m_arg = m_tagAliases->expandAliases(arg); + m_escapeChars.clear(); + m_substring.reserve(m_arg.size()); + m_patternName.reserve(m_arg.size()); + m_realPatternPos = 0; + + for (m_pos = 0; m_pos < m_arg.size(); ++m_pos) + //if visitChar fails + if (!visitChar(m_arg[m_pos])) { + m_testSpec.m_invalidArgs.push_back(arg); + break; + } + endMode(); + return *this; +} +TestSpec TestSpecParser::testSpec() +{ + addFilter(); + return m_testSpec; +} +bool TestSpecParser::visitChar(char c) +{ + if ((m_mode != EscapedName) && (c == '\\')) { + escape(); + addCharToPattern(c); + return true; + } else if ((m_mode != EscapedName) && (c == ',')) { + return separate(); + } + + switch (m_mode) { + case None: + if (processNoneChar(c)) + return true; + break; + case Name: + processNameChar(c); + break; + case EscapedName: + endMode(); + addCharToPattern(c); + return true; + default: + case Tag: + case QuotedName: + if (processOtherChar(c)) + return true; + break; + } + + m_substring += c; + if (!isControlChar(c)) { + m_patternName += c; + m_realPatternPos++; + } + return true; +} +// Two of the processing methods return true to signal the caller to return +// without adding the given character to the current pattern strings +bool TestSpecParser::processNoneChar(char c) +{ + switch (c) { + case ' ': + return true; + case '~': + m_exclusion = true; + return false; + case '[': + startNewMode(Tag); + return false; + case '"': + startNewMode(QuotedName); + return false; + default: + startNewMode(Name); + return false; + } +} +void TestSpecParser::processNameChar(char c) +{ + if (c == '[') { + if (m_substring == "exclude:") + m_exclusion = true; + else + endMode(); + startNewMode(Tag); + } +} +bool TestSpecParser::processOtherChar(char c) +{ + if (!isControlChar(c)) + return false; + m_substring += c; + endMode(); + return true; +} +void TestSpecParser::startNewMode(Mode mode) +{ + m_mode = mode; +} +void TestSpecParser::endMode() +{ + switch (m_mode) { + case Name: + case QuotedName: + return addNamePattern(); + case Tag: + return addTagPattern(); + case EscapedName: + revertBackToLastMode(); + return; + case None: + default: + return startNewMode(None); + } +} +void TestSpecParser::escape() +{ + saveLastMode(); + m_mode = EscapedName; + m_escapeChars.push_back(m_realPatternPos); +} +bool TestSpecParser::isControlChar(char c) const +{ + switch (m_mode) { + default: + return false; + case None: + return c == '~'; + case Name: + return c == '['; + case EscapedName: + return true; + case QuotedName: + return c == '"'; + case Tag: + return c == '[' || c == ']'; + } +} + +void TestSpecParser::addFilter() +{ + if (!m_currentFilter.m_patterns.empty()) { + m_testSpec.m_filters.push_back(m_currentFilter); + m_currentFilter = TestSpec::Filter(); + } +} + +void TestSpecParser::saveLastMode() +{ + lastMode = m_mode; +} + +void TestSpecParser::revertBackToLastMode() +{ + m_mode = lastMode; +} + +bool TestSpecParser::separate() +{ + if ((m_mode == QuotedName) || (m_mode == Tag)) { + //invalid argument, signal failure to previous scope. + m_mode = None; + m_pos = m_arg.size(); + m_substring.clear(); + m_patternName.clear(); + m_realPatternPos = 0; + return false; + } + endMode(); + addFilter(); + return true; //success +} + +std::string TestSpecParser::preprocessPattern() +{ + std::string token = m_patternName; + for (std::size_t i = 0; i < m_escapeChars.size(); ++i) + token = token.substr(0, m_escapeChars[i] - i) + + token.substr(m_escapeChars[i] - i + 1); + m_escapeChars.clear(); + if (startsWith(token, "exclude:")) { + m_exclusion = true; + token = token.substr(8); + } + + m_patternName.clear(); + m_realPatternPos = 0; + + return token; +} + +void TestSpecParser::addNamePattern() +{ + auto token = preprocessPattern(); + + if (!token.empty()) { + TestSpec::PatternPtr pattern = + std::make_shared(token, + m_substring); + if (m_exclusion) + pattern = std::make_shared( + pattern); + m_currentFilter.m_patterns.push_back(pattern); + } + m_substring.clear(); + m_exclusion = false; + m_mode = None; +} + +void TestSpecParser::addTagPattern() +{ + auto token = preprocessPattern(); + + if (!token.empty()) { + // If the tag pattern is the "hide and tag" shorthand (e.g. [.foo]) + // we have to create a separate hide tag and shorten the real one + if (token.size() > 1 && token[0] == '.') { + token.erase(token.begin()); + TestSpec::PatternPtr pattern = + std::make_shared( + ".", m_substring); + if (m_exclusion) { + pattern = std::make_shared< + TestSpec::ExcludedPattern>(pattern); + } + m_currentFilter.m_patterns.push_back(pattern); + } + + TestSpec::PatternPtr pattern = + std::make_shared(token, + m_substring); + + if (m_exclusion) { + pattern = std::make_shared( + pattern); + } + m_currentFilter.m_patterns.push_back(pattern); + } + m_substring.clear(); + m_exclusion = false; + m_mode = None; +} + +TestSpec parseTestSpec(std::string const &arg) +{ + return TestSpecParser(ITagAliasRegistry::get()).parse(arg).testSpec(); +} + +} // namespace Catch +// end catch_test_spec_parser.cpp +// start catch_timer.cpp + +#include + +static const uint64_t nanosecondsInSecond = 1000000000; + +namespace Catch { + +auto getCurrentNanosecondsSinceEpoch() -> uint64_t +{ + return std::chrono::duration_cast( + std::chrono::high_resolution_clock::now() + .time_since_epoch()) + .count(); +} + +namespace { +auto estimateClockResolution() -> uint64_t +{ + uint64_t sum = 0; + static const uint64_t iterations = 1000000; + + auto startTime = getCurrentNanosecondsSinceEpoch(); + + for (std::size_t i = 0; i < iterations; ++i) { + + uint64_t ticks; + uint64_t baseTicks = getCurrentNanosecondsSinceEpoch(); + do { + ticks = getCurrentNanosecondsSinceEpoch(); + } while (ticks == baseTicks); + + auto delta = ticks - baseTicks; + sum += delta; + + // If we have been calibrating for over 3 seconds -- the clock + // is terrible and we should move on. + // TBD: How to signal that the measured resolution is probably wrong? + if (ticks > startTime + 3 * nanosecondsInSecond) { + return sum / (i + 1u); + } + } + + // We're just taking the mean, here. To do better we could take the std. dev and exclude outliers + // - and potentially do more iterations if there's a high variance. + return sum / iterations; +} +} +auto getEstimatedClockResolution() -> uint64_t +{ + static auto s_resolution = estimateClockResolution(); + return s_resolution; +} + +void Timer::start() +{ + m_nanoseconds = getCurrentNanosecondsSinceEpoch(); +} +auto Timer::getElapsedNanoseconds() const -> uint64_t +{ + return getCurrentNanosecondsSinceEpoch() - m_nanoseconds; +} +auto Timer::getElapsedMicroseconds() const -> uint64_t +{ + return getElapsedNanoseconds() / 1000; +} +auto Timer::getElapsedMilliseconds() const -> unsigned int +{ + return static_cast(getElapsedMicroseconds() / 1000); +} +auto Timer::getElapsedSeconds() const -> double +{ + return getElapsedMicroseconds() / 1000000.0; +} + +} // namespace Catch +// end catch_timer.cpp +// start catch_tostring.cpp + +#if defined(__clang__) +#pragma clang diagnostic push +#pragma clang diagnostic ignored "-Wexit-time-destructors" +#pragma clang diagnostic ignored "-Wglobal-constructors" +#endif + +// Enable specific decls locally +#if !defined(CATCH_CONFIG_ENABLE_CHRONO_STRINGMAKER) +#define CATCH_CONFIG_ENABLE_CHRONO_STRINGMAKER +#endif + +#include +#include + +namespace Catch { + +namespace Detail { + +const std::string unprintableString = "{?}"; + +namespace { +const int hexThreshold = 255; + +struct Endianness { + enum Arch { Big, Little }; + + static Arch which() + { + int one = 1; + // If the lowest byte we read is non-zero, we can assume + // that little endian format is used. + auto value = *reinterpret_cast(&one); + return value ? Little : Big; + } +}; +} + +std::string rawMemoryToString(const void *object, std::size_t size) +{ + // Reverse order for little endian architectures + int i = 0, end = static_cast(size), inc = 1; + if (Endianness::which() == Endianness::Little) { + i = end - 1; + end = inc = -1; + } + + unsigned char const *bytes = static_cast(object); + ReusableStringStream rss; + rss << "0x" << std::setfill('0') << std::hex; + for (; i != end; i += inc) + rss << std::setw(2) << static_cast(bytes[i]); + return rss.str(); +} +} + +template std::string fpToString(T value, int precision) +{ + if (Catch::isnan(value)) { + return "nan"; + } + + ReusableStringStream rss; + rss << std::setprecision(precision) << std::fixed << value; + std::string d = rss.str(); + std::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; +} + +//// ======================================================= //// +// +// Out-of-line defs for full specialization of StringMaker +// +//// ======================================================= //// + +std::string StringMaker::convert(const std::string &str) +{ + if (!getCurrentContext().getConfig()->showInvisibles()) { + return '"' + str + '"'; + } + + std::string s("\""); + for (char c : str) { + switch (c) { + case '\n': + s.append("\\n"); + break; + case '\t': + s.append("\\t"); + break; + default: + s.push_back(c); + break; + } + } + s.append("\""); + return s; +} + +#ifdef CATCH_CONFIG_CPP17_STRING_VIEW +std::string StringMaker::convert(std::string_view str) +{ + return ::Catch::Detail::stringify(std::string{str}); +} +#endif + +std::string StringMaker::convert(char const *str) +{ + if (str) { + return ::Catch::Detail::stringify(std::string{str}); + } else { + return {"{null string}"}; + } +} +std::string StringMaker::convert(char *str) +{ + if (str) { + return ::Catch::Detail::stringify(std::string{str}); + } else { + return {"{null string}"}; + } +} + +#ifdef CATCH_CONFIG_WCHAR +std::string StringMaker::convert(const std::wstring &wstr) +{ + std::string s; + s.reserve(wstr.size()); + for (auto c : wstr) { + s += (c <= 0xff) ? static_cast(c) : '?'; + } + return ::Catch::Detail::stringify(s); +} + +#ifdef CATCH_CONFIG_CPP17_STRING_VIEW +std::string StringMaker::convert(std::wstring_view str) +{ + return StringMaker::convert(std::wstring(str)); +} +#endif + +std::string StringMaker::convert(wchar_t const *str) +{ + if (str) { + return ::Catch::Detail::stringify(std::wstring{str}); + } else { + return {"{null string}"}; + } +} +std::string StringMaker::convert(wchar_t *str) +{ + if (str) { + return ::Catch::Detail::stringify(std::wstring{str}); + } else { + return {"{null string}"}; + } +} +#endif + +#if defined(CATCH_CONFIG_CPP17_BYTE) +#include +std::string StringMaker::convert(std::byte value) +{ + return ::Catch::Detail::stringify( + std::to_integer(value)); +} +#endif // defined(CATCH_CONFIG_CPP17_BYTE) + +std::string StringMaker::convert(int value) +{ + return ::Catch::Detail::stringify(static_cast(value)); +} +std::string StringMaker::convert(long value) +{ + return ::Catch::Detail::stringify(static_cast(value)); +} +std::string StringMaker::convert(long long value) +{ + ReusableStringStream rss; + rss << value; + if (value > Detail::hexThreshold) { + rss << " (0x" << std::hex << value << ')'; + } + return rss.str(); +} + +std::string StringMaker::convert(unsigned int value) +{ + return ::Catch::Detail::stringify( + static_cast(value)); +} +std::string StringMaker::convert(unsigned long value) +{ + return ::Catch::Detail::stringify( + static_cast(value)); +} +std::string StringMaker::convert(unsigned long long value) +{ + ReusableStringStream rss; + rss << value; + if (value > Detail::hexThreshold) { + rss << " (0x" << std::hex << value << ')'; + } + return rss.str(); +} + +std::string StringMaker::convert(bool b) +{ + return b ? "true" : "false"; +} + +std::string StringMaker::convert(signed char value) +{ + if (value == '\r') { + return "'\\r'"; + } else if (value == '\f') { + return "'\\f'"; + } else if (value == '\n') { + return "'\\n'"; + } else if (value == '\t') { + return "'\\t'"; + } else if ('\0' <= value && value < ' ') { + return ::Catch::Detail::stringify( + static_cast(value)); + } else { + char chstr[] = "' '"; + chstr[1] = value; + return chstr; + } +} +std::string StringMaker::convert(char c) +{ + return ::Catch::Detail::stringify(static_cast(c)); +} +std::string StringMaker::convert(unsigned char c) +{ + return ::Catch::Detail::stringify(static_cast(c)); +} + +std::string StringMaker::convert(std::nullptr_t) +{ + return "nullptr"; +} + +int StringMaker::precision = 5; + +std::string StringMaker::convert(float value) +{ + return fpToString(value, precision) + 'f'; +} + +int StringMaker::precision = 10; + +std::string StringMaker::convert(double value) +{ + return fpToString(value, precision); +} + +std::string ratio_string::symbol() +{ + return "a"; +} +std::string ratio_string::symbol() +{ + return "f"; +} +std::string ratio_string::symbol() +{ + return "p"; +} +std::string ratio_string::symbol() +{ + return "n"; +} +std::string ratio_string::symbol() +{ + return "u"; +} +std::string ratio_string::symbol() +{ + return "m"; +} + +} // end namespace Catch + +#if defined(__clang__) +#pragma clang diagnostic pop +#endif + +// end catch_tostring.cpp +// start catch_totals.cpp + +namespace Catch { + +Counts Counts::operator-(Counts const &other) const +{ + Counts diff; + diff.passed = passed - other.passed; + diff.failed = failed - other.failed; + diff.failedButOk = failedButOk - other.failedButOk; + return diff; +} + +Counts &Counts::operator+=(Counts const &other) +{ + passed += other.passed; + failed += other.failed; + failedButOk += other.failedButOk; + return *this; +} + +std::size_t Counts::total() const +{ + return passed + failed + failedButOk; +} +bool Counts::allPassed() const +{ + return failed == 0 && failedButOk == 0; +} +bool Counts::allOk() const +{ + return failed == 0; +} + +Totals Totals::operator-(Totals const &other) const +{ + Totals diff; + diff.assertions = assertions - other.assertions; + diff.testCases = testCases - other.testCases; + return diff; +} + +Totals &Totals::operator+=(Totals const &other) +{ + assertions += other.assertions; + testCases += other.testCases; + return *this; +} + +Totals Totals::delta(Totals const &prevTotals) const +{ + Totals diff = *this - prevTotals; + if (diff.assertions.failed > 0) + ++diff.testCases.failed; + else if (diff.assertions.failedButOk > 0) + ++diff.testCases.failedButOk; + else + ++diff.testCases.passed; + return diff; +} + +} +// end catch_totals.cpp +// start catch_uncaught_exceptions.cpp + +// start catch_config_uncaught_exceptions.hpp + +// Copyright Catch2 Authors +// Distributed under the Boost Software License, Version 1.0. +// (See accompanying file LICENSE_1_0.txt or copy at +// https://www.boost.org/LICENSE_1_0.txt) + +// SPDX-License-Identifier: BSL-1.0 + +#ifndef CATCH_CONFIG_UNCAUGHT_EXCEPTIONS_HPP +#define CATCH_CONFIG_UNCAUGHT_EXCEPTIONS_HPP + +#if defined(_MSC_VER) +#if _MSC_VER >= 1900 // Visual Studio 2015 or newer +#define CATCH_INTERNAL_CONFIG_CPP17_UNCAUGHT_EXCEPTIONS +#endif +#endif + +#include + +#if defined(__cpp_lib_uncaught_exceptions) && \ + !defined(CATCH_INTERNAL_CONFIG_CPP17_UNCAUGHT_EXCEPTIONS) + +#define CATCH_INTERNAL_CONFIG_CPP17_UNCAUGHT_EXCEPTIONS +#endif // __cpp_lib_uncaught_exceptions + +#if defined(CATCH_INTERNAL_CONFIG_CPP17_UNCAUGHT_EXCEPTIONS) && \ + !defined(CATCH_CONFIG_NO_CPP17_UNCAUGHT_EXCEPTIONS) && \ + !defined(CATCH_CONFIG_CPP17_UNCAUGHT_EXCEPTIONS) + +#define CATCH_CONFIG_CPP17_UNCAUGHT_EXCEPTIONS +#endif + +#endif // CATCH_CONFIG_UNCAUGHT_EXCEPTIONS_HPP +// end catch_config_uncaught_exceptions.hpp +#include + +namespace Catch { +bool uncaught_exceptions() +{ +#if defined(CATCH_CONFIG_DISABLE_EXCEPTIONS) + return false; +#elif defined(CATCH_CONFIG_CPP17_UNCAUGHT_EXCEPTIONS) + return std::uncaught_exceptions() > 0; +#else + return std::uncaught_exception(); +#endif +} +} // end namespace Catch +// end catch_uncaught_exceptions.cpp +// start catch_version.cpp + +#include + +namespace Catch { + +Version::Version(unsigned int _majorVersion, unsigned int _minorVersion, + unsigned int _patchNumber, char const *const _branchName, + unsigned int _buildNumber) + : majorVersion(_majorVersion), + minorVersion(_minorVersion), + patchNumber(_patchNumber), + branchName(_branchName), + buildNumber(_buildNumber) +{ +} + +std::ostream &operator<<(std::ostream &os, Version const &version) +{ + os << version.majorVersion << '.' << version.minorVersion << '.' + << version.patchNumber; + // branchName is never null -> 0th char is \0 if it is empty + if (version.branchName[0]) { + os << '-' << version.branchName << '.' << version.buildNumber; + } + return os; +} + +Version const &libraryVersion() +{ + static Version version(2, 13, 8, "", 0); + return version; +} + +} +// end catch_version.cpp +// start catch_wildcard_pattern.cpp + +namespace Catch { + +WildcardPattern::WildcardPattern(std::string const &pattern, + CaseSensitive::Choice caseSensitivity) + : m_caseSensitivity(caseSensitivity), + m_pattern(normaliseString(pattern)) +{ + if (startsWith(m_pattern, '*')) { + m_pattern = m_pattern.substr(1); + m_wildcard = WildcardAtStart; + } + if (endsWith(m_pattern, '*')) { + m_pattern = m_pattern.substr(0, m_pattern.size() - 1); + m_wildcard = static_cast(m_wildcard | + WildcardAtEnd); + } +} + +bool WildcardPattern::matches(std::string const &str) const +{ + switch (m_wildcard) { + case NoWildcard: + return m_pattern == normaliseString(str); + case WildcardAtStart: + return endsWith(normaliseString(str), m_pattern); + case WildcardAtEnd: + return startsWith(normaliseString(str), m_pattern); + case WildcardAtBothEnds: + return contains(normaliseString(str), m_pattern); + default: + CATCH_INTERNAL_ERROR("Unknown enum"); + } +} + +std::string WildcardPattern::normaliseString(std::string const &str) const +{ + return trim(m_caseSensitivity == CaseSensitive::No ? toLower(str) + : str); +} +} +// end catch_wildcard_pattern.cpp +// start catch_xmlwriter.cpp + +#include +#include + +namespace Catch { + +namespace { + +size_t trailingBytes(unsigned char c) +{ + if ((c & 0xE0) == 0xC0) { + return 2; + } + if ((c & 0xF0) == 0xE0) { + return 3; + } + if ((c & 0xF8) == 0xF0) { + return 4; + } + CATCH_INTERNAL_ERROR("Invalid multibyte utf-8 start byte encountered"); +} + +uint32_t headerValue(unsigned char c) +{ + if ((c & 0xE0) == 0xC0) { + return c & 0x1F; + } + if ((c & 0xF0) == 0xE0) { + return c & 0x0F; + } + if ((c & 0xF8) == 0xF0) { + return c & 0x07; + } + CATCH_INTERNAL_ERROR("Invalid multibyte utf-8 start byte encountered"); +} + +void hexEscapeChar(std::ostream &os, unsigned char c) +{ + std::ios_base::fmtflags f(os.flags()); + os << "\\x" << std::uppercase << std::hex << std::setfill('0') + << std::setw(2) << static_cast(c); + os.flags(f); +} + +bool shouldNewline(XmlFormatting fmt) +{ + return !!(static_cast::type>( + fmt & XmlFormatting::Newline)); +} + +bool shouldIndent(XmlFormatting fmt) +{ + return !!(static_cast::type>( + fmt & XmlFormatting::Indent)); +} + +} // anonymous namespace + +XmlFormatting operator|(XmlFormatting lhs, XmlFormatting rhs) +{ + return static_cast( + static_cast::type>(lhs) | + static_cast::type>(rhs)); +} + +XmlFormatting operator&(XmlFormatting lhs, XmlFormatting rhs) +{ + return static_cast( + static_cast::type>(lhs) & + static_cast::type>(rhs)); +} + +XmlEncode::XmlEncode(std::string const &str, ForWhat forWhat) + : m_str(str), m_forWhat(forWhat) +{ +} + +void XmlEncode::encodeTo(std::ostream &os) const +{ + // Apostrophe escaping not necessary if we always use " to write attributes + // (see: http://www.w3.org/TR/xml/#syntax) + + for (std::size_t idx = 0; idx < m_str.size(); ++idx) { + unsigned char c = m_str[idx]; + switch (c) { + case '<': + os << "<"; + break; + case '&': + os << "&"; + break; + + case '>': + // See: http://www.w3.org/TR/xml/#syntax + if (idx > 2 && m_str[idx - 1] == ']' && + m_str[idx - 2] == ']') + os << ">"; + else + os << c; + break; + + case '\"': + if (m_forWhat == ForAttributes) + os << """; + else + os << c; + break; + + default: + // Check for control characters and invalid utf-8 + + // Escape control characters in standard ascii + // see http://stackoverflow.com/questions/404107/why-are-control-characters-illegal-in-xml-1-0 + if (c < 0x09 || (c > 0x0D && c < 0x20) || c == 0x7F) { + hexEscapeChar(os, c); + break; + } + + // Plain ASCII: Write it to stream + if (c < 0x7F) { + os << c; + break; + } + + // UTF-8 territory + // Check if the encoding is valid and if it is not, hex escape bytes. + // Important: We do not check the exact decoded values for validity, only the encoding format + // First check that this bytes is a valid lead byte: + // This means that it is not encoded as 1111 1XXX + // Or as 10XX XXXX + if (c < 0xC0 || c >= 0xF8) { + hexEscapeChar(os, c); + break; + } + + auto encBytes = trailingBytes(c); + // Are there enough bytes left to avoid accessing out-of-bounds memory? + if (idx + encBytes - 1 >= m_str.size()) { + hexEscapeChar(os, c); + break; + } + // The header is valid, check data + // The next encBytes bytes must together be a valid utf-8 + // This means: bitpattern 10XX XXXX and the extracted value is sane (ish) + bool valid = true; + uint32_t value = headerValue(c); + for (std::size_t n = 1; n < encBytes; ++n) { + unsigned char nc = m_str[idx + n]; + valid &= ((nc & 0xC0) == 0x80); + value = (value << 6) | (nc & 0x3F); + } + + if ( + // Wrong bit pattern of following bytes + (!valid) || + // Overlong encodings + (value < 0x80) || + (0x80 <= value && value < 0x800 && + encBytes > 2) || + (0x800 < value && value < 0x10000 && + encBytes > 3) || + // Encoded value out of range + (value >= 0x110000)) { + hexEscapeChar(os, c); + break; + } + + // If we got here, this is in fact a valid(ish) utf-8 sequence + for (std::size_t n = 0; n < encBytes; ++n) { + os << m_str[idx + n]; + } + idx += encBytes - 1; + break; + } + } +} + +std::ostream &operator<<(std::ostream &os, XmlEncode const &xmlEncode) +{ + xmlEncode.encodeTo(os); + return os; +} + +XmlWriter::ScopedElement::ScopedElement(XmlWriter *writer, XmlFormatting fmt) + : m_writer(writer), m_fmt(fmt) +{ +} + +XmlWriter::ScopedElement::ScopedElement(ScopedElement &&other) noexcept + : m_writer(other.m_writer), m_fmt(other.m_fmt) +{ + other.m_writer = nullptr; + other.m_fmt = XmlFormatting::None; +} +XmlWriter::ScopedElement & +XmlWriter::ScopedElement::operator=(ScopedElement &&other) noexcept +{ + if (m_writer) { + m_writer->endElement(); + } + m_writer = other.m_writer; + other.m_writer = nullptr; + m_fmt = other.m_fmt; + other.m_fmt = XmlFormatting::None; + return *this; +} + +XmlWriter::ScopedElement::~ScopedElement() +{ + if (m_writer) { + m_writer->endElement(m_fmt); + } +} + +XmlWriter::ScopedElement & +XmlWriter::ScopedElement::writeText(std::string const &text, XmlFormatting fmt) +{ + m_writer->writeText(text, fmt); + return *this; +} + +XmlWriter::XmlWriter(std::ostream &os) : m_os(os) +{ + writeDeclaration(); +} + +XmlWriter::~XmlWriter() +{ + while (!m_tags.empty()) { + endElement(); + } + newlineIfNecessary(); +} + +XmlWriter &XmlWriter::startElement(std::string const &name, XmlFormatting fmt) +{ + ensureTagClosed(); + newlineIfNecessary(); + if (shouldIndent(fmt)) { + m_os << m_indent; + m_indent += " "; + } + m_os << '<' << name; + m_tags.push_back(name); + m_tagIsOpen = true; + applyFormatting(fmt); + return *this; +} + +XmlWriter::ScopedElement XmlWriter::scopedElement(std::string const &name, + XmlFormatting fmt) +{ + ScopedElement scoped(this, fmt); + startElement(name, fmt); + return scoped; +} + +XmlWriter &XmlWriter::endElement(XmlFormatting fmt) +{ + m_indent = m_indent.substr(0, m_indent.size() - 2); + + if (m_tagIsOpen) { + m_os << "/>"; + m_tagIsOpen = false; + } else { + newlineIfNecessary(); + if (shouldIndent(fmt)) { + m_os << m_indent; + } + m_os << ""; + } + m_os << std::flush; + applyFormatting(fmt); + m_tags.pop_back(); + return *this; +} + +XmlWriter &XmlWriter::writeAttribute(std::string const &name, + std::string const &attribute) +{ + if (!name.empty() && !attribute.empty()) + m_os << ' ' << name << "=\"" + << XmlEncode(attribute, XmlEncode::ForAttributes) << '"'; + return *this; +} + +XmlWriter &XmlWriter::writeAttribute(std::string const &name, bool attribute) +{ + m_os << ' ' << name << "=\"" << (attribute ? "true" : "false") << '"'; + return *this; +} + +XmlWriter &XmlWriter::writeText(std::string const &text, XmlFormatting fmt) +{ + if (!text.empty()) { + bool tagWasOpen = m_tagIsOpen; + ensureTagClosed(); + if (tagWasOpen && shouldIndent(fmt)) { + m_os << m_indent; + } + m_os << XmlEncode(text); + applyFormatting(fmt); + } + return *this; +} + +XmlWriter &XmlWriter::writeComment(std::string const &text, XmlFormatting fmt) +{ + ensureTagClosed(); + if (shouldIndent(fmt)) { + m_os << m_indent; + } + m_os << ""; + applyFormatting(fmt); + return *this; +} + +void XmlWriter::writeStylesheetRef(std::string const &url) +{ + m_os << "\n"; +} + +XmlWriter &XmlWriter::writeBlankLine() +{ + ensureTagClosed(); + m_os << '\n'; + return *this; +} + +void XmlWriter::ensureTagClosed() +{ + if (m_tagIsOpen) { + m_os << '>' << std::flush; + newlineIfNecessary(); + m_tagIsOpen = false; + } +} + +void XmlWriter::applyFormatting(XmlFormatting fmt) +{ + m_needsNewline = shouldNewline(fmt); +} + +void XmlWriter::writeDeclaration() +{ + m_os << "\n"; +} + +void XmlWriter::newlineIfNecessary() +{ + if (m_needsNewline) { + m_os << std::endl; + m_needsNewline = false; + } +} +} +// end catch_xmlwriter.cpp +// start catch_reporter_bases.cpp + +#include +#include +#include +#include +#include + +namespace Catch { +void prepareExpandedExpression(AssertionResult &result) +{ + result.getExpandedExpression(); +} + +// Because formatting using c++ streams is stateful, drop down to C is required +// Alternatively we could use stringstream, but its performance is... not good. +std::string getFormattedDuration(double duration) +{ + // Max exponent + 1 is required to represent the whole part + // + 1 for decimal point + // + 3 for the 3 decimal places + // + 1 for null terminator + const std::size_t maxDoubleSize = DBL_MAX_10_EXP + 1 + 1 + 3 + 1; + char buffer[maxDoubleSize]; + + // Save previous errno, to prevent sprintf from overwriting it + ErrnoGuard guard; +#ifdef _MSC_VER + sprintf_s(buffer, "%.3f", duration); +#else + std::sprintf(buffer, "%.3f", duration); +#endif + return std::string(buffer); +} + +bool shouldShowDuration(IConfig const &config, double duration) +{ + if (config.showDurations() == ShowDurations::Always) { + return true; + } + if (config.showDurations() == ShowDurations::Never) { + return false; + } + const double min = config.minDuration(); + return min >= 0 && duration >= min; +} + +std::string serializeFilters(std::vector const &container) +{ + ReusableStringStream oss; + bool first = true; + for (auto &&filter : container) { + if (!first) + oss << ' '; + else + first = false; + + oss << filter; + } + return oss.str(); +} + +TestEventListenerBase::TestEventListenerBase(ReporterConfig const &_config) + : StreamingReporterBase(_config) +{ +} + +std::set TestEventListenerBase::getSupportedVerbosities() +{ + return {Verbosity::Quiet, Verbosity::Normal, Verbosity::High}; +} + +void TestEventListenerBase::assertionStarting(AssertionInfo const &) {} + +bool TestEventListenerBase::assertionEnded(AssertionStats const &) +{ + return false; +} + +} // end namespace Catch +// end catch_reporter_bases.cpp +// start catch_reporter_compact.cpp + +namespace { + +#ifdef CATCH_PLATFORM_MAC +const char *failedString() +{ + return "FAILED"; +} +const char *passedString() +{ + return "PASSED"; +} +#else +const char *failedString() +{ + return "failed"; +} +const char *passedString() +{ + return "passed"; +} +#endif + +// Colour::LightGrey +Catch::Colour::Code dimColour() +{ + return Catch::Colour::FileName; +} + +std::string bothOrAll(std::size_t count) +{ + return count == 1 ? std::string() : count == 2 ? "both " : "all "; +} + +} // anon namespace + +namespace Catch { +namespace { +// Colour, message variants: +// - white: No tests ran. +// - red: Failed [both/all] N test cases, failed [both/all] M assertions. +// - white: Passed [both/all] N test cases (no assertions). +// - red: Failed N tests cases, failed M assertions. +// - green: Passed [both/all] N tests cases with M assertions. +void printTotals(std::ostream &out, const Totals &totals) +{ + if (totals.testCases.total() == 0) { + out << "No tests ran."; + } else if (totals.testCases.failed == totals.testCases.total()) { + Colour colour(Colour::ResultError); + const std::string qualify_assertions_failed = + totals.assertions.failed == totals.assertions.total() + ? bothOrAll(totals.assertions.failed) + : std::string(); + out << "Failed " << bothOrAll(totals.testCases.failed) + << pluralise(totals.testCases.failed, "test case") + << ", " + "failed " + << qualify_assertions_failed + << pluralise(totals.assertions.failed, "assertion") << '.'; + } else if (totals.assertions.total() == 0) { + out << "Passed " << bothOrAll(totals.testCases.total()) + << pluralise(totals.testCases.total(), "test case") + << " (no assertions)."; + } else if (totals.assertions.failed) { + Colour colour(Colour::ResultError); + out << "Failed " + << pluralise(totals.testCases.failed, "test case") + << ", " + "failed " + << pluralise(totals.assertions.failed, "assertion") << '.'; + } else { + Colour colour(Colour::ResultSuccess); + out << "Passed " << bothOrAll(totals.testCases.passed) + << pluralise(totals.testCases.passed, "test case") + << " with " + << pluralise(totals.assertions.passed, "assertion") << '.'; + } +} + +// Implementation of CompactReporter formatting +class AssertionPrinter { +public: + AssertionPrinter &operator=(AssertionPrinter const &) = delete; + AssertionPrinter(AssertionPrinter const &) = delete; + AssertionPrinter(std::ostream &_stream, AssertionStats const &_stats, + bool _printInfoMessages) + : stream(_stream), + result(_stats.assertionResult), + messages(_stats.infoMessages), + itMessage(_stats.infoMessages.begin()), + printInfoMessages(_printInfoMessages) + { + } + + void print() + { + printSourceInfo(); + + itMessage = messages.begin(); + + switch (result.getResultType()) { + case ResultWas::Ok: + printResultType(Colour::ResultSuccess, passedString()); + printOriginalExpression(); + printReconstructedExpression(); + if (!result.hasExpression()) + printRemainingMessages(Colour::None); + else + printRemainingMessages(); + break; + case ResultWas::ExpressionFailed: + if (result.isOk()) + printResultType( + Colour::ResultSuccess, + failedString() + + std::string(" - but was ok")); + else + printResultType(Colour::Error, failedString()); + printOriginalExpression(); + printReconstructedExpression(); + printRemainingMessages(); + break; + case ResultWas::ThrewException: + printResultType(Colour::Error, failedString()); + printIssue("unexpected exception with message:"); + printMessage(); + printExpressionWas(); + printRemainingMessages(); + break; + case ResultWas::FatalErrorCondition: + printResultType(Colour::Error, failedString()); + printIssue("fatal error condition with message:"); + printMessage(); + printExpressionWas(); + printRemainingMessages(); + break; + case ResultWas::DidntThrowException: + printResultType(Colour::Error, failedString()); + printIssue("expected exception, got none"); + printExpressionWas(); + printRemainingMessages(); + break; + case ResultWas::Info: + printResultType(Colour::None, "info"); + printMessage(); + printRemainingMessages(); + break; + case ResultWas::Warning: + printResultType(Colour::None, "warning"); + printMessage(); + printRemainingMessages(); + break; + case ResultWas::ExplicitFailure: + printResultType(Colour::Error, failedString()); + printIssue("explicitly"); + printRemainingMessages(Colour::None); + break; + // These cases are here to prevent compiler warnings + case ResultWas::Unknown: + case ResultWas::FailureBit: + case ResultWas::Exception: + printResultType(Colour::Error, "** internal error **"); + break; + } + } + +private: + void printSourceInfo() const + { + Colour colourGuard(Colour::FileName); + stream << result.getSourceInfo() << ':'; + } + + void printResultType(Colour::Code colour, + std::string const &passOrFail) const + { + if (!passOrFail.empty()) { + { + Colour colourGuard(colour); + stream << ' ' << passOrFail; + } + stream << ':'; + } + } + + void printIssue(std::string const &issue) const + { + stream << ' ' << issue; + } + + void printExpressionWas() + { + if (result.hasExpression()) { + stream << ';'; + { + Colour colour(dimColour()); + stream << " expression was:"; + } + printOriginalExpression(); + } + } + + void printOriginalExpression() const + { + if (result.hasExpression()) { + stream << ' ' << result.getExpression(); + } + } + + void printReconstructedExpression() const + { + if (result.hasExpandedExpression()) { + { + Colour colour(dimColour()); + stream << " for: "; + } + stream << result.getExpandedExpression(); + } + } + + void printMessage() + { + if (itMessage != messages.end()) { + stream << " '" << itMessage->message << '\''; + ++itMessage; + } + } + + void printRemainingMessages(Colour::Code colour = dimColour()) + { + if (itMessage == messages.end()) + return; + + const auto itEnd = messages.cend(); + const auto N = static_cast( + std::distance(itMessage, itEnd)); + + { + Colour colourGuard(colour); + stream << " with " << pluralise(N, "message") << ':'; + } + + while (itMessage != itEnd) { + // If this assertion is a warning ignore any INFO messages + if (printInfoMessages || + itMessage->type != ResultWas::Info) { + printMessage(); + if (itMessage != itEnd) { + Colour colourGuard(dimColour()); + stream << " and"; + } + continue; + } + ++itMessage; + } + } + +private: + std::ostream &stream; + AssertionResult const &result; + std::vector messages; + std::vector::const_iterator itMessage; + bool printInfoMessages; +}; + +} // anon namespace + +std::string CompactReporter::getDescription() +{ + return "Reports test results on a single line, suitable for IDEs"; +} + +void CompactReporter::noMatchingTestCases(std::string const &spec) +{ + stream << "No test cases matched '" << spec << '\'' << std::endl; +} + +void CompactReporter::assertionStarting(AssertionInfo const &) {} + +bool CompactReporter::assertionEnded(AssertionStats const &_assertionStats) +{ + AssertionResult const &result = _assertionStats.assertionResult; + + bool printInfoMessages = true; + + // Drop out if result was successful and we're not printing those + if (!m_config->includeSuccessfulResults() && result.isOk()) { + if (result.getResultType() != ResultWas::Warning) + return false; + printInfoMessages = false; + } + + AssertionPrinter printer(stream, _assertionStats, printInfoMessages); + printer.print(); + + stream << std::endl; + return true; +} + +void CompactReporter::sectionEnded(SectionStats const &_sectionStats) +{ + double dur = _sectionStats.durationInSeconds; + if (shouldShowDuration(*m_config, dur)) { + stream << getFormattedDuration(dur) + << " s: " << _sectionStats.sectionInfo.name << std::endl; + } +} + +void CompactReporter::testRunEnded(TestRunStats const &_testRunStats) +{ + printTotals(stream, _testRunStats.totals); + stream << '\n' << std::endl; + StreamingReporterBase::testRunEnded(_testRunStats); +} + +CompactReporter::~CompactReporter() {} + +CATCH_REGISTER_REPORTER("compact", CompactReporter) + +} // end namespace Catch +// end catch_reporter_compact.cpp +// start catch_reporter_console.cpp + +#include +#include + +#if defined(_MSC_VER) +#pragma warning(push) +#pragma warning( \ + disable : 4061) // Not all labels are EXPLICITLY handled in switch +// Note that 4062 (not all labels are handled and default is missing) is enabled +#endif + +#if defined(__clang__) +#pragma clang diagnostic push +// For simplicity, benchmarking-only helpers are always enabled +#pragma clang diagnostic ignored "-Wunused-function" +#endif + +namespace Catch { + +namespace { + +// Formatter impl for ConsoleReporter +class ConsoleAssertionPrinter { +public: + ConsoleAssertionPrinter & + operator=(ConsoleAssertionPrinter const &) = delete; + ConsoleAssertionPrinter(ConsoleAssertionPrinter const &) = delete; + ConsoleAssertionPrinter(std::ostream &_stream, + AssertionStats const &_stats, + bool _printInfoMessages) + : stream(_stream), + stats(_stats), + result(_stats.assertionResult), + colour(Colour::None), + message(result.getMessage()), + messages(_stats.infoMessages), + printInfoMessages(_printInfoMessages) + { + switch (result.getResultType()) { + case ResultWas::Ok: + colour = Colour::Success; + passOrFail = "PASSED"; + //if( result.hasMessage() ) + if (_stats.infoMessages.size() == 1) + messageLabel = "with message"; + if (_stats.infoMessages.size() > 1) + messageLabel = "with messages"; + break; + case ResultWas::ExpressionFailed: + if (result.isOk()) { + colour = Colour::Success; + passOrFail = "FAILED - but was ok"; + } else { + colour = Colour::Error; + passOrFail = "FAILED"; + } + if (_stats.infoMessages.size() == 1) + messageLabel = "with message"; + if (_stats.infoMessages.size() > 1) + messageLabel = "with messages"; + break; + case ResultWas::ThrewException: + colour = Colour::Error; + passOrFail = "FAILED"; + messageLabel = "due to unexpected exception with "; + if (_stats.infoMessages.size() == 1) + messageLabel += "message"; + if (_stats.infoMessages.size() > 1) + messageLabel += "messages"; + break; + case ResultWas::FatalErrorCondition: + colour = Colour::Error; + passOrFail = "FAILED"; + messageLabel = "due to a fatal error condition"; + break; + case ResultWas::DidntThrowException: + colour = Colour::Error; + passOrFail = "FAILED"; + messageLabel = + "because no exception was thrown where one was expected"; + break; + case ResultWas::Info: + messageLabel = "info"; + break; + case ResultWas::Warning: + messageLabel = "warning"; + break; + case ResultWas::ExplicitFailure: + passOrFail = "FAILED"; + colour = Colour::Error; + if (_stats.infoMessages.size() == 1) + messageLabel = "explicitly with message"; + if (_stats.infoMessages.size() > 1) + messageLabel = "explicitly with messages"; + break; + // These cases are here to prevent compiler warnings + case ResultWas::Unknown: + case ResultWas::FailureBit: + case ResultWas::Exception: + passOrFail = "** internal error **"; + colour = Colour::Error; + break; + } + } + + void print() const + { + printSourceInfo(); + if (stats.totals.assertions.total() > 0) { + printResultType(); + printOriginalExpression(); + printReconstructedExpression(); + } else { + stream << '\n'; + } + printMessage(); + } + +private: + void printResultType() const + { + if (!passOrFail.empty()) { + Colour colourGuard(colour); + stream << passOrFail << ":\n"; + } + } + void printOriginalExpression() const + { + if (result.hasExpression()) { + Colour colourGuard(Colour::OriginalExpression); + stream << " "; + stream << result.getExpressionInMacro(); + stream << '\n'; + } + } + void printReconstructedExpression() const + { + if (result.hasExpandedExpression()) { + stream << "with expansion:\n"; + Colour colourGuard(Colour::ReconstructedExpression); + stream << Column(result.getExpandedExpression()) + .indent(2) + << '\n'; + } + } + void printMessage() const + { + if (!messageLabel.empty()) + stream << messageLabel << ':' << '\n'; + for (auto const &msg : messages) { + // If this assertion is a warning ignore any INFO messages + if (printInfoMessages || msg.type != ResultWas::Info) + stream << Column(msg.message).indent(2) << '\n'; + } + } + void printSourceInfo() const + { + Colour colourGuard(Colour::FileName); + stream << result.getSourceInfo() << ": "; + } + + std::ostream &stream; + AssertionStats const &stats; + AssertionResult const &result; + Colour::Code colour; + std::string passOrFail; + std::string messageLabel; + std::string message; + std::vector messages; + bool printInfoMessages; +}; + +std::size_t makeRatio(std::size_t number, std::size_t total) +{ + std::size_t ratio = + total > 0 ? CATCH_CONFIG_CONSOLE_WIDTH * number / total : 0; + return (ratio == 0 && number > 0) ? 1 : ratio; +} + +std::size_t &findMax(std::size_t &i, std::size_t &j, std::size_t &k) +{ + if (i > j && i > k) + return i; + else if (j > k) + return j; + else + return k; +} + +struct ColumnInfo { + enum Justification { Left, Right }; + std::string name; + int width; + Justification justification; +}; +struct ColumnBreak { +}; +struct RowBreak { +}; + +class Duration { + enum class Unit { + Auto, + Nanoseconds, + Microseconds, + Milliseconds, + Seconds, + Minutes + }; + static const uint64_t s_nanosecondsInAMicrosecond = 1000; + static const uint64_t s_nanosecondsInAMillisecond = + 1000 * s_nanosecondsInAMicrosecond; + static const uint64_t s_nanosecondsInASecond = + 1000 * s_nanosecondsInAMillisecond; + static const uint64_t s_nanosecondsInAMinute = + 60 * s_nanosecondsInASecond; + + double m_inNanoseconds; + Unit m_units; + +public: + explicit Duration(double inNanoseconds, Unit units = Unit::Auto) + : m_inNanoseconds(inNanoseconds), m_units(units) + { + if (m_units == Unit::Auto) { + if (m_inNanoseconds < s_nanosecondsInAMicrosecond) + m_units = Unit::Nanoseconds; + else if (m_inNanoseconds < s_nanosecondsInAMillisecond) + m_units = Unit::Microseconds; + else if (m_inNanoseconds < s_nanosecondsInASecond) + m_units = Unit::Milliseconds; + else if (m_inNanoseconds < s_nanosecondsInAMinute) + m_units = Unit::Seconds; + else + m_units = Unit::Minutes; + } + } + + auto value() const -> double + { + switch (m_units) { + case Unit::Microseconds: + return m_inNanoseconds / + static_cast(s_nanosecondsInAMicrosecond); + case Unit::Milliseconds: + return m_inNanoseconds / + static_cast(s_nanosecondsInAMillisecond); + case Unit::Seconds: + return m_inNanoseconds / + static_cast(s_nanosecondsInASecond); + case Unit::Minutes: + return m_inNanoseconds / + static_cast(s_nanosecondsInAMinute); + default: + return m_inNanoseconds; + } + } + auto unitsAsString() const -> std::string + { + switch (m_units) { + case Unit::Nanoseconds: + return "ns"; + case Unit::Microseconds: + return "us"; + case Unit::Milliseconds: + return "ms"; + case Unit::Seconds: + return "s"; + case Unit::Minutes: + return "m"; + default: + return "** internal error **"; + } + } + friend auto operator<<(std::ostream &os, Duration const &duration) + -> std::ostream & + { + return os << duration.value() << ' ' + << duration.unitsAsString(); + } +}; +} // end anon namespace + +class TablePrinter { + std::ostream &m_os; + std::vector m_columnInfos; + std::ostringstream m_oss; + int m_currentColumn = -1; + bool m_isOpen = false; + +public: + TablePrinter(std::ostream &os, std::vector columnInfos) + : m_os(os), m_columnInfos(std::move(columnInfos)) + { + } + + auto columnInfos() const -> std::vector const & + { + return m_columnInfos; + } + + void open() + { + if (!m_isOpen) { + m_isOpen = true; + *this << RowBreak(); + + Columns headerCols; + Spacer spacer(2); + for (auto const &info : m_columnInfos) { + headerCols += Column(info.name).width( + static_cast(info.width - + 2)); + headerCols += spacer; + } + m_os << headerCols << '\n'; + + m_os << Catch::getLineOfChars<'-'>() << '\n'; + } + } + void close() + { + if (m_isOpen) { + *this << RowBreak(); + m_os << std::endl; + m_isOpen = false; + } + } + + template + friend TablePrinter &operator<<(TablePrinter &tp, T const &value) + { + tp.m_oss << value; + return tp; + } + + friend TablePrinter &operator<<(TablePrinter &tp, ColumnBreak) + { + auto colStr = tp.m_oss.str(); + const auto strSize = colStr.size(); + tp.m_oss.str(""); + tp.open(); + if (tp.m_currentColumn == + static_cast(tp.m_columnInfos.size() - 1)) { + tp.m_currentColumn = -1; + tp.m_os << '\n'; + } + tp.m_currentColumn++; + + auto colInfo = tp.m_columnInfos[tp.m_currentColumn]; + auto padding = + (strSize + 1 < static_cast(colInfo.width)) + ? std::string(colInfo.width - (strSize + 1), + ' ') + : std::string(); + if (colInfo.justification == ColumnInfo::Left) + tp.m_os << colStr << padding << ' '; + else + tp.m_os << padding << colStr << ' '; + return tp; + } + + friend TablePrinter &operator<<(TablePrinter &tp, RowBreak) + { + if (tp.m_currentColumn > 0) { + tp.m_os << '\n'; + tp.m_currentColumn = -1; + } + return tp; + } +}; + +ConsoleReporter::ConsoleReporter(ReporterConfig const &config) + : StreamingReporterBase(config), + m_tablePrinter(new TablePrinter( + config.stream(), [&config]() -> std::vector { + if (config.fullConfig()->benchmarkNoAnalysis()) { + return {{"benchmark name", + CATCH_CONFIG_CONSOLE_WIDTH - 43, + ColumnInfo::Left}, + {" samples", 14, + ColumnInfo::Right}, + {" iterations", 14, + ColumnInfo::Right}, + {" mean", 14, + ColumnInfo::Right}}; + } else { + return {{"benchmark name", + CATCH_CONFIG_CONSOLE_WIDTH - 43, + ColumnInfo::Left}, + {"samples mean std dev", + 14, ColumnInfo::Right}, + {"iterations low mean low std dev", + 14, ColumnInfo::Right}, + {"estimated high mean high std dev", + 14, ColumnInfo::Right}}; + } + }())) +{ +} +ConsoleReporter::~ConsoleReporter() = default; + +std::string ConsoleReporter::getDescription() +{ + return "Reports test results as plain lines of text"; +} + +void ConsoleReporter::noMatchingTestCases(std::string const &spec) +{ + stream << "No test cases matched '" << spec << '\'' << std::endl; +} + +void ConsoleReporter::reportInvalidArguments(std::string const &arg) +{ + stream << "Invalid Filter: " << arg << std::endl; +} + +void ConsoleReporter::assertionStarting(AssertionInfo const &) {} + +bool ConsoleReporter::assertionEnded(AssertionStats const &_assertionStats) +{ + AssertionResult const &result = _assertionStats.assertionResult; + + bool includeResults = m_config->includeSuccessfulResults() || + !result.isOk(); + + // Drop out if result was successful but we're not printing them. + if (!includeResults && result.getResultType() != ResultWas::Warning) + return false; + + lazyPrint(); + + ConsoleAssertionPrinter printer(stream, _assertionStats, + includeResults); + printer.print(); + stream << std::endl; + return true; +} + +void ConsoleReporter::sectionStarting(SectionInfo const &_sectionInfo) +{ + m_tablePrinter->close(); + m_headerPrinted = false; + StreamingReporterBase::sectionStarting(_sectionInfo); +} +void ConsoleReporter::sectionEnded(SectionStats const &_sectionStats) +{ + m_tablePrinter->close(); + if (_sectionStats.missingAssertions) { + lazyPrint(); + Colour colour(Colour::ResultError); + if (m_sectionStack.size() > 1) + stream << "\nNo assertions in section"; + else + stream << "\nNo assertions in test case"; + stream << " '" << _sectionStats.sectionInfo.name << "'\n" + << std::endl; + } + double dur = _sectionStats.durationInSeconds; + if (shouldShowDuration(*m_config, dur)) { + stream << getFormattedDuration(dur) + << " s: " << _sectionStats.sectionInfo.name << std::endl; + } + if (m_headerPrinted) { + m_headerPrinted = false; + } + StreamingReporterBase::sectionEnded(_sectionStats); +} + +#if defined(CATCH_CONFIG_ENABLE_BENCHMARKING) +void ConsoleReporter::benchmarkPreparing(std::string const &name) +{ + lazyPrintWithoutClosingBenchmarkTable(); + + auto nameCol = Column(name).width(static_cast( + m_tablePrinter->columnInfos()[0].width - 2)); + + bool firstLine = true; + for (auto line : nameCol) { + if (!firstLine) + (*m_tablePrinter) << ColumnBreak() << ColumnBreak() + << ColumnBreak(); + else + firstLine = false; + + (*m_tablePrinter) << line << ColumnBreak(); + } +} + +void ConsoleReporter::benchmarkStarting(BenchmarkInfo const &info) +{ + (*m_tablePrinter) << info.samples << ColumnBreak() << info.iterations + << ColumnBreak(); + if (!m_config->benchmarkNoAnalysis()) + (*m_tablePrinter) + << Duration(info.estimatedDuration) << ColumnBreak(); +} +void ConsoleReporter::benchmarkEnded(BenchmarkStats<> const &stats) +{ + if (m_config->benchmarkNoAnalysis()) { + (*m_tablePrinter) + << Duration(stats.mean.point.count()) << ColumnBreak(); + } else { + (*m_tablePrinter) + << ColumnBreak() << Duration(stats.mean.point.count()) + << ColumnBreak() + << Duration(stats.mean.lower_bound.count()) + << ColumnBreak() + << Duration(stats.mean.upper_bound.count()) + << ColumnBreak() << ColumnBreak() + << Duration(stats.standardDeviation.point.count()) + << ColumnBreak() + << Duration(stats.standardDeviation.lower_bound.count()) + << ColumnBreak() + << Duration(stats.standardDeviation.upper_bound.count()) + << ColumnBreak() << ColumnBreak() << ColumnBreak() + << ColumnBreak() << ColumnBreak(); + } +} + +void ConsoleReporter::benchmarkFailed(std::string const &error) +{ + Colour colour(Colour::Red); + (*m_tablePrinter) << "Benchmark failed (" << error << ')' + << ColumnBreak() << RowBreak(); +} +#endif // CATCH_CONFIG_ENABLE_BENCHMARKING + +void ConsoleReporter::testCaseEnded(TestCaseStats const &_testCaseStats) +{ + m_tablePrinter->close(); + StreamingReporterBase::testCaseEnded(_testCaseStats); + m_headerPrinted = false; +} +void ConsoleReporter::testGroupEnded(TestGroupStats const &_testGroupStats) +{ + if (currentGroupInfo.used) { + printSummaryDivider(); + stream << "Summary for group '" + << _testGroupStats.groupInfo.name << "':\n"; + printTotals(_testGroupStats.totals); + stream << '\n' << std::endl; + } + StreamingReporterBase::testGroupEnded(_testGroupStats); +} +void ConsoleReporter::testRunEnded(TestRunStats const &_testRunStats) +{ + printTotalsDivider(_testRunStats.totals); + printTotals(_testRunStats.totals); + stream << std::endl; + StreamingReporterBase::testRunEnded(_testRunStats); +} +void ConsoleReporter::testRunStarting(TestRunInfo const &_testInfo) +{ + StreamingReporterBase::testRunStarting(_testInfo); + printTestFilters(); +} + +void ConsoleReporter::lazyPrint() +{ + + m_tablePrinter->close(); + lazyPrintWithoutClosingBenchmarkTable(); +} + +void ConsoleReporter::lazyPrintWithoutClosingBenchmarkTable() +{ + + if (!currentTestRunInfo.used) + lazyPrintRunInfo(); + if (!currentGroupInfo.used) + lazyPrintGroupInfo(); + + if (!m_headerPrinted) { + printTestCaseAndSectionHeader(); + m_headerPrinted = true; + } +} +void ConsoleReporter::lazyPrintRunInfo() +{ + stream << '\n' << getLineOfChars<'~'>() << '\n'; + Colour colour(Colour::SecondaryText); + stream << currentTestRunInfo->name << " is a Catch v" + << libraryVersion() << " host application.\n" + << "Run with -? for options\n\n"; + + if (m_config->rngSeed() != 0) + stream << "Randomness seeded to: " << m_config->rngSeed() + << "\n\n"; + + currentTestRunInfo.used = true; +} +void ConsoleReporter::lazyPrintGroupInfo() +{ + if (!currentGroupInfo->name.empty() && + currentGroupInfo->groupsCounts > 1) { + printClosedHeader("Group: " + currentGroupInfo->name); + currentGroupInfo.used = true; + } +} +void ConsoleReporter::printTestCaseAndSectionHeader() +{ + assert(!m_sectionStack.empty()); + printOpenHeader(currentTestCaseInfo->name); + + if (m_sectionStack.size() > 1) { + Colour colourGuard(Colour::Headers); + + auto it = m_sectionStack.begin() + + 1, // Skip first section (test case) + itEnd = m_sectionStack.end(); + for (; it != itEnd; ++it) + printHeaderString(it->name, 2); + } + + SourceLineInfo lineInfo = m_sectionStack.back().lineInfo; + + stream << getLineOfChars<'-'>() << '\n'; + Colour colourGuard(Colour::FileName); + stream << lineInfo << '\n'; + stream << getLineOfChars<'.'>() << '\n' << std::endl; +} + +void ConsoleReporter::printClosedHeader(std::string const &_name) +{ + printOpenHeader(_name); + stream << getLineOfChars<'.'>() << '\n'; +} +void ConsoleReporter::printOpenHeader(std::string const &_name) +{ + stream << getLineOfChars<'-'>() << '\n'; + { + Colour colourGuard(Colour::Headers); + printHeaderString(_name); + } +} + +// if string has a : in first line will set indent to follow it on +// subsequent lines +void ConsoleReporter::printHeaderString(std::string const &_string, + std::size_t indent) +{ + std::size_t i = _string.find(": "); + if (i != std::string::npos) + i += 2; + else + i = 0; + stream << Column(_string).indent(indent + i).initialIndent(indent) + << '\n'; +} + +struct SummaryColumn { + + SummaryColumn(std::string _label, Colour::Code _colour) + : label(std::move(_label)), colour(_colour) + { + } + SummaryColumn addRow(std::size_t count) + { + ReusableStringStream rss; + rss << count; + std::string row = rss.str(); + for (auto &oldRow : rows) { + while (oldRow.size() < row.size()) + oldRow = ' ' + oldRow; + while (oldRow.size() > row.size()) + row = ' ' + row; + } + rows.push_back(row); + return *this; + } + + std::string label; + Colour::Code colour; + std::vector rows; +}; + +void ConsoleReporter::printTotals(Totals const &totals) +{ + if (totals.testCases.total() == 0) { + stream << Colour(Colour::Warning) << "No tests ran\n"; + } else if (totals.assertions.total() > 0 && + totals.testCases.allPassed()) { + stream << Colour(Colour::ResultSuccess) << "All tests passed"; + stream << " (" + << pluralise(totals.assertions.passed, "assertion") + << " in " + << pluralise(totals.testCases.passed, "test case") << ')' + << '\n'; + } else { + + std::vector columns; + columns.push_back(SummaryColumn("", Colour::None) + .addRow(totals.testCases.total()) + .addRow(totals.assertions.total())); + columns.push_back(SummaryColumn("passed", Colour::Success) + .addRow(totals.testCases.passed) + .addRow(totals.assertions.passed)); + columns.push_back(SummaryColumn("failed", Colour::ResultError) + .addRow(totals.testCases.failed) + .addRow(totals.assertions.failed)); + columns.push_back( + SummaryColumn("failed as expected", + Colour::ResultExpectedFailure) + .addRow(totals.testCases.failedButOk) + .addRow(totals.assertions.failedButOk)); + + printSummaryRow("test cases", columns, 0); + printSummaryRow("assertions", columns, 1); + } +} +void ConsoleReporter::printSummaryRow(std::string const &label, + std::vector const &cols, + std::size_t row) +{ + for (auto col : cols) { + std::string value = col.rows[row]; + if (col.label.empty()) { + stream << label << ": "; + if (value != "0") + stream << value; + else + stream << Colour(Colour::Warning) << "- none -"; + } else if (value != "0") { + stream << Colour(Colour::LightGrey) << " | "; + stream << Colour(col.colour) << value << ' ' + << col.label; + } + } + stream << '\n'; +} + +void ConsoleReporter::printTotalsDivider(Totals const &totals) +{ + if (totals.testCases.total() > 0) { + std::size_t failedRatio = makeRatio(totals.testCases.failed, + totals.testCases.total()); + std::size_t failedButOkRatio = makeRatio( + totals.testCases.failedButOk, totals.testCases.total()); + std::size_t passedRatio = makeRatio(totals.testCases.passed, + totals.testCases.total()); + while (failedRatio + failedButOkRatio + passedRatio < + CATCH_CONFIG_CONSOLE_WIDTH - 1) + findMax(failedRatio, failedButOkRatio, passedRatio)++; + while (failedRatio + failedButOkRatio + passedRatio > + CATCH_CONFIG_CONSOLE_WIDTH - 1) + findMax(failedRatio, failedButOkRatio, passedRatio)--; + + stream << Colour(Colour::Error) + << std::string(failedRatio, '='); + stream << Colour(Colour::ResultExpectedFailure) + << std::string(failedButOkRatio, '='); + if (totals.testCases.allPassed()) + stream << Colour(Colour::ResultSuccess) + << std::string(passedRatio, '='); + else + stream << Colour(Colour::Success) + << std::string(passedRatio, '='); + } else { + stream << Colour(Colour::Warning) + << std::string(CATCH_CONFIG_CONSOLE_WIDTH - 1, '='); + } + stream << '\n'; +} +void ConsoleReporter::printSummaryDivider() +{ + stream << getLineOfChars<'-'>() << '\n'; +} + +void ConsoleReporter::printTestFilters() +{ + if (m_config->testSpec().hasFilters()) { + Colour guard(Colour::BrightYellow); + stream << "Filters: " + << serializeFilters(m_config->getTestsOrTags()) << '\n'; + } +} + +CATCH_REGISTER_REPORTER("console", ConsoleReporter) + +} // end namespace Catch + +#if defined(_MSC_VER) +#pragma warning(pop) +#endif + +#if defined(__clang__) +#pragma clang diagnostic pop +#endif +// end catch_reporter_console.cpp +// start catch_reporter_junit.cpp + +#include +#include +#include +#include +#include + +namespace Catch { + +namespace { +std::string getCurrentTimestamp() +{ + // Beware, this is not reentrant because of backward compatibility issues + // Also, UTC only, again because of backward compatibility (%z is C++11) + time_t rawtime; + std::time(&rawtime); + auto const timeStampSize = sizeof("2017-01-16T17:06:45Z"); + +#ifdef _MSC_VER + std::tm timeInfo = {}; + gmtime_s(&timeInfo, &rawtime); +#else + std::tm *timeInfo; + timeInfo = std::gmtime(&rawtime); +#endif + + char timeStamp[timeStampSize]; + const char *const fmt = "%Y-%m-%dT%H:%M:%SZ"; + +#ifdef _MSC_VER + std::strftime(timeStamp, timeStampSize, fmt, &timeInfo); +#else + std::strftime(timeStamp, timeStampSize, fmt, timeInfo); +#endif + return std::string(timeStamp, timeStampSize - 1); +} + +std::string fileNameTag(const std::vector &tags) +{ + auto it = std::find_if(begin(tags), end(tags), + [](std::string const &tag) { + return tag.front() == '#'; + }); + if (it != tags.end()) + return it->substr(1); + return std::string(); +} + +// Formats the duration in seconds to 3 decimal places. +// This is done because some genius defined Maven Surefire schema +// in a way that only accepts 3 decimal places, and tools like +// Jenkins use that schema for validation JUnit reporter output. +std::string formatDuration(double seconds) +{ + ReusableStringStream rss; + rss << std::fixed << std::setprecision(3) << seconds; + return rss.str(); +} + +} // anonymous namespace + +JunitReporter::JunitReporter(ReporterConfig const &_config) + : CumulativeReporterBase(_config), xml(_config.stream()) +{ + m_reporterPrefs.shouldRedirectStdOut = true; + m_reporterPrefs.shouldReportAllAssertions = true; +} + +JunitReporter::~JunitReporter() {} + +std::string JunitReporter::getDescription() +{ + return "Reports test results in an XML format that looks like Ant's junitreport target"; +} + +void JunitReporter::noMatchingTestCases(std::string const & /*spec*/) {} + +void JunitReporter::testRunStarting(TestRunInfo const &runInfo) +{ + CumulativeReporterBase::testRunStarting(runInfo); + xml.startElement("testsuites"); +} + +void JunitReporter::testGroupStarting(GroupInfo const &groupInfo) +{ + suiteTimer.start(); + stdOutForSuite.clear(); + stdErrForSuite.clear(); + unexpectedExceptions = 0; + CumulativeReporterBase::testGroupStarting(groupInfo); +} + +void JunitReporter::testCaseStarting(TestCaseInfo const &testCaseInfo) +{ + m_okToFail = testCaseInfo.okToFail(); +} + +bool JunitReporter::assertionEnded(AssertionStats const &assertionStats) +{ + if (assertionStats.assertionResult.getResultType() == + ResultWas::ThrewException && + !m_okToFail) + unexpectedExceptions++; + return CumulativeReporterBase::assertionEnded(assertionStats); +} + +void JunitReporter::testCaseEnded(TestCaseStats const &testCaseStats) +{ + stdOutForSuite += testCaseStats.stdOut; + stdErrForSuite += testCaseStats.stdErr; + CumulativeReporterBase::testCaseEnded(testCaseStats); +} + +void JunitReporter::testGroupEnded(TestGroupStats const &testGroupStats) +{ + double suiteTime = suiteTimer.getElapsedSeconds(); + CumulativeReporterBase::testGroupEnded(testGroupStats); + writeGroup(*m_testGroups.back(), suiteTime); +} + +void JunitReporter::testRunEndedCumulative() +{ + xml.endElement(); +} + +void JunitReporter::writeGroup(TestGroupNode const &groupNode, double suiteTime) +{ + XmlWriter::ScopedElement e = xml.scopedElement("testsuite"); + + TestGroupStats const &stats = groupNode.value; + xml.writeAttribute("name", stats.groupInfo.name); + xml.writeAttribute("errors", unexpectedExceptions); + xml.writeAttribute("failures", stats.totals.assertions.failed - + unexpectedExceptions); + xml.writeAttribute("tests", stats.totals.assertions.total()); + xml.writeAttribute("hostname", "tbd"); // !TBD + if (m_config->showDurations() == ShowDurations::Never) + xml.writeAttribute("time", ""); + else + xml.writeAttribute("time", formatDuration(suiteTime)); + xml.writeAttribute("timestamp", getCurrentTimestamp()); + + // Write properties if there are any + if (m_config->hasTestFilters() || m_config->rngSeed() != 0) { + auto properties = xml.scopedElement("properties"); + if (m_config->hasTestFilters()) { + xml.scopedElement("property") + .writeAttribute("name", "filters") + .writeAttribute( + "value", + serializeFilters( + m_config->getTestsOrTags())); + } + if (m_config->rngSeed() != 0) { + xml.scopedElement("property") + .writeAttribute("name", "random-seed") + .writeAttribute("value", m_config->rngSeed()); + } + } + + // Write test cases + for (auto const &child : groupNode.children) + writeTestCase(*child); + + xml.scopedElement("system-out") + .writeText(trim(stdOutForSuite), XmlFormatting::Newline); + xml.scopedElement("system-err") + .writeText(trim(stdErrForSuite), XmlFormatting::Newline); +} + +void JunitReporter::writeTestCase(TestCaseNode const &testCaseNode) +{ + TestCaseStats const &stats = testCaseNode.value; + + // All test cases have exactly one section - which represents the + // test case itself. That section may have 0-n nested sections + assert(testCaseNode.children.size() == 1); + SectionNode const &rootSection = *testCaseNode.children.front(); + + std::string className = stats.testInfo.className; + + if (className.empty()) { + className = fileNameTag(stats.testInfo.tags); + if (className.empty()) + className = "global"; + } + + if (!m_config->name().empty()) + className = m_config->name() + "." + className; + + writeSection(className, "", rootSection, stats.testInfo.okToFail()); +} + +void JunitReporter::writeSection(std::string const &className, + std::string const &rootName, + SectionNode const §ionNode, + bool testOkToFail) +{ + std::string name = trim(sectionNode.stats.sectionInfo.name); + if (!rootName.empty()) + name = rootName + '/' + name; + + if (!sectionNode.assertions.empty() || !sectionNode.stdOut.empty() || + !sectionNode.stdErr.empty()) { + XmlWriter::ScopedElement e = xml.scopedElement("testcase"); + if (className.empty()) { + xml.writeAttribute("classname", name); + xml.writeAttribute("name", "root"); + } else { + xml.writeAttribute("classname", className); + xml.writeAttribute("name", name); + } + xml.writeAttribute( + "time", + formatDuration(sectionNode.stats.durationInSeconds)); + // This is not ideal, but it should be enough to mimic gtest's + // junit output. + // Ideally the JUnit reporter would also handle `skipTest` + // events and write those out appropriately. + xml.writeAttribute("status", "run"); + + if (sectionNode.stats.assertions.failedButOk) { + xml.scopedElement("skipped").writeAttribute( + "message", "TEST_CASE tagged with !mayfail"); + } + + writeAssertions(sectionNode); + + if (!sectionNode.stdOut.empty()) + xml.scopedElement("system-out") + .writeText(trim(sectionNode.stdOut), + XmlFormatting::Newline); + if (!sectionNode.stdErr.empty()) + xml.scopedElement("system-err") + .writeText(trim(sectionNode.stdErr), + XmlFormatting::Newline); + } + for (auto const &childNode : sectionNode.childSections) + if (className.empty()) + writeSection(name, "", *childNode, testOkToFail); + else + writeSection(className, name, *childNode, testOkToFail); +} + +void JunitReporter::writeAssertions(SectionNode const §ionNode) +{ + for (auto const &assertion : sectionNode.assertions) + writeAssertion(assertion); +} + +void JunitReporter::writeAssertion(AssertionStats const &stats) +{ + AssertionResult const &result = stats.assertionResult; + if (!result.isOk()) { + std::string elementName; + switch (result.getResultType()) { + case ResultWas::ThrewException: + case ResultWas::FatalErrorCondition: + elementName = "error"; + break; + case ResultWas::ExplicitFailure: + case ResultWas::ExpressionFailed: + case ResultWas::DidntThrowException: + elementName = "failure"; + break; + + // We should never see these here: + case ResultWas::Info: + case ResultWas::Warning: + case ResultWas::Ok: + case ResultWas::Unknown: + case ResultWas::FailureBit: + case ResultWas::Exception: + elementName = "internalError"; + break; + } + + XmlWriter::ScopedElement e = xml.scopedElement(elementName); + + xml.writeAttribute("message", result.getExpression()); + xml.writeAttribute("type", result.getTestMacroName()); + + ReusableStringStream rss; + if (stats.totals.assertions.total() > 0) { + rss << "FAILED" + << ":\n"; + if (result.hasExpression()) { + rss << " "; + rss << result.getExpressionInMacro(); + rss << '\n'; + } + if (result.hasExpandedExpression()) { + rss << "with expansion:\n"; + rss << Column(result.getExpandedExpression()) + .indent(2) + << '\n'; + } + } else { + rss << '\n'; + } + + if (!result.getMessage().empty()) + rss << result.getMessage() << '\n'; + for (auto const &msg : stats.infoMessages) + if (msg.type == ResultWas::Info) + rss << msg.message << '\n'; + + rss << "at " << result.getSourceInfo(); + xml.writeText(rss.str(), XmlFormatting::Newline); + } +} + +CATCH_REGISTER_REPORTER("junit", JunitReporter) + +} // end namespace Catch +// end catch_reporter_junit.cpp +// start catch_reporter_listening.cpp + +#include + +namespace Catch { + +ListeningReporter::ListeningReporter() +{ + // We will assume that listeners will always want all assertions + m_preferences.shouldReportAllAssertions = true; +} + +void ListeningReporter::addListener(IStreamingReporterPtr &&listener) +{ + m_listeners.push_back(std::move(listener)); +} + +void ListeningReporter::addReporter(IStreamingReporterPtr &&reporter) +{ + assert(!m_reporter && + "Listening reporter can wrap only 1 real reporter"); + m_reporter = std::move(reporter); + m_preferences.shouldRedirectStdOut = + m_reporter->getPreferences().shouldRedirectStdOut; +} + +ReporterPreferences ListeningReporter::getPreferences() const +{ + return m_preferences; +} + +std::set ListeningReporter::getSupportedVerbosities() +{ + return std::set{}; +} + +void ListeningReporter::noMatchingTestCases(std::string const &spec) +{ + for (auto const &listener : m_listeners) { + listener->noMatchingTestCases(spec); + } + m_reporter->noMatchingTestCases(spec); +} + +void ListeningReporter::reportInvalidArguments(std::string const &arg) +{ + for (auto const &listener : m_listeners) { + listener->reportInvalidArguments(arg); + } + m_reporter->reportInvalidArguments(arg); +} + +#if defined(CATCH_CONFIG_ENABLE_BENCHMARKING) +void ListeningReporter::benchmarkPreparing(std::string const &name) +{ + for (auto const &listener : m_listeners) { + listener->benchmarkPreparing(name); + } + m_reporter->benchmarkPreparing(name); +} +void ListeningReporter::benchmarkStarting(BenchmarkInfo const &benchmarkInfo) +{ + for (auto const &listener : m_listeners) { + listener->benchmarkStarting(benchmarkInfo); + } + m_reporter->benchmarkStarting(benchmarkInfo); +} +void ListeningReporter::benchmarkEnded(BenchmarkStats<> const &benchmarkStats) +{ + for (auto const &listener : m_listeners) { + listener->benchmarkEnded(benchmarkStats); + } + m_reporter->benchmarkEnded(benchmarkStats); +} + +void ListeningReporter::benchmarkFailed(std::string const &error) +{ + for (auto const &listener : m_listeners) { + listener->benchmarkFailed(error); + } + m_reporter->benchmarkFailed(error); +} +#endif // CATCH_CONFIG_ENABLE_BENCHMARKING + +void ListeningReporter::testRunStarting(TestRunInfo const &testRunInfo) +{ + for (auto const &listener : m_listeners) { + listener->testRunStarting(testRunInfo); + } + m_reporter->testRunStarting(testRunInfo); +} + +void ListeningReporter::testGroupStarting(GroupInfo const &groupInfo) +{ + for (auto const &listener : m_listeners) { + listener->testGroupStarting(groupInfo); + } + m_reporter->testGroupStarting(groupInfo); +} + +void ListeningReporter::testCaseStarting(TestCaseInfo const &testInfo) +{ + for (auto const &listener : m_listeners) { + listener->testCaseStarting(testInfo); + } + m_reporter->testCaseStarting(testInfo); +} + +void ListeningReporter::sectionStarting(SectionInfo const §ionInfo) +{ + for (auto const &listener : m_listeners) { + listener->sectionStarting(sectionInfo); + } + m_reporter->sectionStarting(sectionInfo); +} + +void ListeningReporter::assertionStarting(AssertionInfo const &assertionInfo) +{ + for (auto const &listener : m_listeners) { + listener->assertionStarting(assertionInfo); + } + m_reporter->assertionStarting(assertionInfo); +} + +// The return value indicates if the messages buffer should be cleared: +bool ListeningReporter::assertionEnded(AssertionStats const &assertionStats) +{ + for (auto const &listener : m_listeners) { + static_cast(listener->assertionEnded(assertionStats)); + } + return m_reporter->assertionEnded(assertionStats); +} + +void ListeningReporter::sectionEnded(SectionStats const §ionStats) +{ + for (auto const &listener : m_listeners) { + listener->sectionEnded(sectionStats); + } + m_reporter->sectionEnded(sectionStats); +} + +void ListeningReporter::testCaseEnded(TestCaseStats const &testCaseStats) +{ + for (auto const &listener : m_listeners) { + listener->testCaseEnded(testCaseStats); + } + m_reporter->testCaseEnded(testCaseStats); +} + +void ListeningReporter::testGroupEnded(TestGroupStats const &testGroupStats) +{ + for (auto const &listener : m_listeners) { + listener->testGroupEnded(testGroupStats); + } + m_reporter->testGroupEnded(testGroupStats); +} + +void ListeningReporter::testRunEnded(TestRunStats const &testRunStats) +{ + for (auto const &listener : m_listeners) { + listener->testRunEnded(testRunStats); + } + m_reporter->testRunEnded(testRunStats); +} + +void ListeningReporter::skipTest(TestCaseInfo const &testInfo) +{ + for (auto const &listener : m_listeners) { + listener->skipTest(testInfo); + } + m_reporter->skipTest(testInfo); +} + +bool ListeningReporter::isMulti() const +{ + return true; +} + +} // end namespace Catch +// end catch_reporter_listening.cpp +// start catch_reporter_xml.cpp + +#if defined(_MSC_VER) +#pragma warning(push) +#pragma warning( \ + disable : 4061) // Not all labels are EXPLICITLY handled in switch \ + // Note that 4062 (not all labels are handled \ + // and default is missing) is enabled +#endif + +namespace Catch { +XmlReporter::XmlReporter(ReporterConfig const &_config) + : StreamingReporterBase(_config), m_xml(_config.stream()) +{ + m_reporterPrefs.shouldRedirectStdOut = true; + m_reporterPrefs.shouldReportAllAssertions = true; +} + +XmlReporter::~XmlReporter() = default; + +std::string XmlReporter::getDescription() +{ + return "Reports test results as an XML document"; +} + +std::string XmlReporter::getStylesheetRef() const +{ + return std::string(); +} + +void XmlReporter::writeSourceInfo(SourceLineInfo const &sourceInfo) +{ + m_xml.writeAttribute("filename", sourceInfo.file) + .writeAttribute("line", sourceInfo.line); +} + +void XmlReporter::noMatchingTestCases(std::string const &s) +{ + StreamingReporterBase::noMatchingTestCases(s); +} + +void XmlReporter::testRunStarting(TestRunInfo const &testInfo) +{ + StreamingReporterBase::testRunStarting(testInfo); + std::string stylesheetRef = getStylesheetRef(); + if (!stylesheetRef.empty()) + m_xml.writeStylesheetRef(stylesheetRef); + m_xml.startElement("Catch"); + if (!m_config->name().empty()) + m_xml.writeAttribute("name", m_config->name()); + if (m_config->testSpec().hasFilters()) + m_xml.writeAttribute( + "filters", + serializeFilters(m_config->getTestsOrTags())); + if (m_config->rngSeed() != 0) + m_xml.scopedElement("Randomness") + .writeAttribute("seed", m_config->rngSeed()); +} + +void XmlReporter::testGroupStarting(GroupInfo const &groupInfo) +{ + StreamingReporterBase::testGroupStarting(groupInfo); + m_xml.startElement("Group").writeAttribute("name", groupInfo.name); +} + +void XmlReporter::testCaseStarting(TestCaseInfo const &testInfo) +{ + StreamingReporterBase::testCaseStarting(testInfo); + m_xml.startElement("TestCase") + .writeAttribute("name", trim(testInfo.name)) + .writeAttribute("description", testInfo.description) + .writeAttribute("tags", testInfo.tagsAsString()); + + writeSourceInfo(testInfo.lineInfo); + + if (m_config->showDurations() == ShowDurations::Always) + m_testCaseTimer.start(); + m_xml.ensureTagClosed(); +} + +void XmlReporter::sectionStarting(SectionInfo const §ionInfo) +{ + StreamingReporterBase::sectionStarting(sectionInfo); + if (m_sectionDepth++ > 0) { + m_xml.startElement("Section").writeAttribute( + "name", trim(sectionInfo.name)); + writeSourceInfo(sectionInfo.lineInfo); + m_xml.ensureTagClosed(); + } +} + +void XmlReporter::assertionStarting(AssertionInfo const &) {} + +bool XmlReporter::assertionEnded(AssertionStats const &assertionStats) +{ + + AssertionResult const &result = assertionStats.assertionResult; + + bool includeResults = m_config->includeSuccessfulResults() || + !result.isOk(); + + if (includeResults || result.getResultType() == ResultWas::Warning) { + // Print any info messages in tags. + for (auto const &msg : assertionStats.infoMessages) { + if (msg.type == ResultWas::Info && includeResults) { + m_xml.scopedElement("Info").writeText( + msg.message); + } else if (msg.type == ResultWas::Warning) { + m_xml.scopedElement("Warning").writeText( + msg.message); + } + } + } + + // Drop out if result was successful but we're not printing them. + if (!includeResults && result.getResultType() != ResultWas::Warning) + return true; + + // Print the expression if there is one. + if (result.hasExpression()) { + m_xml.startElement("Expression") + .writeAttribute("success", result.succeeded()) + .writeAttribute("type", result.getTestMacroName()); + + writeSourceInfo(result.getSourceInfo()); + + m_xml.scopedElement("Original") + .writeText(result.getExpression()); + m_xml.scopedElement("Expanded") + .writeText(result.getExpandedExpression()); + } + + // And... Print a result applicable to each result type. + switch (result.getResultType()) { + case ResultWas::ThrewException: + m_xml.startElement("Exception"); + writeSourceInfo(result.getSourceInfo()); + m_xml.writeText(result.getMessage()); + m_xml.endElement(); + break; + case ResultWas::FatalErrorCondition: + m_xml.startElement("FatalErrorCondition"); + writeSourceInfo(result.getSourceInfo()); + m_xml.writeText(result.getMessage()); + m_xml.endElement(); + break; + case ResultWas::Info: + m_xml.scopedElement("Info").writeText(result.getMessage()); + break; + case ResultWas::Warning: + // Warning will already have been written + break; + case ResultWas::ExplicitFailure: + m_xml.startElement("Failure"); + writeSourceInfo(result.getSourceInfo()); + m_xml.writeText(result.getMessage()); + m_xml.endElement(); + break; + default: + break; + } + + if (result.hasExpression()) + m_xml.endElement(); + + return true; +} + +void XmlReporter::sectionEnded(SectionStats const §ionStats) +{ + StreamingReporterBase::sectionEnded(sectionStats); + if (--m_sectionDepth > 0) { + XmlWriter::ScopedElement e = + m_xml.scopedElement("OverallResults"); + e.writeAttribute("successes", sectionStats.assertions.passed); + e.writeAttribute("failures", sectionStats.assertions.failed); + e.writeAttribute("expectedFailures", + sectionStats.assertions.failedButOk); + + if (m_config->showDurations() == ShowDurations::Always) + e.writeAttribute("durationInSeconds", + sectionStats.durationInSeconds); + + m_xml.endElement(); + } +} + +void XmlReporter::testCaseEnded(TestCaseStats const &testCaseStats) +{ + StreamingReporterBase::testCaseEnded(testCaseStats); + XmlWriter::ScopedElement e = m_xml.scopedElement("OverallResult"); + e.writeAttribute("success", testCaseStats.totals.assertions.allOk()); + + if (m_config->showDurations() == ShowDurations::Always) + e.writeAttribute("durationInSeconds", + m_testCaseTimer.getElapsedSeconds()); + + if (!testCaseStats.stdOut.empty()) + m_xml.scopedElement("StdOut").writeText( + trim(testCaseStats.stdOut), XmlFormatting::Newline); + if (!testCaseStats.stdErr.empty()) + m_xml.scopedElement("StdErr").writeText( + trim(testCaseStats.stdErr), XmlFormatting::Newline); + + m_xml.endElement(); +} + +void XmlReporter::testGroupEnded(TestGroupStats const &testGroupStats) +{ + StreamingReporterBase::testGroupEnded(testGroupStats); + // TODO: Check testGroupStats.aborting and act accordingly. + m_xml.scopedElement("OverallResults") + .writeAttribute("successes", + testGroupStats.totals.assertions.passed) + .writeAttribute("failures", + testGroupStats.totals.assertions.failed) + .writeAttribute("expectedFailures", + testGroupStats.totals.assertions.failedButOk); + m_xml.scopedElement("OverallResultsCases") + .writeAttribute("successes", + testGroupStats.totals.testCases.passed) + .writeAttribute("failures", + testGroupStats.totals.testCases.failed) + .writeAttribute("expectedFailures", + testGroupStats.totals.testCases.failedButOk); + m_xml.endElement(); +} + +void XmlReporter::testRunEnded(TestRunStats const &testRunStats) +{ + StreamingReporterBase::testRunEnded(testRunStats); + m_xml.scopedElement("OverallResults") + .writeAttribute("successes", + testRunStats.totals.assertions.passed) + .writeAttribute("failures", + testRunStats.totals.assertions.failed) + .writeAttribute("expectedFailures", + testRunStats.totals.assertions.failedButOk); + m_xml.scopedElement("OverallResultsCases") + .writeAttribute("successes", + testRunStats.totals.testCases.passed) + .writeAttribute("failures", + testRunStats.totals.testCases.failed) + .writeAttribute("expectedFailures", + testRunStats.totals.testCases.failedButOk); + m_xml.endElement(); +} + +#if defined(CATCH_CONFIG_ENABLE_BENCHMARKING) +void XmlReporter::benchmarkPreparing(std::string const &name) +{ + m_xml.startElement("BenchmarkResults").writeAttribute("name", name); +} + +void XmlReporter::benchmarkStarting(BenchmarkInfo const &info) +{ + m_xml.writeAttribute("samples", info.samples) + .writeAttribute("resamples", info.resamples) + .writeAttribute("iterations", info.iterations) + .writeAttribute("clockResolution", info.clockResolution) + .writeAttribute("estimatedDuration", info.estimatedDuration) + .writeComment("All values in nano seconds"); +} + +void XmlReporter::benchmarkEnded(BenchmarkStats<> const &benchmarkStats) +{ + m_xml.startElement("mean") + .writeAttribute("value", benchmarkStats.mean.point.count()) + .writeAttribute("lowerBound", + benchmarkStats.mean.lower_bound.count()) + .writeAttribute("upperBound", + benchmarkStats.mean.upper_bound.count()) + .writeAttribute("ci", benchmarkStats.mean.confidence_interval); + m_xml.endElement(); + m_xml.startElement("standardDeviation") + .writeAttribute("value", + benchmarkStats.standardDeviation.point.count()) + .writeAttribute( + "lowerBound", + benchmarkStats.standardDeviation.lower_bound.count()) + .writeAttribute( + "upperBound", + benchmarkStats.standardDeviation.upper_bound.count()) + .writeAttribute( + "ci", + benchmarkStats.standardDeviation.confidence_interval); + m_xml.endElement(); + m_xml.startElement("outliers") + .writeAttribute("variance", benchmarkStats.outlierVariance) + .writeAttribute("lowMild", benchmarkStats.outliers.low_mild) + .writeAttribute("lowSevere", benchmarkStats.outliers.low_severe) + .writeAttribute("highMild", benchmarkStats.outliers.high_mild) + .writeAttribute("highSevere", + benchmarkStats.outliers.high_severe); + m_xml.endElement(); + m_xml.endElement(); +} + +void XmlReporter::benchmarkFailed(std::string const &error) +{ + m_xml.scopedElement("failed").writeAttribute("message", error); + m_xml.endElement(); +} +#endif // CATCH_CONFIG_ENABLE_BENCHMARKING + +CATCH_REGISTER_REPORTER("xml", XmlReporter) + +} // end namespace Catch + +#if defined(_MSC_VER) +#pragma warning(pop) +#endif +// end catch_reporter_xml.cpp + +namespace Catch { +LeakDetector leakDetector; +} + +#ifdef __clang__ +#pragma clang diagnostic pop +#endif + +// end catch_impl.hpp +#endif + +#ifdef CATCH_CONFIG_MAIN +// start catch_default_main.hpp + +#ifndef __OBJC__ + +#if defined(CATCH_CONFIG_WCHAR) && defined(CATCH_PLATFORM_WINDOWS) && \ + defined(_UNICODE) && !defined(DO_NOT_USE_WMAIN) +// Standard C/C++ Win32 Unicode wmain entry point +extern "C" int wmain(int argc, wchar_t *argv[], wchar_t *[]) +{ +#else +// Standard C/C++ main entry point +int main(int argc, char *argv[]) +{ +#endif + + return Catch::Session().run(argc, argv); +} + +#else // __OBJC__ + +// Objective-C entry point +int main(int argc, char *const argv[]) +{ +#if !CATCH_ARC_ENABLED + NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init]; +#endif + + Catch::registerTestMethods(); + int result = Catch::Session().run(argc, (char **)argv); + +#if !CATCH_ARC_ENABLED + [pool drain]; +#endif + + return result; +} + +#endif // __OBJC__ + +// end catch_default_main.hpp +#endif + +#if !defined(CATCH_CONFIG_IMPL_ONLY) + +#ifdef CLARA_CONFIG_MAIN_NOT_DEFINED +#undef CLARA_CONFIG_MAIN +#endif + +#if !defined(CATCH_CONFIG_DISABLE) +////// +// If this config identifier is defined then all CATCH macros are prefixed with CATCH_ +#ifdef CATCH_CONFIG_PREFIX_ALL + +#define CATCH_REQUIRE(...) \ + INTERNAL_CATCH_TEST("CATCH_REQUIRE", Catch::ResultDisposition::Normal, \ + __VA_ARGS__) +#define CATCH_REQUIRE_FALSE(...) \ + INTERNAL_CATCH_TEST("CATCH_REQUIRE_FALSE", \ + Catch::ResultDisposition::Normal | \ + Catch::ResultDisposition::FalseTest, \ + __VA_ARGS__) + +#define CATCH_REQUIRE_THROWS(...) \ + INTERNAL_CATCH_THROWS("CATCH_REQUIRE_THROWS", \ + Catch::ResultDisposition::Normal, __VA_ARGS__) +#define CATCH_REQUIRE_THROWS_AS(expr, exceptionType) \ + INTERNAL_CATCH_THROWS_AS("CATCH_REQUIRE_THROWS_AS", exceptionType, \ + Catch::ResultDisposition::Normal, expr) +#define CATCH_REQUIRE_THROWS_WITH(expr, matcher) \ + INTERNAL_CATCH_THROWS_STR_MATCHES("CATCH_REQUIRE_THROWS_WITH", \ + Catch::ResultDisposition::Normal, \ + matcher, expr) +#if !defined(CATCH_CONFIG_DISABLE_MATCHERS) +#define CATCH_REQUIRE_THROWS_MATCHES(expr, exceptionType, matcher) \ + INTERNAL_CATCH_THROWS_MATCHES("CATCH_REQUIRE_THROWS_MATCHES", \ + exceptionType, \ + Catch::ResultDisposition::Normal, \ + matcher, expr) +#endif // CATCH_CONFIG_DISABLE_MATCHERS +#define CATCH_REQUIRE_NOTHROW(...) \ + INTERNAL_CATCH_NO_THROW("CATCH_REQUIRE_NOTHROW", \ + Catch::ResultDisposition::Normal, __VA_ARGS__) + +#define CATCH_CHECK(...) \ + INTERNAL_CATCH_TEST("CATCH_CHECK", \ + Catch::ResultDisposition::ContinueOnFailure, \ + __VA_ARGS__) +#define CATCH_CHECK_FALSE(...) \ + INTERNAL_CATCH_TEST("CATCH_CHECK_FALSE", \ + Catch::ResultDisposition::ContinueOnFailure | \ + Catch::ResultDisposition::FalseTest, \ + __VA_ARGS__) +#define CATCH_CHECKED_IF(...) \ + INTERNAL_CATCH_IF("CATCH_CHECKED_IF", \ + Catch::ResultDisposition::ContinueOnFailure, \ + __VA_ARGS__) +#define CATCH_CHECKED_ELSE(...) \ + INTERNAL_CATCH_ELSE("CATCH_CHECKED_ELSE", \ + Catch::ResultDisposition::ContinueOnFailure, \ + __VA_ARGS__) +#define CATCH_CHECK_NOFAIL(...) \ + INTERNAL_CATCH_TEST("CATCH_CHECK_NOFAIL", \ + Catch::ResultDisposition::ContinueOnFailure | \ + Catch::ResultDisposition::SuppressFail, \ + __VA_ARGS__) + +#define CATCH_CHECK_THROWS(...) \ + INTERNAL_CATCH_THROWS("CATCH_CHECK_THROWS", \ + Catch::ResultDisposition::ContinueOnFailure, \ + __VA_ARGS__) +#define CATCH_CHECK_THROWS_AS(expr, exceptionType) \ + INTERNAL_CATCH_THROWS_AS("CATCH_CHECK_THROWS_AS", exceptionType, \ + Catch::ResultDisposition::ContinueOnFailure, \ + expr) +#define CATCH_CHECK_THROWS_WITH(expr, matcher) \ + INTERNAL_CATCH_THROWS_STR_MATCHES( \ + "CATCH_CHECK_THROWS_WITH", \ + Catch::ResultDisposition::ContinueOnFailure, matcher, expr) +#if !defined(CATCH_CONFIG_DISABLE_MATCHERS) +#define CATCH_CHECK_THROWS_MATCHES(expr, exceptionType, matcher) \ + INTERNAL_CATCH_THROWS_MATCHES( \ + "CATCH_CHECK_THROWS_MATCHES", exceptionType, \ + Catch::ResultDisposition::ContinueOnFailure, matcher, expr) +#endif // CATCH_CONFIG_DISABLE_MATCHERS +#define CATCH_CHECK_NOTHROW(...) \ + INTERNAL_CATCH_NO_THROW("CATCH_CHECK_NOTHROW", \ + Catch::ResultDisposition::ContinueOnFailure, \ + __VA_ARGS__) + +#if !defined(CATCH_CONFIG_DISABLE_MATCHERS) +#define CATCH_CHECK_THAT(arg, matcher) \ + INTERNAL_CHECK_THAT("CATCH_CHECK_THAT", matcher, \ + Catch::ResultDisposition::ContinueOnFailure, arg) + +#define CATCH_REQUIRE_THAT(arg, matcher) \ + INTERNAL_CHECK_THAT("CATCH_REQUIRE_THAT", matcher, \ + Catch::ResultDisposition::Normal, arg) +#endif // CATCH_CONFIG_DISABLE_MATCHERS + +#define CATCH_INFO(msg) INTERNAL_CATCH_INFO("CATCH_INFO", msg) +#define CATCH_UNSCOPED_INFO(msg) \ + INTERNAL_CATCH_UNSCOPED_INFO("CATCH_UNSCOPED_INFO", msg) +#define CATCH_WARN(msg) \ + INTERNAL_CATCH_MSG("CATCH_WARN", Catch::ResultWas::Warning, \ + Catch::ResultDisposition::ContinueOnFailure, msg) +#define CATCH_CAPTURE(...) \ + INTERNAL_CATCH_CAPTURE(INTERNAL_CATCH_UNIQUE_NAME(capturer), \ + "CATCH_CAPTURE", __VA_ARGS__) + +#define CATCH_TEST_CASE(...) INTERNAL_CATCH_TESTCASE(__VA_ARGS__) +#define CATCH_TEST_CASE_METHOD(className, ...) \ + INTERNAL_CATCH_TEST_CASE_METHOD(className, __VA_ARGS__) +#define CATCH_METHOD_AS_TEST_CASE(method, ...) \ + INTERNAL_CATCH_METHOD_AS_TEST_CASE(method, __VA_ARGS__) +#define CATCH_REGISTER_TEST_CASE(Function, ...) \ + INTERNAL_CATCH_REGISTER_TESTCASE(Function, __VA_ARGS__) +#define CATCH_SECTION(...) INTERNAL_CATCH_SECTION(__VA_ARGS__) +#define CATCH_DYNAMIC_SECTION(...) INTERNAL_CATCH_DYNAMIC_SECTION(__VA_ARGS__) +#define CATCH_FAIL(...) \ + INTERNAL_CATCH_MSG("CATCH_FAIL", Catch::ResultWas::ExplicitFailure, \ + Catch::ResultDisposition::Normal, __VA_ARGS__) +#define CATCH_FAIL_CHECK(...) \ + INTERNAL_CATCH_MSG("CATCH_FAIL_CHECK", \ + Catch::ResultWas::ExplicitFailure, \ + Catch::ResultDisposition::ContinueOnFailure, \ + __VA_ARGS__) +#define CATCH_SUCCEED(...) \ + INTERNAL_CATCH_MSG("CATCH_SUCCEED", Catch::ResultWas::Ok, \ + Catch::ResultDisposition::ContinueOnFailure, \ + __VA_ARGS__) + +#define CATCH_ANON_TEST_CASE() INTERNAL_CATCH_TESTCASE() + +#ifndef CATCH_CONFIG_TRADITIONAL_MSVC_PREPROCESSOR +#define CATCH_TEMPLATE_TEST_CASE(...) \ + INTERNAL_CATCH_TEMPLATE_TEST_CASE(__VA_ARGS__) +#define CATCH_TEMPLATE_TEST_CASE_SIG(...) \ + INTERNAL_CATCH_TEMPLATE_TEST_CASE_SIG(__VA_ARGS__) +#define CATCH_TEMPLATE_TEST_CASE_METHOD(className, ...) \ + INTERNAL_CATCH_TEMPLATE_TEST_CASE_METHOD(className, __VA_ARGS__) +#define CATCH_TEMPLATE_TEST_CASE_METHOD_SIG(className, ...) \ + INTERNAL_CATCH_TEMPLATE_TEST_CASE_METHOD_SIG(className, __VA_ARGS__) +#define CATCH_TEMPLATE_PRODUCT_TEST_CASE(...) \ + INTERNAL_CATCH_TEMPLATE_PRODUCT_TEST_CASE(__VA_ARGS__) +#define CATCH_TEMPLATE_PRODUCT_TEST_CASE_SIG(...) \ + INTERNAL_CATCH_TEMPLATE_PRODUCT_TEST_CASE_SIG(__VA_ARGS__) +#define CATCH_TEMPLATE_PRODUCT_TEST_CASE_METHOD(className, ...) \ + INTERNAL_CATCH_TEMPLATE_PRODUCT_TEST_CASE_METHOD(className, __VA_ARGS__) +#define CATCH_TEMPLATE_PRODUCT_TEST_CASE_METHOD_SIG(className, ...) \ + INTERNAL_CATCH_TEMPLATE_PRODUCT_TEST_CASE_METHOD_SIG(className, \ + __VA_ARGS__) +#else +#define CATCH_TEMPLATE_TEST_CASE(...) \ + INTERNAL_CATCH_EXPAND_VARGS( \ + INTERNAL_CATCH_TEMPLATE_TEST_CASE(__VA_ARGS__)) +#define CATCH_TEMPLATE_TEST_CASE_SIG(...) \ + INTERNAL_CATCH_EXPAND_VARGS( \ + INTERNAL_CATCH_TEMPLATE_TEST_CASE_SIG(__VA_ARGS__)) +#define CATCH_TEMPLATE_TEST_CASE_METHOD(className, ...) \ + INTERNAL_CATCH_EXPAND_VARGS(INTERNAL_CATCH_TEMPLATE_TEST_CASE_METHOD( \ + className, __VA_ARGS__)) +#define CATCH_TEMPLATE_TEST_CASE_METHOD_SIG(className, ...) \ + INTERNAL_CATCH_EXPAND_VARGS( \ + INTERNAL_CATCH_TEMPLATE_TEST_CASE_METHOD_SIG(className, \ + __VA_ARGS__)) +#define CATCH_TEMPLATE_PRODUCT_TEST_CASE(...) \ + INTERNAL_CATCH_EXPAND_VARGS( \ + INTERNAL_CATCH_TEMPLATE_PRODUCT_TEST_CASE(__VA_ARGS__)) +#define CATCH_TEMPLATE_PRODUCT_TEST_CASE_SIG(...) \ + INTERNAL_CATCH_EXPAND_VARGS( \ + INTERNAL_CATCH_TEMPLATE_PRODUCT_TEST_CASE_SIG(__VA_ARGS__)) +#define CATCH_TEMPLATE_PRODUCT_TEST_CASE_METHOD(className, ...) \ + INTERNAL_CATCH_EXPAND_VARGS( \ + INTERNAL_CATCH_TEMPLATE_PRODUCT_TEST_CASE_METHOD(className, \ + __VA_ARGS__)) +#define CATCH_TEMPLATE_PRODUCT_TEST_CASE_METHOD_SIG(className, ...) \ + INTERNAL_CATCH_EXPAND_VARGS( \ + INTERNAL_CATCH_TEMPLATE_PRODUCT_TEST_CASE_METHOD_SIG( \ + className, __VA_ARGS__)) +#endif + +#if !defined(CATCH_CONFIG_RUNTIME_STATIC_REQUIRE) +#define CATCH_STATIC_REQUIRE(...) \ + static_assert(__VA_ARGS__, #__VA_ARGS__); \ + CATCH_SUCCEED(#__VA_ARGS__) +#define CATCH_STATIC_REQUIRE_FALSE(...) \ + static_assert(!(__VA_ARGS__), "!(" #__VA_ARGS__ ")"); \ + CATCH_SUCCEED(#__VA_ARGS__) +#else +#define CATCH_STATIC_REQUIRE(...) CATCH_REQUIRE(__VA_ARGS__) +#define CATCH_STATIC_REQUIRE_FALSE(...) CATCH_REQUIRE_FALSE(__VA_ARGS__) +#endif + +// "BDD-style" convenience wrappers +#define CATCH_SCENARIO(...) CATCH_TEST_CASE("Scenario: " __VA_ARGS__) +#define CATCH_SCENARIO_METHOD(className, ...) \ + INTERNAL_CATCH_TEST_CASE_METHOD(className, "Scenario: " __VA_ARGS__) +#define CATCH_GIVEN(desc) INTERNAL_CATCH_DYNAMIC_SECTION(" Given: " << desc) +#define CATCH_AND_GIVEN(desc) \ + INTERNAL_CATCH_DYNAMIC_SECTION("And given: " << desc) +#define CATCH_WHEN(desc) INTERNAL_CATCH_DYNAMIC_SECTION(" When: " << desc) +#define CATCH_AND_WHEN(desc) \ + INTERNAL_CATCH_DYNAMIC_SECTION(" And when: " << desc) +#define CATCH_THEN(desc) INTERNAL_CATCH_DYNAMIC_SECTION(" Then: " << desc) +#define CATCH_AND_THEN(desc) \ + INTERNAL_CATCH_DYNAMIC_SECTION(" And: " << desc) + +#if defined(CATCH_CONFIG_ENABLE_BENCHMARKING) +#define CATCH_BENCHMARK(...) \ + INTERNAL_CATCH_BENCHMARK( \ + INTERNAL_CATCH_UNIQUE_NAME(C_A_T_C_H_B_E_N_C_H_), \ + INTERNAL_CATCH_GET_1_ARG(__VA_ARGS__, , ), \ + INTERNAL_CATCH_GET_2_ARG(__VA_ARGS__, , )) +#define CATCH_BENCHMARK_ADVANCED(name) \ + INTERNAL_CATCH_BENCHMARK_ADVANCED( \ + INTERNAL_CATCH_UNIQUE_NAME(C_A_T_C_H_B_E_N_C_H_), name) +#endif // CATCH_CONFIG_ENABLE_BENCHMARKING + +// If CATCH_CONFIG_PREFIX_ALL is not defined then the CATCH_ prefix is not required +#else + +#define REQUIRE(...) \ + INTERNAL_CATCH_TEST("REQUIRE", Catch::ResultDisposition::Normal, \ + __VA_ARGS__) +#define REQUIRE_FALSE(...) \ + INTERNAL_CATCH_TEST("REQUIRE_FALSE", \ + Catch::ResultDisposition::Normal | \ + Catch::ResultDisposition::FalseTest, \ + __VA_ARGS__) + +#define REQUIRE_THROWS(...) \ + INTERNAL_CATCH_THROWS("REQUIRE_THROWS", \ + Catch::ResultDisposition::Normal, __VA_ARGS__) +#define REQUIRE_THROWS_AS(expr, exceptionType) \ + INTERNAL_CATCH_THROWS_AS("REQUIRE_THROWS_AS", exceptionType, \ + Catch::ResultDisposition::Normal, expr) +#define REQUIRE_THROWS_WITH(expr, matcher) \ + INTERNAL_CATCH_THROWS_STR_MATCHES("REQUIRE_THROWS_WITH", \ + Catch::ResultDisposition::Normal, \ + matcher, expr) +#if !defined(CATCH_CONFIG_DISABLE_MATCHERS) +#define REQUIRE_THROWS_MATCHES(expr, exceptionType, matcher) \ + INTERNAL_CATCH_THROWS_MATCHES("REQUIRE_THROWS_MATCHES", exceptionType, \ + Catch::ResultDisposition::Normal, \ + matcher, expr) +#endif // CATCH_CONFIG_DISABLE_MATCHERS +#define REQUIRE_NOTHROW(...) \ + INTERNAL_CATCH_NO_THROW("REQUIRE_NOTHROW", \ + Catch::ResultDisposition::Normal, __VA_ARGS__) + +#define CHECK(...) \ + INTERNAL_CATCH_TEST("CHECK", \ + Catch::ResultDisposition::ContinueOnFailure, \ + __VA_ARGS__) +#define CHECK_FALSE(...) \ + INTERNAL_CATCH_TEST("CHECK_FALSE", \ + Catch::ResultDisposition::ContinueOnFailure | \ + Catch::ResultDisposition::FalseTest, \ + __VA_ARGS__) +#define CHECKED_IF(...) \ + INTERNAL_CATCH_IF("CHECKED_IF", \ + Catch::ResultDisposition::ContinueOnFailure, \ + __VA_ARGS__) +#define CHECKED_ELSE(...) \ + INTERNAL_CATCH_ELSE("CHECKED_ELSE", \ + Catch::ResultDisposition::ContinueOnFailure, \ + __VA_ARGS__) +#define CHECK_NOFAIL(...) \ + INTERNAL_CATCH_TEST("CHECK_NOFAIL", \ + Catch::ResultDisposition::ContinueOnFailure | \ + Catch::ResultDisposition::SuppressFail, \ + __VA_ARGS__) + +#define CHECK_THROWS(...) \ + INTERNAL_CATCH_THROWS("CHECK_THROWS", \ + Catch::ResultDisposition::ContinueOnFailure, \ + __VA_ARGS__) +#define CHECK_THROWS_AS(expr, exceptionType) \ + INTERNAL_CATCH_THROWS_AS("CHECK_THROWS_AS", exceptionType, \ + Catch::ResultDisposition::ContinueOnFailure, \ + expr) +#define CHECK_THROWS_WITH(expr, matcher) \ + INTERNAL_CATCH_THROWS_STR_MATCHES( \ + "CHECK_THROWS_WITH", \ + Catch::ResultDisposition::ContinueOnFailure, matcher, expr) +#if !defined(CATCH_CONFIG_DISABLE_MATCHERS) +#define CHECK_THROWS_MATCHES(expr, exceptionType, matcher) \ + INTERNAL_CATCH_THROWS_MATCHES( \ + "CHECK_THROWS_MATCHES", exceptionType, \ + Catch::ResultDisposition::ContinueOnFailure, matcher, expr) +#endif // CATCH_CONFIG_DISABLE_MATCHERS +#define CHECK_NOTHROW(...) \ + INTERNAL_CATCH_NO_THROW("CHECK_NOTHROW", \ + Catch::ResultDisposition::ContinueOnFailure, \ + __VA_ARGS__) + +#if !defined(CATCH_CONFIG_DISABLE_MATCHERS) +#define CHECK_THAT(arg, matcher) \ + INTERNAL_CHECK_THAT("CHECK_THAT", matcher, \ + Catch::ResultDisposition::ContinueOnFailure, arg) + +#define REQUIRE_THAT(arg, matcher) \ + INTERNAL_CHECK_THAT("REQUIRE_THAT", matcher, \ + Catch::ResultDisposition::Normal, arg) +#endif // CATCH_CONFIG_DISABLE_MATCHERS + +#define INFO(msg) INTERNAL_CATCH_INFO("INFO", msg) +#define UNSCOPED_INFO(msg) INTERNAL_CATCH_UNSCOPED_INFO("UNSCOPED_INFO", msg) +#define WARN(msg) \ + INTERNAL_CATCH_MSG("WARN", Catch::ResultWas::Warning, \ + Catch::ResultDisposition::ContinueOnFailure, msg) +#define CAPTURE(...) \ + INTERNAL_CATCH_CAPTURE(INTERNAL_CATCH_UNIQUE_NAME(capturer), \ + "CAPTURE", __VA_ARGS__) + +#define TEST_CASE(...) INTERNAL_CATCH_TESTCASE(__VA_ARGS__) +#define TEST_CASE_METHOD(className, ...) \ + INTERNAL_CATCH_TEST_CASE_METHOD(className, __VA_ARGS__) +#define METHOD_AS_TEST_CASE(method, ...) \ + INTERNAL_CATCH_METHOD_AS_TEST_CASE(method, __VA_ARGS__) +#define REGISTER_TEST_CASE(Function, ...) \ + INTERNAL_CATCH_REGISTER_TESTCASE(Function, __VA_ARGS__) +#define SECTION(...) INTERNAL_CATCH_SECTION(__VA_ARGS__) +#define DYNAMIC_SECTION(...) INTERNAL_CATCH_DYNAMIC_SECTION(__VA_ARGS__) +#define FAIL(...) \ + INTERNAL_CATCH_MSG("FAIL", Catch::ResultWas::ExplicitFailure, \ + Catch::ResultDisposition::Normal, __VA_ARGS__) +#define FAIL_CHECK(...) \ + INTERNAL_CATCH_MSG("FAIL_CHECK", Catch::ResultWas::ExplicitFailure, \ + Catch::ResultDisposition::ContinueOnFailure, \ + __VA_ARGS__) +#define SUCCEED(...) \ + INTERNAL_CATCH_MSG("SUCCEED", Catch::ResultWas::Ok, \ + Catch::ResultDisposition::ContinueOnFailure, \ + __VA_ARGS__) +#define ANON_TEST_CASE() INTERNAL_CATCH_TESTCASE() + +#ifndef CATCH_CONFIG_TRADITIONAL_MSVC_PREPROCESSOR +#define TEMPLATE_TEST_CASE(...) INTERNAL_CATCH_TEMPLATE_TEST_CASE(__VA_ARGS__) +#define TEMPLATE_TEST_CASE_SIG(...) \ + INTERNAL_CATCH_TEMPLATE_TEST_CASE_SIG(__VA_ARGS__) +#define TEMPLATE_TEST_CASE_METHOD(className, ...) \ + INTERNAL_CATCH_TEMPLATE_TEST_CASE_METHOD(className, __VA_ARGS__) +#define TEMPLATE_TEST_CASE_METHOD_SIG(className, ...) \ + INTERNAL_CATCH_TEMPLATE_TEST_CASE_METHOD_SIG(className, __VA_ARGS__) +#define TEMPLATE_PRODUCT_TEST_CASE(...) \ + INTERNAL_CATCH_TEMPLATE_PRODUCT_TEST_CASE(__VA_ARGS__) +#define TEMPLATE_PRODUCT_TEST_CASE_SIG(...) \ + INTERNAL_CATCH_TEMPLATE_PRODUCT_TEST_CASE_SIG(__VA_ARGS__) +#define TEMPLATE_PRODUCT_TEST_CASE_METHOD(className, ...) \ + INTERNAL_CATCH_TEMPLATE_PRODUCT_TEST_CASE_METHOD(className, __VA_ARGS__) +#define TEMPLATE_PRODUCT_TEST_CASE_METHOD_SIG(className, ...) \ + INTERNAL_CATCH_TEMPLATE_PRODUCT_TEST_CASE_METHOD_SIG(className, \ + __VA_ARGS__) +#define TEMPLATE_LIST_TEST_CASE(...) \ + INTERNAL_CATCH_TEMPLATE_LIST_TEST_CASE(__VA_ARGS__) +#define TEMPLATE_LIST_TEST_CASE_METHOD(className, ...) \ + INTERNAL_CATCH_TEMPLATE_LIST_TEST_CASE_METHOD(className, __VA_ARGS__) +#else +#define TEMPLATE_TEST_CASE(...) \ + INTERNAL_CATCH_EXPAND_VARGS( \ + INTERNAL_CATCH_TEMPLATE_TEST_CASE(__VA_ARGS__)) +#define TEMPLATE_TEST_CASE_SIG(...) \ + INTERNAL_CATCH_EXPAND_VARGS( \ + INTERNAL_CATCH_TEMPLATE_TEST_CASE_SIG(__VA_ARGS__)) +#define TEMPLATE_TEST_CASE_METHOD(className, ...) \ + INTERNAL_CATCH_EXPAND_VARGS(INTERNAL_CATCH_TEMPLATE_TEST_CASE_METHOD( \ + className, __VA_ARGS__)) +#define TEMPLATE_TEST_CASE_METHOD_SIG(className, ...) \ + INTERNAL_CATCH_EXPAND_VARGS( \ + INTERNAL_CATCH_TEMPLATE_TEST_CASE_METHOD_SIG(className, \ + __VA_ARGS__)) +#define TEMPLATE_PRODUCT_TEST_CASE(...) \ + INTERNAL_CATCH_EXPAND_VARGS( \ + INTERNAL_CATCH_TEMPLATE_PRODUCT_TEST_CASE(__VA_ARGS__)) +#define TEMPLATE_PRODUCT_TEST_CASE_SIG(...) \ + INTERNAL_CATCH_EXPAND_VARGS( \ + INTERNAL_CATCH_TEMPLATE_PRODUCT_TEST_CASE_SIG(__VA_ARGS__)) +#define TEMPLATE_PRODUCT_TEST_CASE_METHOD(className, ...) \ + INTERNAL_CATCH_EXPAND_VARGS( \ + INTERNAL_CATCH_TEMPLATE_PRODUCT_TEST_CASE_METHOD(className, \ + __VA_ARGS__)) +#define TEMPLATE_PRODUCT_TEST_CASE_METHOD_SIG(className, ...) \ + INTERNAL_CATCH_EXPAND_VARGS( \ + INTERNAL_CATCH_TEMPLATE_PRODUCT_TEST_CASE_METHOD_SIG( \ + className, __VA_ARGS__)) +#define TEMPLATE_LIST_TEST_CASE(...) \ + INTERNAL_CATCH_EXPAND_VARGS( \ + INTERNAL_CATCH_TEMPLATE_LIST_TEST_CASE(__VA_ARGS__)) +#define TEMPLATE_LIST_TEST_CASE_METHOD(className, ...) \ + INTERNAL_CATCH_EXPAND_VARGS( \ + INTERNAL_CATCH_TEMPLATE_LIST_TEST_CASE_METHOD(className, \ + __VA_ARGS__)) +#endif + +#if !defined(CATCH_CONFIG_RUNTIME_STATIC_REQUIRE) +#define STATIC_REQUIRE(...) \ + static_assert(__VA_ARGS__, #__VA_ARGS__); \ + SUCCEED(#__VA_ARGS__) +#define STATIC_REQUIRE_FALSE(...) \ + static_assert(!(__VA_ARGS__), "!(" #__VA_ARGS__ ")"); \ + SUCCEED("!(" #__VA_ARGS__ ")") +#else +#define STATIC_REQUIRE(...) REQUIRE(__VA_ARGS__) +#define STATIC_REQUIRE_FALSE(...) REQUIRE_FALSE(__VA_ARGS__) +#endif + +#endif + +#define CATCH_TRANSLATE_EXCEPTION(signature) \ + INTERNAL_CATCH_TRANSLATE_EXCEPTION(signature) + +// "BDD-style" convenience wrappers +#define SCENARIO(...) TEST_CASE("Scenario: " __VA_ARGS__) +#define SCENARIO_METHOD(className, ...) \ + INTERNAL_CATCH_TEST_CASE_METHOD(className, "Scenario: " __VA_ARGS__) + +#define GIVEN(desc) INTERNAL_CATCH_DYNAMIC_SECTION(" Given: " << desc) +#define AND_GIVEN(desc) INTERNAL_CATCH_DYNAMIC_SECTION("And given: " << desc) +#define WHEN(desc) INTERNAL_CATCH_DYNAMIC_SECTION(" When: " << desc) +#define AND_WHEN(desc) INTERNAL_CATCH_DYNAMIC_SECTION(" And when: " << desc) +#define THEN(desc) INTERNAL_CATCH_DYNAMIC_SECTION(" Then: " << desc) +#define AND_THEN(desc) INTERNAL_CATCH_DYNAMIC_SECTION(" And: " << desc) + +#if defined(CATCH_CONFIG_ENABLE_BENCHMARKING) +#define BENCHMARK(...) \ + INTERNAL_CATCH_BENCHMARK( \ + INTERNAL_CATCH_UNIQUE_NAME(C_A_T_C_H_B_E_N_C_H_), \ + INTERNAL_CATCH_GET_1_ARG(__VA_ARGS__, , ), \ + INTERNAL_CATCH_GET_2_ARG(__VA_ARGS__, , )) +#define BENCHMARK_ADVANCED(name) \ + INTERNAL_CATCH_BENCHMARK_ADVANCED( \ + INTERNAL_CATCH_UNIQUE_NAME(C_A_T_C_H_B_E_N_C_H_), name) +#endif // CATCH_CONFIG_ENABLE_BENCHMARKING + +using Catch::Detail::Approx; + +#else // CATCH_CONFIG_DISABLE + +////// +// If this config identifier is defined then all CATCH macros are prefixed with CATCH_ +#ifdef CATCH_CONFIG_PREFIX_ALL + +#define CATCH_REQUIRE(...) (void)(0) +#define CATCH_REQUIRE_FALSE(...) (void)(0) + +#define CATCH_REQUIRE_THROWS(...) (void)(0) +#define CATCH_REQUIRE_THROWS_AS(expr, exceptionType) (void)(0) +#define CATCH_REQUIRE_THROWS_WITH(expr, matcher) (void)(0) +#if !defined(CATCH_CONFIG_DISABLE_MATCHERS) +#define CATCH_REQUIRE_THROWS_MATCHES(expr, exceptionType, matcher) (void)(0) +#endif // CATCH_CONFIG_DISABLE_MATCHERS +#define CATCH_REQUIRE_NOTHROW(...) (void)(0) + +#define CATCH_CHECK(...) (void)(0) +#define CATCH_CHECK_FALSE(...) (void)(0) +#define CATCH_CHECKED_IF(...) if (__VA_ARGS__) +#define CATCH_CHECKED_ELSE(...) if (!(__VA_ARGS__)) +#define CATCH_CHECK_NOFAIL(...) (void)(0) + +#define CATCH_CHECK_THROWS(...) (void)(0) +#define CATCH_CHECK_THROWS_AS(expr, exceptionType) (void)(0) +#define CATCH_CHECK_THROWS_WITH(expr, matcher) (void)(0) +#if !defined(CATCH_CONFIG_DISABLE_MATCHERS) +#define CATCH_CHECK_THROWS_MATCHES(expr, exceptionType, matcher) (void)(0) +#endif // CATCH_CONFIG_DISABLE_MATCHERS +#define CATCH_CHECK_NOTHROW(...) (void)(0) + +#if !defined(CATCH_CONFIG_DISABLE_MATCHERS) +#define CATCH_CHECK_THAT(arg, matcher) (void)(0) + +#define CATCH_REQUIRE_THAT(arg, matcher) (void)(0) +#endif // CATCH_CONFIG_DISABLE_MATCHERS + +#define CATCH_INFO(msg) (void)(0) +#define CATCH_UNSCOPED_INFO(msg) (void)(0) +#define CATCH_WARN(msg) (void)(0) +#define CATCH_CAPTURE(msg) (void)(0) + +#define CATCH_TEST_CASE(...) \ + INTERNAL_CATCH_TESTCASE_NO_REGISTRATION( \ + INTERNAL_CATCH_UNIQUE_NAME(C_A_T_C_H_T_E_S_T_)) +#define CATCH_TEST_CASE_METHOD(className, ...) \ + INTERNAL_CATCH_TESTCASE_NO_REGISTRATION( \ + INTERNAL_CATCH_UNIQUE_NAME(C_A_T_C_H_T_E_S_T_)) +#define CATCH_METHOD_AS_TEST_CASE(method, ...) +#define CATCH_REGISTER_TEST_CASE(Function, ...) (void)(0) +#define CATCH_SECTION(...) +#define CATCH_DYNAMIC_SECTION(...) +#define CATCH_FAIL(...) (void)(0) +#define CATCH_FAIL_CHECK(...) (void)(0) +#define CATCH_SUCCEED(...) (void)(0) + +#define CATCH_ANON_TEST_CASE() \ + INTERNAL_CATCH_TESTCASE_NO_REGISTRATION( \ + INTERNAL_CATCH_UNIQUE_NAME(C_A_T_C_H_T_E_S_T_)) + +#ifndef CATCH_CONFIG_TRADITIONAL_MSVC_PREPROCESSOR +#define CATCH_TEMPLATE_TEST_CASE(...) \ + INTERNAL_CATCH_TEMPLATE_TEST_CASE_NO_REGISTRATION(__VA_ARGS__) +#define CATCH_TEMPLATE_TEST_CASE_SIG(...) \ + INTERNAL_CATCH_TEMPLATE_TEST_CASE_SIG_NO_REGISTRATION(__VA_ARGS__) +#define CATCH_TEMPLATE_TEST_CASE_METHOD(className, ...) \ + INTERNAL_CATCH_TEMPLATE_TEST_CASE_METHOD_NO_REGISTRATION(className, \ + __VA_ARGS__) +#define CATCH_TEMPLATE_TEST_CASE_METHOD_SIG(className, ...) \ + INTERNAL_CATCH_TEMPLATE_TEST_CASE_METHOD_SIG_NO_REGISTRATION( \ + className, __VA_ARGS__) +#define CATCH_TEMPLATE_PRODUCT_TEST_CASE(...) \ + CATCH_TEMPLATE_TEST_CASE(__VA_ARGS__) +#define CATCH_TEMPLATE_PRODUCT_TEST_CASE_SIG(...) \ + CATCH_TEMPLATE_TEST_CASE(__VA_ARGS__) +#define CATCH_TEMPLATE_PRODUCT_TEST_CASE_METHOD(className, ...) \ + CATCH_TEMPLATE_TEST_CASE_METHOD(className, __VA_ARGS__) +#define CATCH_TEMPLATE_PRODUCT_TEST_CASE_METHOD_SIG(className, ...) \ + CATCH_TEMPLATE_TEST_CASE_METHOD(className, __VA_ARGS__) +#else +#define CATCH_TEMPLATE_TEST_CASE(...) \ + INTERNAL_CATCH_EXPAND_VARGS( \ + INTERNAL_CATCH_TEMPLATE_TEST_CASE_NO_REGISTRATION( \ + __VA_ARGS__)) +#define CATCH_TEMPLATE_TEST_CASE_SIG(...) \ + INTERNAL_CATCH_EXPAND_VARGS( \ + INTERNAL_CATCH_TEMPLATE_TEST_CASE_SIG_NO_REGISTRATION( \ + __VA_ARGS__)) +#define CATCH_TEMPLATE_TEST_CASE_METHOD(className, ...) \ + INTERNAL_CATCH_EXPAND_VARGS( \ + INTERNAL_CATCH_TEMPLATE_TEST_CASE_METHOD_NO_REGISTRATION( \ + className, __VA_ARGS__)) +#define CATCH_TEMPLATE_TEST_CASE_METHOD_SIG(className, ...) \ + INTERNAL_CATCH_EXPAND_VARGS( \ + INTERNAL_CATCH_TEMPLATE_TEST_CASE_METHOD_SIG_NO_REGISTRATION( \ + className, __VA_ARGS__)) +#define CATCH_TEMPLATE_PRODUCT_TEST_CASE(...) \ + CATCH_TEMPLATE_TEST_CASE(__VA_ARGS__) +#define CATCH_TEMPLATE_PRODUCT_TEST_CASE_SIG(...) \ + CATCH_TEMPLATE_TEST_CASE(__VA_ARGS__) +#define CATCH_TEMPLATE_PRODUCT_TEST_CASE_METHOD(className, ...) \ + CATCH_TEMPLATE_TEST_CASE_METHOD(className, __VA_ARGS__) +#define CATCH_TEMPLATE_PRODUCT_TEST_CASE_METHOD_SIG(className, ...) \ + CATCH_TEMPLATE_TEST_CASE_METHOD(className, __VA_ARGS__) +#endif + +// "BDD-style" convenience wrappers +#define CATCH_SCENARIO(...) \ + INTERNAL_CATCH_TESTCASE_NO_REGISTRATION( \ + INTERNAL_CATCH_UNIQUE_NAME(C_A_T_C_H_T_E_S_T_)) +#define CATCH_SCENARIO_METHOD(className, ...) \ + INTERNAL_CATCH_TESTCASE_METHOD_NO_REGISTRATION( \ + INTERNAL_CATCH_UNIQUE_NAME(C_A_T_C_H_T_E_S_T_), className) +#define CATCH_GIVEN(desc) +#define CATCH_AND_GIVEN(desc) +#define CATCH_WHEN(desc) +#define CATCH_AND_WHEN(desc) +#define CATCH_THEN(desc) +#define CATCH_AND_THEN(desc) + +#define CATCH_STATIC_REQUIRE(...) (void)(0) +#define CATCH_STATIC_REQUIRE_FALSE(...) (void)(0) + +// If CATCH_CONFIG_PREFIX_ALL is not defined then the CATCH_ prefix is not required +#else + +#define REQUIRE(...) (void)(0) +#define REQUIRE_FALSE(...) (void)(0) + +#define REQUIRE_THROWS(...) (void)(0) +#define REQUIRE_THROWS_AS(expr, exceptionType) (void)(0) +#define REQUIRE_THROWS_WITH(expr, matcher) (void)(0) +#if !defined(CATCH_CONFIG_DISABLE_MATCHERS) +#define REQUIRE_THROWS_MATCHES(expr, exceptionType, matcher) (void)(0) +#endif // CATCH_CONFIG_DISABLE_MATCHERS +#define REQUIRE_NOTHROW(...) (void)(0) + +#define CHECK(...) (void)(0) +#define CHECK_FALSE(...) (void)(0) +#define CHECKED_IF(...) if (__VA_ARGS__) +#define CHECKED_ELSE(...) if (!(__VA_ARGS__)) +#define CHECK_NOFAIL(...) (void)(0) + +#define CHECK_THROWS(...) (void)(0) +#define CHECK_THROWS_AS(expr, exceptionType) (void)(0) +#define CHECK_THROWS_WITH(expr, matcher) (void)(0) +#if !defined(CATCH_CONFIG_DISABLE_MATCHERS) +#define CHECK_THROWS_MATCHES(expr, exceptionType, matcher) (void)(0) +#endif // CATCH_CONFIG_DISABLE_MATCHERS +#define CHECK_NOTHROW(...) (void)(0) + +#if !defined(CATCH_CONFIG_DISABLE_MATCHERS) +#define CHECK_THAT(arg, matcher) (void)(0) + +#define REQUIRE_THAT(arg, matcher) (void)(0) +#endif // CATCH_CONFIG_DISABLE_MATCHERS + +#define INFO(msg) (void)(0) +#define UNSCOPED_INFO(msg) (void)(0) +#define WARN(msg) (void)(0) +#define CAPTURE(msg) (void)(0) + +#define TEST_CASE(...) \ + INTERNAL_CATCH_TESTCASE_NO_REGISTRATION( \ + INTERNAL_CATCH_UNIQUE_NAME(C_A_T_C_H_T_E_S_T_)) +#define TEST_CASE_METHOD(className, ...) \ + INTERNAL_CATCH_TESTCASE_NO_REGISTRATION( \ + INTERNAL_CATCH_UNIQUE_NAME(C_A_T_C_H_T_E_S_T_)) +#define METHOD_AS_TEST_CASE(method, ...) +#define REGISTER_TEST_CASE(Function, ...) (void)(0) +#define SECTION(...) +#define DYNAMIC_SECTION(...) +#define FAIL(...) (void)(0) +#define FAIL_CHECK(...) (void)(0) +#define SUCCEED(...) (void)(0) +#define ANON_TEST_CASE() \ + INTERNAL_CATCH_TESTCASE_NO_REGISTRATION( \ + INTERNAL_CATCH_UNIQUE_NAME(C_A_T_C_H_T_E_S_T_)) + +#ifndef CATCH_CONFIG_TRADITIONAL_MSVC_PREPROCESSOR +#define TEMPLATE_TEST_CASE(...) \ + INTERNAL_CATCH_TEMPLATE_TEST_CASE_NO_REGISTRATION(__VA_ARGS__) +#define TEMPLATE_TEST_CASE_SIG(...) \ + INTERNAL_CATCH_TEMPLATE_TEST_CASE_SIG_NO_REGISTRATION(__VA_ARGS__) +#define TEMPLATE_TEST_CASE_METHOD(className, ...) \ + INTERNAL_CATCH_TEMPLATE_TEST_CASE_METHOD_NO_REGISTRATION(className, \ + __VA_ARGS__) +#define TEMPLATE_TEST_CASE_METHOD_SIG(className, ...) \ + INTERNAL_CATCH_TEMPLATE_TEST_CASE_METHOD_SIG_NO_REGISTRATION( \ + className, __VA_ARGS__) +#define TEMPLATE_PRODUCT_TEST_CASE(...) TEMPLATE_TEST_CASE(__VA_ARGS__) +#define TEMPLATE_PRODUCT_TEST_CASE_SIG(...) TEMPLATE_TEST_CASE(__VA_ARGS__) +#define TEMPLATE_PRODUCT_TEST_CASE_METHOD(className, ...) \ + TEMPLATE_TEST_CASE_METHOD(className, __VA_ARGS__) +#define TEMPLATE_PRODUCT_TEST_CASE_METHOD_SIG(className, ...) \ + TEMPLATE_TEST_CASE_METHOD(className, __VA_ARGS__) +#else +#define TEMPLATE_TEST_CASE(...) \ + INTERNAL_CATCH_EXPAND_VARGS( \ + INTERNAL_CATCH_TEMPLATE_TEST_CASE_NO_REGISTRATION( \ + __VA_ARGS__)) +#define TEMPLATE_TEST_CASE_SIG(...) \ + INTERNAL_CATCH_EXPAND_VARGS( \ + INTERNAL_CATCH_TEMPLATE_TEST_CASE_SIG_NO_REGISTRATION( \ + __VA_ARGS__)) +#define TEMPLATE_TEST_CASE_METHOD(className, ...) \ + INTERNAL_CATCH_EXPAND_VARGS( \ + INTERNAL_CATCH_TEMPLATE_TEST_CASE_METHOD_NO_REGISTRATION( \ + className, __VA_ARGS__)) +#define TEMPLATE_TEST_CASE_METHOD_SIG(className, ...) \ + INTERNAL_CATCH_EXPAND_VARGS( \ + INTERNAL_CATCH_TEMPLATE_TEST_CASE_METHOD_SIG_NO_REGISTRATION( \ + className, __VA_ARGS__)) +#define TEMPLATE_PRODUCT_TEST_CASE(...) TEMPLATE_TEST_CASE(__VA_ARGS__) +#define TEMPLATE_PRODUCT_TEST_CASE_SIG(...) TEMPLATE_TEST_CASE(__VA_ARGS__) +#define TEMPLATE_PRODUCT_TEST_CASE_METHOD(className, ...) \ + TEMPLATE_TEST_CASE_METHOD(className, __VA_ARGS__) +#define TEMPLATE_PRODUCT_TEST_CASE_METHOD_SIG(className, ...) \ + TEMPLATE_TEST_CASE_METHOD(className, __VA_ARGS__) +#endif + +#define STATIC_REQUIRE(...) (void)(0) +#define STATIC_REQUIRE_FALSE(...) (void)(0) + +#endif + +#define CATCH_TRANSLATE_EXCEPTION(signature) \ + INTERNAL_CATCH_TRANSLATE_EXCEPTION_NO_REG( \ + INTERNAL_CATCH_UNIQUE_NAME( \ + catch_internal_ExceptionTranslator), \ + signature) + +// "BDD-style" convenience wrappers +#define SCENARIO(...) \ + INTERNAL_CATCH_TESTCASE_NO_REGISTRATION( \ + INTERNAL_CATCH_UNIQUE_NAME(C_A_T_C_H_T_E_S_T_)) +#define SCENARIO_METHOD(className, ...) \ + INTERNAL_CATCH_TESTCASE_METHOD_NO_REGISTRATION( \ + INTERNAL_CATCH_UNIQUE_NAME(C_A_T_C_H_T_E_S_T_), className) + +#define GIVEN(desc) +#define AND_GIVEN(desc) +#define WHEN(desc) +#define AND_WHEN(desc) +#define THEN(desc) +#define AND_THEN(desc) + +using Catch::Detail::Approx; + +#endif + +#endif // ! CATCH_CONFIG_IMPL_ONLY + +// start catch_reenable_warnings.h + +#ifdef __clang__ +#ifdef __ICC // icpc defines the __clang__ macro +#pragma warning(pop) +#else +#pragma clang diagnostic pop +#endif +#elif defined __GNUC__ +#pragma GCC diagnostic pop +#endif + +// end catch_reenable_warnings.h +// end catch.hpp +#endif // TWOBLUECUBES_SINGLE_INCLUDE_CATCH_HPP_INCLUDED diff --git a/tests/tests.cpp b/tests/tests.cpp new file mode 100644 index 00000000..159b8819 --- /dev/null +++ b/tests/tests.cpp @@ -0,0 +1,71 @@ +#define CATCH_CONFIG_MAIN +#include "catch.hpp" + +#include + +TEST_CASE("Expressions are evaluated successfully", "[math-helpers]") +{ + auto expressionResult = EvalMathExpression("1"); + auto *doubleValuePtr = std::get_if(&expressionResult); + + REQUIRE(doubleValuePtr != nullptr); + REQUIRE(*doubleValuePtr == 1.0); + + expressionResult = EvalMathExpression("1 + 2"); + doubleValuePtr = std::get_if(&expressionResult); + + REQUIRE(doubleValuePtr != nullptr); + REQUIRE(*doubleValuePtr == 3.0); + + expressionResult = EvalMathExpression("1 + 2 * 3"); + doubleValuePtr = std::get_if(&expressionResult); + + REQUIRE(doubleValuePtr != nullptr); + REQUIRE(*doubleValuePtr == 7.0); + + expressionResult = EvalMathExpression("(1 + 2) * 3"); + doubleValuePtr = std::get_if(&expressionResult); + + REQUIRE(doubleValuePtr != nullptr); + REQUIRE(*doubleValuePtr == 9.0); + + expressionResult = + EvalMathExpression("cos(abs(1 - sqrt(10 - 2 * 3)) -1)"); + doubleValuePtr = std::get_if(&expressionResult); + + REQUIRE(doubleValuePtr != nullptr); + REQUIRE(*doubleValuePtr == 1.0); +} + +TEST_CASE("Invalid expressions are not evaluated", "[math-helpers]") +{ + auto expressionResult = EvalMathExpression(""); + auto *doubleValuePtr = std::get_if(&expressionResult); + + REQUIRE(doubleValuePtr == nullptr); + + expressionResult = EvalMathExpression(")"); + doubleValuePtr = std::get_if(&expressionResult); + + REQUIRE(doubleValuePtr == nullptr); + + expressionResult = EvalMathExpression("("); + doubleValuePtr = std::get_if(&expressionResult); + + REQUIRE(doubleValuePtr == nullptr); + + expressionResult = EvalMathExpression("()"); + doubleValuePtr = std::get_if(&expressionResult); + + REQUIRE(doubleValuePtr == nullptr); + + expressionResult = EvalMathExpression("1 + 2)"); + doubleValuePtr = std::get_if(&expressionResult); + + REQUIRE(doubleValuePtr == nullptr); + + expressionResult = EvalMathExpression("1 + 2 * asdf"); + doubleValuePtr = std::get_if(&expressionResult); + + REQUIRE(doubleValuePtr == nullptr); +}