mirror of
https://github.com/catchorg/Catch2.git
synced 2024-11-26 07:16:10 +01:00
a5245ac76b
Classes will automatically inherit the virtual-ness of their base class destructors. If the base class already has a virtual destructor and the derived class needs default destructor semantics then the derived class can omit defining the destructor in favor of the compiler automatically defining it. This has an additional benefit of reenabling move semantics. The presence of a user-specified destructor automatically disables move operations.
13736 lines
496 KiB
C++
13736 lines
496 KiB
C++
|
|
// Copyright Catch2 Authors
|
|
// Distributed under the Boost Software License, Version 1.0.
|
|
// (See accompanying file LICENSE.txt or copy at
|
|
// https://www.boost.org/LICENSE_1_0.txt)
|
|
|
|
// SPDX-License-Identifier: BSL-1.0
|
|
|
|
// Catch v3.5.0
|
|
// Generated: 2023-12-11 00:51:06.770598
|
|
// ----------------------------------------------------------
|
|
// This file is an amalgamation of multiple different files.
|
|
// You probably shouldn't edit it directly.
|
|
// ----------------------------------------------------------
|
|
#ifndef CATCH_AMALGAMATED_HPP_INCLUDED
|
|
#define CATCH_AMALGAMATED_HPP_INCLUDED
|
|
|
|
|
|
/** \file
|
|
* This is a convenience header for Catch2. It includes **all** of Catch2 headers.
|
|
*
|
|
* Generally the Catch2 users should use specific includes they need,
|
|
* but this header can be used instead for ease-of-experimentation, or
|
|
* just plain convenience, at the cost of (significantly) increased
|
|
* compilation times.
|
|
*
|
|
* When a new header is added to either the top level folder, or to the
|
|
* corresponding internal subfolder, it should be added here. Headers
|
|
* added to the various subparts (e.g. matchers, generators, etc...),
|
|
* should go their respective catch-all headers.
|
|
*/
|
|
|
|
#ifndef CATCH_ALL_HPP_INCLUDED
|
|
#define CATCH_ALL_HPP_INCLUDED
|
|
|
|
|
|
|
|
/** \file
|
|
* This is a convenience header for Catch2's benchmarking. It includes
|
|
* **all** of Catch2 headers related to benchmarking.
|
|
*
|
|
* Generally the Catch2 users should use specific includes they need,
|
|
* but this header can be used instead for ease-of-experimentation, or
|
|
* just plain convenience, at the cost of (significantly) increased
|
|
* compilation times.
|
|
*
|
|
* When a new header is added to either the `benchmark` folder, or to
|
|
* the corresponding internal (detail) subfolder, it should be added here.
|
|
*/
|
|
|
|
#ifndef CATCH_BENCHMARK_ALL_HPP_INCLUDED
|
|
#define CATCH_BENCHMARK_ALL_HPP_INCLUDED
|
|
|
|
|
|
|
|
// Adapted from donated nonius code.
|
|
|
|
#ifndef CATCH_BENCHMARK_HPP_INCLUDED
|
|
#define CATCH_BENCHMARK_HPP_INCLUDED
|
|
|
|
|
|
|
|
#ifndef CATCH_COMPILER_CAPABILITIES_HPP_INCLUDED
|
|
#define CATCH_COMPILER_CAPABILITIES_HPP_INCLUDED
|
|
|
|
// Detect a number of compiler features - by compiler
|
|
// The following features are defined:
|
|
//
|
|
// 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_<feature name> 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.
|
|
|
|
|
|
|
|
#ifndef CATCH_PLATFORM_HPP_INCLUDED
|
|
#define CATCH_PLATFORM_HPP_INCLUDED
|
|
|
|
// See e.g.:
|
|
// https://opensource.apple.com/source/CarbonHeaders/CarbonHeaders-18.1/TargetConditionals.h.auto.html
|
|
#ifdef __APPLE__
|
|
# include <TargetConditionals.h>
|
|
# 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
|
|
|
|
# if defined( WINAPI_FAMILY ) && ( WINAPI_FAMILY == WINAPI_FAMILY_APP )
|
|
# define CATCH_PLATFORM_WINDOWS_UWP
|
|
# endif
|
|
|
|
#elif defined(__ORBIS__) || defined(__PROSPERO__)
|
|
# define CATCH_PLATFORM_PLAYSTATION
|
|
|
|
#endif
|
|
|
|
#endif // CATCH_PLATFORM_HPP_INCLUDED
|
|
|
|
#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__) && !defined(__NVCOMPILER)
|
|
# define CATCH_INTERNAL_START_WARNINGS_SUPPRESSION _Pragma( "GCC diagnostic push" )
|
|
# define CATCH_INTERNAL_STOP_WARNINGS_SUPPRESSION _Pragma( "GCC diagnostic pop" )
|
|
|
|
// This only works on GCC 9+. so we have to also add a global suppression of Wparentheses
|
|
// for older versions of GCC.
|
|
# define CATCH_INTERNAL_SUPPRESS_PARENTHESES_WARNINGS \
|
|
_Pragma( "GCC diagnostic ignored \"-Wparentheses\"" )
|
|
|
|
# define CATCH_INTERNAL_SUPPRESS_UNUSED_RESULT \
|
|
_Pragma( "GCC diagnostic ignored \"-Wunused-result\"" )
|
|
|
|
# define CATCH_INTERNAL_SUPPRESS_UNUSED_VARIABLE_WARNINGS \
|
|
_Pragma( "GCC diagnostic ignored \"-Wunused-variable\"" )
|
|
|
|
# define CATCH_INTERNAL_SUPPRESS_USELESS_CAST_WARNINGS \
|
|
_Pragma( "GCC diagnostic ignored \"-Wuseless-cast\"" )
|
|
|
|
# define CATCH_INTERNAL_SUPPRESS_SHADOW_WARNINGS \
|
|
_Pragma( "GCC diagnostic ignored \"-Wshadow\"" )
|
|
|
|
# define CATCH_INTERNAL_IGNORE_BUT_WARN(...) (void)__builtin_constant_p(__VA_ARGS__)
|
|
|
|
#endif
|
|
|
|
#if defined(__NVCOMPILER)
|
|
# define CATCH_INTERNAL_START_WARNINGS_SUPPRESSION _Pragma( "diag push" )
|
|
# define CATCH_INTERNAL_STOP_WARNINGS_SUPPRESSION _Pragma( "diag pop" )
|
|
# define CATCH_INTERNAL_SUPPRESS_UNUSED_VARIABLE_WARNINGS _Pragma( "diag_suppress declared_but_not_referenced" )
|
|
#endif
|
|
|
|
#if defined(__CUDACC__) && !defined(__clang__)
|
|
# ifdef __NVCC_DIAG_PRAGMA_SUPPORT__
|
|
// New pragmas introduced in CUDA 11.5+
|
|
# define CATCH_INTERNAL_START_WARNINGS_SUPPRESSION _Pragma( "nv_diagnostic push" )
|
|
# define CATCH_INTERNAL_STOP_WARNINGS_SUPPRESSION _Pragma( "nv_diagnostic pop" )
|
|
# define CATCH_INTERNAL_SUPPRESS_UNUSED_VARIABLE_WARNINGS _Pragma( "nv_diag_suppress 177" )
|
|
# else
|
|
# define CATCH_INTERNAL_SUPPRESS_UNUSED_VARIABLE_WARNINGS _Pragma( "diag_suppress 177" )
|
|
# endif
|
|
#endif
|
|
|
|
// clang-cl defines _MSC_VER as well as __clang__, which could cause the
|
|
// start/stop internal suppression macros to be double defined.
|
|
#if defined(__clang__) && !defined(_MSC_VER)
|
|
|
|
# define CATCH_INTERNAL_START_WARNINGS_SUPPRESSION _Pragma( "clang diagnostic push" )
|
|
# define CATCH_INTERNAL_STOP_WARNINGS_SUPPRESSION _Pragma( "clang diagnostic pop" )
|
|
|
|
#endif // __clang__ && !_MSC_VER
|
|
|
|
#if defined(__clang__)
|
|
|
|
// 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")
|
|
// ```
|
|
//
|
|
// Similarly, NVHPC's implementation of `__builtin_constant_p` has a bug which
|
|
// results in calls to the immediately evaluated lambda expressions to be
|
|
// reported as unevaluated lambdas.
|
|
// https://developer.nvidia.com/nvidia_bug/3321845.
|
|
//
|
|
// Therefore, `CATCH_INTERNAL_IGNORE_BUT_WARN` is not implemented.
|
|
# if !defined(__ibmxl__) && !defined(__CUDACC__) && !defined( __NVCOMPILER )
|
|
# 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_VARIABLE_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\"" )
|
|
|
|
# define CATCH_INTERNAL_SUPPRESS_COMMA_WARNINGS \
|
|
_Pragma( "clang diagnostic ignored \"-Wcomma\"" )
|
|
|
|
# define CATCH_INTERNAL_SUPPRESS_SHADOW_WARNINGS \
|
|
_Pragma( "clang diagnostic ignored \"-Wshadow\"" )
|
|
|
|
#endif // __clang__
|
|
|
|
|
|
////////////////////////////////////////////////////////////////////////////////
|
|
// We know some environments not to support full POSIX signals
|
|
#if defined( CATCH_PLATFORM_WINDOWS ) || \
|
|
defined( CATCH_PLATFORM_PLAYSTATION ) || \
|
|
defined( __CYGWIN__ ) || \
|
|
defined( __QNX__ ) || \
|
|
defined( __EMSCRIPTEN__ ) || \
|
|
defined( __DJGPP__ ) || \
|
|
defined( __OS400__ )
|
|
# define CATCH_INTERNAL_CONFIG_NO_POSIX_SIGNALS
|
|
#else
|
|
# define CATCH_INTERNAL_CONFIG_POSIX_SIGNALS
|
|
#endif
|
|
|
|
////////////////////////////////////////////////////////////////////////////////
|
|
// Assume that some platforms do not support getenv.
|
|
#if defined( CATCH_PLATFORM_WINDOWS_UWP ) || \
|
|
defined( CATCH_PLATFORM_PLAYSTATION ) || \
|
|
defined( _GAMING_XBOX )
|
|
# define CATCH_INTERNAL_CONFIG_NO_GETENV
|
|
#else
|
|
# define CATCH_INTERNAL_CONFIG_GETENV
|
|
#endif
|
|
|
|
////////////////////////////////////////////////////////////////////////////////
|
|
// Android somehow still does not support std::to_string
|
|
#if defined(__ANDROID__)
|
|
# define CATCH_INTERNAL_CONFIG_NO_CPP11_TO_STRING
|
|
#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)
|
|
|
|
// We want to defer to nvcc-specific warning suppression if we are compiled
|
|
// with nvcc masquerading for MSVC.
|
|
# if !defined( __CUDACC__ )
|
|
# define CATCH_INTERNAL_START_WARNINGS_SUPPRESSION \
|
|
__pragma( warning( push ) )
|
|
# define CATCH_INTERNAL_STOP_WARNINGS_SUPPRESSION \
|
|
__pragma( warning( pop ) )
|
|
# endif
|
|
|
|
// Universal Windows platform does not support SEH
|
|
// Or console colours (or console at all...)
|
|
# if defined(CATCH_PLATFORM_WINDOWS_UWP)
|
|
# define CATCH_INTERNAL_CONFIG_NO_COLOUR_WIN32
|
|
# else
|
|
# define CATCH_INTERNAL_CONFIG_WINDOWS_SEH
|
|
# endif
|
|
|
|
// 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(__clang__) // Handle Clang masquerading for msvc
|
|
# if !defined(_MSVC_TRADITIONAL) || (defined(_MSVC_TRADITIONAL) && _MSVC_TRADITIONAL)
|
|
# define CATCH_INTERNAL_CONFIG_TRADITIONAL_MSVC_PREPROCESSOR
|
|
# endif // MSVC_TRADITIONAL
|
|
# 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
|
|
|
|
|
|
////////////////////////////////////////////////////////////////////////////////
|
|
// Embarcadero C++Build
|
|
#if defined(__BORLANDC__)
|
|
#define CATCH_INTERNAL_CONFIG_POLYFILL_ISNAN
|
|
#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_INTERNAL_CONFIG_NO_COLOUR_WIN32
|
|
#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(<string_view>) && defined(CATCH_CPP17_OR_GREATER)
|
|
# define CATCH_INTERNAL_CONFIG_CPP17_STRING_VIEW
|
|
#endif
|
|
|
|
// Check if optional is available and usable
|
|
# if __has_include(<optional>) && defined(CATCH_CPP17_OR_GREATER)
|
|
# define CATCH_INTERNAL_CONFIG_CPP17_OPTIONAL
|
|
# endif // __has_include(<optional>) && defined(CATCH_CPP17_OR_GREATER)
|
|
|
|
// Check if byte is available and usable
|
|
# if __has_include(<cstddef>) && defined(CATCH_CPP17_OR_GREATER)
|
|
# include <cstddef>
|
|
# if defined(__cpp_lib_byte) && (__cpp_lib_byte > 0)
|
|
# define CATCH_INTERNAL_CONFIG_CPP17_BYTE
|
|
# endif
|
|
# endif // __has_include(<cstddef>) && defined(CATCH_CPP17_OR_GREATER)
|
|
|
|
// Check if variant is available and usable
|
|
# if __has_include(<variant>) && 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 <ciso646>
|
|
# 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(<variant>) && defined(CATCH_CPP17_OR_GREATER)
|
|
#endif // defined(__has_include)
|
|
|
|
|
|
#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
|
|
|
|
#if defined(CATCH_INTERNAL_CONFIG_GETENV) && !defined(CATCH_INTERNAL_CONFIG_NO_GETENV) && !defined(CATCH_CONFIG_NO_GETENV) && !defined(CATCH_CONFIG_GETENV)
|
|
# define CATCH_CONFIG_GETENV
|
|
#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 ) && \
|
|
!defined( CATCH_CONFIG_NO_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_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_RESULT)
|
|
# define CATCH_INTERNAL_SUPPRESS_UNUSED_RESULT
|
|
#endif
|
|
#if !defined(CATCH_INTERNAL_SUPPRESS_UNUSED_VARIABLE_WARNINGS)
|
|
# define CATCH_INTERNAL_SUPPRESS_UNUSED_VARIABLE_WARNINGS
|
|
#endif
|
|
#if !defined(CATCH_INTERNAL_SUPPRESS_USELESS_CAST_WARNINGS)
|
|
# define CATCH_INTERNAL_SUPPRESS_USELESS_CAST_WARNINGS
|
|
#endif
|
|
#if !defined(CATCH_INTERNAL_SUPPRESS_ZERO_VARIADIC_WARNINGS)
|
|
# define CATCH_INTERNAL_SUPPRESS_ZERO_VARIADIC_WARNINGS
|
|
#endif
|
|
#if !defined( CATCH_INTERNAL_SUPPRESS_UNUSED_TEMPLATE_WARNINGS )
|
|
# define CATCH_INTERNAL_SUPPRESS_UNUSED_TEMPLATE_WARNINGS
|
|
#endif
|
|
#if !defined( CATCH_INTERNAL_SUPPRESS_COMMA_WARNINGS )
|
|
# define CATCH_INTERNAL_SUPPRESS_COMMA_WARNINGS
|
|
#endif
|
|
#if !defined( CATCH_INTERNAL_SUPPRESS_SHADOW_WARNINGS )
|
|
# define CATCH_INTERNAL_SUPPRESS_SHADOW_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_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
|
|
|
|
#if defined( CATCH_PLATFORM_WINDOWS ) && \
|
|
!defined( CATCH_CONFIG_COLOUR_WIN32 ) && \
|
|
!defined( CATCH_CONFIG_NO_COLOUR_WIN32 ) && \
|
|
!defined( CATCH_INTERNAL_CONFIG_NO_COLOUR_WIN32 )
|
|
# define CATCH_CONFIG_COLOUR_WIN32
|
|
#endif
|
|
|
|
#if defined( CATCH_CONFIG_SHARED_LIBRARY ) && defined( _MSC_VER ) && \
|
|
!defined( CATCH_CONFIG_STATIC )
|
|
# ifdef Catch2_EXPORTS
|
|
# define CATCH_EXPORT //__declspec( dllexport ) // not needed
|
|
# else
|
|
# define CATCH_EXPORT __declspec( dllimport )
|
|
# endif
|
|
#else
|
|
# define CATCH_EXPORT
|
|
#endif
|
|
|
|
#endif // CATCH_COMPILER_CAPABILITIES_HPP_INCLUDED
|
|
|
|
|
|
#ifndef CATCH_CONTEXT_HPP_INCLUDED
|
|
#define CATCH_CONTEXT_HPP_INCLUDED
|
|
|
|
|
|
namespace Catch {
|
|
|
|
class IResultCapture;
|
|
class IConfig;
|
|
|
|
class Context {
|
|
IConfig const* m_config = nullptr;
|
|
IResultCapture* m_resultCapture = nullptr;
|
|
|
|
CATCH_EXPORT static Context* currentContext;
|
|
friend Context& getCurrentMutableContext();
|
|
friend Context const& getCurrentContext();
|
|
static void createContext();
|
|
friend void cleanUpContext();
|
|
|
|
public:
|
|
IResultCapture* getResultCapture() const { return m_resultCapture; }
|
|
IConfig const* getConfig() const { return m_config; }
|
|
void setResultCapture( IResultCapture* resultCapture );
|
|
void setConfig( IConfig const* config );
|
|
};
|
|
|
|
Context& getCurrentMutableContext();
|
|
|
|
inline Context const& getCurrentContext() {
|
|
// We duplicate the logic from `getCurrentMutableContext` here,
|
|
// to avoid paying the call overhead in debug mode.
|
|
if ( !Context::currentContext ) { Context::createContext(); }
|
|
// NOLINTNEXTLINE(clang-analyzer-core.uninitialized.UndefReturn)
|
|
return *Context::currentContext;
|
|
}
|
|
|
|
void cleanUpContext();
|
|
|
|
class SimplePcg32;
|
|
SimplePcg32& sharedRng();
|
|
}
|
|
|
|
#endif // CATCH_CONTEXT_HPP_INCLUDED
|
|
|
|
|
|
#ifndef CATCH_MOVE_AND_FORWARD_HPP_INCLUDED
|
|
#define CATCH_MOVE_AND_FORWARD_HPP_INCLUDED
|
|
|
|
#include <type_traits>
|
|
|
|
//! Replacement for std::move with better compile time performance
|
|
#define CATCH_MOVE(...) static_cast<std::remove_reference_t<decltype(__VA_ARGS__)>&&>(__VA_ARGS__)
|
|
|
|
//! Replacement for std::forward with better compile time performance
|
|
#define CATCH_FORWARD(...) static_cast<decltype(__VA_ARGS__)&&>(__VA_ARGS__)
|
|
|
|
#endif // CATCH_MOVE_AND_FORWARD_HPP_INCLUDED
|
|
|
|
|
|
#ifndef CATCH_TEST_FAILURE_EXCEPTION_HPP_INCLUDED
|
|
#define CATCH_TEST_FAILURE_EXCEPTION_HPP_INCLUDED
|
|
|
|
namespace Catch {
|
|
|
|
//! Used to signal that an assertion macro failed
|
|
struct TestFailureException{};
|
|
//! Used to signal that the remainder of a test should be skipped
|
|
struct TestSkipException {};
|
|
|
|
/**
|
|
* Outlines throwing of `TestFailureException` into a single TU
|
|
*
|
|
* Also handles `CATCH_CONFIG_DISABLE_EXCEPTIONS` for callers.
|
|
*/
|
|
[[noreturn]] void throw_test_failure_exception();
|
|
|
|
/**
|
|
* Outlines throwing of `TestSkipException` into a single TU
|
|
*
|
|
* Also handles `CATCH_CONFIG_DISABLE_EXCEPTIONS` for callers.
|
|
*/
|
|
[[noreturn]] void throw_test_skip_exception();
|
|
|
|
} // namespace Catch
|
|
|
|
#endif // CATCH_TEST_FAILURE_EXCEPTION_HPP_INCLUDED
|
|
|
|
|
|
#ifndef CATCH_UNIQUE_NAME_HPP_INCLUDED
|
|
#define CATCH_UNIQUE_NAME_HPP_INCLUDED
|
|
|
|
|
|
|
|
|
|
/** \file
|
|
* Wrapper for the CONFIG configuration option
|
|
*
|
|
* When generating internal unique names, there are two options. Either
|
|
* we mix in the current line number, or mix in an incrementing number.
|
|
* We prefer the latter, using `__COUNTER__`, but users might want to
|
|
* use the former.
|
|
*/
|
|
|
|
#ifndef CATCH_CONFIG_COUNTER_HPP_INCLUDED
|
|
#define CATCH_CONFIG_COUNTER_HPP_INCLUDED
|
|
|
|
|
|
#if ( !defined(__JETBRAINS_IDE__) || __JETBRAINS_IDE__ >= 20170300L )
|
|
#define CATCH_INTERNAL_CONFIG_COUNTER
|
|
#endif
|
|
|
|
#if defined( CATCH_INTERNAL_CONFIG_COUNTER ) && \
|
|
!defined( CATCH_CONFIG_NO_COUNTER ) && \
|
|
!defined( CATCH_CONFIG_COUNTER )
|
|
# define CATCH_CONFIG_COUNTER
|
|
#endif
|
|
|
|
|
|
#endif // CATCH_CONFIG_COUNTER_HPP_INCLUDED
|
|
#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
|
|
|
|
#endif // CATCH_UNIQUE_NAME_HPP_INCLUDED
|
|
|
|
|
|
#ifndef CATCH_INTERFACES_CAPTURE_HPP_INCLUDED
|
|
#define CATCH_INTERFACES_CAPTURE_HPP_INCLUDED
|
|
|
|
#include <string>
|
|
#include <chrono>
|
|
|
|
|
|
|
|
#ifndef CATCH_STRINGREF_HPP_INCLUDED
|
|
#define CATCH_STRINGREF_HPP_INCLUDED
|
|
|
|
#include <cstddef>
|
|
#include <string>
|
|
#include <iosfwd>
|
|
#include <cassert>
|
|
|
|
#include <cstring>
|
|
|
|
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 other ) const noexcept -> bool {
|
|
return m_size == other.m_size
|
|
&& (std::memcmp( m_start, other.m_start, m_size ) == 0);
|
|
}
|
|
auto operator != (StringRef other) const noexcept -> bool {
|
|
return !(*this == other);
|
|
}
|
|
|
|
constexpr auto operator[] ( size_type index ) const noexcept -> char {
|
|
assert(index < m_size);
|
|
return m_start[index];
|
|
}
|
|
|
|
bool operator<(StringRef rhs) const noexcept;
|
|
|
|
public: // named queries
|
|
constexpr auto empty() const noexcept -> bool {
|
|
return m_size == 0;
|
|
}
|
|
constexpr auto size() const noexcept -> size_type {
|
|
return m_size;
|
|
}
|
|
|
|
// Returns a substring of [start, start + length).
|
|
// If start + length > size(), then the substring is [start, start + size()).
|
|
// If start > size(), then the substring is empty.
|
|
constexpr StringRef substr(size_type start, size_type length) const noexcept {
|
|
if (start < m_size) {
|
|
const auto shortened_size = m_size - start;
|
|
return StringRef(m_start + start, (shortened_size < length) ? shortened_size : length);
|
|
} else {
|
|
return StringRef();
|
|
}
|
|
}
|
|
|
|
// Returns the current start pointer. May not be null-terminated.
|
|
constexpr char const* data() const noexcept {
|
|
return m_start;
|
|
}
|
|
|
|
constexpr const_iterator begin() const { return m_start; }
|
|
constexpr const_iterator end() const { return m_start + m_size; }
|
|
|
|
|
|
friend std::string& operator += (std::string& lhs, StringRef sr);
|
|
friend std::ostream& operator << (std::ostream& os, StringRef sr);
|
|
friend std::string operator+(StringRef lhs, StringRef rhs);
|
|
|
|
/**
|
|
* Provides a three-way comparison with rhs
|
|
*
|
|
* Returns negative number if lhs < rhs, 0 if lhs == rhs, and a positive
|
|
* number if lhs > rhs
|
|
*/
|
|
int compare( StringRef rhs ) const;
|
|
};
|
|
|
|
|
|
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 );
|
|
}
|
|
|
|
#endif // CATCH_STRINGREF_HPP_INCLUDED
|
|
|
|
|
|
#ifndef CATCH_RESULT_TYPE_HPP_INCLUDED
|
|
#define CATCH_RESULT_TYPE_HPP_INCLUDED
|
|
|
|
namespace Catch {
|
|
|
|
// ResultWas::OfType enum
|
|
struct ResultWas { enum OfType {
|
|
Unknown = -1,
|
|
Ok = 0,
|
|
Info = 1,
|
|
Warning = 2,
|
|
// TODO: Should explicit skip be considered "not OK" (cf. isOk)? I.e., should it have the failure bit?
|
|
ExplicitSkip = 4,
|
|
|
|
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
|
|
|
|
#endif // CATCH_RESULT_TYPE_HPP_INCLUDED
|
|
|
|
|
|
#ifndef CATCH_UNIQUE_PTR_HPP_INCLUDED
|
|
#define CATCH_UNIQUE_PTR_HPP_INCLUDED
|
|
|
|
#include <cassert>
|
|
#include <type_traits>
|
|
|
|
|
|
namespace Catch {
|
|
namespace Detail {
|
|
/**
|
|
* A reimplementation of `std::unique_ptr` for improved compilation performance
|
|
*
|
|
* Does not support arrays nor custom deleters.
|
|
*/
|
|
template <typename T>
|
|
class unique_ptr {
|
|
T* m_ptr;
|
|
public:
|
|
constexpr unique_ptr(std::nullptr_t = nullptr):
|
|
m_ptr{}
|
|
{}
|
|
explicit constexpr unique_ptr(T* ptr):
|
|
m_ptr(ptr)
|
|
{}
|
|
|
|
template <typename U, typename = std::enable_if_t<std::is_base_of<T, U>::value>>
|
|
unique_ptr(unique_ptr<U>&& from):
|
|
m_ptr(from.release())
|
|
{}
|
|
|
|
template <typename U, typename = std::enable_if_t<std::is_base_of<T, U>::value>>
|
|
unique_ptr& operator=(unique_ptr<U>&& from) {
|
|
reset(from.release());
|
|
|
|
return *this;
|
|
}
|
|
|
|
unique_ptr(unique_ptr const&) = delete;
|
|
unique_ptr& operator=(unique_ptr const&) = delete;
|
|
|
|
unique_ptr(unique_ptr&& rhs) noexcept:
|
|
m_ptr(rhs.m_ptr) {
|
|
rhs.m_ptr = nullptr;
|
|
}
|
|
unique_ptr& operator=(unique_ptr&& rhs) noexcept {
|
|
reset(rhs.release());
|
|
|
|
return *this;
|
|
}
|
|
|
|
~unique_ptr() {
|
|
delete m_ptr;
|
|
}
|
|
|
|
T& operator*() {
|
|
assert(m_ptr);
|
|
return *m_ptr;
|
|
}
|
|
T const& operator*() const {
|
|
assert(m_ptr);
|
|
return *m_ptr;
|
|
}
|
|
T* operator->() noexcept {
|
|
assert(m_ptr);
|
|
return m_ptr;
|
|
}
|
|
T const* operator->() const noexcept {
|
|
assert(m_ptr);
|
|
return m_ptr;
|
|
}
|
|
|
|
T* get() { return m_ptr; }
|
|
T const* get() const { return m_ptr; }
|
|
|
|
void reset(T* ptr = nullptr) {
|
|
delete m_ptr;
|
|
m_ptr = ptr;
|
|
}
|
|
|
|
T* release() {
|
|
auto temp = m_ptr;
|
|
m_ptr = nullptr;
|
|
return temp;
|
|
}
|
|
|
|
explicit operator bool() const {
|
|
return m_ptr;
|
|
}
|
|
|
|
friend void swap(unique_ptr& lhs, unique_ptr& rhs) {
|
|
auto temp = lhs.m_ptr;
|
|
lhs.m_ptr = rhs.m_ptr;
|
|
rhs.m_ptr = temp;
|
|
}
|
|
};
|
|
|
|
//! Specialization to cause compile-time error for arrays
|
|
template <typename T>
|
|
class unique_ptr<T[]>;
|
|
|
|
template <typename T, typename... Args>
|
|
unique_ptr<T> make_unique(Args&&... args) {
|
|
return unique_ptr<T>(new T(CATCH_FORWARD(args)...));
|
|
}
|
|
|
|
|
|
} // end namespace Detail
|
|
} // end namespace Catch
|
|
|
|
#endif // CATCH_UNIQUE_PTR_HPP_INCLUDED
|
|
|
|
|
|
#ifndef CATCH_BENCHMARK_STATS_FWD_HPP_INCLUDED
|
|
#define CATCH_BENCHMARK_STATS_FWD_HPP_INCLUDED
|
|
|
|
|
|
|
|
// Adapted from donated nonius code.
|
|
|
|
#ifndef CATCH_CLOCK_HPP_INCLUDED
|
|
#define CATCH_CLOCK_HPP_INCLUDED
|
|
|
|
#include <chrono>
|
|
|
|
namespace Catch {
|
|
namespace Benchmark {
|
|
using IDuration = std::chrono::nanoseconds;
|
|
using FDuration = std::chrono::duration<double, std::nano>;
|
|
|
|
template <typename Clock>
|
|
using TimePoint = typename Clock::time_point;
|
|
|
|
using default_clock = std::chrono::steady_clock;
|
|
} // namespace Benchmark
|
|
} // namespace Catch
|
|
|
|
#endif // CATCH_CLOCK_HPP_INCLUDED
|
|
|
|
namespace Catch {
|
|
|
|
// We cannot forward declare the type with default template argument
|
|
// multiple times, so it is split out into a separate header so that
|
|
// we can prevent multiple declarations in dependees
|
|
template <typename Duration = Benchmark::FDuration>
|
|
struct BenchmarkStats;
|
|
|
|
} // end namespace Catch
|
|
|
|
#endif // CATCH_BENCHMARK_STATS_FWD_HPP_INCLUDED
|
|
|
|
namespace Catch {
|
|
|
|
class AssertionResult;
|
|
struct AssertionInfo;
|
|
struct SectionInfo;
|
|
struct SectionEndInfo;
|
|
struct MessageInfo;
|
|
struct MessageBuilder;
|
|
struct Counts;
|
|
struct AssertionReaction;
|
|
struct SourceLineInfo;
|
|
|
|
class ITransientExpression;
|
|
class IGeneratorTracker;
|
|
|
|
struct BenchmarkInfo;
|
|
|
|
namespace Generators {
|
|
class GeneratorUntypedBase;
|
|
using GeneratorBasePtr = Catch::Detail::unique_ptr<GeneratorUntypedBase>;
|
|
}
|
|
|
|
|
|
class IResultCapture {
|
|
public:
|
|
virtual ~IResultCapture();
|
|
|
|
virtual void notifyAssertionStarted( AssertionInfo const& info ) = 0;
|
|
virtual bool sectionStarted( StringRef sectionName,
|
|
SourceLineInfo const& sectionLineInfo,
|
|
Counts& assertions ) = 0;
|
|
virtual void sectionEnded( SectionEndInfo&& endInfo ) = 0;
|
|
virtual void sectionEndedEarly( SectionEndInfo&& endInfo ) = 0;
|
|
|
|
virtual IGeneratorTracker*
|
|
acquireGeneratorTracker( StringRef generatorName,
|
|
SourceLineInfo const& lineInfo ) = 0;
|
|
virtual IGeneratorTracker*
|
|
createGeneratorTracker( StringRef generatorName,
|
|
SourceLineInfo lineInfo,
|
|
Generators::GeneratorBasePtr&& generator ) = 0;
|
|
|
|
virtual void benchmarkPreparing( StringRef name ) = 0;
|
|
virtual void benchmarkStarting( BenchmarkInfo const& info ) = 0;
|
|
virtual void benchmarkEnded( BenchmarkStats<> const& stats ) = 0;
|
|
virtual void benchmarkFailed( StringRef error ) = 0;
|
|
|
|
virtual void pushScopedMessage( MessageInfo const& message ) = 0;
|
|
virtual void popScopedMessage( MessageInfo const& message ) = 0;
|
|
|
|
virtual void emplaceUnscopedMessage( MessageBuilder&& 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 message,
|
|
AssertionReaction& reaction ) = 0;
|
|
virtual void handleUnexpectedExceptionNotThrown
|
|
( AssertionInfo const& info,
|
|
AssertionReaction& reaction ) = 0;
|
|
virtual void handleUnexpectedInflightException
|
|
( AssertionInfo const& info,
|
|
std::string&& 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();
|
|
}
|
|
|
|
#endif // CATCH_INTERFACES_CAPTURE_HPP_INCLUDED
|
|
|
|
|
|
#ifndef CATCH_INTERFACES_CONFIG_HPP_INCLUDED
|
|
#define CATCH_INTERFACES_CONFIG_HPP_INCLUDED
|
|
|
|
|
|
|
|
#ifndef CATCH_NONCOPYABLE_HPP_INCLUDED
|
|
#define CATCH_NONCOPYABLE_HPP_INCLUDED
|
|
|
|
namespace Catch {
|
|
namespace Detail {
|
|
|
|
//! Deriving classes become noncopyable and nonmovable
|
|
class NonCopyable {
|
|
NonCopyable( NonCopyable const& ) = delete;
|
|
NonCopyable( NonCopyable&& ) = delete;
|
|
NonCopyable& operator=( NonCopyable const& ) = delete;
|
|
NonCopyable& operator=( NonCopyable&& ) = delete;
|
|
|
|
protected:
|
|
NonCopyable() noexcept = default;
|
|
};
|
|
|
|
} // namespace Detail
|
|
} // namespace Catch
|
|
|
|
#endif // CATCH_NONCOPYABLE_HPP_INCLUDED
|
|
|
|
#include <chrono>
|
|
#include <iosfwd>
|
|
#include <string>
|
|
#include <vector>
|
|
|
|
namespace Catch {
|
|
|
|
enum class Verbosity {
|
|
Quiet = 0,
|
|
Normal,
|
|
High
|
|
};
|
|
|
|
struct WarnAbout { enum What {
|
|
Nothing = 0x00,
|
|
//! A test case or leaf section did not run any assertions
|
|
NoAssertions = 0x01,
|
|
//! A command line test spec matched no test cases
|
|
UnmatchedTestSpec = 0x02,
|
|
}; };
|
|
|
|
enum class ShowDurations {
|
|
DefaultForReporter,
|
|
Always,
|
|
Never
|
|
};
|
|
enum class TestRunOrder {
|
|
Declared,
|
|
LexicographicallySorted,
|
|
Randomized
|
|
};
|
|
enum class ColourMode : std::uint8_t {
|
|
//! Let Catch2 pick implementation based on platform detection
|
|
PlatformDefault,
|
|
//! Use ANSI colour code escapes
|
|
ANSI,
|
|
//! Use Win32 console colour API
|
|
Win32,
|
|
//! Don't use any colour
|
|
None
|
|
};
|
|
struct WaitForKeypress { enum When {
|
|
Never,
|
|
BeforeStart = 1,
|
|
BeforeExit = 2,
|
|
BeforeStartAndExit = BeforeStart | BeforeExit
|
|
}; };
|
|
|
|
class TestSpec;
|
|
class IStream;
|
|
|
|
class IConfig : public Detail::NonCopyable {
|
|
public:
|
|
virtual ~IConfig();
|
|
|
|
virtual bool allowThrows() const = 0;
|
|
virtual StringRef name() const = 0;
|
|
virtual bool includeSuccessfulResults() const = 0;
|
|
virtual bool shouldDebugBreak() const = 0;
|
|
virtual bool warnAboutMissingAssertions() const = 0;
|
|
virtual bool warnAboutUnmatchedTestSpecs() const = 0;
|
|
virtual bool zeroTestsCountAsSuccess() const = 0;
|
|
virtual int abortAfter() const = 0;
|
|
virtual bool showInvisibles() const = 0;
|
|
virtual ShowDurations showDurations() const = 0;
|
|
virtual double minDuration() const = 0;
|
|
virtual TestSpec const& testSpec() const = 0;
|
|
virtual bool hasTestFilters() const = 0;
|
|
virtual std::vector<std::string> const& getTestsOrTags() const = 0;
|
|
virtual TestRunOrder runOrder() const = 0;
|
|
virtual uint32_t rngSeed() const = 0;
|
|
virtual unsigned int shardCount() const = 0;
|
|
virtual unsigned int shardIndex() const = 0;
|
|
virtual ColourMode defaultColourMode() const = 0;
|
|
virtual std::vector<std::string> const& getSectionsToRun() const = 0;
|
|
virtual Verbosity verbosity() const = 0;
|
|
|
|
virtual bool skipBenchmarks() const = 0;
|
|
virtual bool benchmarkNoAnalysis() const = 0;
|
|
virtual unsigned int benchmarkSamples() const = 0;
|
|
virtual double benchmarkConfidenceInterval() const = 0;
|
|
virtual unsigned int benchmarkResamples() const = 0;
|
|
virtual std::chrono::milliseconds benchmarkWarmupTime() const = 0;
|
|
};
|
|
}
|
|
|
|
#endif // CATCH_INTERFACES_CONFIG_HPP_INCLUDED
|
|
|
|
|
|
#ifndef CATCH_INTERFACES_REGISTRY_HUB_HPP_INCLUDED
|
|
#define CATCH_INTERFACES_REGISTRY_HUB_HPP_INCLUDED
|
|
|
|
|
|
#include <string>
|
|
|
|
namespace Catch {
|
|
|
|
class TestCaseHandle;
|
|
struct TestCaseInfo;
|
|
class ITestCaseRegistry;
|
|
class IExceptionTranslatorRegistry;
|
|
class IExceptionTranslator;
|
|
class ReporterRegistry;
|
|
class IReporterFactory;
|
|
class ITagAliasRegistry;
|
|
class ITestInvoker;
|
|
class IMutableEnumValuesRegistry;
|
|
struct SourceLineInfo;
|
|
|
|
class StartupExceptionRegistry;
|
|
class EventListenerFactory;
|
|
|
|
using IReporterFactoryPtr = Detail::unique_ptr<IReporterFactory>;
|
|
|
|
class IRegistryHub {
|
|
public:
|
|
virtual ~IRegistryHub(); // = default
|
|
|
|
virtual ReporterRegistry 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;
|
|
};
|
|
|
|
class IMutableRegistryHub {
|
|
public:
|
|
virtual ~IMutableRegistryHub(); // = default
|
|
virtual void registerReporter( std::string const& name, IReporterFactoryPtr factory ) = 0;
|
|
virtual void registerListener( Detail::unique_ptr<EventListenerFactory> factory ) = 0;
|
|
virtual void registerTest(Detail::unique_ptr<TestCaseInfo>&& testInfo, Detail::unique_ptr<ITestInvoker>&& invoker) = 0;
|
|
virtual void registerTranslator( Detail::unique_ptr<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();
|
|
|
|
}
|
|
|
|
#endif // CATCH_INTERFACES_REGISTRY_HUB_HPP_INCLUDED
|
|
|
|
|
|
#ifndef CATCH_BENCHMARK_STATS_HPP_INCLUDED
|
|
#define CATCH_BENCHMARK_STATS_HPP_INCLUDED
|
|
|
|
|
|
|
|
// Adapted from donated nonius code.
|
|
|
|
#ifndef CATCH_ESTIMATE_HPP_INCLUDED
|
|
#define CATCH_ESTIMATE_HPP_INCLUDED
|
|
|
|
namespace Catch {
|
|
namespace Benchmark {
|
|
template <typename Type>
|
|
struct Estimate {
|
|
Type point;
|
|
Type lower_bound;
|
|
Type upper_bound;
|
|
double confidence_interval;
|
|
};
|
|
} // namespace Benchmark
|
|
} // namespace Catch
|
|
|
|
#endif // CATCH_ESTIMATE_HPP_INCLUDED
|
|
|
|
|
|
// Adapted from donated nonius code.
|
|
|
|
#ifndef CATCH_OUTLIER_CLASSIFICATION_HPP_INCLUDED
|
|
#define CATCH_OUTLIER_CLASSIFICATION_HPP_INCLUDED
|
|
|
|
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
|
|
|
|
#endif // CATCH_OUTLIERS_CLASSIFICATION_HPP_INCLUDED
|
|
// The fwd decl & default specialization needs to be seen by VS2017 before
|
|
// BenchmarkStats itself, or VS2017 will report compilation error.
|
|
|
|
#include <string>
|
|
#include <vector>
|
|
|
|
namespace Catch {
|
|
|
|
struct BenchmarkInfo {
|
|
std::string name;
|
|
double estimatedDuration;
|
|
int iterations;
|
|
unsigned int samples;
|
|
unsigned int resamples;
|
|
double clockResolution;
|
|
double clockCost;
|
|
};
|
|
|
|
// We need to keep template parameter for backwards compatibility,
|
|
// but we also do not want to use the template paraneter.
|
|
template <class Dummy>
|
|
struct BenchmarkStats {
|
|
BenchmarkInfo info;
|
|
|
|
std::vector<Benchmark::FDuration> samples;
|
|
Benchmark::Estimate<Benchmark::FDuration> mean;
|
|
Benchmark::Estimate<Benchmark::FDuration> standardDeviation;
|
|
Benchmark::OutlierClassification outliers;
|
|
double outlierVariance;
|
|
};
|
|
|
|
|
|
} // end namespace Catch
|
|
|
|
#endif // CATCH_BENCHMARK_STATS_HPP_INCLUDED
|
|
|
|
|
|
// Adapted from donated nonius code.
|
|
|
|
#ifndef CATCH_ENVIRONMENT_HPP_INCLUDED
|
|
#define CATCH_ENVIRONMENT_HPP_INCLUDED
|
|
|
|
|
|
namespace Catch {
|
|
namespace Benchmark {
|
|
struct EnvironmentEstimate {
|
|
FDuration mean;
|
|
OutlierClassification outliers;
|
|
};
|
|
struct Environment {
|
|
EnvironmentEstimate clock_resolution;
|
|
EnvironmentEstimate clock_cost;
|
|
};
|
|
} // namespace Benchmark
|
|
} // namespace Catch
|
|
|
|
#endif // CATCH_ENVIRONMENT_HPP_INCLUDED
|
|
|
|
|
|
// Adapted from donated nonius code.
|
|
|
|
#ifndef CATCH_EXECUTION_PLAN_HPP_INCLUDED
|
|
#define CATCH_EXECUTION_PLAN_HPP_INCLUDED
|
|
|
|
|
|
|
|
// Adapted from donated nonius code.
|
|
|
|
#ifndef CATCH_BENCHMARK_FUNCTION_HPP_INCLUDED
|
|
#define CATCH_BENCHMARK_FUNCTION_HPP_INCLUDED
|
|
|
|
|
|
|
|
// Adapted from donated nonius code.
|
|
|
|
#ifndef CATCH_CHRONOMETER_HPP_INCLUDED
|
|
#define CATCH_CHRONOMETER_HPP_INCLUDED
|
|
|
|
|
|
|
|
// Adapted from donated nonius code.
|
|
|
|
#ifndef CATCH_OPTIMIZER_HPP_INCLUDED
|
|
#define CATCH_OPTIMIZER_HPP_INCLUDED
|
|
|
|
#if defined(_MSC_VER) || defined(__IAR_SYSTEMS_ICC__)
|
|
# include <atomic> // atomic_thread_fence
|
|
#endif
|
|
|
|
|
|
#include <type_traits>
|
|
|
|
namespace Catch {
|
|
namespace Benchmark {
|
|
#if defined(__GNUC__) || defined(__clang__)
|
|
template <typename T>
|
|
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) || defined(__IAR_SYSTEMS_ICC__)
|
|
|
|
#if defined(_MSVC_VER)
|
|
#pragma optimize("", off)
|
|
#elif defined(__IAR_SYSTEMS_ICC__)
|
|
// For IAR the pragma only affects the following function
|
|
#pragma optimize=disable
|
|
#endif
|
|
template <typename T>
|
|
inline void keep_memory(T* p) {
|
|
// thanks @milleniumbug
|
|
*reinterpret_cast<char volatile*>(p) = *reinterpret_cast<char const volatile*>(p);
|
|
}
|
|
// TODO equivalent keep_memory()
|
|
#if defined(_MSVC_VER)
|
|
#pragma optimize("", on)
|
|
#endif
|
|
|
|
namespace Detail {
|
|
inline void optimizer_barrier() {
|
|
std::atomic_thread_fence(std::memory_order_seq_cst);
|
|
}
|
|
} // namespace Detail
|
|
|
|
#endif
|
|
|
|
template <typename T>
|
|
inline void deoptimize_value(T&& x) {
|
|
keep_memory(&x);
|
|
}
|
|
|
|
template <typename Fn, typename... Args>
|
|
inline auto invoke_deoptimized(Fn&& fn, Args&&... args) -> std::enable_if_t<!std::is_same<void, decltype(fn(args...))>::value> {
|
|
deoptimize_value(CATCH_FORWARD(fn) (CATCH_FORWARD(args)...));
|
|
}
|
|
|
|
template <typename Fn, typename... Args>
|
|
inline auto invoke_deoptimized(Fn&& fn, Args&&... args) -> std::enable_if_t<std::is_same<void, decltype(fn(args...))>::value> {
|
|
CATCH_FORWARD((fn)) (CATCH_FORWARD(args)...);
|
|
}
|
|
} // namespace Benchmark
|
|
} // namespace Catch
|
|
|
|
#endif // CATCH_OPTIMIZER_HPP_INCLUDED
|
|
|
|
|
|
#ifndef CATCH_META_HPP_INCLUDED
|
|
#define CATCH_META_HPP_INCLUDED
|
|
|
|
#include <type_traits>
|
|
|
|
namespace Catch {
|
|
template <typename>
|
|
struct true_given : std::true_type {};
|
|
|
|
struct is_callable_tester {
|
|
template <typename Fun, typename... Args>
|
|
static true_given<decltype(std::declval<Fun>()(std::declval<Args>()...))> test(int);
|
|
template <typename...>
|
|
static std::false_type test(...);
|
|
};
|
|
|
|
template <typename T>
|
|
struct is_callable;
|
|
|
|
template <typename Fun, typename... Args>
|
|
struct is_callable<Fun(Args...)> : decltype(is_callable_tester::test<Fun, Args...>(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 <typename Func, typename... U>
|
|
using FunctionReturnType = std::remove_reference_t<std::remove_cv_t<std::invoke_result_t<Func, U...>>>;
|
|
#else
|
|
template <typename Func, typename... U>
|
|
using FunctionReturnType = std::remove_reference_t<std::remove_cv_t<std::result_of_t<Func(U...)>>>;
|
|
#endif
|
|
|
|
} // namespace Catch
|
|
|
|
namespace mpl_{
|
|
struct na;
|
|
}
|
|
|
|
#endif // CATCH_META_HPP_INCLUDED
|
|
|
|
namespace Catch {
|
|
namespace Benchmark {
|
|
namespace Detail {
|
|
struct ChronometerConcept {
|
|
virtual void start() = 0;
|
|
virtual void finish() = 0;
|
|
virtual ~ChronometerConcept(); // = default;
|
|
|
|
ChronometerConcept() = default;
|
|
ChronometerConcept(ChronometerConcept const&) = default;
|
|
ChronometerConcept& operator=(ChronometerConcept const&) = default;
|
|
};
|
|
template <typename Clock>
|
|
struct ChronometerModel final : public ChronometerConcept {
|
|
void start() override { started = Clock::now(); }
|
|
void finish() override { finished = Clock::now(); }
|
|
|
|
IDuration elapsed() const {
|
|
return std::chrono::duration_cast<std::chrono::nanoseconds>(
|
|
finished - started );
|
|
}
|
|
|
|
TimePoint<Clock> started;
|
|
TimePoint<Clock> finished;
|
|
};
|
|
} // namespace Detail
|
|
|
|
struct Chronometer {
|
|
public:
|
|
template <typename Fun>
|
|
void measure(Fun&& fun) { measure(CATCH_FORWARD(fun), is_callable<Fun(int)>()); }
|
|
|
|
int runs() const { return repeats; }
|
|
|
|
Chronometer(Detail::ChronometerConcept& meter, int repeats_)
|
|
: impl(&meter)
|
|
, repeats(repeats_) {}
|
|
|
|
private:
|
|
template <typename Fun>
|
|
void measure(Fun&& fun, std::false_type) {
|
|
measure([&fun](int) { return fun(); }, std::true_type());
|
|
}
|
|
|
|
template <typename Fun>
|
|
void measure(Fun&& fun, std::true_type) {
|
|
Detail::optimizer_barrier();
|
|
impl->start();
|
|
for (int i = 0; i < repeats; ++i) invoke_deoptimized(fun, i);
|
|
impl->finish();
|
|
Detail::optimizer_barrier();
|
|
}
|
|
|
|
Detail::ChronometerConcept* impl;
|
|
int repeats;
|
|
};
|
|
} // namespace Benchmark
|
|
} // namespace Catch
|
|
|
|
#endif // CATCH_CHRONOMETER_HPP_INCLUDED
|
|
|
|
#include <type_traits>
|
|
|
|
namespace Catch {
|
|
namespace Benchmark {
|
|
namespace Detail {
|
|
template <typename T, typename U>
|
|
struct is_related
|
|
: std::is_same<std::decay_t<T>, std::decay_t<U>> {};
|
|
|
|
/// 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 Catch::Detail::unique_ptr<callable> clone() const = 0;
|
|
virtual ~callable(); // = default;
|
|
|
|
callable() = default;
|
|
callable(callable const&) = default;
|
|
callable& operator=(callable const&) = default;
|
|
};
|
|
template <typename Fun>
|
|
struct model : public callable {
|
|
model(Fun&& fun_) : fun(CATCH_MOVE(fun_)) {}
|
|
model(Fun const& fun_) : fun(fun_) {}
|
|
|
|
Catch::Detail::unique_ptr<callable> clone() const override {
|
|
return Catch::Detail::make_unique<model<Fun>>( *this );
|
|
}
|
|
|
|
void call(Chronometer meter) const override {
|
|
call(meter, is_callable<Fun(Chronometer)>());
|
|
}
|
|
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 <typename T>
|
|
BenchmarkFunction(model<T>* c) : f(c) {}
|
|
|
|
public:
|
|
BenchmarkFunction()
|
|
: f(new model<do_nothing>{ {} }) {}
|
|
|
|
template <typename Fun,
|
|
std::enable_if_t<!is_related<Fun, BenchmarkFunction>::value, int> = 0>
|
|
BenchmarkFunction(Fun&& fun)
|
|
: f(new model<std::decay_t<Fun>>(CATCH_FORWARD(fun))) {}
|
|
|
|
BenchmarkFunction( BenchmarkFunction&& that ) noexcept:
|
|
f( CATCH_MOVE( that.f ) ) {}
|
|
|
|
BenchmarkFunction(BenchmarkFunction const& that)
|
|
: f(that.f->clone()) {}
|
|
|
|
BenchmarkFunction&
|
|
operator=( BenchmarkFunction&& that ) noexcept {
|
|
f = CATCH_MOVE( that.f );
|
|
return *this;
|
|
}
|
|
|
|
BenchmarkFunction& operator=(BenchmarkFunction const& that) {
|
|
f = that.f->clone();
|
|
return *this;
|
|
}
|
|
|
|
void operator()(Chronometer meter) const { f->call(meter); }
|
|
|
|
private:
|
|
Catch::Detail::unique_ptr<callable> f;
|
|
};
|
|
} // namespace Detail
|
|
} // namespace Benchmark
|
|
} // namespace Catch
|
|
|
|
#endif // CATCH_BENCHMARK_FUNCTION_HPP_INCLUDED
|
|
|
|
|
|
// Adapted from donated nonius code.
|
|
|
|
#ifndef CATCH_REPEAT_HPP_INCLUDED
|
|
#define CATCH_REPEAT_HPP_INCLUDED
|
|
|
|
#include <type_traits>
|
|
|
|
namespace Catch {
|
|
namespace Benchmark {
|
|
namespace Detail {
|
|
template <typename Fun>
|
|
struct repeater {
|
|
void operator()(int k) const {
|
|
for (int i = 0; i < k; ++i) {
|
|
fun();
|
|
}
|
|
}
|
|
Fun fun;
|
|
};
|
|
template <typename Fun>
|
|
repeater<std::decay_t<Fun>> repeat(Fun&& fun) {
|
|
return { CATCH_FORWARD(fun) };
|
|
}
|
|
} // namespace Detail
|
|
} // namespace Benchmark
|
|
} // namespace Catch
|
|
|
|
#endif // CATCH_REPEAT_HPP_INCLUDED
|
|
|
|
|
|
// Adapted from donated nonius code.
|
|
|
|
#ifndef CATCH_RUN_FOR_AT_LEAST_HPP_INCLUDED
|
|
#define CATCH_RUN_FOR_AT_LEAST_HPP_INCLUDED
|
|
|
|
|
|
|
|
// Adapted from donated nonius code.
|
|
|
|
#ifndef CATCH_MEASURE_HPP_INCLUDED
|
|
#define CATCH_MEASURE_HPP_INCLUDED
|
|
|
|
|
|
|
|
// Adapted from donated nonius code.
|
|
|
|
#ifndef CATCH_COMPLETE_INVOKE_HPP_INCLUDED
|
|
#define CATCH_COMPLETE_INVOKE_HPP_INCLUDED
|
|
|
|
|
|
namespace Catch {
|
|
namespace Benchmark {
|
|
namespace Detail {
|
|
template <typename T>
|
|
struct CompleteType { using type = T; };
|
|
template <>
|
|
struct CompleteType<void> { struct type {}; };
|
|
|
|
template <typename T>
|
|
using CompleteType_t = typename CompleteType<T>::type;
|
|
|
|
template <typename Result>
|
|
struct CompleteInvoker {
|
|
template <typename Fun, typename... Args>
|
|
static Result invoke(Fun&& fun, Args&&... args) {
|
|
return CATCH_FORWARD(fun)(CATCH_FORWARD(args)...);
|
|
}
|
|
};
|
|
template <>
|
|
struct CompleteInvoker<void> {
|
|
template <typename Fun, typename... Args>
|
|
static CompleteType_t<void> invoke(Fun&& fun, Args&&... args) {
|
|
CATCH_FORWARD(fun)(CATCH_FORWARD(args)...);
|
|
return {};
|
|
}
|
|
};
|
|
|
|
// invoke and not return void :(
|
|
template <typename Fun, typename... Args>
|
|
CompleteType_t<FunctionReturnType<Fun, Args...>> complete_invoke(Fun&& fun, Args&&... args) {
|
|
return CompleteInvoker<FunctionReturnType<Fun, Args...>>::invoke(CATCH_FORWARD(fun), CATCH_FORWARD(args)...);
|
|
}
|
|
|
|
} // namespace Detail
|
|
|
|
template <typename Fun>
|
|
Detail::CompleteType_t<FunctionReturnType<Fun>> user_code(Fun&& fun) {
|
|
return Detail::complete_invoke(CATCH_FORWARD(fun));
|
|
}
|
|
} // namespace Benchmark
|
|
} // namespace Catch
|
|
|
|
#endif // CATCH_COMPLETE_INVOKE_HPP_INCLUDED
|
|
|
|
|
|
// Adapted from donated nonius code.
|
|
|
|
#ifndef CATCH_TIMING_HPP_INCLUDED
|
|
#define CATCH_TIMING_HPP_INCLUDED
|
|
|
|
|
|
#include <type_traits>
|
|
|
|
namespace Catch {
|
|
namespace Benchmark {
|
|
template <typename Result>
|
|
struct Timing {
|
|
IDuration elapsed;
|
|
Result result;
|
|
int iterations;
|
|
};
|
|
template <typename Func, typename... Args>
|
|
using TimingOf = Timing<Detail::CompleteType_t<FunctionReturnType<Func, Args...>>>;
|
|
} // namespace Benchmark
|
|
} // namespace Catch
|
|
|
|
#endif // CATCH_TIMING_HPP_INCLUDED
|
|
|
|
namespace Catch {
|
|
namespace Benchmark {
|
|
namespace Detail {
|
|
template <typename Clock, typename Fun, typename... Args>
|
|
TimingOf<Fun, Args...> measure(Fun&& fun, Args&&... args) {
|
|
auto start = Clock::now();
|
|
auto&& r = Detail::complete_invoke(fun, CATCH_FORWARD(args)...);
|
|
auto end = Clock::now();
|
|
auto delta = end - start;
|
|
return { delta, CATCH_FORWARD(r), 1 };
|
|
}
|
|
} // namespace Detail
|
|
} // namespace Benchmark
|
|
} // namespace Catch
|
|
|
|
#endif // CATCH_MEASURE_HPP_INCLUDED
|
|
|
|
#include <type_traits>
|
|
|
|
namespace Catch {
|
|
namespace Benchmark {
|
|
namespace Detail {
|
|
template <typename Clock, typename Fun>
|
|
TimingOf<Fun, int> measure_one(Fun&& fun, int iters, std::false_type) {
|
|
return Detail::measure<Clock>(fun, iters);
|
|
}
|
|
template <typename Clock, typename Fun>
|
|
TimingOf<Fun, Chronometer> measure_one(Fun&& fun, int iters, std::true_type) {
|
|
Detail::ChronometerModel<Clock> meter;
|
|
auto&& result = Detail::complete_invoke(fun, Chronometer(meter, iters));
|
|
|
|
return { meter.elapsed(), CATCH_MOVE(result), iters };
|
|
}
|
|
|
|
template <typename Clock, typename Fun>
|
|
using run_for_at_least_argument_t = std::conditional_t<is_callable<Fun(Chronometer)>::value, Chronometer, int>;
|
|
|
|
|
|
[[noreturn]]
|
|
void throw_optimized_away_error();
|
|
|
|
template <typename Clock, typename Fun>
|
|
TimingOf<Fun, run_for_at_least_argument_t<Clock, Fun>>
|
|
run_for_at_least(IDuration how_long,
|
|
const int initial_iterations,
|
|
Fun&& fun) {
|
|
auto iters = initial_iterations;
|
|
while (iters < (1 << 30)) {
|
|
auto&& Timing = measure_one<Clock>(fun, iters, is_callable<Fun(Chronometer)>());
|
|
|
|
if (Timing.elapsed >= how_long) {
|
|
return { Timing.elapsed, CATCH_MOVE(Timing.result), iters };
|
|
}
|
|
iters *= 2;
|
|
}
|
|
throw_optimized_away_error();
|
|
}
|
|
} // namespace Detail
|
|
} // namespace Benchmark
|
|
} // namespace Catch
|
|
|
|
#endif // CATCH_RUN_FOR_AT_LEAST_HPP_INCLUDED
|
|
|
|
#include <vector>
|
|
|
|
namespace Catch {
|
|
namespace Benchmark {
|
|
struct ExecutionPlan {
|
|
int iterations_per_sample;
|
|
FDuration estimated_duration;
|
|
Detail::BenchmarkFunction benchmark;
|
|
FDuration warmup_time;
|
|
int warmup_iterations;
|
|
|
|
template <typename Clock>
|
|
std::vector<FDuration> run(const IConfig &cfg, Environment env) const {
|
|
// warmup a bit
|
|
Detail::run_for_at_least<Clock>(
|
|
std::chrono::duration_cast<IDuration>( warmup_time ),
|
|
warmup_iterations,
|
|
Detail::repeat( []() { return Clock::now(); } )
|
|
);
|
|
|
|
std::vector<FDuration> times;
|
|
const auto num_samples = cfg.benchmarkSamples();
|
|
times.reserve( num_samples );
|
|
for ( size_t i = 0; i < num_samples; ++i ) {
|
|
Detail::ChronometerModel<Clock> model;
|
|
this->benchmark( Chronometer( model, iterations_per_sample ) );
|
|
auto sample_time = model.elapsed() - env.clock_cost.mean;
|
|
if ( sample_time < FDuration::zero() ) {
|
|
sample_time = FDuration::zero();
|
|
}
|
|
times.push_back(sample_time / iterations_per_sample);
|
|
}
|
|
return times;
|
|
}
|
|
};
|
|
} // namespace Benchmark
|
|
} // namespace Catch
|
|
|
|
#endif // CATCH_EXECUTION_PLAN_HPP_INCLUDED
|
|
|
|
|
|
// Adapted from donated nonius code.
|
|
|
|
#ifndef CATCH_ESTIMATE_CLOCK_HPP_INCLUDED
|
|
#define CATCH_ESTIMATE_CLOCK_HPP_INCLUDED
|
|
|
|
|
|
|
|
// Adapted from donated nonius code.
|
|
|
|
#ifndef CATCH_STATS_HPP_INCLUDED
|
|
#define CATCH_STATS_HPP_INCLUDED
|
|
|
|
|
|
#include <vector>
|
|
|
|
namespace Catch {
|
|
namespace Benchmark {
|
|
namespace Detail {
|
|
using sample = std::vector<double>;
|
|
|
|
double weighted_average_quantile( int k,
|
|
int q,
|
|
double* first,
|
|
double* last );
|
|
|
|
OutlierClassification
|
|
classify_outliers( double const* first, double const* last );
|
|
|
|
double mean( double const* first, double const* last );
|
|
|
|
double normal_cdf( double x );
|
|
|
|
double erfc_inv(double x);
|
|
|
|
double normal_quantile(double p);
|
|
|
|
Estimate<double>
|
|
bootstrap( double confidence_level,
|
|
double* first,
|
|
double* last,
|
|
sample const& resample,
|
|
double ( *estimator )( double const*, double const* ) );
|
|
|
|
struct bootstrap_analysis {
|
|
Estimate<double> mean;
|
|
Estimate<double> standard_deviation;
|
|
double outlier_variance;
|
|
};
|
|
|
|
bootstrap_analysis analyse_samples(double confidence_level,
|
|
unsigned int n_resamples,
|
|
double* first,
|
|
double* last);
|
|
} // namespace Detail
|
|
} // namespace Benchmark
|
|
} // namespace Catch
|
|
|
|
#endif // CATCH_STATS_HPP_INCLUDED
|
|
|
|
#include <algorithm>
|
|
#include <vector>
|
|
#include <cmath>
|
|
|
|
namespace Catch {
|
|
namespace Benchmark {
|
|
namespace Detail {
|
|
template <typename Clock>
|
|
std::vector<double> resolution(int k) {
|
|
std::vector<TimePoint<Clock>> times;
|
|
times.reserve(static_cast<size_t>(k + 1));
|
|
for ( int i = 0; i < k + 1; ++i ) {
|
|
times.push_back( Clock::now() );
|
|
}
|
|
|
|
std::vector<double> deltas;
|
|
deltas.reserve(static_cast<size_t>(k));
|
|
for ( size_t idx = 1; idx < times.size(); ++idx ) {
|
|
deltas.push_back( static_cast<double>(
|
|
( times[idx] - times[idx - 1] ).count() ) );
|
|
}
|
|
|
|
return deltas;
|
|
}
|
|
|
|
constexpr auto warmup_iterations = 10000;
|
|
constexpr auto warmup_time = std::chrono::milliseconds(100);
|
|
constexpr auto minimum_ticks = 1000;
|
|
constexpr auto warmup_seed = 10000;
|
|
constexpr auto clock_resolution_estimation_time = std::chrono::milliseconds(500);
|
|
constexpr auto clock_cost_estimation_time_limit = std::chrono::seconds(1);
|
|
constexpr auto clock_cost_estimation_tick_limit = 100000;
|
|
constexpr auto clock_cost_estimation_time = std::chrono::milliseconds(10);
|
|
constexpr auto clock_cost_estimation_iterations = 10000;
|
|
|
|
template <typename Clock>
|
|
int warmup() {
|
|
return run_for_at_least<Clock>(std::chrono::duration_cast<IDuration>(warmup_time), warmup_seed, &resolution<Clock>)
|
|
.iterations;
|
|
}
|
|
template <typename Clock>
|
|
EnvironmentEstimate estimate_clock_resolution(int iterations) {
|
|
auto r = run_for_at_least<Clock>(std::chrono::duration_cast<IDuration>(clock_resolution_estimation_time), iterations, &resolution<Clock>)
|
|
.result;
|
|
return {
|
|
FDuration(mean(r.data(), r.data() + r.size())),
|
|
classify_outliers(r.data(), r.data() + r.size()),
|
|
};
|
|
}
|
|
template <typename Clock>
|
|
EnvironmentEstimate estimate_clock_cost(FDuration resolution) {
|
|
auto time_limit = (std::min)(
|
|
resolution * clock_cost_estimation_tick_limit,
|
|
FDuration(clock_cost_estimation_time_limit));
|
|
auto time_clock = [](int k) {
|
|
return Detail::measure<Clock>([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<Clock>(std::chrono::duration_cast<IDuration>(clock_cost_estimation_time), iters, time_clock);
|
|
std::vector<double> times;
|
|
int nsamples = static_cast<int>(std::ceil(time_limit / r.elapsed));
|
|
times.reserve(static_cast<size_t>(nsamples));
|
|
for ( int s = 0; s < nsamples; ++s ) {
|
|
times.push_back( static_cast<double>(
|
|
( time_clock( r.iterations ) / r.iterations )
|
|
.count() ) );
|
|
}
|
|
return {
|
|
FDuration(mean(times.data(), times.data() + times.size())),
|
|
classify_outliers(times.data(), times.data() + times.size()),
|
|
};
|
|
}
|
|
|
|
template <typename Clock>
|
|
Environment measure_environment() {
|
|
#if defined(__clang__)
|
|
# pragma clang diagnostic push
|
|
# pragma clang diagnostic ignored "-Wexit-time-destructors"
|
|
#endif
|
|
static Catch::Detail::unique_ptr<Environment> env;
|
|
#if defined(__clang__)
|
|
# pragma clang diagnostic pop
|
|
#endif
|
|
if (env) {
|
|
return *env;
|
|
}
|
|
|
|
auto iters = Detail::warmup<Clock>();
|
|
auto resolution = Detail::estimate_clock_resolution<Clock>(iters);
|
|
auto cost = Detail::estimate_clock_cost<Clock>(resolution.mean);
|
|
|
|
env = Catch::Detail::make_unique<Environment>( Environment{resolution, cost} );
|
|
return *env;
|
|
}
|
|
} // namespace Detail
|
|
} // namespace Benchmark
|
|
} // namespace Catch
|
|
|
|
#endif // CATCH_ESTIMATE_CLOCK_HPP_INCLUDED
|
|
|
|
|
|
// Adapted from donated nonius code.
|
|
|
|
#ifndef CATCH_ANALYSE_HPP_INCLUDED
|
|
#define CATCH_ANALYSE_HPP_INCLUDED
|
|
|
|
|
|
|
|
// Adapted from donated nonius code.
|
|
|
|
#ifndef CATCH_SAMPLE_ANALYSIS_HPP_INCLUDED
|
|
#define CATCH_SAMPLE_ANALYSIS_HPP_INCLUDED
|
|
|
|
|
|
#include <vector>
|
|
|
|
namespace Catch {
|
|
namespace Benchmark {
|
|
struct SampleAnalysis {
|
|
std::vector<FDuration> samples;
|
|
Estimate<FDuration> mean;
|
|
Estimate<FDuration> standard_deviation;
|
|
OutlierClassification outliers;
|
|
double outlier_variance;
|
|
};
|
|
} // namespace Benchmark
|
|
} // namespace Catch
|
|
|
|
#endif // CATCH_SAMPLE_ANALYSIS_HPP_INCLUDED
|
|
|
|
|
|
namespace Catch {
|
|
class IConfig;
|
|
|
|
namespace Benchmark {
|
|
namespace Detail {
|
|
SampleAnalysis analyse(const IConfig &cfg, FDuration* first, FDuration* last);
|
|
} // namespace Detail
|
|
} // namespace Benchmark
|
|
} // namespace Catch
|
|
|
|
#endif // CATCH_ANALYSE_HPP_INCLUDED
|
|
|
|
#include <algorithm>
|
|
#include <chrono>
|
|
#include <exception>
|
|
#include <string>
|
|
#include <cmath>
|
|
|
|
namespace Catch {
|
|
namespace Benchmark {
|
|
struct Benchmark {
|
|
Benchmark(std::string&& benchmarkName)
|
|
: name(CATCH_MOVE(benchmarkName)) {}
|
|
|
|
template <class FUN>
|
|
Benchmark(std::string&& benchmarkName , FUN &&func)
|
|
: fun(CATCH_MOVE(func)), name(CATCH_MOVE(benchmarkName)) {}
|
|
|
|
template <typename Clock>
|
|
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<decltype(min_time)>(cfg.benchmarkWarmupTime()));
|
|
auto&& test = Detail::run_for_at_least<Clock>(std::chrono::duration_cast<IDuration>(run_time), 1, fun);
|
|
int new_iters = static_cast<int>(std::ceil(min_time * test.iterations / test.elapsed));
|
|
return { new_iters, test.elapsed / test.iterations * new_iters * cfg.benchmarkSamples(), fun, std::chrono::duration_cast<FDuration>(cfg.benchmarkWarmupTime()), Detail::warmup_iterations };
|
|
}
|
|
|
|
template <typename Clock = default_clock>
|
|
void run() {
|
|
static_assert( Clock::is_steady,
|
|
"Benchmarking clock should be steady" );
|
|
auto const* cfg = getCurrentContext().getConfig();
|
|
|
|
auto env = Detail::measure_environment<Clock>();
|
|
|
|
getResultCapture().benchmarkPreparing(name);
|
|
CATCH_TRY{
|
|
auto plan = user_code([&] {
|
|
return prepare<Clock>(*cfg, env);
|
|
});
|
|
|
|
BenchmarkInfo info {
|
|
CATCH_MOVE(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<Clock>(*cfg, env);
|
|
});
|
|
|
|
auto analysis = Detail::analyse(*cfg, samples.data(), samples.data() + samples.size());
|
|
BenchmarkStats<> stats{ CATCH_MOVE(info), CATCH_MOVE(analysis.samples), analysis.mean, analysis.standard_deviation, analysis.outliers, analysis.outlier_variance };
|
|
getResultCapture().benchmarkEnded(stats);
|
|
} CATCH_CATCH_ANON (TestFailureException const&) {
|
|
getResultCapture().benchmarkFailed("Benchmark failed due to failed assertion"_sr);
|
|
} CATCH_CATCH_ALL{
|
|
getResultCapture().benchmarkFailed(translateActiveException());
|
|
// We let the exception go further up so that the
|
|
// test case is marked as failed.
|
|
std::rethrow_exception(std::current_exception());
|
|
}
|
|
}
|
|
|
|
// sets lambda to be used in fun *and* executes benchmark!
|
|
template <typename Fun, std::enable_if_t<!Detail::is_related<Fun, Benchmark>::value, int> = 0>
|
|
Benchmark & operator=(Fun func) {
|
|
auto const* cfg = getCurrentContext().getConfig();
|
|
if (!cfg->skipBenchmarks()) {
|
|
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 = [&]
|
|
|
|
#if defined(CATCH_CONFIG_PREFIX_ALL)
|
|
|
|
#define CATCH_BENCHMARK(...) \
|
|
INTERNAL_CATCH_BENCHMARK(INTERNAL_CATCH_UNIQUE_NAME(CATCH2_INTERNAL_BENCHMARK_), 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(CATCH2_INTERNAL_BENCHMARK_), name)
|
|
|
|
#else
|
|
|
|
#define BENCHMARK(...) \
|
|
INTERNAL_CATCH_BENCHMARK(INTERNAL_CATCH_UNIQUE_NAME(CATCH2_INTERNAL_BENCHMARK_), 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(CATCH2_INTERNAL_BENCHMARK_), name)
|
|
|
|
#endif
|
|
|
|
#endif // CATCH_BENCHMARK_HPP_INCLUDED
|
|
|
|
|
|
// Adapted from donated nonius code.
|
|
|
|
#ifndef CATCH_CONSTRUCTOR_HPP_INCLUDED
|
|
#define CATCH_CONSTRUCTOR_HPP_INCLUDED
|
|
|
|
|
|
#include <type_traits>
|
|
|
|
namespace Catch {
|
|
namespace Benchmark {
|
|
namespace Detail {
|
|
template <typename T, bool Destruct>
|
|
struct ObjectStorage
|
|
{
|
|
ObjectStorage() = default;
|
|
|
|
ObjectStorage(const ObjectStorage& other)
|
|
{
|
|
new(&data) T(other.stored_object());
|
|
}
|
|
|
|
ObjectStorage(ObjectStorage&& other)
|
|
{
|
|
new(data) T(CATCH_MOVE(other.stored_object()));
|
|
}
|
|
|
|
~ObjectStorage() { destruct_on_exit<T>(); }
|
|
|
|
template <typename... Args>
|
|
void construct(Args&&... args)
|
|
{
|
|
new (data) T(CATCH_FORWARD(args)...);
|
|
}
|
|
|
|
template <bool AllowManualDestruction = !Destruct>
|
|
std::enable_if_t<AllowManualDestruction> destruct()
|
|
{
|
|
stored_object().~T();
|
|
}
|
|
|
|
private:
|
|
// If this is a constructor benchmark, destruct the underlying object
|
|
template <typename U>
|
|
void destruct_on_exit(std::enable_if_t<Destruct, U>* = nullptr) { destruct<true>(); }
|
|
// Otherwise, don't
|
|
template <typename U>
|
|
void destruct_on_exit(std::enable_if_t<!Destruct, U>* = nullptr) { }
|
|
|
|
#if defined( __GNUC__ ) && __GNUC__ <= 6
|
|
# pragma GCC diagnostic push
|
|
# pragma GCC diagnostic ignored "-Wstrict-aliasing"
|
|
#endif
|
|
T& stored_object() { return *reinterpret_cast<T*>( data ); }
|
|
|
|
T const& stored_object() const {
|
|
return *reinterpret_cast<T const*>( data );
|
|
}
|
|
#if defined( __GNUC__ ) && __GNUC__ <= 6
|
|
# pragma GCC diagnostic pop
|
|
#endif
|
|
|
|
alignas( T ) unsigned char data[sizeof( T )]{};
|
|
};
|
|
} // namespace Detail
|
|
|
|
template <typename T>
|
|
using storage_for = Detail::ObjectStorage<T, true>;
|
|
|
|
template <typename T>
|
|
using destructable_object = Detail::ObjectStorage<T, false>;
|
|
} // namespace Benchmark
|
|
} // namespace Catch
|
|
|
|
#endif // CATCH_CONSTRUCTOR_HPP_INCLUDED
|
|
|
|
#endif // CATCH_BENCHMARK_ALL_HPP_INCLUDED
|
|
|
|
|
|
#ifndef CATCH_APPROX_HPP_INCLUDED
|
|
#define CATCH_APPROX_HPP_INCLUDED
|
|
|
|
|
|
|
|
#ifndef CATCH_TOSTRING_HPP_INCLUDED
|
|
#define CATCH_TOSTRING_HPP_INCLUDED
|
|
|
|
|
|
#include <vector>
|
|
#include <cstddef>
|
|
#include <type_traits>
|
|
#include <string>
|
|
|
|
|
|
|
|
|
|
/** \file
|
|
* Wrapper for the WCHAR configuration option
|
|
*
|
|
* We want to support platforms that do not provide `wchar_t`, so we
|
|
* sometimes have to disable providing wchar_t overloads through Catch2,
|
|
* e.g. the StringMaker specialization for `std::wstring`.
|
|
*/
|
|
|
|
#ifndef CATCH_CONFIG_WCHAR_HPP_INCLUDED
|
|
#define CATCH_CONFIG_WCHAR_HPP_INCLUDED
|
|
|
|
|
|
// We assume that WCHAR should be enabled by default, and only disabled
|
|
// for a shortlist (so far only DJGPP) of compilers.
|
|
|
|
#if defined(__DJGPP__)
|
|
# define CATCH_INTERNAL_CONFIG_NO_WCHAR
|
|
#endif // __DJGPP__
|
|
|
|
#if !defined( CATCH_INTERNAL_CONFIG_NO_WCHAR ) && \
|
|
!defined( CATCH_CONFIG_NO_WCHAR ) && \
|
|
!defined( CATCH_CONFIG_WCHAR )
|
|
# define CATCH_CONFIG_WCHAR
|
|
#endif
|
|
|
|
#endif // CATCH_CONFIG_WCHAR_HPP_INCLUDED
|
|
|
|
|
|
#ifndef CATCH_REUSABLE_STRING_STREAM_HPP_INCLUDED
|
|
#define CATCH_REUSABLE_STRING_STREAM_HPP_INCLUDED
|
|
|
|
|
|
#include <iosfwd>
|
|
#include <cstddef>
|
|
#include <ostream>
|
|
#include <string>
|
|
|
|
namespace Catch {
|
|
|
|
class ReusableStringStream : Detail::NonCopyable {
|
|
std::size_t m_index;
|
|
std::ostream* m_oss;
|
|
public:
|
|
ReusableStringStream();
|
|
~ReusableStringStream();
|
|
|
|
//! Returns the serialized state
|
|
std::string str() const;
|
|
//! Sets internal state to `str`
|
|
void str(std::string const& str);
|
|
|
|
#if defined(__GNUC__) && !defined(__clang__)
|
|
#pragma GCC diagnostic push
|
|
// Old versions of GCC do not understand -Wnonnull-compare
|
|
#pragma GCC diagnostic ignored "-Wpragmas"
|
|
// Streaming a function pointer triggers Waddress and Wnonnull-compare
|
|
// on GCC, because it implicitly converts it to bool and then decides
|
|
// that the check it uses (a? true : false) is tautological and cannot
|
|
// be null...
|
|
#pragma GCC diagnostic ignored "-Waddress"
|
|
#pragma GCC diagnostic ignored "-Wnonnull-compare"
|
|
#endif
|
|
|
|
template<typename T>
|
|
auto operator << ( T const& value ) -> ReusableStringStream& {
|
|
*m_oss << value;
|
|
return *this;
|
|
}
|
|
|
|
#if defined(__GNUC__) && !defined(__clang__)
|
|
#pragma GCC diagnostic pop
|
|
#endif
|
|
auto get() -> std::ostream& { return *m_oss; }
|
|
};
|
|
}
|
|
|
|
#endif // CATCH_REUSABLE_STRING_STREAM_HPP_INCLUDED
|
|
|
|
|
|
#ifndef CATCH_VOID_TYPE_HPP_INCLUDED
|
|
#define CATCH_VOID_TYPE_HPP_INCLUDED
|
|
|
|
|
|
namespace Catch {
|
|
namespace Detail {
|
|
|
|
template <typename...>
|
|
struct make_void { using type = void; };
|
|
|
|
template <typename... Ts>
|
|
using void_t = typename make_void<Ts...>::type;
|
|
|
|
} // namespace Detail
|
|
} // namespace Catch
|
|
|
|
|
|
#endif // CATCH_VOID_TYPE_HPP_INCLUDED
|
|
|
|
|
|
#ifndef CATCH_INTERFACES_ENUM_VALUES_REGISTRY_HPP_INCLUDED
|
|
#define CATCH_INTERFACES_ENUM_VALUES_REGISTRY_HPP_INCLUDED
|
|
|
|
|
|
#include <vector>
|
|
|
|
namespace Catch {
|
|
|
|
namespace Detail {
|
|
struct EnumInfo {
|
|
StringRef m_name;
|
|
std::vector<std::pair<int, StringRef>> m_values;
|
|
|
|
~EnumInfo();
|
|
|
|
StringRef lookup( int value ) const;
|
|
};
|
|
} // namespace Detail
|
|
|
|
class IMutableEnumValuesRegistry {
|
|
public:
|
|
virtual ~IMutableEnumValuesRegistry(); // = default;
|
|
|
|
virtual Detail::EnumInfo const& registerEnum( StringRef enumName, StringRef allEnums, std::vector<int> const& values ) = 0;
|
|
|
|
template<typename E>
|
|
Detail::EnumInfo const& registerEnum( StringRef enumName, StringRef allEnums, std::initializer_list<E> values ) {
|
|
static_assert(sizeof(int) >= sizeof(E), "Cannot serialize enum to int");
|
|
std::vector<int> intValues;
|
|
intValues.reserve( values.size() );
|
|
for( auto enumValue : values )
|
|
intValues.push_back( static_cast<int>( enumValue ) );
|
|
return registerEnum( enumName, allEnums, intValues );
|
|
}
|
|
};
|
|
|
|
} // Catch
|
|
|
|
#endif // CATCH_INTERFACES_ENUM_VALUES_REGISTRY_HPP_INCLUDED
|
|
|
|
#ifdef CATCH_CONFIG_CPP17_STRING_VIEW
|
|
#include <string_view>
|
|
#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
|
|
|
|
// 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 {
|
|
// Bring in global namespace operator<< for ADL lookup in
|
|
// `IsStreamInsertable` below.
|
|
using ::operator<<;
|
|
|
|
namespace Detail {
|
|
|
|
inline std::size_t catch_strnlen(const char *str, std::size_t n) {
|
|
auto ret = std::char_traits<char>::find(str, n, '\0');
|
|
if (ret != nullptr) {
|
|
return static_cast<std::size_t>(ret - str);
|
|
}
|
|
return n;
|
|
}
|
|
|
|
constexpr StringRef unprintableString = "{?}"_sr;
|
|
|
|
//! Encases `string in quotes, and optionally escapes invisibles
|
|
std::string convertIntoString( StringRef string, bool escapeInvisibles );
|
|
|
|
//! Encases `string` in quotes, and escapes invisibles if user requested
|
|
//! it via CLI
|
|
std::string convertIntoString( StringRef string );
|
|
|
|
std::string rawMemoryToString( const void *object, std::size_t size );
|
|
|
|
template<typename T>
|
|
std::string rawMemoryToString( const T& object ) {
|
|
return rawMemoryToString( &object, sizeof(object) );
|
|
}
|
|
|
|
template<typename T>
|
|
class IsStreamInsertable {
|
|
template<typename Stream, typename U>
|
|
static auto test(int)
|
|
-> decltype(std::declval<Stream&>() << std::declval<U>(), std::true_type());
|
|
|
|
template<typename, typename>
|
|
static auto test(...)->std::false_type;
|
|
|
|
public:
|
|
static const bool value = decltype(test<std::ostream, const T&>(0))::value;
|
|
};
|
|
|
|
template<typename E>
|
|
std::string convertUnknownEnumToString( E e );
|
|
|
|
template<typename T>
|
|
std::enable_if_t<
|
|
!std::is_enum<T>::value && !std::is_base_of<std::exception, T>::value,
|
|
std::string> convertUnstreamable( T const& ) {
|
|
return std::string(Detail::unprintableString);
|
|
}
|
|
template<typename T>
|
|
std::enable_if_t<
|
|
!std::is_enum<T>::value && std::is_base_of<std::exception, T>::value,
|
|
std::string> convertUnstreamable(T const& ex) {
|
|
return ex.what();
|
|
}
|
|
|
|
|
|
template<typename T>
|
|
std::enable_if_t<
|
|
std::is_enum<T>::value,
|
|
std::string> convertUnstreamable( T const& value ) {
|
|
return convertUnknownEnumToString( value );
|
|
}
|
|
|
|
#if defined(_MANAGED)
|
|
//! Convert a CLR string to a utf8 std::string
|
|
template<typename T>
|
|
std::string clrReferenceToString( T^ ref ) {
|
|
if (ref == nullptr)
|
|
return std::string("null");
|
|
auto bytes = System::Text::Encoding::UTF8->GetBytes(ref->ToString());
|
|
cli::pin_ptr<System::Byte> p = &bytes[0];
|
|
return std::string(reinterpret_cast<char const *>(p), bytes->Length);
|
|
}
|
|
#endif
|
|
|
|
} // namespace Detail
|
|
|
|
|
|
template <typename T, typename = void>
|
|
struct StringMaker {
|
|
template <typename Fake = T>
|
|
static
|
|
std::enable_if_t<::Catch::Detail::IsStreamInsertable<Fake>::value, std::string>
|
|
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 <typename Fake = T>
|
|
static
|
|
std::enable_if_t<!::Catch::Detail::IsStreamInsertable<Fake>::value, std::string>
|
|
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 <typename T>
|
|
std::string stringify(const T& e) {
|
|
return ::Catch::StringMaker<std::remove_cv_t<std::remove_reference_t<T>>>::convert(e);
|
|
}
|
|
|
|
template<typename E>
|
|
std::string convertUnknownEnumToString( E e ) {
|
|
return ::Catch::Detail::stringify(static_cast<std::underlying_type_t<E>>(e));
|
|
}
|
|
|
|
#if defined(_MANAGED)
|
|
template <typename T>
|
|
std::string stringify( T^ e ) {
|
|
return ::Catch::StringMaker<T^>::convert(e);
|
|
}
|
|
#endif
|
|
|
|
} // namespace Detail
|
|
|
|
// Some predefined specializations
|
|
|
|
template<>
|
|
struct StringMaker<std::string> {
|
|
static std::string convert(const std::string& str);
|
|
};
|
|
|
|
#ifdef CATCH_CONFIG_CPP17_STRING_VIEW
|
|
template<>
|
|
struct StringMaker<std::string_view> {
|
|
static std::string convert(std::string_view str);
|
|
};
|
|
#endif
|
|
|
|
template<>
|
|
struct StringMaker<char const *> {
|
|
static std::string convert(char const * str);
|
|
};
|
|
template<>
|
|
struct StringMaker<char *> {
|
|
static std::string convert(char * str);
|
|
};
|
|
|
|
#if defined(CATCH_CONFIG_WCHAR)
|
|
template<>
|
|
struct StringMaker<std::wstring> {
|
|
static std::string convert(const std::wstring& wstr);
|
|
};
|
|
|
|
# ifdef CATCH_CONFIG_CPP17_STRING_VIEW
|
|
template<>
|
|
struct StringMaker<std::wstring_view> {
|
|
static std::string convert(std::wstring_view str);
|
|
};
|
|
# endif
|
|
|
|
template<>
|
|
struct StringMaker<wchar_t const *> {
|
|
static std::string convert(wchar_t const * str);
|
|
};
|
|
template<>
|
|
struct StringMaker<wchar_t *> {
|
|
static std::string convert(wchar_t * str);
|
|
};
|
|
#endif // CATCH_CONFIG_WCHAR
|
|
|
|
template<size_t SZ>
|
|
struct StringMaker<char[SZ]> {
|
|
static std::string convert(char const* str) {
|
|
return Detail::convertIntoString(
|
|
StringRef( str, Detail::catch_strnlen( str, SZ ) ) );
|
|
}
|
|
};
|
|
template<size_t SZ>
|
|
struct StringMaker<signed char[SZ]> {
|
|
static std::string convert(signed char const* str) {
|
|
auto reinterpreted = reinterpret_cast<char const*>(str);
|
|
return Detail::convertIntoString(
|
|
StringRef(reinterpreted, Detail::catch_strnlen(reinterpreted, SZ)));
|
|
}
|
|
};
|
|
template<size_t SZ>
|
|
struct StringMaker<unsigned char[SZ]> {
|
|
static std::string convert(unsigned char const* str) {
|
|
auto reinterpreted = reinterpret_cast<char const*>(str);
|
|
return Detail::convertIntoString(
|
|
StringRef(reinterpreted, Detail::catch_strnlen(reinterpreted, SZ)));
|
|
}
|
|
};
|
|
|
|
#if defined(CATCH_CONFIG_CPP17_BYTE)
|
|
template<>
|
|
struct StringMaker<std::byte> {
|
|
static std::string convert(std::byte value);
|
|
};
|
|
#endif // defined(CATCH_CONFIG_CPP17_BYTE)
|
|
template<>
|
|
struct StringMaker<int> {
|
|
static std::string convert(int value);
|
|
};
|
|
template<>
|
|
struct StringMaker<long> {
|
|
static std::string convert(long value);
|
|
};
|
|
template<>
|
|
struct StringMaker<long long> {
|
|
static std::string convert(long long value);
|
|
};
|
|
template<>
|
|
struct StringMaker<unsigned int> {
|
|
static std::string convert(unsigned int value);
|
|
};
|
|
template<>
|
|
struct StringMaker<unsigned long> {
|
|
static std::string convert(unsigned long value);
|
|
};
|
|
template<>
|
|
struct StringMaker<unsigned long long> {
|
|
static std::string convert(unsigned long long value);
|
|
};
|
|
|
|
template<>
|
|
struct StringMaker<bool> {
|
|
static std::string convert(bool b) {
|
|
using namespace std::string_literals;
|
|
return b ? "true"s : "false"s;
|
|
}
|
|
};
|
|
|
|
template<>
|
|
struct StringMaker<char> {
|
|
static std::string convert(char c);
|
|
};
|
|
template<>
|
|
struct StringMaker<signed char> {
|
|
static std::string convert(signed char c);
|
|
};
|
|
template<>
|
|
struct StringMaker<unsigned char> {
|
|
static std::string convert(unsigned char c);
|
|
};
|
|
|
|
template<>
|
|
struct StringMaker<std::nullptr_t> {
|
|
static std::string convert(std::nullptr_t) {
|
|
using namespace std::string_literals;
|
|
return "nullptr"s;
|
|
}
|
|
};
|
|
|
|
template<>
|
|
struct StringMaker<float> {
|
|
static std::string convert(float value);
|
|
CATCH_EXPORT static int precision;
|
|
};
|
|
|
|
template<>
|
|
struct StringMaker<double> {
|
|
static std::string convert(double value);
|
|
CATCH_EXPORT static int precision;
|
|
};
|
|
|
|
template <typename T>
|
|
struct StringMaker<T*> {
|
|
template <typename U>
|
|
static std::string convert(U* p) {
|
|
if (p) {
|
|
return ::Catch::Detail::rawMemoryToString(p);
|
|
} else {
|
|
return "nullptr";
|
|
}
|
|
}
|
|
};
|
|
|
|
template <typename R, typename C>
|
|
struct StringMaker<R C::*> {
|
|
static std::string convert(R C::* p) {
|
|
if (p) {
|
|
return ::Catch::Detail::rawMemoryToString(p);
|
|
} else {
|
|
return "nullptr";
|
|
}
|
|
}
|
|
};
|
|
|
|
#if defined(_MANAGED)
|
|
template <typename T>
|
|
struct StringMaker<T^> {
|
|
static std::string convert( T^ ref ) {
|
|
return ::Catch::Detail::clrReferenceToString(ref);
|
|
}
|
|
};
|
|
#endif
|
|
|
|
namespace Detail {
|
|
template<typename InputIterator, typename Sentinel = InputIterator>
|
|
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();
|
|
}
|
|
}
|
|
|
|
} // namespace Catch
|
|
|
|
//////////////////////////////////////////////////////
|
|
// Separate std-lib types stringification, so it can be selectively enabled
|
|
// This means that we do not bring in their headers
|
|
|
|
#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_OPTIONAL_STRINGMAKER
|
|
#endif
|
|
|
|
// Separate std::pair specialization
|
|
#if defined(CATCH_CONFIG_ENABLE_PAIR_STRINGMAKER)
|
|
#include <utility>
|
|
namespace Catch {
|
|
template<typename T1, typename T2>
|
|
struct StringMaker<std::pair<T1, T2> > {
|
|
static std::string convert(const std::pair<T1, T2>& 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 <optional>
|
|
namespace Catch {
|
|
template<typename T>
|
|
struct StringMaker<std::optional<T> > {
|
|
static std::string convert(const std::optional<T>& optional) {
|
|
if (optional.has_value()) {
|
|
return ::Catch::Detail::stringify(*optional);
|
|
} else {
|
|
return "{ }";
|
|
}
|
|
}
|
|
};
|
|
template <>
|
|
struct StringMaker<std::nullopt_t> {
|
|
static std::string convert(const std::nullopt_t&) {
|
|
return "{ }";
|
|
}
|
|
};
|
|
}
|
|
#endif // CATCH_CONFIG_ENABLE_OPTIONAL_STRINGMAKER
|
|
|
|
// Separate std::tuple specialization
|
|
#if defined(CATCH_CONFIG_ENABLE_TUPLE_STRINGMAKER)
|
|
#include <tuple>
|
|
namespace Catch {
|
|
namespace Detail {
|
|
template<
|
|
typename Tuple,
|
|
std::size_t N = 0,
|
|
bool = (N < std::tuple_size<Tuple>::value)
|
|
>
|
|
struct TupleElementPrinter {
|
|
static void print(const Tuple& tuple, std::ostream& os) {
|
|
os << (N ? ", " : " ")
|
|
<< ::Catch::Detail::stringify(std::get<N>(tuple));
|
|
TupleElementPrinter<Tuple, N + 1>::print(tuple, os);
|
|
}
|
|
};
|
|
|
|
template<
|
|
typename Tuple,
|
|
std::size_t N
|
|
>
|
|
struct TupleElementPrinter<Tuple, N, false> {
|
|
static void print(const Tuple&, std::ostream&) {}
|
|
};
|
|
|
|
}
|
|
|
|
|
|
template<typename ...Types>
|
|
struct StringMaker<std::tuple<Types...>> {
|
|
static std::string convert(const std::tuple<Types...>& tuple) {
|
|
ReusableStringStream rss;
|
|
rss << '{';
|
|
Detail::TupleElementPrinter<std::tuple<Types...>>::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 <variant>
|
|
namespace Catch {
|
|
template<>
|
|
struct StringMaker<std::monostate> {
|
|
static std::string convert(const std::monostate&) {
|
|
return "{ }";
|
|
}
|
|
};
|
|
|
|
template<typename... Elements>
|
|
struct StringMaker<std::variant<Elements...>> {
|
|
static std::string convert(const std::variant<Elements...>& 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 <typename T, typename = void>
|
|
struct is_range_impl : std::false_type {};
|
|
|
|
template <typename T>
|
|
struct is_range_impl<T, void_t<decltype(begin(std::declval<T>()))>> : std::true_type {};
|
|
} // namespace Detail
|
|
|
|
template <typename T>
|
|
struct is_range : Detail::is_range_impl<T> {};
|
|
|
|
#if defined(_MANAGED) // Managed types are never ranges
|
|
template <typename T>
|
|
struct is_range<T^> {
|
|
static const bool value = false;
|
|
};
|
|
#endif
|
|
|
|
template<typename Range>
|
|
std::string rangeToString( Range const& range ) {
|
|
return ::Catch::Detail::rangeToString( begin( range ), end( range ) );
|
|
}
|
|
|
|
// Handle vector<bool> specially
|
|
template<typename Allocator>
|
|
std::string rangeToString( std::vector<bool, Allocator> 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<typename R>
|
|
struct StringMaker<R, std::enable_if_t<is_range<R>::value && !::Catch::Detail::IsStreamInsertable<R>::value>> {
|
|
static std::string convert( R const& range ) {
|
|
return rangeToString( range );
|
|
}
|
|
};
|
|
|
|
template <typename T, size_t SZ>
|
|
struct StringMaker<T[SZ]> {
|
|
static std::string convert(T const(&arr)[SZ]) {
|
|
return rangeToString(arr);
|
|
}
|
|
};
|
|
|
|
|
|
} // namespace Catch
|
|
|
|
// Separate std::chrono::duration specialization
|
|
#include <ctime>
|
|
#include <ratio>
|
|
#include <chrono>
|
|
|
|
|
|
namespace Catch {
|
|
|
|
template <class Ratio>
|
|
struct ratio_string {
|
|
static std::string symbol() {
|
|
Catch::ReusableStringStream rss;
|
|
rss << '[' << Ratio::num << '/'
|
|
<< Ratio::den << ']';
|
|
return rss.str();
|
|
}
|
|
};
|
|
|
|
template <>
|
|
struct ratio_string<std::atto> {
|
|
static char symbol() { return 'a'; }
|
|
};
|
|
template <>
|
|
struct ratio_string<std::femto> {
|
|
static char symbol() { return 'f'; }
|
|
};
|
|
template <>
|
|
struct ratio_string<std::pico> {
|
|
static char symbol() { return 'p'; }
|
|
};
|
|
template <>
|
|
struct ratio_string<std::nano> {
|
|
static char symbol() { return 'n'; }
|
|
};
|
|
template <>
|
|
struct ratio_string<std::micro> {
|
|
static char symbol() { return 'u'; }
|
|
};
|
|
template <>
|
|
struct ratio_string<std::milli> {
|
|
static char symbol() { return 'm'; }
|
|
};
|
|
|
|
////////////
|
|
// std::chrono::duration specializations
|
|
template<typename Value, typename Ratio>
|
|
struct StringMaker<std::chrono::duration<Value, Ratio>> {
|
|
static std::string convert(std::chrono::duration<Value, Ratio> const& duration) {
|
|
ReusableStringStream rss;
|
|
rss << duration.count() << ' ' << ratio_string<Ratio>::symbol() << 's';
|
|
return rss.str();
|
|
}
|
|
};
|
|
template<typename Value>
|
|
struct StringMaker<std::chrono::duration<Value, std::ratio<1>>> {
|
|
static std::string convert(std::chrono::duration<Value, std::ratio<1>> const& duration) {
|
|
ReusableStringStream rss;
|
|
rss << duration.count() << " s";
|
|
return rss.str();
|
|
}
|
|
};
|
|
template<typename Value>
|
|
struct StringMaker<std::chrono::duration<Value, std::ratio<60>>> {
|
|
static std::string convert(std::chrono::duration<Value, std::ratio<60>> const& duration) {
|
|
ReusableStringStream rss;
|
|
rss << duration.count() << " m";
|
|
return rss.str();
|
|
}
|
|
};
|
|
template<typename Value>
|
|
struct StringMaker<std::chrono::duration<Value, std::ratio<3600>>> {
|
|
static std::string convert(std::chrono::duration<Value, std::ratio<3600>> 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<system_clock>
|
|
template<typename Clock, typename Duration>
|
|
struct StringMaker<std::chrono::time_point<Clock, Duration>> {
|
|
static std::string convert(std::chrono::time_point<Clock, Duration> const& time_point) {
|
|
return ::Catch::Detail::stringify(time_point.time_since_epoch()) + " since epoch";
|
|
}
|
|
};
|
|
// std::chrono::time_point<system_clock> specialization
|
|
template<typename Duration>
|
|
struct StringMaker<std::chrono::time_point<std::chrono::system_clock, Duration>> {
|
|
static std::string convert(std::chrono::time_point<std::chrono::system_clock, Duration> 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, timeStampSize - 1);
|
|
}
|
|
};
|
|
}
|
|
|
|
|
|
#define INTERNAL_CATCH_REGISTER_ENUM( enumName, ... ) \
|
|
namespace Catch { \
|
|
template<> struct StringMaker<enumName> { \
|
|
static std::string convert( enumName value ) { \
|
|
static const auto& enumInfo = ::Catch::getMutableRegistryHub().getMutableEnumValuesRegistry().registerEnum( #enumName, #__VA_ARGS__, { __VA_ARGS__ } ); \
|
|
return static_cast<std::string>(enumInfo.lookup( static_cast<int>( value ) )); \
|
|
} \
|
|
}; \
|
|
}
|
|
|
|
#define CATCH_REGISTER_ENUM( enumName, ... ) INTERNAL_CATCH_REGISTER_ENUM( enumName, __VA_ARGS__ )
|
|
|
|
#ifdef _MSC_VER
|
|
#pragma warning(pop)
|
|
#endif
|
|
|
|
#endif // CATCH_TOSTRING_HPP_INCLUDED
|
|
|
|
#include <type_traits>
|
|
|
|
namespace Catch {
|
|
|
|
class Approx {
|
|
private:
|
|
bool equalityComparisonImpl(double other) const;
|
|
// Sets and validates the new margin (margin >= 0)
|
|
void setMargin(double margin);
|
|
// Sets and validates the new epsilon (0 < epsilon < 1)
|
|
void setEpsilon(double epsilon);
|
|
|
|
public:
|
|
explicit Approx ( double value );
|
|
|
|
static Approx custom();
|
|
|
|
Approx operator-() const;
|
|
|
|
template <typename T, typename = std::enable_if_t<std::is_constructible<double, T>::value>>
|
|
Approx operator()( T const& value ) const {
|
|
Approx approx( static_cast<double>(value) );
|
|
approx.m_epsilon = m_epsilon;
|
|
approx.m_margin = m_margin;
|
|
approx.m_scale = m_scale;
|
|
return approx;
|
|
}
|
|
|
|
template <typename T, typename = std::enable_if_t<std::is_constructible<double, T>::value>>
|
|
explicit Approx( T const& value ): Approx(static_cast<double>(value))
|
|
{}
|
|
|
|
|
|
template <typename T, typename = std::enable_if_t<std::is_constructible<double, T>::value>>
|
|
friend bool operator == ( const T& lhs, Approx const& rhs ) {
|
|
auto lhs_v = static_cast<double>(lhs);
|
|
return rhs.equalityComparisonImpl(lhs_v);
|
|
}
|
|
|
|
template <typename T, typename = std::enable_if_t<std::is_constructible<double, T>::value>>
|
|
friend bool operator == ( Approx const& lhs, const T& rhs ) {
|
|
return operator==( rhs, lhs );
|
|
}
|
|
|
|
template <typename T, typename = std::enable_if_t<std::is_constructible<double, T>::value>>
|
|
friend bool operator != ( T const& lhs, Approx const& rhs ) {
|
|
return !operator==( lhs, rhs );
|
|
}
|
|
|
|
template <typename T, typename = std::enable_if_t<std::is_constructible<double, T>::value>>
|
|
friend bool operator != ( Approx const& lhs, T const& rhs ) {
|
|
return !operator==( rhs, lhs );
|
|
}
|
|
|
|
template <typename T, typename = std::enable_if_t<std::is_constructible<double, T>::value>>
|
|
friend bool operator <= ( T const& lhs, Approx const& rhs ) {
|
|
return static_cast<double>(lhs) < rhs.m_value || lhs == rhs;
|
|
}
|
|
|
|
template <typename T, typename = std::enable_if_t<std::is_constructible<double, T>::value>>
|
|
friend bool operator <= ( Approx const& lhs, T const& rhs ) {
|
|
return lhs.m_value < static_cast<double>(rhs) || lhs == rhs;
|
|
}
|
|
|
|
template <typename T, typename = std::enable_if_t<std::is_constructible<double, T>::value>>
|
|
friend bool operator >= ( T const& lhs, Approx const& rhs ) {
|
|
return static_cast<double>(lhs) > rhs.m_value || lhs == rhs;
|
|
}
|
|
|
|
template <typename T, typename = std::enable_if_t<std::is_constructible<double, T>::value>>
|
|
friend bool operator >= ( Approx const& lhs, T const& rhs ) {
|
|
return lhs.m_value > static_cast<double>(rhs) || lhs == rhs;
|
|
}
|
|
|
|
template <typename T, typename = std::enable_if_t<std::is_constructible<double, T>::value>>
|
|
Approx& epsilon( T const& newEpsilon ) {
|
|
const auto epsilonAsDouble = static_cast<double>(newEpsilon);
|
|
setEpsilon(epsilonAsDouble);
|
|
return *this;
|
|
}
|
|
|
|
template <typename T, typename = std::enable_if_t<std::is_constructible<double, T>::value>>
|
|
Approx& margin( T const& newMargin ) {
|
|
const auto marginAsDouble = static_cast<double>(newMargin);
|
|
setMargin(marginAsDouble);
|
|
return *this;
|
|
}
|
|
|
|
template <typename T, typename = std::enable_if_t<std::is_constructible<double, T>::value>>
|
|
Approx& scale( T const& newScale ) {
|
|
m_scale = static_cast<double>(newScale);
|
|
return *this;
|
|
}
|
|
|
|
std::string toString() const;
|
|
|
|
private:
|
|
double m_epsilon;
|
|
double m_margin;
|
|
double m_scale;
|
|
double m_value;
|
|
};
|
|
|
|
namespace literals {
|
|
Approx operator ""_a(long double val);
|
|
Approx operator ""_a(unsigned long long val);
|
|
} // end namespace literals
|
|
|
|
template<>
|
|
struct StringMaker<Catch::Approx> {
|
|
static std::string convert(Catch::Approx const& value);
|
|
};
|
|
|
|
} // end namespace Catch
|
|
|
|
#endif // CATCH_APPROX_HPP_INCLUDED
|
|
|
|
|
|
#ifndef CATCH_ASSERTION_INFO_HPP_INCLUDED
|
|
#define CATCH_ASSERTION_INFO_HPP_INCLUDED
|
|
|
|
|
|
|
|
#ifndef CATCH_SOURCE_LINE_INFO_HPP_INCLUDED
|
|
#define CATCH_SOURCE_LINE_INFO_HPP_INCLUDED
|
|
|
|
#include <cstddef>
|
|
#include <iosfwd>
|
|
|
|
namespace Catch {
|
|
|
|
struct SourceLineInfo {
|
|
|
|
SourceLineInfo() = delete;
|
|
constexpr SourceLineInfo( char const* _file, std::size_t _line ) noexcept:
|
|
file( _file ),
|
|
line( _line )
|
|
{}
|
|
|
|
bool operator == ( SourceLineInfo const& other ) const noexcept;
|
|
bool operator < ( SourceLineInfo const& other ) const noexcept;
|
|
|
|
char const* file;
|
|
std::size_t line;
|
|
|
|
friend std::ostream& operator << (std::ostream& os, SourceLineInfo const& info);
|
|
};
|
|
}
|
|
|
|
#define CATCH_INTERNAL_LINEINFO \
|
|
::Catch::SourceLineInfo( __FILE__, static_cast<std::size_t>( __LINE__ ) )
|
|
|
|
#endif // CATCH_SOURCE_LINE_INFO_HPP_INCLUDED
|
|
|
|
namespace Catch {
|
|
|
|
struct AssertionInfo {
|
|
// AssertionInfo() = delete;
|
|
|
|
StringRef macroName;
|
|
SourceLineInfo lineInfo;
|
|
StringRef capturedExpression;
|
|
ResultDisposition::Flags resultDisposition;
|
|
};
|
|
|
|
} // end namespace Catch
|
|
|
|
#endif // CATCH_ASSERTION_INFO_HPP_INCLUDED
|
|
|
|
|
|
#ifndef CATCH_ASSERTION_RESULT_HPP_INCLUDED
|
|
#define CATCH_ASSERTION_RESULT_HPP_INCLUDED
|
|
|
|
|
|
|
|
#ifndef CATCH_LAZY_EXPR_HPP_INCLUDED
|
|
#define CATCH_LAZY_EXPR_HPP_INCLUDED
|
|
|
|
#include <iosfwd>
|
|
|
|
namespace Catch {
|
|
|
|
class ITransientExpression;
|
|
|
|
class LazyExpression {
|
|
friend class AssertionHandler;
|
|
friend struct AssertionStats;
|
|
friend class RunContext;
|
|
|
|
ITransientExpression const* m_transientExpression = nullptr;
|
|
bool m_isNegated;
|
|
public:
|
|
LazyExpression( bool isNegated ):
|
|
m_isNegated(isNegated)
|
|
{}
|
|
LazyExpression(LazyExpression const& other) = default;
|
|
LazyExpression& operator = ( LazyExpression const& ) = delete;
|
|
|
|
explicit operator bool() const {
|
|
return m_transientExpression != nullptr;
|
|
}
|
|
|
|
friend auto operator << ( std::ostream& os, LazyExpression const& lazyExpr ) -> std::ostream&;
|
|
};
|
|
|
|
} // namespace Catch
|
|
|
|
#endif // CATCH_LAZY_EXPR_HPP_INCLUDED
|
|
|
|
#include <string>
|
|
|
|
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&& 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;
|
|
StringRef getMessage() const;
|
|
SourceLineInfo getSourceInfo() const;
|
|
StringRef getTestMacroName() const;
|
|
|
|
//protected:
|
|
AssertionInfo m_info;
|
|
AssertionResultData m_resultData;
|
|
};
|
|
|
|
} // end namespace Catch
|
|
|
|
#endif // CATCH_ASSERTION_RESULT_HPP_INCLUDED
|
|
|
|
|
|
#ifndef CATCH_CONFIG_HPP_INCLUDED
|
|
#define CATCH_CONFIG_HPP_INCLUDED
|
|
|
|
|
|
|
|
#ifndef CATCH_TEST_SPEC_HPP_INCLUDED
|
|
#define CATCH_TEST_SPEC_HPP_INCLUDED
|
|
|
|
#ifdef __clang__
|
|
#pragma clang diagnostic push
|
|
#pragma clang diagnostic ignored "-Wpadded"
|
|
#endif
|
|
|
|
|
|
|
|
#ifndef CATCH_WILDCARD_PATTERN_HPP_INCLUDED
|
|
#define CATCH_WILDCARD_PATTERN_HPP_INCLUDED
|
|
|
|
|
|
|
|
#ifndef CATCH_CASE_SENSITIVE_HPP_INCLUDED
|
|
#define CATCH_CASE_SENSITIVE_HPP_INCLUDED
|
|
|
|
namespace Catch {
|
|
|
|
enum class CaseSensitive { Yes, No };
|
|
|
|
} // namespace Catch
|
|
|
|
#endif // CATCH_CASE_SENSITIVE_HPP_INCLUDED
|
|
|
|
#include <string>
|
|
|
|
namespace Catch
|
|
{
|
|
class WildcardPattern {
|
|
enum WildcardPosition {
|
|
NoWildcard = 0,
|
|
WildcardAtStart = 1,
|
|
WildcardAtEnd = 2,
|
|
WildcardAtBothEnds = WildcardAtStart | WildcardAtEnd
|
|
};
|
|
|
|
public:
|
|
|
|
WildcardPattern( std::string const& pattern, CaseSensitive caseSensitivity );
|
|
bool matches( std::string const& str ) const;
|
|
|
|
private:
|
|
std::string normaliseString( std::string const& str ) const;
|
|
CaseSensitive m_caseSensitivity;
|
|
WildcardPosition m_wildcard = NoWildcard;
|
|
std::string m_pattern;
|
|
};
|
|
}
|
|
|
|
#endif // CATCH_WILDCARD_PATTERN_HPP_INCLUDED
|
|
|
|
#include <iosfwd>
|
|
#include <string>
|
|
#include <vector>
|
|
|
|
namespace Catch {
|
|
|
|
class IConfig;
|
|
struct TestCaseInfo;
|
|
class TestCaseHandle;
|
|
|
|
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:
|
|
virtual void serializeTo( std::ostream& out ) const = 0;
|
|
// Writes string that would be reparsed into the pattern
|
|
friend std::ostream& operator<<(std::ostream& out,
|
|
Pattern const& pattern) {
|
|
pattern.serializeTo( out );
|
|
return out;
|
|
}
|
|
|
|
std::string const m_name;
|
|
};
|
|
|
|
class NamePattern : public Pattern {
|
|
public:
|
|
explicit NamePattern( std::string const& name, std::string const& filterString );
|
|
bool matches( TestCaseInfo const& testCase ) const override;
|
|
private:
|
|
void serializeTo( std::ostream& out ) const override;
|
|
|
|
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:
|
|
void serializeTo( std::ostream& out ) const override;
|
|
|
|
std::string m_tag;
|
|
};
|
|
|
|
struct Filter {
|
|
std::vector<Detail::unique_ptr<Pattern>> m_required;
|
|
std::vector<Detail::unique_ptr<Pattern>> m_forbidden;
|
|
|
|
//! Serializes this filter into a string that would be parsed into
|
|
//! an equivalent filter
|
|
void serializeTo( std::ostream& out ) const;
|
|
friend std::ostream& operator<<(std::ostream& out, Filter const& f) {
|
|
f.serializeTo( out );
|
|
return out;
|
|
}
|
|
|
|
bool matches( TestCaseInfo const& testCase ) const;
|
|
};
|
|
|
|
static std::string extractFilterName( Filter const& filter );
|
|
|
|
public:
|
|
struct FilterMatch {
|
|
std::string name;
|
|
std::vector<TestCaseHandle const*> tests;
|
|
};
|
|
using Matches = std::vector<FilterMatch>;
|
|
using vectorStrings = std::vector<std::string>;
|
|
|
|
bool hasFilters() const;
|
|
bool matches( TestCaseInfo const& testCase ) const;
|
|
Matches matchesByFilter( std::vector<TestCaseHandle> const& testCases, IConfig const& config ) const;
|
|
const vectorStrings & getInvalidSpecs() const;
|
|
|
|
private:
|
|
std::vector<Filter> m_filters;
|
|
std::vector<std::string> m_invalidSpecs;
|
|
|
|
friend class TestSpecParser;
|
|
//! Serializes this test spec into a string that would be parsed into
|
|
//! equivalent test spec
|
|
void serializeTo( std::ostream& out ) const;
|
|
friend std::ostream& operator<<(std::ostream& out,
|
|
TestSpec const& spec) {
|
|
spec.serializeTo( out );
|
|
return out;
|
|
}
|
|
};
|
|
}
|
|
|
|
#ifdef __clang__
|
|
#pragma clang diagnostic pop
|
|
#endif
|
|
|
|
#endif // CATCH_TEST_SPEC_HPP_INCLUDED
|
|
|
|
|
|
#ifndef CATCH_OPTIONAL_HPP_INCLUDED
|
|
#define CATCH_OPTIONAL_HPP_INCLUDED
|
|
|
|
|
|
#include <cassert>
|
|
|
|
namespace Catch {
|
|
|
|
// An optional type
|
|
template<typename T>
|
|
class Optional {
|
|
public:
|
|
Optional(): nullableValue( nullptr ) {}
|
|
~Optional() { reset(); }
|
|
|
|
Optional( T const& _value ):
|
|
nullableValue( new ( storage ) T( _value ) ) {}
|
|
Optional( T&& _value ):
|
|
nullableValue( new ( storage ) T( CATCH_MOVE( _value ) ) ) {}
|
|
|
|
Optional& operator=( T const& _value ) {
|
|
reset();
|
|
nullableValue = new ( storage ) T( _value );
|
|
return *this;
|
|
}
|
|
Optional& operator=( T&& _value ) {
|
|
reset();
|
|
nullableValue = new ( storage ) T( CATCH_MOVE( _value ) );
|
|
return *this;
|
|
}
|
|
|
|
Optional( Optional const& _other ):
|
|
nullableValue( _other ? new ( storage ) T( *_other ) : nullptr ) {}
|
|
Optional( Optional&& _other ):
|
|
nullableValue( _other ? new ( storage ) T( CATCH_MOVE( *_other ) )
|
|
: nullptr ) {}
|
|
|
|
Optional& operator=( Optional const& _other ) {
|
|
if ( &_other != this ) {
|
|
reset();
|
|
if ( _other ) { nullableValue = new ( storage ) T( *_other ); }
|
|
}
|
|
return *this;
|
|
}
|
|
Optional& operator=( Optional&& _other ) {
|
|
if ( &_other != this ) {
|
|
reset();
|
|
if ( _other ) {
|
|
nullableValue = new ( storage ) T( CATCH_MOVE( *_other ) );
|
|
}
|
|
}
|
|
return *this;
|
|
}
|
|
|
|
void reset() {
|
|
if ( nullableValue ) { nullableValue->~T(); }
|
|
nullableValue = nullptr;
|
|
}
|
|
|
|
T& operator*() {
|
|
assert(nullableValue);
|
|
return *nullableValue;
|
|
}
|
|
T const& operator*() const {
|
|
assert(nullableValue);
|
|
return *nullableValue;
|
|
}
|
|
T* operator->() {
|
|
assert(nullableValue);
|
|
return nullableValue;
|
|
}
|
|
const T* operator->() const {
|
|
assert(nullableValue);
|
|
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();
|
|
}
|
|
|
|
friend bool operator==(Optional const& a, Optional const& b) {
|
|
if (a.none() && b.none()) {
|
|
return true;
|
|
} else if (a.some() && b.some()) {
|
|
return *a == *b;
|
|
} else {
|
|
return false;
|
|
}
|
|
}
|
|
friend bool operator!=(Optional const& a, Optional const& b) {
|
|
return !( a == b );
|
|
}
|
|
|
|
private:
|
|
T* nullableValue;
|
|
alignas(alignof(T)) char storage[sizeof(T)];
|
|
};
|
|
|
|
} // end namespace Catch
|
|
|
|
#endif // CATCH_OPTIONAL_HPP_INCLUDED
|
|
|
|
|
|
#ifndef CATCH_RANDOM_SEED_GENERATION_HPP_INCLUDED
|
|
#define CATCH_RANDOM_SEED_GENERATION_HPP_INCLUDED
|
|
|
|
#include <cstdint>
|
|
|
|
namespace Catch {
|
|
|
|
enum class GenerateFrom {
|
|
Time,
|
|
RandomDevice,
|
|
//! Currently equivalent to RandomDevice, but can change at any point
|
|
Default
|
|
};
|
|
|
|
std::uint32_t generateRandomSeed(GenerateFrom from);
|
|
|
|
} // end namespace Catch
|
|
|
|
#endif // CATCH_RANDOM_SEED_GENERATION_HPP_INCLUDED
|
|
|
|
|
|
#ifndef CATCH_REPORTER_SPEC_PARSER_HPP_INCLUDED
|
|
#define CATCH_REPORTER_SPEC_PARSER_HPP_INCLUDED
|
|
|
|
|
|
|
|
#ifndef CATCH_CONSOLE_COLOUR_HPP_INCLUDED
|
|
#define CATCH_CONSOLE_COLOUR_HPP_INCLUDED
|
|
|
|
|
|
#include <iosfwd>
|
|
#include <cstdint>
|
|
|
|
namespace Catch {
|
|
|
|
enum class ColourMode : std::uint8_t;
|
|
class IStream;
|
|
|
|
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,
|
|
Skip = LightGrey,
|
|
|
|
OriginalExpression = Cyan,
|
|
ReconstructedExpression = BrightYellow,
|
|
|
|
SecondaryText = LightGrey,
|
|
Headers = White
|
|
};
|
|
};
|
|
|
|
class ColourImpl {
|
|
protected:
|
|
//! The associated stream of this ColourImpl instance
|
|
IStream* m_stream;
|
|
public:
|
|
ColourImpl( IStream* stream ): m_stream( stream ) {}
|
|
|
|
//! RAII wrapper around writing specific colour of text using specific
|
|
//! colour impl into a stream.
|
|
class ColourGuard {
|
|
ColourImpl const* m_colourImpl;
|
|
Colour::Code m_code;
|
|
bool m_engaged = false;
|
|
|
|
public:
|
|
//! Does **not** engage the guard/start the colour
|
|
ColourGuard( Colour::Code code,
|
|
ColourImpl const* colour );
|
|
|
|
ColourGuard( ColourGuard const& rhs ) = delete;
|
|
ColourGuard& operator=( ColourGuard const& rhs ) = delete;
|
|
|
|
ColourGuard( ColourGuard&& rhs ) noexcept;
|
|
ColourGuard& operator=( ColourGuard&& rhs ) noexcept;
|
|
|
|
//! Removes colour _if_ the guard was engaged
|
|
~ColourGuard();
|
|
|
|
/**
|
|
* Explicitly engages colour for given stream.
|
|
*
|
|
* The API based on operator<< should be preferred.
|
|
*/
|
|
ColourGuard& engage( std::ostream& stream ) &;
|
|
/**
|
|
* Explicitly engages colour for given stream.
|
|
*
|
|
* The API based on operator<< should be preferred.
|
|
*/
|
|
ColourGuard&& engage( std::ostream& stream ) &&;
|
|
|
|
private:
|
|
//! Engages the guard and starts using colour
|
|
friend std::ostream& operator<<( std::ostream& lhs,
|
|
ColourGuard& guard ) {
|
|
guard.engageImpl( lhs );
|
|
return lhs;
|
|
}
|
|
//! Engages the guard and starts using colour
|
|
friend std::ostream& operator<<( std::ostream& lhs,
|
|
ColourGuard&& guard) {
|
|
guard.engageImpl( lhs );
|
|
return lhs;
|
|
}
|
|
|
|
void engageImpl( std::ostream& stream );
|
|
|
|
};
|
|
|
|
virtual ~ColourImpl(); // = default
|
|
/**
|
|
* Creates a guard object for given colour and this colour impl
|
|
*
|
|
* **Important:**
|
|
* the guard starts disengaged, and has to be engaged explicitly.
|
|
*/
|
|
ColourGuard guardColour( Colour::Code colourCode );
|
|
|
|
private:
|
|
virtual void use( Colour::Code colourCode ) const = 0;
|
|
};
|
|
|
|
//! Provides ColourImpl based on global config and target compilation platform
|
|
Detail::unique_ptr<ColourImpl> makeColourImpl( ColourMode colourSelection,
|
|
IStream* stream );
|
|
|
|
//! Checks if specific colour impl has been compiled into the binary
|
|
bool isColourImplAvailable( ColourMode colourSelection );
|
|
|
|
} // end namespace Catch
|
|
|
|
#endif // CATCH_CONSOLE_COLOUR_HPP_INCLUDED
|
|
|
|
#include <map>
|
|
#include <string>
|
|
#include <vector>
|
|
|
|
namespace Catch {
|
|
|
|
enum class ColourMode : std::uint8_t;
|
|
|
|
namespace Detail {
|
|
//! Splits the reporter spec into reporter name and kv-pair options
|
|
std::vector<std::string> splitReporterSpec( StringRef reporterSpec );
|
|
|
|
Optional<ColourMode> stringToColourMode( StringRef colourMode );
|
|
}
|
|
|
|
/**
|
|
* Structured reporter spec that a reporter can be created from
|
|
*
|
|
* Parsing has been validated, but semantics have not. This means e.g.
|
|
* that the colour mode is known to Catch2, but it might not be
|
|
* compiled into the binary, and the output filename might not be
|
|
* openable.
|
|
*/
|
|
class ReporterSpec {
|
|
std::string m_name;
|
|
Optional<std::string> m_outputFileName;
|
|
Optional<ColourMode> m_colourMode;
|
|
std::map<std::string, std::string> m_customOptions;
|
|
|
|
friend bool operator==( ReporterSpec const& lhs,
|
|
ReporterSpec const& rhs );
|
|
friend bool operator!=( ReporterSpec const& lhs,
|
|
ReporterSpec const& rhs ) {
|
|
return !( lhs == rhs );
|
|
}
|
|
|
|
public:
|
|
ReporterSpec(
|
|
std::string name,
|
|
Optional<std::string> outputFileName,
|
|
Optional<ColourMode> colourMode,
|
|
std::map<std::string, std::string> customOptions );
|
|
|
|
std::string const& name() const { return m_name; }
|
|
|
|
Optional<std::string> const& outputFile() const {
|
|
return m_outputFileName;
|
|
}
|
|
|
|
Optional<ColourMode> const& colourMode() const { return m_colourMode; }
|
|
|
|
std::map<std::string, std::string> const& customOptions() const {
|
|
return m_customOptions;
|
|
}
|
|
};
|
|
|
|
/**
|
|
* Parses provided reporter spec string into
|
|
*
|
|
* Returns empty optional on errors, e.g.
|
|
* * field that is not first and not a key+value pair
|
|
* * duplicated keys in kv pair
|
|
* * unknown catch reporter option
|
|
* * empty key/value in an custom kv pair
|
|
* * ...
|
|
*/
|
|
Optional<ReporterSpec> parseReporterSpec( StringRef reporterSpec );
|
|
|
|
}
|
|
|
|
#endif // CATCH_REPORTER_SPEC_PARSER_HPP_INCLUDED
|
|
|
|
#include <chrono>
|
|
#include <map>
|
|
#include <string>
|
|
#include <vector>
|
|
|
|
namespace Catch {
|
|
|
|
class IStream;
|
|
|
|
/**
|
|
* `ReporterSpec` but with the defaults filled in.
|
|
*
|
|
* Like `ReporterSpec`, the semantics are unchecked.
|
|
*/
|
|
struct ProcessedReporterSpec {
|
|
std::string name;
|
|
std::string outputFilename;
|
|
ColourMode colourMode;
|
|
std::map<std::string, std::string> customOptions;
|
|
friend bool operator==( ProcessedReporterSpec const& lhs,
|
|
ProcessedReporterSpec const& rhs );
|
|
friend bool operator!=( ProcessedReporterSpec const& lhs,
|
|
ProcessedReporterSpec const& rhs ) {
|
|
return !( lhs == rhs );
|
|
}
|
|
};
|
|
|
|
struct ConfigData {
|
|
|
|
bool listTests = false;
|
|
bool listTags = false;
|
|
bool listReporters = false;
|
|
bool listListeners = false;
|
|
|
|
bool showSuccessfulTests = false;
|
|
bool shouldDebugBreak = false;
|
|
bool noThrow = false;
|
|
bool showHelp = false;
|
|
bool showInvisibles = false;
|
|
bool filenamesAsTags = false;
|
|
bool libIdentify = false;
|
|
bool allowZeroTests = false;
|
|
|
|
int abortAfter = -1;
|
|
uint32_t rngSeed = generateRandomSeed(GenerateFrom::Default);
|
|
|
|
unsigned int shardCount = 1;
|
|
unsigned int shardIndex = 0;
|
|
|
|
bool skipBenchmarks = false;
|
|
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 showDurations = ShowDurations::DefaultForReporter;
|
|
double minDuration = -1;
|
|
TestRunOrder runOrder = TestRunOrder::Declared;
|
|
ColourMode defaultColourMode = ColourMode::PlatformDefault;
|
|
WaitForKeypress::When waitForKeypress = WaitForKeypress::Never;
|
|
|
|
std::string defaultOutputFilename;
|
|
std::string name;
|
|
std::string processName;
|
|
std::vector<ReporterSpec> reporterSpecifications;
|
|
|
|
std::vector<std::string> testsOrTags;
|
|
std::vector<std::string> sectionsToRun;
|
|
};
|
|
|
|
|
|
class Config : public IConfig {
|
|
public:
|
|
|
|
Config() = default;
|
|
Config( ConfigData const& data );
|
|
~Config() override; // = default in the cpp file
|
|
|
|
bool listTests() const;
|
|
bool listTags() const;
|
|
bool listReporters() const;
|
|
bool listListeners() const;
|
|
|
|
std::vector<ReporterSpec> const& getReporterSpecs() const;
|
|
std::vector<ProcessedReporterSpec> const&
|
|
getProcessedReporterSpecs() const;
|
|
|
|
std::vector<std::string> const& getTestsOrTags() const override;
|
|
std::vector<std::string> const& getSectionsToRun() const override;
|
|
|
|
TestSpec const& testSpec() const override;
|
|
bool hasTestFilters() const override;
|
|
|
|
bool showHelp() const;
|
|
|
|
// IConfig interface
|
|
bool allowThrows() const override;
|
|
StringRef name() const override;
|
|
bool includeSuccessfulResults() const override;
|
|
bool warnAboutMissingAssertions() const override;
|
|
bool warnAboutUnmatchedTestSpecs() const override;
|
|
bool zeroTestsCountAsSuccess() const override;
|
|
ShowDurations showDurations() const override;
|
|
double minDuration() const override;
|
|
TestRunOrder runOrder() const override;
|
|
uint32_t rngSeed() const override;
|
|
unsigned int shardCount() const override;
|
|
unsigned int shardIndex() const override;
|
|
ColourMode defaultColourMode() const override;
|
|
bool shouldDebugBreak() const override;
|
|
int abortAfter() const override;
|
|
bool showInvisibles() const override;
|
|
Verbosity verbosity() const override;
|
|
bool skipBenchmarks() const override;
|
|
bool benchmarkNoAnalysis() const override;
|
|
unsigned int benchmarkSamples() const override;
|
|
double benchmarkConfidenceInterval() const override;
|
|
unsigned int benchmarkResamples() const override;
|
|
std::chrono::milliseconds benchmarkWarmupTime() const override;
|
|
|
|
private:
|
|
// Reads Bazel env vars and applies them to the config
|
|
void readBazelEnvVars();
|
|
|
|
ConfigData m_data;
|
|
std::vector<ProcessedReporterSpec> m_processedReporterSpecs;
|
|
TestSpec m_testSpec;
|
|
bool m_hasTestFilters = false;
|
|
};
|
|
} // end namespace Catch
|
|
|
|
#endif // CATCH_CONFIG_HPP_INCLUDED
|
|
|
|
|
|
#ifndef CATCH_GET_RANDOM_SEED_HPP_INCLUDED
|
|
#define CATCH_GET_RANDOM_SEED_HPP_INCLUDED
|
|
|
|
#include <cstdint>
|
|
|
|
namespace Catch {
|
|
//! Returns Catch2's current RNG seed.
|
|
std::uint32_t getSeed();
|
|
}
|
|
|
|
#endif // CATCH_GET_RANDOM_SEED_HPP_INCLUDED
|
|
|
|
|
|
#ifndef CATCH_MESSAGE_HPP_INCLUDED
|
|
#define CATCH_MESSAGE_HPP_INCLUDED
|
|
|
|
|
|
|
|
|
|
/** \file
|
|
* Wrapper for the CATCH_CONFIG_PREFIX_MESSAGES configuration option
|
|
*
|
|
* CATCH_CONFIG_PREFIX_ALL can be used to avoid clashes with other macros
|
|
* by prepending CATCH_. This may not be desirable if the only clashes are with
|
|
* logger macros such as INFO and WARN. In this cases
|
|
* CATCH_CONFIG_PREFIX_MESSAGES can be used to only prefix a small subset
|
|
* of relevant macros.
|
|
*
|
|
*/
|
|
|
|
#ifndef CATCH_CONFIG_PREFIX_MESSAGES_HPP_INCLUDED
|
|
#define CATCH_CONFIG_PREFIX_MESSAGES_HPP_INCLUDED
|
|
|
|
|
|
#if defined(CATCH_CONFIG_PREFIX_ALL) && !defined(CATCH_CONFIG_PREFIX_MESSAGES)
|
|
#define CATCH_CONFIG_PREFIX_MESSAGES
|
|
#endif
|
|
|
|
#endif // CATCH_CONFIG_PREFIX_MESSAGES_HPP_INCLUDED
|
|
|
|
|
|
#ifndef CATCH_STREAM_END_STOP_HPP_INCLUDED
|
|
#define CATCH_STREAM_END_STOP_HPP_INCLUDED
|
|
|
|
|
|
namespace Catch {
|
|
|
|
// Use this in variadic streaming macros to allow
|
|
// << +StreamEndStop
|
|
// as well as
|
|
// << stuff +StreamEndStop
|
|
struct StreamEndStop {
|
|
constexpr StringRef operator+() const { return StringRef(); }
|
|
|
|
template <typename T>
|
|
constexpr friend T const& operator+( T const& value, StreamEndStop ) {
|
|
return value;
|
|
}
|
|
};
|
|
|
|
} // namespace Catch
|
|
|
|
#endif // CATCH_STREAM_END_STOP_HPP_INCLUDED
|
|
|
|
|
|
#ifndef CATCH_MESSAGE_INFO_HPP_INCLUDED
|
|
#define CATCH_MESSAGE_INFO_HPP_INCLUDED
|
|
|
|
|
|
#include <string>
|
|
|
|
namespace Catch {
|
|
|
|
struct MessageInfo {
|
|
MessageInfo( StringRef _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 {
|
|
return sequence == other.sequence;
|
|
}
|
|
bool operator < (MessageInfo const& other) const {
|
|
return sequence < other.sequence;
|
|
}
|
|
private:
|
|
static unsigned int globalCount;
|
|
};
|
|
|
|
} // end namespace Catch
|
|
|
|
#endif // CATCH_MESSAGE_INFO_HPP_INCLUDED
|
|
|
|
#include <string>
|
|
#include <vector>
|
|
|
|
namespace Catch {
|
|
|
|
struct SourceLineInfo;
|
|
class IResultCapture;
|
|
|
|
struct MessageStream {
|
|
|
|
template<typename T>
|
|
MessageStream& operator << ( T const& value ) {
|
|
m_stream << value;
|
|
return *this;
|
|
}
|
|
|
|
ReusableStringStream m_stream;
|
|
};
|
|
|
|
struct MessageBuilder : MessageStream {
|
|
MessageBuilder( StringRef macroName,
|
|
SourceLineInfo const& lineInfo,
|
|
ResultWas::OfType type ):
|
|
m_info(macroName, lineInfo, type) {}
|
|
|
|
template<typename T>
|
|
MessageBuilder&& operator << ( T const& value ) && {
|
|
m_stream << value;
|
|
return CATCH_MOVE(*this);
|
|
}
|
|
|
|
MessageInfo m_info;
|
|
};
|
|
|
|
class ScopedMessage {
|
|
public:
|
|
explicit ScopedMessage( MessageBuilder&& builder );
|
|
ScopedMessage( ScopedMessage& duplicate ) = delete;
|
|
ScopedMessage( ScopedMessage&& old ) noexcept;
|
|
~ScopedMessage();
|
|
|
|
MessageInfo m_info;
|
|
bool m_moved = false;
|
|
};
|
|
|
|
class Capturer {
|
|
std::vector<MessageInfo> m_messages;
|
|
IResultCapture& m_resultCapture;
|
|
size_t m_captured = 0;
|
|
public:
|
|
Capturer( StringRef macroName, SourceLineInfo const& lineInfo, ResultWas::OfType resultType, StringRef names );
|
|
|
|
Capturer(Capturer const&) = delete;
|
|
Capturer& operator=(Capturer const&) = delete;
|
|
|
|
~Capturer();
|
|
|
|
void captureValue( size_t index, std::string const& value );
|
|
|
|
template<typename T>
|
|
void captureValues( size_t index, T const& value ) {
|
|
captureValue( index, Catch::Detail::stringify( value ) );
|
|
}
|
|
|
|
template<typename T, typename... Ts>
|
|
void captureValues( size_t index, T const& value, Ts const&... values ) {
|
|
captureValue( index, Catch::Detail::stringify(value) );
|
|
captureValues( index+1, values... );
|
|
}
|
|
};
|
|
|
|
} // end namespace Catch
|
|
|
|
///////////////////////////////////////////////////////////////////////////////
|
|
#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, ... ) \
|
|
Catch::Capturer varName( macroName##_catch_sr, \
|
|
CATCH_INTERNAL_LINEINFO, \
|
|
Catch::ResultWas::Info, \
|
|
#__VA_ARGS__##_catch_sr ); \
|
|
varName.captureValues( 0, __VA_ARGS__ )
|
|
|
|
///////////////////////////////////////////////////////////////////////////////
|
|
#define INTERNAL_CATCH_INFO( macroName, log ) \
|
|
const 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 )
|
|
|
|
|
|
#if defined(CATCH_CONFIG_PREFIX_MESSAGES) && !defined(CATCH_CONFIG_DISABLE)
|
|
|
|
#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__ )
|
|
|
|
#elif defined(CATCH_CONFIG_PREFIX_MESSAGES) && defined(CATCH_CONFIG_DISABLE)
|
|
|
|
#define CATCH_INFO( msg ) (void)(0)
|
|
#define CATCH_UNSCOPED_INFO( msg ) (void)(0)
|
|
#define CATCH_WARN( msg ) (void)(0)
|
|
#define CATCH_CAPTURE( ... ) (void)(0)
|
|
|
|
#elif !defined(CATCH_CONFIG_PREFIX_MESSAGES) && !defined(CATCH_CONFIG_DISABLE)
|
|
|
|
#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__ )
|
|
|
|
#elif !defined(CATCH_CONFIG_PREFIX_MESSAGES) && defined(CATCH_CONFIG_DISABLE)
|
|
|
|
#define INFO( msg ) (void)(0)
|
|
#define UNSCOPED_INFO( msg ) (void)(0)
|
|
#define WARN( msg ) (void)(0)
|
|
#define CAPTURE( ... ) (void)(0)
|
|
|
|
#endif // end of user facing macro declarations
|
|
|
|
|
|
|
|
|
|
#endif // CATCH_MESSAGE_HPP_INCLUDED
|
|
|
|
|
|
#ifndef CATCH_SECTION_INFO_HPP_INCLUDED
|
|
#define CATCH_SECTION_INFO_HPP_INCLUDED
|
|
|
|
|
|
|
|
#ifndef CATCH_TOTALS_HPP_INCLUDED
|
|
#define CATCH_TOTALS_HPP_INCLUDED
|
|
|
|
#include <cstdint>
|
|
|
|
namespace Catch {
|
|
|
|
struct Counts {
|
|
Counts operator - ( Counts const& other ) const;
|
|
Counts& operator += ( Counts const& other );
|
|
|
|
std::uint64_t total() const;
|
|
bool allPassed() const;
|
|
bool allOk() const;
|
|
|
|
std::uint64_t passed = 0;
|
|
std::uint64_t failed = 0;
|
|
std::uint64_t failedButOk = 0;
|
|
std::uint64_t skipped = 0;
|
|
};
|
|
|
|
struct Totals {
|
|
|
|
Totals operator - ( Totals const& other ) const;
|
|
Totals& operator += ( Totals const& other );
|
|
|
|
Totals delta( Totals const& prevTotals ) const;
|
|
|
|
Counts assertions;
|
|
Counts testCases;
|
|
};
|
|
}
|
|
|
|
#endif // CATCH_TOTALS_HPP_INCLUDED
|
|
|
|
#include <string>
|
|
|
|
namespace Catch {
|
|
|
|
struct SectionInfo {
|
|
// The last argument is ignored, so that people can write
|
|
// SECTION("ShortName", "Proper description that is long") and
|
|
// still use the `-c` flag comfortably.
|
|
SectionInfo( SourceLineInfo const& _lineInfo, std::string _name,
|
|
const char* const = nullptr ):
|
|
name(CATCH_MOVE(_name)),
|
|
lineInfo(_lineInfo)
|
|
{}
|
|
|
|
std::string name;
|
|
SourceLineInfo lineInfo;
|
|
};
|
|
|
|
struct SectionEndInfo {
|
|
SectionInfo sectionInfo;
|
|
Counts prevAssertions;
|
|
double durationInSeconds;
|
|
};
|
|
|
|
} // end namespace Catch
|
|
|
|
#endif // CATCH_SECTION_INFO_HPP_INCLUDED
|
|
|
|
|
|
#ifndef CATCH_SESSION_HPP_INCLUDED
|
|
#define CATCH_SESSION_HPP_INCLUDED
|
|
|
|
|
|
|
|
#ifndef CATCH_COMMANDLINE_HPP_INCLUDED
|
|
#define CATCH_COMMANDLINE_HPP_INCLUDED
|
|
|
|
|
|
|
|
#ifndef CATCH_CLARA_HPP_INCLUDED
|
|
#define CATCH_CLARA_HPP_INCLUDED
|
|
|
|
#if defined( __clang__ )
|
|
# pragma clang diagnostic push
|
|
# pragma clang diagnostic ignored "-Wweak-vtables"
|
|
# pragma clang diagnostic ignored "-Wshadow"
|
|
# pragma clang diagnostic ignored "-Wdeprecated"
|
|
#endif
|
|
|
|
#if defined( __GNUC__ )
|
|
# pragma GCC diagnostic push
|
|
# pragma GCC diagnostic ignored "-Wsign-conversion"
|
|
#endif
|
|
|
|
#ifndef CLARA_CONFIG_OPTIONAL_TYPE
|
|
# ifdef __has_include
|
|
# if __has_include( <optional>) && __cplusplus >= 201703L
|
|
# include <optional>
|
|
# define CLARA_CONFIG_OPTIONAL_TYPE std::optional
|
|
# endif
|
|
# endif
|
|
#endif
|
|
|
|
|
|
#include <cassert>
|
|
#include <memory>
|
|
#include <ostream>
|
|
#include <sstream>
|
|
#include <string>
|
|
#include <type_traits>
|
|
#include <vector>
|
|
|
|
namespace Catch {
|
|
namespace Clara {
|
|
|
|
class Args;
|
|
class Parser;
|
|
|
|
// enum of result types from a parse
|
|
enum class ParseResultType {
|
|
Matched,
|
|
NoMatch,
|
|
ShortCircuitAll,
|
|
ShortCircuitSame
|
|
};
|
|
|
|
struct accept_many_t {};
|
|
constexpr accept_many_t accept_many {};
|
|
|
|
namespace Detail {
|
|
struct fake_arg {
|
|
template <typename T>
|
|
operator T();
|
|
};
|
|
|
|
template <typename F, typename = void>
|
|
struct is_unary_function : std::false_type {};
|
|
|
|
template <typename F>
|
|
struct is_unary_function<
|
|
F,
|
|
Catch::Detail::void_t<decltype(
|
|
std::declval<F>()( fake_arg() ) )
|
|
>
|
|
> : std::true_type {};
|
|
|
|
// Traits for extracting arg and return type of lambdas (for single
|
|
// argument lambdas)
|
|
template <typename L>
|
|
struct UnaryLambdaTraits
|
|
: UnaryLambdaTraits<decltype( &L::operator() )> {};
|
|
|
|
template <typename ClassT, typename ReturnT, typename... Args>
|
|
struct UnaryLambdaTraits<ReturnT ( ClassT::* )( Args... ) const> {
|
|
static const bool isValid = false;
|
|
};
|
|
|
|
template <typename ClassT, typename ReturnT, typename ArgT>
|
|
struct UnaryLambdaTraits<ReturnT ( ClassT::* )( ArgT ) const> {
|
|
static const bool isValid = true;
|
|
using ArgType = std::remove_const_t<std::remove_reference_t<ArgT>>;
|
|
using ReturnType = ReturnT;
|
|
};
|
|
|
|
class TokenStream;
|
|
|
|
// 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;
|
|
};
|
|
|
|
// Abstracts iterators into args as a stream of tokens, with option
|
|
// arguments uniformly handled
|
|
class TokenStream {
|
|
using Iterator = std::vector<std::string>::const_iterator;
|
|
Iterator it;
|
|
Iterator itEnd;
|
|
std::vector<Token> m_tokenBuffer;
|
|
|
|
void loadBuffer();
|
|
|
|
public:
|
|
explicit TokenStream( Args const& args );
|
|
TokenStream( Iterator it, Iterator itEnd );
|
|
|
|
explicit operator bool() const {
|
|
return !m_tokenBuffer.empty() || it != itEnd;
|
|
}
|
|
|
|
size_t count() const {
|
|
return m_tokenBuffer.size() + ( itEnd - it );
|
|
}
|
|
|
|
Token operator*() const {
|
|
assert( !m_tokenBuffer.empty() );
|
|
return m_tokenBuffer.front();
|
|
}
|
|
|
|
Token const* operator->() const {
|
|
assert( !m_tokenBuffer.empty() );
|
|
return &m_tokenBuffer.front();
|
|
}
|
|
|
|
TokenStream& operator++();
|
|
};
|
|
|
|
//! Denotes type of a parsing result
|
|
enum class ResultType {
|
|
Ok, ///< No errors
|
|
LogicError, ///< Error in user-specified arguments for
|
|
///< construction
|
|
RuntimeError ///< Error in parsing inputs
|
|
};
|
|
|
|
class ResultBase {
|
|
protected:
|
|
ResultBase( ResultType type ): m_type( type ) {}
|
|
virtual ~ResultBase(); // = default;
|
|
|
|
|
|
ResultBase(ResultBase const&) = default;
|
|
ResultBase& operator=(ResultBase const&) = default;
|
|
ResultBase(ResultBase&&) = default;
|
|
ResultBase& operator=(ResultBase&&) = default;
|
|
|
|
virtual void enforceOk() const = 0;
|
|
|
|
ResultType m_type;
|
|
};
|
|
|
|
template <typename T> class ResultValueBase : public ResultBase {
|
|
public:
|
|
auto value() const -> T const& {
|
|
enforceOk();
|
|
return m_value;
|
|
}
|
|
|
|
protected:
|
|
ResultValueBase( ResultType type ): ResultBase( type ) {}
|
|
|
|
ResultValueBase( ResultValueBase const& other ):
|
|
ResultBase( other ) {
|
|
if ( m_type == ResultType::Ok )
|
|
new ( &m_value ) T( other.m_value );
|
|
}
|
|
|
|
ResultValueBase( ResultType, T const& value ): ResultBase( ResultType::Ok ) {
|
|
new ( &m_value ) T( value );
|
|
}
|
|
|
|
auto operator=( ResultValueBase const& other )
|
|
-> ResultValueBase& {
|
|
if ( m_type == ResultType::Ok )
|
|
m_value.~T();
|
|
ResultBase::operator=( other );
|
|
if ( m_type == ResultType::Ok )
|
|
new ( &m_value ) T( other.m_value );
|
|
return *this;
|
|
}
|
|
|
|
~ResultValueBase() override {
|
|
if ( m_type == ResultType::Ok )
|
|
m_value.~T();
|
|
}
|
|
|
|
union {
|
|
T m_value;
|
|
};
|
|
};
|
|
|
|
template <> class ResultValueBase<void> : public ResultBase {
|
|
protected:
|
|
using ResultBase::ResultBase;
|
|
};
|
|
|
|
template <typename T = void>
|
|
class BasicResult : public ResultValueBase<T> {
|
|
public:
|
|
template <typename U>
|
|
explicit BasicResult( BasicResult<U> const& other ):
|
|
ResultValueBase<T>( other.type() ),
|
|
m_errorMessage( other.errorMessage() ) {
|
|
assert( type() != ResultType::Ok );
|
|
}
|
|
|
|
template <typename U>
|
|
static auto ok( U const& value ) -> BasicResult {
|
|
return { ResultType::Ok, value };
|
|
}
|
|
static auto ok() -> BasicResult { return { ResultType::Ok }; }
|
|
static auto logicError( std::string&& message )
|
|
-> BasicResult {
|
|
return { ResultType::LogicError, CATCH_MOVE(message) };
|
|
}
|
|
static auto runtimeError( std::string&& message )
|
|
-> BasicResult {
|
|
return { ResultType::RuntimeError, CATCH_MOVE(message) };
|
|
}
|
|
|
|
explicit operator bool() const {
|
|
return m_type == ResultType::Ok;
|
|
}
|
|
auto type() const -> ResultType { return m_type; }
|
|
auto errorMessage() const -> std::string const& {
|
|
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 != ResultType::LogicError );
|
|
assert( m_type != ResultType::RuntimeError );
|
|
if ( m_type != ResultType::Ok )
|
|
std::abort();
|
|
}
|
|
|
|
std::string
|
|
m_errorMessage; // Only populated if resultType is an error
|
|
|
|
BasicResult( ResultType type,
|
|
std::string&& message ):
|
|
ResultValueBase<T>( type ), m_errorMessage( CATCH_MOVE(message) ) {
|
|
assert( m_type != ResultType::Ok );
|
|
}
|
|
|
|
using ResultValueBase<T>::ResultValueBase;
|
|
using ResultBase::m_type;
|
|
};
|
|
|
|
class ParseState {
|
|
public:
|
|
ParseState( ParseResultType type,
|
|
TokenStream const& remainingTokens );
|
|
|
|
ParseResultType type() const { return m_type; }
|
|
TokenStream const& remainingTokens() const {
|
|
return m_remainingTokens;
|
|
}
|
|
|
|
private:
|
|
ParseResultType m_type;
|
|
TokenStream m_remainingTokens;
|
|
};
|
|
|
|
using Result = BasicResult<void>;
|
|
using ParserResult = BasicResult<ParseResultType>;
|
|
using InternalParseResult = BasicResult<ParseState>;
|
|
|
|
struct HelpColumns {
|
|
std::string left;
|
|
std::string right;
|
|
};
|
|
|
|
template <typename T>
|
|
ParserResult convertInto( std::string const& source, T& target ) {
|
|
std::stringstream ss( source );
|
|
ss >> target;
|
|
if ( ss.fail() ) {
|
|
return ParserResult::runtimeError(
|
|
"Unable to convert '" + source +
|
|
"' to destination type" );
|
|
} else {
|
|
return ParserResult::ok( ParseResultType::Matched );
|
|
}
|
|
}
|
|
ParserResult convertInto( std::string const& source,
|
|
std::string& target );
|
|
ParserResult convertInto( std::string const& source, bool& target );
|
|
|
|
#ifdef CLARA_CONFIG_OPTIONAL_TYPE
|
|
template <typename T>
|
|
auto convertInto( std::string const& source,
|
|
CLARA_CONFIG_OPTIONAL_TYPE<T>& target )
|
|
-> ParserResult {
|
|
T temp;
|
|
auto result = convertInto( source, temp );
|
|
if ( result )
|
|
target = CATCH_MOVE( temp );
|
|
return result;
|
|
}
|
|
#endif // CLARA_CONFIG_OPTIONAL_TYPE
|
|
|
|
struct BoundRef : Catch::Detail::NonCopyable {
|
|
virtual ~BoundRef() = default;
|
|
virtual bool isContainer() const;
|
|
virtual bool isFlag() const;
|
|
};
|
|
struct BoundValueRefBase : BoundRef {
|
|
virtual auto setValue( std::string const& arg )
|
|
-> ParserResult = 0;
|
|
};
|
|
struct BoundFlagRefBase : BoundRef {
|
|
virtual auto setFlag( bool flag ) -> ParserResult = 0;
|
|
bool isFlag() const override;
|
|
};
|
|
|
|
template <typename T> struct BoundValueRef : BoundValueRefBase {
|
|
T& m_ref;
|
|
|
|
explicit BoundValueRef( T& ref ): m_ref( ref ) {}
|
|
|
|
ParserResult setValue( std::string const& arg ) override {
|
|
return convertInto( arg, m_ref );
|
|
}
|
|
};
|
|
|
|
template <typename T>
|
|
struct BoundValueRef<std::vector<T>> : BoundValueRefBase {
|
|
std::vector<T>& m_ref;
|
|
|
|
explicit BoundValueRef( std::vector<T>& 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 ) {}
|
|
|
|
ParserResult setFlag( bool flag ) override;
|
|
};
|
|
|
|
template <typename ReturnType> struct LambdaInvoker {
|
|
static_assert(
|
|
std::is_same<ReturnType, ParserResult>::value,
|
|
"Lambda must return void or clara::ParserResult" );
|
|
|
|
template <typename L, typename ArgType>
|
|
static auto invoke( L const& lambda, ArgType const& arg )
|
|
-> ParserResult {
|
|
return lambda( arg );
|
|
}
|
|
};
|
|
|
|
template <> struct LambdaInvoker<void> {
|
|
template <typename L, typename ArgType>
|
|
static auto invoke( L const& lambda, ArgType const& arg )
|
|
-> ParserResult {
|
|
lambda( arg );
|
|
return ParserResult::ok( ParseResultType::Matched );
|
|
}
|
|
};
|
|
|
|
template <typename ArgType, typename L>
|
|
auto invokeLambda( L const& lambda, std::string const& arg )
|
|
-> ParserResult {
|
|
ArgType temp{};
|
|
auto result = convertInto( arg, temp );
|
|
return !result ? result
|
|
: LambdaInvoker<typename UnaryLambdaTraits<
|
|
L>::ReturnType>::invoke( lambda, temp );
|
|
}
|
|
|
|
template <typename L> struct BoundLambda : BoundValueRefBase {
|
|
L m_lambda;
|
|
|
|
static_assert(
|
|
UnaryLambdaTraits<L>::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<typename UnaryLambdaTraits<L>::ArgType>(
|
|
m_lambda, arg );
|
|
}
|
|
};
|
|
|
|
template <typename L> struct BoundManyLambda : BoundLambda<L> {
|
|
explicit BoundManyLambda( L const& lambda ): BoundLambda<L>( lambda ) {}
|
|
bool isContainer() const override { return true; }
|
|
};
|
|
|
|
template <typename L> struct BoundFlagLambda : BoundFlagRefBase {
|
|
L m_lambda;
|
|
|
|
static_assert(
|
|
UnaryLambdaTraits<L>::isValid,
|
|
"Supplied lambda must take exactly one argument" );
|
|
static_assert(
|
|
std::is_same<typename UnaryLambdaTraits<L>::ArgType,
|
|
bool>::value,
|
|
"flags must be boolean" );
|
|
|
|
explicit BoundFlagLambda( L const& lambda ):
|
|
m_lambda( lambda ) {}
|
|
|
|
auto setFlag( bool flag ) -> ParserResult override {
|
|
return LambdaInvoker<typename UnaryLambdaTraits<
|
|
L>::ReturnType>::invoke( m_lambda, flag );
|
|
}
|
|
};
|
|
|
|
enum class Optionality { Optional, Required };
|
|
|
|
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 size_t cardinality() const;
|
|
|
|
InternalParseResult parse( Args const& args ) const;
|
|
};
|
|
|
|
template <typename DerivedT>
|
|
class ComposableParserImpl : public ParserBase {
|
|
public:
|
|
template <typename T>
|
|
auto operator|( T const& other ) const -> Parser;
|
|
};
|
|
|
|
// Common code and state for Args and Opts
|
|
template <typename DerivedT>
|
|
class ParserRefImpl : public ComposableParserImpl<DerivedT> {
|
|
protected:
|
|
Optionality m_optionality = Optionality::Optional;
|
|
std::shared_ptr<BoundRef> m_ref;
|
|
std::string m_hint;
|
|
std::string m_description;
|
|
|
|
explicit ParserRefImpl( std::shared_ptr<BoundRef> const& ref ):
|
|
m_ref( ref ) {}
|
|
|
|
public:
|
|
template <typename LambdaT>
|
|
ParserRefImpl( accept_many_t,
|
|
LambdaT const& ref,
|
|
std::string const& hint ):
|
|
m_ref( std::make_shared<BoundManyLambda<LambdaT>>( ref ) ),
|
|
m_hint( hint ) {}
|
|
|
|
template <typename T,
|
|
typename = typename std::enable_if_t<
|
|
!Detail::is_unary_function<T>::value>>
|
|
ParserRefImpl( T& ref, std::string const& hint ):
|
|
m_ref( std::make_shared<BoundValueRef<T>>( ref ) ),
|
|
m_hint( hint ) {}
|
|
|
|
template <typename LambdaT,
|
|
typename = typename std::enable_if_t<
|
|
Detail::is_unary_function<LambdaT>::value>>
|
|
ParserRefImpl( LambdaT const& ref, std::string const& hint ):
|
|
m_ref( std::make_shared<BoundLambda<LambdaT>>( ref ) ),
|
|
m_hint( hint ) {}
|
|
|
|
auto operator()( std::string const& description ) -> DerivedT& {
|
|
m_description = description;
|
|
return static_cast<DerivedT&>( *this );
|
|
}
|
|
|
|
auto optional() -> DerivedT& {
|
|
m_optionality = Optionality::Optional;
|
|
return static_cast<DerivedT&>( *this );
|
|
}
|
|
|
|
auto required() -> DerivedT& {
|
|
m_optionality = Optionality::Required;
|
|
return static_cast<DerivedT&>( *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;
|
|
}
|
|
|
|
std::string const& hint() const { return m_hint; }
|
|
};
|
|
|
|
} // namespace detail
|
|
|
|
|
|
// A parser for arguments
|
|
class Arg : public Detail::ParserRefImpl<Arg> {
|
|
public:
|
|
using ParserRefImpl::ParserRefImpl;
|
|
using ParserBase::parse;
|
|
|
|
Detail::InternalParseResult
|
|
parse(std::string const&,
|
|
Detail::TokenStream const& tokens) const override;
|
|
};
|
|
|
|
// A parser for options
|
|
class Opt : public Detail::ParserRefImpl<Opt> {
|
|
protected:
|
|
std::vector<std::string> m_optNames;
|
|
|
|
public:
|
|
template <typename LambdaT>
|
|
explicit Opt(LambdaT const& ref) :
|
|
ParserRefImpl(
|
|
std::make_shared<Detail::BoundFlagLambda<LambdaT>>(ref)) {}
|
|
|
|
explicit Opt(bool& ref);
|
|
|
|
template <typename LambdaT,
|
|
typename = typename std::enable_if_t<
|
|
Detail::is_unary_function<LambdaT>::value>>
|
|
Opt( LambdaT const& ref, std::string const& hint ):
|
|
ParserRefImpl( ref, hint ) {}
|
|
|
|
template <typename LambdaT>
|
|
Opt( accept_many_t, LambdaT const& ref, std::string const& hint ):
|
|
ParserRefImpl( accept_many, ref, hint ) {}
|
|
|
|
template <typename T,
|
|
typename = typename std::enable_if_t<
|
|
!Detail::is_unary_function<T>::value>>
|
|
Opt( T& ref, std::string const& hint ):
|
|
ParserRefImpl( ref, hint ) {}
|
|
|
|
auto operator[](std::string const& optName) -> Opt& {
|
|
m_optNames.push_back(optName);
|
|
return *this;
|
|
}
|
|
|
|
std::vector<Detail::HelpColumns> getHelpColumns() const;
|
|
|
|
bool isMatch(std::string const& optToken) const;
|
|
|
|
using ParserBase::parse;
|
|
|
|
Detail::InternalParseResult
|
|
parse(std::string const&,
|
|
Detail::TokenStream const& tokens) const override;
|
|
|
|
Detail::Result validate() const override;
|
|
};
|
|
|
|
// Specifies the name of the executable
|
|
class ExeName : public Detail::ComposableParserImpl<ExeName> {
|
|
std::shared_ptr<std::string> m_name;
|
|
std::shared_ptr<Detail::BoundValueRefBase> m_ref;
|
|
|
|
public:
|
|
ExeName();
|
|
explicit ExeName(std::string& ref);
|
|
|
|
template <typename LambdaT>
|
|
explicit ExeName(LambdaT const& lambda) : ExeName() {
|
|
m_ref = std::make_shared<Detail::BoundLambda<LambdaT>>(lambda);
|
|
}
|
|
|
|
// The exe name is not parsed out of the normal tokens, but is
|
|
// handled specially
|
|
Detail::InternalParseResult
|
|
parse(std::string const&,
|
|
Detail::TokenStream const& tokens) const override;
|
|
|
|
std::string const& name() const { return *m_name; }
|
|
Detail::ParserResult set(std::string const& newName);
|
|
};
|
|
|
|
|
|
// A Combined parser
|
|
class Parser : Detail::ParserBase {
|
|
mutable ExeName m_exeName;
|
|
std::vector<Opt> m_options;
|
|
std::vector<Arg> m_args;
|
|
|
|
public:
|
|
|
|
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;
|
|
}
|
|
|
|
Parser& operator|=(Parser const& other);
|
|
|
|
template <typename T>
|
|
auto operator|(T const& other) const -> Parser {
|
|
return Parser(*this) |= other;
|
|
}
|
|
|
|
std::vector<Detail::HelpColumns> getHelpColumns() const;
|
|
|
|
void writeToStream(std::ostream& os) const;
|
|
|
|
friend auto operator<<(std::ostream& os, Parser const& parser)
|
|
-> std::ostream& {
|
|
parser.writeToStream(os);
|
|
return os;
|
|
}
|
|
|
|
Detail::Result validate() const override;
|
|
|
|
using ParserBase::parse;
|
|
Detail::InternalParseResult
|
|
parse(std::string const& exeName,
|
|
Detail::TokenStream const& tokens) const override;
|
|
};
|
|
|
|
// Transport for raw args (copied from main args, or supplied via
|
|
// init list for testing)
|
|
class Args {
|
|
friend Detail::TokenStream;
|
|
std::string m_exeName;
|
|
std::vector<std::string> m_args;
|
|
|
|
public:
|
|
Args(int argc, char const* const* argv);
|
|
Args(std::initializer_list<std::string> args);
|
|
|
|
std::string const& exeName() const { return m_exeName; }
|
|
};
|
|
|
|
|
|
// Convenience wrapper for option parser that specifies the help option
|
|
struct Help : Opt {
|
|
Help(bool& showHelpFlag);
|
|
};
|
|
|
|
// Result type for parser operation
|
|
using Detail::ParserResult;
|
|
|
|
namespace Detail {
|
|
template <typename DerivedT>
|
|
template <typename T>
|
|
Parser
|
|
ComposableParserImpl<DerivedT>::operator|(T const& other) const {
|
|
return Parser() | static_cast<DerivedT const&>(*this) | other;
|
|
}
|
|
}
|
|
|
|
} // namespace Clara
|
|
} // namespace Catch
|
|
|
|
#if defined( __clang__ )
|
|
# pragma clang diagnostic pop
|
|
#endif
|
|
|
|
#if defined( __GNUC__ )
|
|
# pragma GCC diagnostic pop
|
|
#endif
|
|
|
|
#endif // CATCH_CLARA_HPP_INCLUDED
|
|
|
|
namespace Catch {
|
|
|
|
struct ConfigData;
|
|
|
|
Clara::Parser makeCommandLineParser( ConfigData& config );
|
|
|
|
} // end namespace Catch
|
|
|
|
#endif // CATCH_COMMANDLINE_HPP_INCLUDED
|
|
|
|
namespace Catch {
|
|
|
|
class Session : Detail::NonCopyable {
|
|
public:
|
|
|
|
Session();
|
|
~Session();
|
|
|
|
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<typename CharT>
|
|
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;
|
|
Detail::unique_ptr<Config> m_config;
|
|
bool m_startupExceptions = false;
|
|
};
|
|
|
|
} // end namespace Catch
|
|
|
|
#endif // CATCH_SESSION_HPP_INCLUDED
|
|
|
|
|
|
#ifndef CATCH_TAG_ALIAS_HPP_INCLUDED
|
|
#define CATCH_TAG_ALIAS_HPP_INCLUDED
|
|
|
|
|
|
#include <string>
|
|
|
|
namespace Catch {
|
|
|
|
struct TagAlias {
|
|
TagAlias(std::string const& _tag, SourceLineInfo _lineInfo):
|
|
tag(_tag),
|
|
lineInfo(_lineInfo)
|
|
{}
|
|
|
|
std::string tag;
|
|
SourceLineInfo lineInfo;
|
|
};
|
|
|
|
} // end namespace Catch
|
|
|
|
#endif // CATCH_TAG_ALIAS_HPP_INCLUDED
|
|
|
|
|
|
#ifndef CATCH_TAG_ALIAS_AUTOREGISTRAR_HPP_INCLUDED
|
|
#define CATCH_TAG_ALIAS_AUTOREGISTRAR_HPP_INCLUDED
|
|
|
|
|
|
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
|
|
|
|
#endif // CATCH_TAG_ALIAS_AUTOREGISTRAR_HPP_INCLUDED
|
|
|
|
|
|
#ifndef CATCH_TEMPLATE_TEST_MACROS_HPP_INCLUDED
|
|
#define CATCH_TEMPLATE_TEST_MACROS_HPP_INCLUDED
|
|
|
|
// We need this suppression to leak, because it took until GCC 10
|
|
// for the front end to handle local suppression via _Pragma properly
|
|
// inside templates (so `TEMPLATE_TEST_CASE` and co).
|
|
// **THIS IS DIFFERENT FOR STANDARD TESTS, WHERE GCC 9 IS SUFFICIENT**
|
|
#if defined(__GNUC__) && !defined(__clang__) && !defined(__ICC) && __GNUC__ < 10
|
|
#pragma GCC diagnostic ignored "-Wparentheses"
|
|
#endif
|
|
|
|
|
|
|
|
|
|
#ifndef CATCH_TEST_MACROS_HPP_INCLUDED
|
|
#define CATCH_TEST_MACROS_HPP_INCLUDED
|
|
|
|
|
|
|
|
#ifndef CATCH_TEST_MACRO_IMPL_HPP_INCLUDED
|
|
#define CATCH_TEST_MACRO_IMPL_HPP_INCLUDED
|
|
|
|
|
|
|
|
#ifndef CATCH_ASSERTION_HANDLER_HPP_INCLUDED
|
|
#define CATCH_ASSERTION_HANDLER_HPP_INCLUDED
|
|
|
|
|
|
|
|
#ifndef CATCH_DECOMPOSER_HPP_INCLUDED
|
|
#define CATCH_DECOMPOSER_HPP_INCLUDED
|
|
|
|
|
|
|
|
#ifndef CATCH_COMPARE_TRAITS_HPP_INCLUDED
|
|
#define CATCH_COMPARE_TRAITS_HPP_INCLUDED
|
|
|
|
|
|
#include <type_traits>
|
|
|
|
namespace Catch {
|
|
namespace Detail {
|
|
|
|
#if defined( __GNUC__ ) && !defined( __clang__ )
|
|
# pragma GCC diagnostic push
|
|
// GCC likes to complain about comparing bool with 0, in the decltype()
|
|
// that defines the comparable traits below.
|
|
# pragma GCC diagnostic ignored "-Wbool-compare"
|
|
// "ordered comparison of pointer with integer zero" same as above,
|
|
// but it does not have a separate warning flag to suppress
|
|
# pragma GCC diagnostic ignored "-Wextra"
|
|
// Did you know that comparing floats with `0` directly
|
|
// is super-duper dangerous in unevaluated context?
|
|
# pragma GCC diagnostic ignored "-Wfloat-equal"
|
|
#endif
|
|
|
|
#if defined( __clang__ )
|
|
# pragma clang diagnostic push
|
|
// Did you know that comparing floats with `0` directly
|
|
// is super-duper dangerous in unevaluated context?
|
|
# pragma clang diagnostic ignored "-Wfloat-equal"
|
|
#endif
|
|
|
|
#define CATCH_DEFINE_COMPARABLE_TRAIT( id, op ) \
|
|
template <typename, typename, typename = void> \
|
|
struct is_##id##_comparable : std::false_type {}; \
|
|
template <typename T, typename U> \
|
|
struct is_##id##_comparable< \
|
|
T, \
|
|
U, \
|
|
void_t<decltype( std::declval<T>() op std::declval<U>() )>> \
|
|
: std::true_type {}; \
|
|
template <typename, typename = void> \
|
|
struct is_##id##_0_comparable : std::false_type {}; \
|
|
template <typename T> \
|
|
struct is_##id##_0_comparable<T, \
|
|
void_t<decltype( std::declval<T>() op 0 )>> \
|
|
: std::true_type {};
|
|
|
|
// We need all 6 pre-spaceship comparison ops: <, <=, >, >=, ==, !=
|
|
CATCH_DEFINE_COMPARABLE_TRAIT( lt, < )
|
|
CATCH_DEFINE_COMPARABLE_TRAIT( le, <= )
|
|
CATCH_DEFINE_COMPARABLE_TRAIT( gt, > )
|
|
CATCH_DEFINE_COMPARABLE_TRAIT( ge, >= )
|
|
CATCH_DEFINE_COMPARABLE_TRAIT( eq, == )
|
|
CATCH_DEFINE_COMPARABLE_TRAIT( ne, != )
|
|
|
|
#undef CATCH_DEFINE_COMPARABLE_TRAIT
|
|
|
|
#if defined( __GNUC__ ) && !defined( __clang__ )
|
|
# pragma GCC diagnostic pop
|
|
#endif
|
|
#if defined( __clang__ )
|
|
# pragma clang diagnostic pop
|
|
#endif
|
|
|
|
|
|
} // namespace Detail
|
|
} // namespace Catch
|
|
|
|
#endif // CATCH_COMPARE_TRAITS_HPP_INCLUDED
|
|
|
|
|
|
#ifndef CATCH_LOGICAL_TRAITS_HPP_INCLUDED
|
|
#define CATCH_LOGICAL_TRAITS_HPP_INCLUDED
|
|
|
|
#include <type_traits>
|
|
|
|
namespace Catch {
|
|
namespace Detail {
|
|
|
|
#if defined( __cpp_lib_logical_traits ) && __cpp_lib_logical_traits >= 201510
|
|
|
|
using std::conjunction;
|
|
using std::disjunction;
|
|
using std::negation;
|
|
|
|
#else
|
|
|
|
template <class...> struct conjunction : std::true_type {};
|
|
template <class B1> struct conjunction<B1> : B1 {};
|
|
template <class B1, class... Bn>
|
|
struct conjunction<B1, Bn...>
|
|
: std::conditional_t<bool( B1::value ), conjunction<Bn...>, B1> {};
|
|
|
|
template <class...> struct disjunction : std::false_type {};
|
|
template <class B1> struct disjunction<B1> : B1 {};
|
|
template <class B1, class... Bn>
|
|
struct disjunction<B1, Bn...>
|
|
: std::conditional_t<bool( B1::value ), B1, disjunction<Bn...>> {};
|
|
|
|
template <class B>
|
|
struct negation : std::integral_constant<bool, !bool(B::value)> {};
|
|
|
|
#endif
|
|
|
|
} // namespace Detail
|
|
} // namespace Catch
|
|
|
|
#endif // CATCH_LOGICAL_TRAITS_HPP_INCLUDED
|
|
|
|
#include <type_traits>
|
|
#include <iosfwd>
|
|
|
|
#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
|
|
|
|
#ifdef __clang__
|
|
# pragma clang diagnostic push
|
|
# pragma clang diagnostic ignored "-Wsign-compare"
|
|
#elif defined __GNUC__
|
|
# pragma GCC diagnostic push
|
|
# pragma GCC diagnostic ignored "-Wsign-compare"
|
|
#endif
|
|
|
|
namespace Catch {
|
|
|
|
template <typename T>
|
|
struct always_false : std::false_type {};
|
|
|
|
class ITransientExpression {
|
|
bool m_isBinaryExpression;
|
|
bool m_result;
|
|
|
|
public:
|
|
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 )
|
|
{}
|
|
|
|
ITransientExpression() = default;
|
|
ITransientExpression(ITransientExpression const&) = default;
|
|
ITransientExpression& operator=(ITransientExpression const&) = default;
|
|
|
|
// We don't actually need a virtual destructor, but many static analysers
|
|
// complain if it's not here :-(
|
|
virtual ~ITransientExpression(); // = default;
|
|
|
|
friend std::ostream& operator<<(std::ostream& out, ITransientExpression const& expr) {
|
|
expr.streamReconstructedExpression(out);
|
|
return out;
|
|
}
|
|
};
|
|
|
|
void formatReconstructedExpression( std::ostream &os, std::string const& lhs, StringRef op, std::string const& rhs );
|
|
|
|
template<typename LhsT, typename RhsT>
|
|
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<typename T>
|
|
auto operator && ( T ) const -> BinaryExpr<LhsT, RhsT const&> const {
|
|
static_assert(always_false<T>::value,
|
|
"chained comparisons are not supported inside assertions, "
|
|
"wrap the expression inside parentheses, or decompose it");
|
|
}
|
|
|
|
template<typename T>
|
|
auto operator || ( T ) const -> BinaryExpr<LhsT, RhsT const&> const {
|
|
static_assert(always_false<T>::value,
|
|
"chained comparisons are not supported inside assertions, "
|
|
"wrap the expression inside parentheses, or decompose it");
|
|
}
|
|
|
|
template<typename T>
|
|
auto operator == ( T ) const -> BinaryExpr<LhsT, RhsT const&> const {
|
|
static_assert(always_false<T>::value,
|
|
"chained comparisons are not supported inside assertions, "
|
|
"wrap the expression inside parentheses, or decompose it");
|
|
}
|
|
|
|
template<typename T>
|
|
auto operator != ( T ) const -> BinaryExpr<LhsT, RhsT const&> const {
|
|
static_assert(always_false<T>::value,
|
|
"chained comparisons are not supported inside assertions, "
|
|
"wrap the expression inside parentheses, or decompose it");
|
|
}
|
|
|
|
template<typename T>
|
|
auto operator > ( T ) const -> BinaryExpr<LhsT, RhsT const&> const {
|
|
static_assert(always_false<T>::value,
|
|
"chained comparisons are not supported inside assertions, "
|
|
"wrap the expression inside parentheses, or decompose it");
|
|
}
|
|
|
|
template<typename T>
|
|
auto operator < ( T ) const -> BinaryExpr<LhsT, RhsT const&> const {
|
|
static_assert(always_false<T>::value,
|
|
"chained comparisons are not supported inside assertions, "
|
|
"wrap the expression inside parentheses, or decompose it");
|
|
}
|
|
|
|
template<typename T>
|
|
auto operator >= ( T ) const -> BinaryExpr<LhsT, RhsT const&> const {
|
|
static_assert(always_false<T>::value,
|
|
"chained comparisons are not supported inside assertions, "
|
|
"wrap the expression inside parentheses, or decompose it");
|
|
}
|
|
|
|
template<typename T>
|
|
auto operator <= ( T ) const -> BinaryExpr<LhsT, RhsT const&> const {
|
|
static_assert(always_false<T>::value,
|
|
"chained comparisons are not supported inside assertions, "
|
|
"wrap the expression inside parentheses, or decompose it");
|
|
}
|
|
};
|
|
|
|
template<typename LhsT>
|
|
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<bool>(lhs) },
|
|
m_lhs( lhs )
|
|
{}
|
|
};
|
|
|
|
|
|
template<typename LhsT>
|
|
class ExprLhs {
|
|
LhsT m_lhs;
|
|
public:
|
|
explicit ExprLhs( LhsT lhs ) : m_lhs( lhs ) {}
|
|
|
|
#define CATCH_INTERNAL_DEFINE_EXPRESSION_EQUALITY_OPERATOR( id, op ) \
|
|
template <typename RhsT> \
|
|
friend auto operator op( ExprLhs&& lhs, RhsT&& rhs ) \
|
|
->std::enable_if_t< \
|
|
Detail::conjunction<Detail::is_##id##_comparable<LhsT, RhsT>, \
|
|
Detail::negation<std::is_arithmetic< \
|
|
std::remove_reference_t<RhsT>>>>::value, \
|
|
BinaryExpr<LhsT, RhsT const&>> { \
|
|
return { \
|
|
static_cast<bool>( lhs.m_lhs op rhs ), lhs.m_lhs, #op##_sr, rhs }; \
|
|
} \
|
|
template <typename RhsT> \
|
|
friend auto operator op( ExprLhs&& lhs, RhsT rhs ) \
|
|
->std::enable_if_t< \
|
|
Detail::conjunction<Detail::is_##id##_comparable<LhsT, RhsT>, \
|
|
std::is_arithmetic<RhsT>>::value, \
|
|
BinaryExpr<LhsT, RhsT>> { \
|
|
return { \
|
|
static_cast<bool>( lhs.m_lhs op rhs ), lhs.m_lhs, #op##_sr, rhs }; \
|
|
} \
|
|
template <typename RhsT> \
|
|
friend auto operator op( ExprLhs&& lhs, RhsT rhs ) \
|
|
->std::enable_if_t< \
|
|
Detail::conjunction< \
|
|
Detail::negation<Detail::is_##id##_comparable<LhsT, RhsT>>, \
|
|
Detail::is_eq_0_comparable<LhsT>, \
|
|
/* We allow long because we want `ptr op NULL` to be accepted */ \
|
|
Detail::disjunction<std::is_same<RhsT, int>, \
|
|
std::is_same<RhsT, long>>>::value, \
|
|
BinaryExpr<LhsT, RhsT>> { \
|
|
if ( rhs != 0 ) { throw_test_failure_exception(); } \
|
|
return { \
|
|
static_cast<bool>( lhs.m_lhs op 0 ), lhs.m_lhs, #op##_sr, rhs }; \
|
|
} \
|
|
template <typename RhsT> \
|
|
friend auto operator op( ExprLhs&& lhs, RhsT rhs ) \
|
|
->std::enable_if_t< \
|
|
Detail::conjunction< \
|
|
Detail::negation<Detail::is_##id##_comparable<LhsT, RhsT>>, \
|
|
Detail::is_eq_0_comparable<RhsT>, \
|
|
/* We allow long because we want `ptr op NULL` to be accepted */ \
|
|
Detail::disjunction<std::is_same<LhsT, int>, \
|
|
std::is_same<LhsT, long>>>::value, \
|
|
BinaryExpr<LhsT, RhsT>> { \
|
|
if ( lhs.m_lhs != 0 ) { throw_test_failure_exception(); } \
|
|
return { static_cast<bool>( 0 op rhs ), lhs.m_lhs, #op##_sr, rhs }; \
|
|
}
|
|
|
|
CATCH_INTERNAL_DEFINE_EXPRESSION_EQUALITY_OPERATOR( eq, == )
|
|
CATCH_INTERNAL_DEFINE_EXPRESSION_EQUALITY_OPERATOR( ne, != )
|
|
|
|
#undef CATCH_INTERNAL_DEFINE_EXPRESSION_EQUALITY_OPERATOR
|
|
|
|
#define CATCH_INTERNAL_DEFINE_EXPRESSION_COMPARISON_OPERATOR( id, op ) \
|
|
template <typename RhsT> \
|
|
friend auto operator op( ExprLhs&& lhs, RhsT&& rhs ) \
|
|
->std::enable_if_t< \
|
|
Detail::conjunction<Detail::is_##id##_comparable<LhsT, RhsT>, \
|
|
Detail::negation<std::is_arithmetic< \
|
|
std::remove_reference_t<RhsT>>>>::value, \
|
|
BinaryExpr<LhsT, RhsT const&>> { \
|
|
return { \
|
|
static_cast<bool>( lhs.m_lhs op rhs ), lhs.m_lhs, #op##_sr, rhs }; \
|
|
} \
|
|
template <typename RhsT> \
|
|
friend auto operator op( ExprLhs&& lhs, RhsT rhs ) \
|
|
->std::enable_if_t< \
|
|
Detail::conjunction<Detail::is_##id##_comparable<LhsT, RhsT>, \
|
|
std::is_arithmetic<RhsT>>::value, \
|
|
BinaryExpr<LhsT, RhsT>> { \
|
|
return { \
|
|
static_cast<bool>( lhs.m_lhs op rhs ), lhs.m_lhs, #op##_sr, rhs }; \
|
|
} \
|
|
template <typename RhsT> \
|
|
friend auto operator op( ExprLhs&& lhs, RhsT rhs ) \
|
|
->std::enable_if_t< \
|
|
Detail::conjunction< \
|
|
Detail::negation<Detail::is_##id##_comparable<LhsT, RhsT>>, \
|
|
Detail::is_##id##_0_comparable<LhsT>, \
|
|
std::is_same<RhsT, int>>::value, \
|
|
BinaryExpr<LhsT, RhsT>> { \
|
|
if ( rhs != 0 ) { throw_test_failure_exception(); } \
|
|
return { \
|
|
static_cast<bool>( lhs.m_lhs op 0 ), lhs.m_lhs, #op##_sr, rhs }; \
|
|
} \
|
|
template <typename RhsT> \
|
|
friend auto operator op( ExprLhs&& lhs, RhsT rhs ) \
|
|
->std::enable_if_t< \
|
|
Detail::conjunction< \
|
|
Detail::negation<Detail::is_##id##_comparable<LhsT, RhsT>>, \
|
|
Detail::is_##id##_0_comparable<RhsT>, \
|
|
std::is_same<LhsT, int>>::value, \
|
|
BinaryExpr<LhsT, RhsT>> { \
|
|
if ( lhs.m_lhs != 0 ) { throw_test_failure_exception(); } \
|
|
return { static_cast<bool>( 0 op rhs ), lhs.m_lhs, #op##_sr, rhs }; \
|
|
}
|
|
|
|
CATCH_INTERNAL_DEFINE_EXPRESSION_COMPARISON_OPERATOR( lt, < )
|
|
CATCH_INTERNAL_DEFINE_EXPRESSION_COMPARISON_OPERATOR( le, <= )
|
|
CATCH_INTERNAL_DEFINE_EXPRESSION_COMPARISON_OPERATOR( gt, > )
|
|
CATCH_INTERNAL_DEFINE_EXPRESSION_COMPARISON_OPERATOR( ge, >= )
|
|
|
|
#undef CATCH_INTERNAL_DEFINE_EXPRESSION_COMPARISON_OPERATOR
|
|
|
|
|
|
#define CATCH_INTERNAL_DEFINE_EXPRESSION_OPERATOR( op ) \
|
|
template <typename RhsT> \
|
|
friend auto operator op( ExprLhs&& lhs, RhsT&& rhs ) \
|
|
->std::enable_if_t< \
|
|
!std::is_arithmetic<std::remove_reference_t<RhsT>>::value, \
|
|
BinaryExpr<LhsT, RhsT const&>> { \
|
|
return { \
|
|
static_cast<bool>( lhs.m_lhs op rhs ), lhs.m_lhs, #op##_sr, rhs }; \
|
|
} \
|
|
template <typename RhsT> \
|
|
friend auto operator op( ExprLhs&& lhs, RhsT rhs ) \
|
|
->std::enable_if_t<std::is_arithmetic<RhsT>::value, \
|
|
BinaryExpr<LhsT, RhsT>> { \
|
|
return { \
|
|
static_cast<bool>( lhs.m_lhs op rhs ), lhs.m_lhs, #op##_sr, rhs }; \
|
|
}
|
|
|
|
CATCH_INTERNAL_DEFINE_EXPRESSION_OPERATOR(|)
|
|
CATCH_INTERNAL_DEFINE_EXPRESSION_OPERATOR(&)
|
|
CATCH_INTERNAL_DEFINE_EXPRESSION_OPERATOR(^)
|
|
|
|
#undef CATCH_INTERNAL_DEFINE_EXPRESSION_OPERATOR
|
|
|
|
template<typename RhsT>
|
|
friend auto operator && ( ExprLhs &&, RhsT && ) -> BinaryExpr<LhsT, RhsT const&> {
|
|
static_assert(always_false<RhsT>::value,
|
|
"operator&& is not supported inside assertions, "
|
|
"wrap the expression inside parentheses, or decompose it");
|
|
}
|
|
|
|
template<typename RhsT>
|
|
friend auto operator || ( ExprLhs &&, RhsT && ) -> BinaryExpr<LhsT, RhsT const&> {
|
|
static_assert(always_false<RhsT>::value,
|
|
"operator|| is not supported inside assertions, "
|
|
"wrap the expression inside parentheses, or decompose it");
|
|
}
|
|
|
|
auto makeUnaryExpr() const -> UnaryExpr<LhsT> {
|
|
return UnaryExpr<LhsT>{ m_lhs };
|
|
}
|
|
};
|
|
|
|
struct Decomposer {
|
|
template<typename T, std::enable_if_t<!std::is_arithmetic<std::remove_reference_t<T>>::value, int> = 0>
|
|
friend auto operator <= ( Decomposer &&, T && lhs ) -> ExprLhs<T const&> {
|
|
return ExprLhs<const T&>{ lhs };
|
|
}
|
|
|
|
template<typename T, std::enable_if_t<std::is_arithmetic<T>::value, int> = 0>
|
|
friend auto operator <= ( Decomposer &&, T value ) -> ExprLhs<T> {
|
|
return ExprLhs<T>{ value };
|
|
}
|
|
};
|
|
|
|
} // end namespace Catch
|
|
|
|
#ifdef _MSC_VER
|
|
#pragma warning(pop)
|
|
#endif
|
|
#ifdef __clang__
|
|
# pragma clang diagnostic pop
|
|
#elif defined __GNUC__
|
|
# pragma GCC diagnostic pop
|
|
#endif
|
|
|
|
#endif // CATCH_DECOMPOSER_HPP_INCLUDED
|
|
|
|
#include <string>
|
|
|
|
namespace Catch {
|
|
|
|
struct AssertionReaction {
|
|
bool shouldDebugBreak = false;
|
|
bool shouldThrow = false;
|
|
bool shouldSkip = false;
|
|
};
|
|
|
|
class AssertionHandler {
|
|
AssertionInfo m_assertionInfo;
|
|
AssertionReaction m_reaction;
|
|
bool m_completed = false;
|
|
IResultCapture& m_resultCapture;
|
|
|
|
public:
|
|
AssertionHandler
|
|
( StringRef macroName,
|
|
SourceLineInfo const& lineInfo,
|
|
StringRef capturedExpression,
|
|
ResultDisposition::Flags resultDisposition );
|
|
~AssertionHandler() {
|
|
if ( !m_completed ) {
|
|
m_resultCapture.handleIncomplete( m_assertionInfo );
|
|
}
|
|
}
|
|
|
|
|
|
template<typename T>
|
|
void handleExpr( ExprLhs<T> const& expr ) {
|
|
handleExpr( expr.makeUnaryExpr() );
|
|
}
|
|
void handleExpr( ITransientExpression const& expr );
|
|
|
|
void handleMessage(ResultWas::OfType resultType, StringRef message);
|
|
|
|
void handleExceptionThrownAsExpected();
|
|
void handleUnexpectedExceptionNotThrown();
|
|
void handleExceptionNotThrownAsExpected();
|
|
void handleThrowingCallSkipped();
|
|
void handleUnexpectedInflightException();
|
|
|
|
void complete();
|
|
|
|
// query
|
|
auto allowThrows() const -> bool;
|
|
};
|
|
|
|
void handleExceptionMatchExpr( AssertionHandler& handler, std::string const& str );
|
|
|
|
} // namespace Catch
|
|
|
|
#endif // CATCH_ASSERTION_HANDLER_HPP_INCLUDED
|
|
|
|
|
|
#ifndef CATCH_PREPROCESSOR_INTERNAL_STRINGIFY_HPP_INCLUDED
|
|
#define CATCH_PREPROCESSOR_INTERNAL_STRINGIFY_HPP_INCLUDED
|
|
|
|
|
|
#if !defined(CATCH_CONFIG_DISABLE_STRINGIFICATION)
|
|
#define CATCH_INTERNAL_STRINGIFY(...) #__VA_ARGS__##_catch_sr
|
|
#else
|
|
#define CATCH_INTERNAL_STRINGIFY(...) "Disabled by CATCH_CONFIG_DISABLE_STRINGIFICATION"_catch_sr
|
|
#endif
|
|
|
|
#endif // CATCH_PREPROCESSOR_INTERNAL_STRINGIFY_HPP_INCLUDED
|
|
|
|
// We need this suppression to leak, because it took until GCC 10
|
|
// for the front end to handle local suppression via _Pragma properly
|
|
#if defined(__GNUC__) && !defined(__clang__) && !defined(__ICC) && __GNUC__ <= 9
|
|
#pragma GCC diagnostic ignored "-Wparentheses"
|
|
#endif
|
|
|
|
#if !defined(CATCH_CONFIG_DISABLE)
|
|
|
|
#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 { /* NOLINT(bugprone-infinite-loop) */ \
|
|
/* The expression should not be evaluated, but warnings should hopefully be checked */ \
|
|
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<const bool&>( !!(__VA_ARGS__) ) ) // the expression here is never evaluated at runtime but it forces the compiler to give it a look
|
|
// The double negation silences MSVC's C4800 warning, the static_cast forces short-circuit evaluation if the type has overloaded &&.
|
|
|
|
///////////////////////////////////////////////////////////////////////////////
|
|
#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 { \
|
|
CATCH_INTERNAL_START_WARNINGS_SUPPRESSION \
|
|
CATCH_INTERNAL_SUPPRESS_USELESS_CAST_WARNINGS \
|
|
static_cast<void>(__VA_ARGS__); \
|
|
CATCH_INTERNAL_STOP_WARNINGS_SUPPRESSION \
|
|
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 { \
|
|
CATCH_INTERNAL_START_WARNINGS_SUPPRESSION \
|
|
CATCH_INTERNAL_SUPPRESS_UNUSED_RESULT \
|
|
CATCH_INTERNAL_SUPPRESS_USELESS_CAST_WARNINGS \
|
|
static_cast<void>(__VA_ARGS__); \
|
|
CATCH_INTERNAL_STOP_WARNINGS_SUPPRESSION \
|
|
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 { \
|
|
CATCH_INTERNAL_START_WARNINGS_SUPPRESSION \
|
|
CATCH_INTERNAL_SUPPRESS_UNUSED_RESULT \
|
|
CATCH_INTERNAL_SUPPRESS_USELESS_CAST_WARNINGS \
|
|
static_cast<void>(expr); \
|
|
CATCH_INTERNAL_STOP_WARNINGS_SUPPRESSION \
|
|
catchAssertionHandler.handleUnexpectedExceptionNotThrown(); \
|
|
} \
|
|
catch( exceptionType const& ) { \
|
|
catchAssertionHandler.handleExceptionThrownAsExpected(); \
|
|
} \
|
|
catch( ... ) { \
|
|
catchAssertionHandler.handleUnexpectedInflightException(); \
|
|
} \
|
|
else \
|
|
catchAssertionHandler.handleThrowingCallSkipped(); \
|
|
INTERNAL_CATCH_REACT( catchAssertionHandler ) \
|
|
} while( false )
|
|
|
|
|
|
|
|
///////////////////////////////////////////////////////////////////////////////
|
|
// 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 { \
|
|
CATCH_INTERNAL_START_WARNINGS_SUPPRESSION \
|
|
CATCH_INTERNAL_SUPPRESS_UNUSED_RESULT \
|
|
CATCH_INTERNAL_SUPPRESS_USELESS_CAST_WARNINGS \
|
|
static_cast<void>(__VA_ARGS__); \
|
|
CATCH_INTERNAL_STOP_WARNINGS_SUPPRESSION \
|
|
catchAssertionHandler.handleUnexpectedExceptionNotThrown(); \
|
|
} \
|
|
catch( ... ) { \
|
|
Catch::handleExceptionMatchExpr( catchAssertionHandler, matcher ); \
|
|
} \
|
|
else \
|
|
catchAssertionHandler.handleThrowingCallSkipped(); \
|
|
INTERNAL_CATCH_REACT( catchAssertionHandler ) \
|
|
} while( false )
|
|
|
|
#endif // CATCH_CONFIG_DISABLE
|
|
|
|
#endif // CATCH_TEST_MACRO_IMPL_HPP_INCLUDED
|
|
|
|
|
|
#ifndef CATCH_SECTION_HPP_INCLUDED
|
|
#define CATCH_SECTION_HPP_INCLUDED
|
|
|
|
|
|
|
|
|
|
/** \file
|
|
* Wrapper for the STATIC_ANALYSIS_SUPPORT configuration option
|
|
*
|
|
* Some of Catch2's macros can be defined differently to work better with
|
|
* static analysis tools, like clang-tidy or coverity.
|
|
* Currently the main use case is to show that `SECTION`s are executed
|
|
* exclusively, and not all in one run of a `TEST_CASE`.
|
|
*/
|
|
|
|
#ifndef CATCH_CONFIG_STATIC_ANALYSIS_SUPPORT_HPP_INCLUDED
|
|
#define CATCH_CONFIG_STATIC_ANALYSIS_SUPPORT_HPP_INCLUDED
|
|
|
|
|
|
#if defined(__clang_analyzer__) || defined(__COVERITY__)
|
|
#define CATCH_INTERNAL_CONFIG_STATIC_ANALYSIS_SUPPORT
|
|
#endif
|
|
|
|
#if defined( CATCH_INTERNAL_CONFIG_STATIC_ANALYSIS_SUPPORT ) && \
|
|
!defined( CATCH_CONFIG_NO_EXPERIMENTAL_STATIC_ANALYSIS_SUPPORT ) && \
|
|
!defined( CATCH_CONFIG_EXPERIMENTAL_STATIC_ANALYSIS_SUPPORT )
|
|
# define CATCH_CONFIG_EXPERIMENTAL_STATIC_ANALYSIS_SUPPORT
|
|
#endif
|
|
|
|
|
|
#endif // CATCH_CONFIG_STATIC_ANALYSIS_SUPPORT_HPP_INCLUDED
|
|
|
|
|
|
#ifndef CATCH_TIMER_HPP_INCLUDED
|
|
#define CATCH_TIMER_HPP_INCLUDED
|
|
|
|
#include <cstdint>
|
|
|
|
namespace Catch {
|
|
|
|
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
|
|
|
|
#endif // CATCH_TIMER_HPP_INCLUDED
|
|
|
|
namespace Catch {
|
|
|
|
class Section : Detail::NonCopyable {
|
|
public:
|
|
Section( SectionInfo&& info );
|
|
Section( SourceLineInfo const& _lineInfo,
|
|
StringRef _name,
|
|
const char* const = nullptr );
|
|
~Section();
|
|
|
|
// This indicates whether the section should be executed or not
|
|
explicit operator bool() const;
|
|
|
|
private:
|
|
SectionInfo m_info;
|
|
|
|
Counts m_assertions;
|
|
bool m_sectionIncluded;
|
|
Timer m_timer;
|
|
};
|
|
|
|
} // end namespace Catch
|
|
|
|
#if !defined(CATCH_CONFIG_EXPERIMENTAL_STATIC_ANALYSIS_SUPPORT)
|
|
# define INTERNAL_CATCH_SECTION( ... ) \
|
|
CATCH_INTERNAL_START_WARNINGS_SUPPRESSION \
|
|
CATCH_INTERNAL_SUPPRESS_UNUSED_VARIABLE_WARNINGS \
|
|
if ( Catch::Section const& INTERNAL_CATCH_UNIQUE_NAME( \
|
|
catch_internal_Section ) = \
|
|
Catch::Section( CATCH_INTERNAL_LINEINFO, __VA_ARGS__ ) ) \
|
|
CATCH_INTERNAL_STOP_WARNINGS_SUPPRESSION
|
|
|
|
# define INTERNAL_CATCH_DYNAMIC_SECTION( ... ) \
|
|
CATCH_INTERNAL_START_WARNINGS_SUPPRESSION \
|
|
CATCH_INTERNAL_SUPPRESS_UNUSED_VARIABLE_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
|
|
|
|
#else
|
|
|
|
// These section definitions imply that at most one section at one level
|
|
// will be intered (because only one section's __LINE__ can be equal to
|
|
// the dummy `catchInternalSectionHint` variable from `TEST_CASE`).
|
|
|
|
namespace Catch {
|
|
namespace Detail {
|
|
// Intentionally without linkage, as it should only be used as a dummy
|
|
// symbol for static analysis.
|
|
int GetNewSectionHint();
|
|
} // namespace Detail
|
|
} // namespace Catch
|
|
|
|
|
|
# define INTERNAL_CATCH_SECTION( ... ) \
|
|
CATCH_INTERNAL_START_WARNINGS_SUPPRESSION \
|
|
CATCH_INTERNAL_SUPPRESS_UNUSED_VARIABLE_WARNINGS \
|
|
CATCH_INTERNAL_SUPPRESS_SHADOW_WARNINGS \
|
|
if ( [[maybe_unused]] const int catchInternalPreviousSectionHint = \
|
|
catchInternalSectionHint, \
|
|
catchInternalSectionHint = Catch::Detail::GetNewSectionHint(); \
|
|
catchInternalPreviousSectionHint == __LINE__ ) \
|
|
CATCH_INTERNAL_STOP_WARNINGS_SUPPRESSION
|
|
|
|
# define INTERNAL_CATCH_DYNAMIC_SECTION( ... ) \
|
|
CATCH_INTERNAL_START_WARNINGS_SUPPRESSION \
|
|
CATCH_INTERNAL_SUPPRESS_UNUSED_VARIABLE_WARNINGS \
|
|
CATCH_INTERNAL_SUPPRESS_SHADOW_WARNINGS \
|
|
if ( [[maybe_unused]] const int catchInternalPreviousSectionHint = \
|
|
catchInternalSectionHint, \
|
|
catchInternalSectionHint = Catch::Detail::GetNewSectionHint(); \
|
|
catchInternalPreviousSectionHint == __LINE__ ) \
|
|
CATCH_INTERNAL_STOP_WARNINGS_SUPPRESSION
|
|
|
|
#endif
|
|
|
|
|
|
#endif // CATCH_SECTION_HPP_INCLUDED
|
|
|
|
|
|
#ifndef CATCH_TEST_REGISTRY_HPP_INCLUDED
|
|
#define CATCH_TEST_REGISTRY_HPP_INCLUDED
|
|
|
|
|
|
|
|
#ifndef CATCH_INTERFACES_TEST_INVOKER_HPP_INCLUDED
|
|
#define CATCH_INTERFACES_TEST_INVOKER_HPP_INCLUDED
|
|
|
|
namespace Catch {
|
|
|
|
class ITestInvoker {
|
|
public:
|
|
virtual void invoke() const = 0;
|
|
virtual ~ITestInvoker(); // = default
|
|
};
|
|
|
|
} // namespace Catch
|
|
|
|
#endif // CATCH_INTERFACES_TEST_INVOKER_HPP_INCLUDED
|
|
|
|
|
|
#ifndef CATCH_PREPROCESSOR_REMOVE_PARENS_HPP_INCLUDED
|
|
#define CATCH_PREPROCESSOR_REMOVE_PARENS_HPP_INCLUDED
|
|
|
|
#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_REMOVE_PARENS( ... ) \
|
|
INTERNAL_CATCH_EXPAND1( INTERNAL_CATCH_DEF __VA_ARGS__ )
|
|
|
|
#endif // CATCH_PREPROCESSOR_REMOVE_PARENS_HPP_INCLUDED
|
|
|
|
// GCC 5 and older do not properly handle disabling unused-variable warning
|
|
// with a _Pragma. This means that we have to leak the suppression to the
|
|
// user code as well :-(
|
|
#if defined(__GNUC__) && !defined(__clang__) && __GNUC__ <= 5
|
|
#pragma GCC diagnostic ignored "-Wunused-variable"
|
|
#endif
|
|
|
|
|
|
|
|
namespace Catch {
|
|
|
|
template<typename C>
|
|
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)();
|
|
}
|
|
};
|
|
|
|
Detail::unique_ptr<ITestInvoker> makeTestInvoker( void(*testAsFunction)() );
|
|
|
|
template<typename C>
|
|
Detail::unique_ptr<ITestInvoker> makeTestInvoker( void (C::*testAsMethod)() ) {
|
|
return Detail::make_unique<TestInvokerAsMethod<C>>( testAsMethod );
|
|
}
|
|
|
|
struct NameAndTags {
|
|
constexpr NameAndTags( StringRef name_ = StringRef(),
|
|
StringRef tags_ = StringRef() ) noexcept:
|
|
name( name_ ), tags( tags_ ) {}
|
|
StringRef name;
|
|
StringRef tags;
|
|
};
|
|
|
|
struct AutoReg : Detail::NonCopyable {
|
|
AutoReg( Detail::unique_ptr<ITestInvoker> invoker, SourceLineInfo const& lineInfo, StringRef classOrMethod, NameAndTags const& nameAndTags ) noexcept;
|
|
};
|
|
|
|
} // end namespace Catch
|
|
|
|
#if defined(CATCH_CONFIG_DISABLE)
|
|
#define INTERNAL_CATCH_TESTCASE_NO_REGISTRATION( TestName, ... ) \
|
|
static inline void TestName()
|
|
#define INTERNAL_CATCH_TESTCASE_METHOD_NO_REGISTRATION( TestName, ClassName, ... ) \
|
|
namespace{ \
|
|
struct TestName : INTERNAL_CATCH_REMOVE_PARENS(ClassName) { \
|
|
void test(); \
|
|
}; \
|
|
} \
|
|
void TestName::test()
|
|
#endif
|
|
|
|
|
|
#if !defined(CATCH_CONFIG_EXPERIMENTAL_STATIC_ANALYSIS_SUPPORT)
|
|
|
|
///////////////////////////////////////////////////////////////////////////////
|
|
#define INTERNAL_CATCH_TESTCASE2( TestName, ... ) \
|
|
static void TestName(); \
|
|
CATCH_INTERNAL_START_WARNINGS_SUPPRESSION \
|
|
CATCH_INTERNAL_SUPPRESS_GLOBALS_WARNINGS \
|
|
CATCH_INTERNAL_SUPPRESS_UNUSED_VARIABLE_WARNINGS \
|
|
namespace{ const 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( CATCH2_INTERNAL_TEST_ ), __VA_ARGS__ )
|
|
|
|
#else // ^^ !CATCH_CONFIG_EXPERIMENTAL_STATIC_ANALYSIS_SUPPORT | vv CATCH_CONFIG_EXPERIMENTAL_STATIC_ANALYSIS_SUPPORT
|
|
|
|
|
|
// Dummy registrator for the dumy test case macros
|
|
namespace Catch {
|
|
namespace Detail {
|
|
struct DummyUse {
|
|
DummyUse( void ( * )( int ) );
|
|
};
|
|
} // namespace Detail
|
|
} // namespace Catch
|
|
|
|
// Note that both the presence of the argument and its exact name are
|
|
// necessary for the section support.
|
|
|
|
// We provide a shadowed variable so that a `SECTION` inside non-`TEST_CASE`
|
|
// tests can compile. The redefined `TEST_CASE` shadows this with param.
|
|
static int catchInternalSectionHint = 0;
|
|
|
|
# define INTERNAL_CATCH_TESTCASE2( fname ) \
|
|
static void fname( int ); \
|
|
CATCH_INTERNAL_START_WARNINGS_SUPPRESSION \
|
|
CATCH_INTERNAL_SUPPRESS_GLOBALS_WARNINGS \
|
|
CATCH_INTERNAL_SUPPRESS_UNUSED_VARIABLE_WARNINGS \
|
|
static const Catch::Detail::DummyUse INTERNAL_CATCH_UNIQUE_NAME( \
|
|
dummyUser )( &(fname) ); \
|
|
CATCH_INTERNAL_SUPPRESS_SHADOW_WARNINGS \
|
|
static void fname( [[maybe_unused]] int catchInternalSectionHint ) \
|
|
CATCH_INTERNAL_STOP_WARNINGS_SUPPRESSION
|
|
# define INTERNAL_CATCH_TESTCASE( ... ) \
|
|
INTERNAL_CATCH_TESTCASE2( INTERNAL_CATCH_UNIQUE_NAME( dummyFunction ) )
|
|
|
|
|
|
#endif // CATCH_CONFIG_EXPERIMENTAL_STATIC_ANALYSIS_SUPPORT
|
|
|
|
///////////////////////////////////////////////////////////////////////////////
|
|
#define INTERNAL_CATCH_TEST_CASE_METHOD2( TestName, ClassName, ... )\
|
|
CATCH_INTERNAL_START_WARNINGS_SUPPRESSION \
|
|
CATCH_INTERNAL_SUPPRESS_GLOBALS_WARNINGS \
|
|
CATCH_INTERNAL_SUPPRESS_UNUSED_VARIABLE_WARNINGS \
|
|
namespace{ \
|
|
struct TestName : INTERNAL_CATCH_REMOVE_PARENS(ClassName) { \
|
|
void test(); \
|
|
}; \
|
|
const Catch::AutoReg INTERNAL_CATCH_UNIQUE_NAME( autoRegistrar )( \
|
|
Catch::makeTestInvoker( &TestName::test ), \
|
|
CATCH_INTERNAL_LINEINFO, \
|
|
#ClassName##_catch_sr, \
|
|
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( CATCH2_INTERNAL_TEST_ ), ClassName, __VA_ARGS__ )
|
|
|
|
|
|
///////////////////////////////////////////////////////////////////////////////
|
|
#define INTERNAL_CATCH_METHOD_AS_TEST_CASE( QualifiedMethod, ... ) \
|
|
CATCH_INTERNAL_START_WARNINGS_SUPPRESSION \
|
|
CATCH_INTERNAL_SUPPRESS_GLOBALS_WARNINGS \
|
|
CATCH_INTERNAL_SUPPRESS_UNUSED_VARIABLE_WARNINGS \
|
|
namespace { \
|
|
const Catch::AutoReg INTERNAL_CATCH_UNIQUE_NAME( autoRegistrar )( \
|
|
Catch::makeTestInvoker( &QualifiedMethod ), \
|
|
CATCH_INTERNAL_LINEINFO, \
|
|
"&" #QualifiedMethod##_catch_sr, \
|
|
Catch::NameAndTags{ __VA_ARGS__ } ); \
|
|
} /* NOLINT */ \
|
|
CATCH_INTERNAL_STOP_WARNINGS_SUPPRESSION
|
|
|
|
|
|
///////////////////////////////////////////////////////////////////////////////
|
|
#define INTERNAL_CATCH_REGISTER_TESTCASE( Function, ... ) \
|
|
do { \
|
|
CATCH_INTERNAL_START_WARNINGS_SUPPRESSION \
|
|
CATCH_INTERNAL_SUPPRESS_GLOBALS_WARNINGS \
|
|
CATCH_INTERNAL_SUPPRESS_UNUSED_VARIABLE_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 \
|
|
} while(false)
|
|
|
|
|
|
#endif // CATCH_TEST_REGISTRY_HPP_INCLUDED
|
|
|
|
|
|
// All of our user-facing macros support configuration toggle, that
|
|
// forces them to be defined prefixed with CATCH_. We also like to
|
|
// support another toggle that can minimize (disable) their implementation.
|
|
// Given this, we have 4 different configuration options below
|
|
|
|
#if defined(CATCH_CONFIG_PREFIX_ALL) && !defined(CATCH_CONFIG_DISABLE)
|
|
|
|
#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_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 | Catch::ResultDisposition::SuppressFail, __VA_ARGS__ )
|
|
#define CATCH_CHECKED_ELSE( ... ) INTERNAL_CATCH_ELSE( "CATCH_CHECKED_ELSE", Catch::ResultDisposition::ContinueOnFailure | Catch::ResultDisposition::SuppressFail, __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_NOTHROW( ... ) INTERNAL_CATCH_NO_THROW( "CATCH_CHECK_NOTHROW", Catch::ResultDisposition::ContinueOnFailure, __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_SKIP( ... ) INTERNAL_CATCH_MSG( "SKIP", Catch::ResultWas::ExplicitSkip, Catch::ResultDisposition::Normal, __VA_ARGS__ )
|
|
|
|
|
|
#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__ )
|
|
#define CATCH_STATIC_CHECK( ... ) static_assert( __VA_ARGS__ , #__VA_ARGS__ ); CATCH_SUCCEED( #__VA_ARGS__ )
|
|
#define CATCH_STATIC_CHECK_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__ )
|
|
#define CATCH_STATIC_CHECK( ... ) CATCH_CHECK( __VA_ARGS__ )
|
|
#define CATCH_STATIC_CHECK_FALSE( ... ) CATCH_CHECK_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 )
|
|
|
|
#elif defined(CATCH_CONFIG_PREFIX_ALL) && defined(CATCH_CONFIG_DISABLE) // ^^ prefixed, implemented | vv prefixed, disabled
|
|
|
|
#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_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_NOTHROW( ... ) (void)(0)
|
|
|
|
#define CATCH_TEST_CASE( ... ) INTERNAL_CATCH_TESTCASE_NO_REGISTRATION(INTERNAL_CATCH_UNIQUE_NAME( CATCH2_INTERNAL_TEST_ ))
|
|
#define CATCH_TEST_CASE_METHOD( className, ... ) INTERNAL_CATCH_TESTCASE_NO_REGISTRATION(INTERNAL_CATCH_UNIQUE_NAME( CATCH2_INTERNAL_TEST_ ))
|
|
#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_SKIP( ... ) (void)(0)
|
|
|
|
#define CATCH_STATIC_REQUIRE( ... ) (void)(0)
|
|
#define CATCH_STATIC_REQUIRE_FALSE( ... ) (void)(0)
|
|
#define CATCH_STATIC_CHECK( ... ) (void)(0)
|
|
#define CATCH_STATIC_CHECK_FALSE( ... ) (void)(0)
|
|
|
|
// "BDD-style" convenience wrappers
|
|
#define CATCH_SCENARIO( ... ) INTERNAL_CATCH_TESTCASE_NO_REGISTRATION(INTERNAL_CATCH_UNIQUE_NAME( CATCH2_INTERNAL_TEST_ ))
|
|
#define CATCH_SCENARIO_METHOD( className, ... ) INTERNAL_CATCH_TESTCASE_METHOD_NO_REGISTRATION(INTERNAL_CATCH_UNIQUE_NAME( CATCH2_INTERNAL_TEST_ ), 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 )
|
|
|
|
#elif !defined(CATCH_CONFIG_PREFIX_ALL) && !defined(CATCH_CONFIG_DISABLE) // ^^ prefixed, disabled | vv unprefixed, implemented
|
|
|
|
#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_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 | Catch::ResultDisposition::SuppressFail, __VA_ARGS__ )
|
|
#define CHECKED_ELSE( ... ) INTERNAL_CATCH_ELSE( "CHECKED_ELSE", Catch::ResultDisposition::ContinueOnFailure | Catch::ResultDisposition::SuppressFail, __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_NOTHROW( ... ) INTERNAL_CATCH_NO_THROW( "CHECK_NOTHROW", Catch::ResultDisposition::ContinueOnFailure, __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 SKIP( ... ) INTERNAL_CATCH_MSG( "SKIP", Catch::ResultWas::ExplicitSkip, Catch::ResultDisposition::Normal, __VA_ARGS__ )
|
|
|
|
|
|
#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__ ")" )
|
|
#define STATIC_CHECK( ... ) static_assert( __VA_ARGS__, #__VA_ARGS__ ); SUCCEED( #__VA_ARGS__ )
|
|
#define STATIC_CHECK_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__ )
|
|
#define STATIC_CHECK( ... ) CHECK( __VA_ARGS__ )
|
|
#define STATIC_CHECK_FALSE( ... ) CHECK_FALSE( __VA_ARGS__ )
|
|
#endif
|
|
|
|
// "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 )
|
|
|
|
#elif !defined(CATCH_CONFIG_PREFIX_ALL) && defined(CATCH_CONFIG_DISABLE) // ^^ unprefixed, implemented | vv unprefixed, disabled
|
|
|
|
#define REQUIRE( ... ) (void)(0)
|
|
#define REQUIRE_FALSE( ... ) (void)(0)
|
|
|
|
#define REQUIRE_THROWS( ... ) (void)(0)
|
|
#define REQUIRE_THROWS_AS( expr, exceptionType ) (void)(0)
|
|
#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_NOTHROW( ... ) (void)(0)
|
|
|
|
#define TEST_CASE( ... ) INTERNAL_CATCH_TESTCASE_NO_REGISTRATION(INTERNAL_CATCH_UNIQUE_NAME( CATCH2_INTERNAL_TEST_ ), __VA_ARGS__)
|
|
#define TEST_CASE_METHOD( className, ... ) INTERNAL_CATCH_TESTCASE_NO_REGISTRATION(INTERNAL_CATCH_UNIQUE_NAME( CATCH2_INTERNAL_TEST_ ))
|
|
#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 SKIP( ... ) (void)(0)
|
|
|
|
#define STATIC_REQUIRE( ... ) (void)(0)
|
|
#define STATIC_REQUIRE_FALSE( ... ) (void)(0)
|
|
#define STATIC_CHECK( ... ) (void)(0)
|
|
#define STATIC_CHECK_FALSE( ... ) (void)(0)
|
|
|
|
// "BDD-style" convenience wrappers
|
|
#define SCENARIO( ... ) INTERNAL_CATCH_TESTCASE_NO_REGISTRATION(INTERNAL_CATCH_UNIQUE_NAME( CATCH2_INTERNAL_TEST_ ) )
|
|
#define SCENARIO_METHOD( className, ... ) INTERNAL_CATCH_TESTCASE_METHOD_NO_REGISTRATION(INTERNAL_CATCH_UNIQUE_NAME( CATCH2_INTERNAL_TEST_ ), className )
|
|
|
|
#define GIVEN( desc )
|
|
#define AND_GIVEN( desc )
|
|
#define WHEN( desc )
|
|
#define AND_WHEN( desc )
|
|
#define THEN( desc )
|
|
#define AND_THEN( desc )
|
|
|
|
#endif // ^^ unprefixed, disabled
|
|
|
|
// end of user facing macros
|
|
|
|
#endif // CATCH_TEST_MACROS_HPP_INCLUDED
|
|
|
|
|
|
#ifndef CATCH_TEMPLATE_TEST_REGISTRY_HPP_INCLUDED
|
|
#define CATCH_TEMPLATE_TEST_REGISTRY_HPP_INCLUDED
|
|
|
|
|
|
|
|
#ifndef CATCH_PREPROCESSOR_HPP_INCLUDED
|
|
#define CATCH_PREPROCESSOR_HPP_INCLUDED
|
|
|
|
|
|
#if defined(__GNUC__)
|
|
// We need to silence "empty __VA_ARGS__ warning", and using just _Pragma does not work
|
|
#pragma GCC system_header
|
|
#endif
|
|
|
|
|
|
#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_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)
|
|
|
|
#ifndef CATCH_CONFIG_TRADITIONAL_MSVC_PREPROCESSOR
|
|
#define INTERNAL_CATCH_MAKE_TYPE_LIST2(...) decltype(get_wrapper<INTERNAL_CATCH_REMOVE_PARENS_GEN(__VA_ARGS__)>())
|
|
#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<INTERNAL_CATCH_REMOVE_PARENS_GEN(__VA_ARGS__)>()))
|
|
#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<typename...> struct TypeList {};\
|
|
template<typename...Ts>\
|
|
constexpr auto get_wrapper() noexcept -> TypeList<Ts...> { return {}; }\
|
|
template<template<typename...> class...> struct TemplateTypeList{};\
|
|
template<template<typename...> class...Cs>\
|
|
constexpr auto get_wrapper() noexcept -> TemplateTypeList<Cs...> { return {}; }\
|
|
template<typename...>\
|
|
struct append;\
|
|
template<typename...>\
|
|
struct rewrap;\
|
|
template<template<typename...> class, typename...>\
|
|
struct create;\
|
|
template<template<typename...> class, typename>\
|
|
struct convert;\
|
|
\
|
|
template<typename T> \
|
|
struct append<T> { using type = T; };\
|
|
template< template<typename...> class L1, typename...E1, template<typename...> class L2, typename...E2, typename...Rest>\
|
|
struct append<L1<E1...>, L2<E2...>, Rest...> { using type = typename append<L1<E1...,E2...>, Rest...>::type; };\
|
|
template< template<typename...> class L1, typename...E1, typename...Rest>\
|
|
struct append<L1<E1...>, TypeList<mpl_::na>, Rest...> { using type = L1<E1...>; };\
|
|
\
|
|
template< template<typename...> class Container, template<typename...> class List, typename...elems>\
|
|
struct rewrap<TemplateTypeList<Container>, List<elems...>> { using type = TypeList<Container<elems...>>; };\
|
|
template< template<typename...> class Container, template<typename...> class List, class...Elems, typename...Elements>\
|
|
struct rewrap<TemplateTypeList<Container>, List<Elems...>, Elements...> { using type = typename append<TypeList<Container<Elems...>>, typename rewrap<TemplateTypeList<Container>, Elements...>::type>::type; };\
|
|
\
|
|
template<template <typename...> class Final, template< typename...> class...Containers, typename...Types>\
|
|
struct create<Final, TemplateTypeList<Containers...>, TypeList<Types...>> { using type = typename append<Final<>, typename rewrap<TemplateTypeList<Containers>, Types...>::type...>::type; };\
|
|
template<template <typename...> class Final, template <typename...> class List, typename...Ts>\
|
|
struct convert<Final, List<Ts...>> { using type = typename append<Final<>,TypeList<Ts>...>::type; };
|
|
|
|
#define INTERNAL_CATCH_NTTP_1(signature, ...)\
|
|
template<INTERNAL_CATCH_REMOVE_PARENS(signature)> struct Nttp{};\
|
|
template<INTERNAL_CATCH_REMOVE_PARENS(signature)>\
|
|
constexpr auto get_wrapper() noexcept -> Nttp<__VA_ARGS__> { return {}; } \
|
|
template<template<INTERNAL_CATCH_REMOVE_PARENS(signature)> class...> struct NttpTemplateTypeList{};\
|
|
template<template<INTERNAL_CATCH_REMOVE_PARENS(signature)> class...Cs>\
|
|
constexpr auto get_wrapper() noexcept -> NttpTemplateTypeList<Cs...> { return {}; } \
|
|
\
|
|
template< template<INTERNAL_CATCH_REMOVE_PARENS(signature)> class Container, template<INTERNAL_CATCH_REMOVE_PARENS(signature)> class List, INTERNAL_CATCH_REMOVE_PARENS(signature)>\
|
|
struct rewrap<NttpTemplateTypeList<Container>, List<__VA_ARGS__>> { using type = TypeList<Container<__VA_ARGS__>>; };\
|
|
template< template<INTERNAL_CATCH_REMOVE_PARENS(signature)> class Container, template<INTERNAL_CATCH_REMOVE_PARENS(signature)> class List, INTERNAL_CATCH_REMOVE_PARENS(signature), typename...Elements>\
|
|
struct rewrap<NttpTemplateTypeList<Container>, List<__VA_ARGS__>, Elements...> { using type = typename append<TypeList<Container<__VA_ARGS__>>, typename rewrap<NttpTemplateTypeList<Container>, Elements...>::type>::type; };\
|
|
template<template <typename...> class Final, template<INTERNAL_CATCH_REMOVE_PARENS(signature)> class...Containers, typename...Types>\
|
|
struct create<Final, NttpTemplateTypeList<Containers...>, TypeList<Types...>> { using type = typename append<Final<>, typename rewrap<NttpTemplateTypeList<Containers>, Types...>::type...>::type; };
|
|
|
|
#define INTERNAL_CATCH_DECLARE_SIG_TEST0(TestName)
|
|
#define INTERNAL_CATCH_DECLARE_SIG_TEST1(TestName, signature)\
|
|
template<INTERNAL_CATCH_REMOVE_PARENS(signature)>\
|
|
static void TestName()
|
|
#define INTERNAL_CATCH_DECLARE_SIG_TEST_X(TestName, signature, ...)\
|
|
template<INTERNAL_CATCH_REMOVE_PARENS(signature)>\
|
|
static void TestName()
|
|
|
|
#define INTERNAL_CATCH_DEFINE_SIG_TEST0(TestName)
|
|
#define INTERNAL_CATCH_DEFINE_SIG_TEST1(TestName, signature)\
|
|
template<INTERNAL_CATCH_REMOVE_PARENS(signature)>\
|
|
static void TestName()
|
|
#define INTERNAL_CATCH_DEFINE_SIG_TEST_X(TestName, signature,...)\
|
|
template<INTERNAL_CATCH_REMOVE_PARENS(signature)>\
|
|
static void TestName()
|
|
|
|
#define INTERNAL_CATCH_NTTP_REGISTER0(TestFunc, signature)\
|
|
template<typename Type>\
|
|
void reg_test(TypeList<Type>, Catch::NameAndTags nameAndTags)\
|
|
{\
|
|
Catch::AutoReg( Catch::makeTestInvoker(&TestFunc<Type>), CATCH_INTERNAL_LINEINFO, Catch::StringRef(), nameAndTags);\
|
|
}
|
|
|
|
#define INTERNAL_CATCH_NTTP_REGISTER(TestFunc, signature, ...)\
|
|
template<INTERNAL_CATCH_REMOVE_PARENS(signature)>\
|
|
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<typename Type>\
|
|
void reg_test(TypeList<Type>, Catch::StringRef className, Catch::NameAndTags nameAndTags)\
|
|
{\
|
|
Catch::AutoReg( Catch::makeTestInvoker(&TestName<Type>::test), CATCH_INTERNAL_LINEINFO, className, nameAndTags);\
|
|
}
|
|
|
|
#define INTERNAL_CATCH_NTTP_REGISTER_METHOD(TestName, signature, ...)\
|
|
template<INTERNAL_CATCH_REMOVE_PARENS(signature)>\
|
|
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<typename TestType> \
|
|
struct TestName : INTERNAL_CATCH_REMOVE_PARENS(ClassName)<TestType> { \
|
|
void test();\
|
|
}
|
|
|
|
#define INTERNAL_CATCH_DECLARE_SIG_TEST_METHOD_X(TestName, ClassName, signature, ...)\
|
|
template<INTERNAL_CATCH_REMOVE_PARENS(signature)> \
|
|
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<typename TestType> \
|
|
void INTERNAL_CATCH_MAKE_NAMESPACE(TestName)::TestName<TestType>::test()
|
|
#define INTERNAL_CATCH_DEFINE_SIG_TEST_METHOD_X(TestName, signature, ...)\
|
|
template<INTERNAL_CATCH_REMOVE_PARENS(signature)> \
|
|
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
|
|
|
|
#endif // CATCH_PREPROCESSOR_HPP_INCLUDED
|
|
|
|
|
|
// GCC 5 and older do not properly handle disabling unused-variable warning
|
|
// with a _Pragma. This means that we have to leak the suppression to the
|
|
// user code as well :-(
|
|
#if defined(__GNUC__) && !defined(__clang__) && __GNUC__ <= 5
|
|
#pragma GCC diagnostic ignored "-Wunused-variable"
|
|
#endif
|
|
|
|
#if defined(CATCH_CONFIG_DISABLE)
|
|
#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( CATCH2_INTERNAL_TEMPLATE_TEST_ ), INTERNAL_CATCH_UNIQUE_NAME( CATCH2_INTERNAL_TEMPLATE_TEST_ ), 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( CATCH2_INTERNAL_TEMPLATE_TEST_ ), INTERNAL_CATCH_UNIQUE_NAME( CATCH2_INTERNAL_TEMPLATE_TEST_ ), 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( CATCH2_INTERNAL_TEMPLATE_TEST_ ), INTERNAL_CATCH_UNIQUE_NAME( CATCH2_INTERNAL_TEMPLATE_TEST_ ), 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( CATCH2_INTERNAL_TEMPLATE_TEST_ ), INTERNAL_CATCH_UNIQUE_NAME( CATCH2_INTERNAL_TEMPLATE_TEST_ ), 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( CATCH2_INTERNAL_TEMPLATE_TEST_CLASS_ ), INTERNAL_CATCH_UNIQUE_NAME( CATCH2_INTERNAL_TEMPLATE_TEST_ ) , 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( CATCH2_INTERNAL_TEMPLATE_TEST_CLASS_ ), INTERNAL_CATCH_UNIQUE_NAME( CATCH2_INTERNAL_TEMPLATE_TEST_ ) , 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( CATCH2_INTERNAL_TEMPLATE_TEST_CLASS_ ), INTERNAL_CATCH_UNIQUE_NAME( CATCH2_INTERNAL_TEMPLATE_TEST_ ) , 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( CATCH2_INTERNAL_TEMPLATE_TEST_CLASS_ ), INTERNAL_CATCH_UNIQUE_NAME( CATCH2_INTERNAL_TEMPLATE_TEST_ ) , ClassName, Name, Tags, Signature, __VA_ARGS__ ) )
|
|
#endif
|
|
#endif
|
|
|
|
|
|
///////////////////////////////////////////////////////////////////////////////
|
|
#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 \
|
|
CATCH_INTERNAL_SUPPRESS_UNUSED_VARIABLE_WARNINGS \
|
|
CATCH_INTERNAL_SUPPRESS_COMMA_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<typename...Types> \
|
|
struct TestName{\
|
|
TestName(){\
|
|
size_t index = 0; \
|
|
constexpr char const* tmpl_types[] = {CATCH_REC_LIST(INTERNAL_CATCH_STRINGIZE_WITHOUT_PARENS, __VA_ARGS__)}; /* NOLINT(cppcoreguidelines-avoid-c-arrays,modernize-avoid-c-arrays,hicpp-avoid-c-arrays) */\
|
|
using expander = size_t[]; /* NOLINT(cppcoreguidelines-avoid-c-arrays,modernize-avoid-c-arrays,hicpp-avoid-c-arrays) */\
|
|
(void)expander{(reg_test(Types{}, Catch::NameAndTags{ Name " - " + std::string(tmpl_types[index]), Tags } ), index++)... };/* NOLINT */ \
|
|
}\
|
|
};\
|
|
static const int INTERNAL_CATCH_UNIQUE_NAME( globalRegistrar ) = [](){\
|
|
TestName<INTERNAL_CATCH_MAKE_TYPE_LISTS_FROM_TYPES(__VA_ARGS__)>();\
|
|
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( CATCH2_INTERNAL_TEMPLATE_TEST_ ), INTERNAL_CATCH_UNIQUE_NAME( CATCH2_INTERNAL_TEMPLATE_TEST_ ), 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( CATCH2_INTERNAL_TEMPLATE_TEST_ ), INTERNAL_CATCH_UNIQUE_NAME( CATCH2_INTERNAL_TEMPLATE_TEST_ ), 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( CATCH2_INTERNAL_TEMPLATE_TEST_ ), INTERNAL_CATCH_UNIQUE_NAME( CATCH2_INTERNAL_TEMPLATE_TEST_ ), 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( CATCH2_INTERNAL_TEMPLATE_TEST_ ), INTERNAL_CATCH_UNIQUE_NAME( CATCH2_INTERNAL_TEMPLATE_TEST_ ), 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 \
|
|
CATCH_INTERNAL_SUPPRESS_UNUSED_VARIABLE_WARNINGS \
|
|
CATCH_INTERNAL_SUPPRESS_COMMA_WARNINGS \
|
|
template<typename TestType> static void TestFuncName(); \
|
|
namespace {\
|
|
namespace INTERNAL_CATCH_MAKE_NAMESPACE(TestName) { \
|
|
INTERNAL_CATCH_TYPE_GEN \
|
|
INTERNAL_CATCH_NTTP_GEN(INTERNAL_CATCH_REMOVE_PARENS(Signature)) \
|
|
template<typename... Types> \
|
|
struct TestName { \
|
|
void reg_tests() { \
|
|
size_t index = 0; \
|
|
using expander = size_t[]; \
|
|
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<Types> ), 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<INTERNAL_CATCH_MAKE_TYPE_LISTS_FROM_TYPES(INTERNAL_CATCH_REMOVE_PARENS(TypesList))>>::type; \
|
|
TestInit t; \
|
|
t.reg_tests(); \
|
|
return 0; \
|
|
}(); \
|
|
} \
|
|
} \
|
|
CATCH_INTERNAL_STOP_WARNINGS_SUPPRESSION \
|
|
template<typename TestType> \
|
|
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( CATCH2_INTERNAL_TEMPLATE_TEST_ ), INTERNAL_CATCH_UNIQUE_NAME( CATCH2_INTERNAL_TEMPLATE_TEST_ ), 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( CATCH2_INTERNAL_TEMPLATE_TEST_ ), INTERNAL_CATCH_UNIQUE_NAME( CATCH2_INTERNAL_TEMPLATE_TEST_ ), 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( CATCH2_INTERNAL_TEMPLATE_TEST_ ), INTERNAL_CATCH_UNIQUE_NAME( CATCH2_INTERNAL_TEMPLATE_TEST_ ), 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( CATCH2_INTERNAL_TEMPLATE_TEST_ ), INTERNAL_CATCH_UNIQUE_NAME( CATCH2_INTERNAL_TEMPLATE_TEST_ ), 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 \
|
|
CATCH_INTERNAL_SUPPRESS_UNUSED_VARIABLE_WARNINGS \
|
|
CATCH_INTERNAL_SUPPRESS_COMMA_WARNINGS \
|
|
template<typename TestType> static void TestFunc(); \
|
|
namespace {\
|
|
namespace INTERNAL_CATCH_MAKE_NAMESPACE(TestName){\
|
|
INTERNAL_CATCH_TYPE_GEN\
|
|
template<typename... Types> \
|
|
struct TestName { \
|
|
void reg_tests() { \
|
|
size_t index = 0; \
|
|
using expander = size_t[]; \
|
|
(void)expander{(Catch::AutoReg( Catch::makeTestInvoker( &TestFunc<Types> ), CATCH_INTERNAL_LINEINFO, Catch::StringRef(), Catch::NameAndTags{ Name " - " INTERNAL_CATCH_STRINGIZE(TmplList) " - " + std::to_string(index), Tags } ), index++)... };/* NOLINT */\
|
|
} \
|
|
};\
|
|
static int INTERNAL_CATCH_UNIQUE_NAME( globalRegistrar ) = [](){ \
|
|
using TestInit = typename convert<TestName, TmplList>::type; \
|
|
TestInit t; \
|
|
t.reg_tests(); \
|
|
return 0; \
|
|
}(); \
|
|
}}\
|
|
CATCH_INTERNAL_STOP_WARNINGS_SUPPRESSION \
|
|
template<typename TestType> \
|
|
static void TestFunc()
|
|
|
|
#define INTERNAL_CATCH_TEMPLATE_LIST_TEST_CASE(Name, Tags, TmplList) \
|
|
INTERNAL_CATCH_TEMPLATE_LIST_TEST_CASE_2( INTERNAL_CATCH_UNIQUE_NAME( CATCH2_INTERNAL_TEMPLATE_TEST_ ), INTERNAL_CATCH_UNIQUE_NAME( CATCH2_INTERNAL_TEMPLATE_TEST_ ), 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 \
|
|
CATCH_INTERNAL_SUPPRESS_UNUSED_VARIABLE_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<typename...Types> \
|
|
struct TestNameClass{\
|
|
TestNameClass(){\
|
|
size_t index = 0; \
|
|
constexpr char const* tmpl_types[] = {CATCH_REC_LIST(INTERNAL_CATCH_STRINGIZE_WITHOUT_PARENS, __VA_ARGS__)};\
|
|
using expander = size_t[];\
|
|
(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<INTERNAL_CATCH_MAKE_TYPE_LISTS_FROM_TYPES(__VA_ARGS__)>();\
|
|
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( CATCH2_INTERNAL_TEMPLATE_TEST_CLASS_ ), INTERNAL_CATCH_UNIQUE_NAME( CATCH2_INTERNAL_TEMPLATE_TEST_ ) , 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( CATCH2_INTERNAL_TEMPLATE_TEST_CLASS_ ), INTERNAL_CATCH_UNIQUE_NAME( CATCH2_INTERNAL_TEMPLATE_TEST_ ) , 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( CATCH2_INTERNAL_TEMPLATE_TEST_CLASS_ ), INTERNAL_CATCH_UNIQUE_NAME( CATCH2_INTERNAL_TEMPLATE_TEST_ ) , 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( CATCH2_INTERNAL_TEMPLATE_TEST_CLASS_ ), INTERNAL_CATCH_UNIQUE_NAME( CATCH2_INTERNAL_TEMPLATE_TEST_ ) , 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 \
|
|
CATCH_INTERNAL_SUPPRESS_UNUSED_VARIABLE_WARNINGS \
|
|
template<typename TestType> \
|
|
struct TestName : INTERNAL_CATCH_REMOVE_PARENS(ClassName <TestType>) { \
|
|
void test();\
|
|
};\
|
|
namespace {\
|
|
namespace INTERNAL_CATCH_MAKE_NAMESPACE(TestNameClass) {\
|
|
INTERNAL_CATCH_TYPE_GEN \
|
|
INTERNAL_CATCH_NTTP_GEN(INTERNAL_CATCH_REMOVE_PARENS(Signature))\
|
|
template<typename...Types>\
|
|
struct TestNameClass{\
|
|
void reg_tests(){\
|
|
std::size_t index = 0;\
|
|
using expander = std::size_t[];\
|
|
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<Types>::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<INTERNAL_CATCH_MAKE_TYPE_LISTS_FROM_TYPES(INTERNAL_CATCH_REMOVE_PARENS(TypesList))>>::type;\
|
|
TestInit t;\
|
|
t.reg_tests();\
|
|
return 0;\
|
|
}(); \
|
|
}\
|
|
}\
|
|
CATCH_INTERNAL_STOP_WARNINGS_SUPPRESSION \
|
|
template<typename TestType> \
|
|
void TestName<TestType>::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( CATCH2_INTERNAL_TEMPLATE_TEST_ ), INTERNAL_CATCH_UNIQUE_NAME( CATCH2_INTERNAL_TEMPLATE_TEST_ ), 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( CATCH2_INTERNAL_TEMPLATE_TEST_ ), INTERNAL_CATCH_UNIQUE_NAME( CATCH2_INTERNAL_TEMPLATE_TEST_ ), 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( CATCH2_INTERNAL_TEMPLATE_TEST_ ), INTERNAL_CATCH_UNIQUE_NAME( CATCH2_INTERNAL_TEMPLATE_TEST_ ), 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( CATCH2_INTERNAL_TEMPLATE_TEST_ ), INTERNAL_CATCH_UNIQUE_NAME( CATCH2_INTERNAL_TEMPLATE_TEST_ ), 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 \
|
|
CATCH_INTERNAL_SUPPRESS_UNUSED_VARIABLE_WARNINGS \
|
|
CATCH_INTERNAL_SUPPRESS_COMMA_WARNINGS \
|
|
template<typename TestType> \
|
|
struct TestName : INTERNAL_CATCH_REMOVE_PARENS(ClassName <TestType>) { \
|
|
void test();\
|
|
};\
|
|
namespace {\
|
|
namespace INTERNAL_CATCH_MAKE_NAMESPACE(TestName){ \
|
|
INTERNAL_CATCH_TYPE_GEN\
|
|
template<typename...Types>\
|
|
struct TestNameClass{\
|
|
void reg_tests(){\
|
|
size_t index = 0;\
|
|
using expander = size_t[];\
|
|
(void)expander{(Catch::AutoReg( Catch::makeTestInvoker( &TestName<Types>::test ), CATCH_INTERNAL_LINEINFO, #ClassName##_catch_sr, Catch::NameAndTags{ Name " - " INTERNAL_CATCH_STRINGIZE(TmplList) " - " + std::to_string(index), Tags } ), index++)... };/* NOLINT */ \
|
|
}\
|
|
};\
|
|
static int INTERNAL_CATCH_UNIQUE_NAME( globalRegistrar ) = [](){\
|
|
using TestInit = typename convert<TestNameClass, TmplList>::type;\
|
|
TestInit t;\
|
|
t.reg_tests();\
|
|
return 0;\
|
|
}(); \
|
|
}}\
|
|
CATCH_INTERNAL_STOP_WARNINGS_SUPPRESSION \
|
|
template<typename TestType> \
|
|
void TestName<TestType>::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( CATCH2_INTERNAL_TEMPLATE_TEST_ ), INTERNAL_CATCH_UNIQUE_NAME( CATCH2_INTERNAL_TEMPLATE_TEST_ ), ClassName, Name, Tags, TmplList )
|
|
|
|
|
|
#endif // CATCH_TEMPLATE_TEST_REGISTRY_HPP_INCLUDED
|
|
|
|
|
|
#if defined(CATCH_CONFIG_PREFIX_ALL) && !defined(CATCH_CONFIG_DISABLE)
|
|
|
|
#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__ )
|
|
#define CATCH_TEMPLATE_LIST_TEST_CASE( ... ) INTERNAL_CATCH_TEMPLATE_LIST_TEST_CASE(__VA_ARGS__)
|
|
#define CATCH_TEMPLATE_LIST_TEST_CASE_METHOD( className, ... ) INTERNAL_CATCH_TEMPLATE_LIST_TEST_CASE_METHOD( 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__ ) )
|
|
#define CATCH_TEMPLATE_LIST_TEST_CASE( ... ) INTERNAL_CATCH_EXPAND_VARGS( INTERNAL_CATCH_TEMPLATE_LIST_TEST_CASE( __VA_ARGS__ ) )
|
|
#define CATCH_TEMPLATE_LIST_TEST_CASE_METHOD( className, ... ) INTERNAL_CATCH_EXPAND_VARGS( INTERNAL_CATCH_TEMPLATE_LIST_TEST_CASE_METHOD( className, __VA_ARGS__ ) )
|
|
#endif
|
|
|
|
#elif defined(CATCH_CONFIG_PREFIX_ALL) && defined(CATCH_CONFIG_DISABLE)
|
|
|
|
#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__ )
|
|
#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__ ) )
|
|
#endif
|
|
|
|
// When disabled, these can be shared between proper preprocessor and MSVC preprocessor
|
|
#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__ )
|
|
#define CATCH_TEMPLATE_LIST_TEST_CASE( ... ) CATCH_TEMPLATE_TEST_CASE(__VA_ARGS__)
|
|
#define CATCH_TEMPLATE_LIST_TEST_CASE_METHOD( className, ... ) CATCH_TEMPLATE_TEST_CASE_METHOD( className, __VA_ARGS__ )
|
|
|
|
#elif !defined(CATCH_CONFIG_PREFIX_ALL) && !defined(CATCH_CONFIG_DISABLE)
|
|
|
|
#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
|
|
|
|
#elif !defined(CATCH_CONFIG_PREFIX_ALL) && defined(CATCH_CONFIG_DISABLE)
|
|
|
|
#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__ )
|
|
#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__ ) )
|
|
#endif
|
|
|
|
// When disabled, these can be shared between proper preprocessor and MSVC preprocessor
|
|
#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__ )
|
|
#define TEMPLATE_LIST_TEST_CASE( ... ) TEMPLATE_TEST_CASE(__VA_ARGS__)
|
|
#define TEMPLATE_LIST_TEST_CASE_METHOD( className, ... ) TEMPLATE_TEST_CASE_METHOD( className, __VA_ARGS__ )
|
|
|
|
#endif // end of user facing macro declarations
|
|
|
|
|
|
#endif // CATCH_TEMPLATE_TEST_MACROS_HPP_INCLUDED
|
|
|
|
|
|
#ifndef CATCH_TEST_CASE_INFO_HPP_INCLUDED
|
|
#define CATCH_TEST_CASE_INFO_HPP_INCLUDED
|
|
|
|
|
|
|
|
#include <cstdint>
|
|
#include <string>
|
|
#include <vector>
|
|
|
|
#ifdef __clang__
|
|
#pragma clang diagnostic push
|
|
#pragma clang diagnostic ignored "-Wpadded"
|
|
#endif
|
|
|
|
namespace Catch {
|
|
|
|
/**
|
|
* A **view** of a tag string that provides case insensitive comparisons
|
|
*
|
|
* Note that in Catch2 internals, the square brackets around tags are
|
|
* not a part of tag's representation, so e.g. "[cool-tag]" is represented
|
|
* as "cool-tag" internally.
|
|
*/
|
|
struct Tag {
|
|
constexpr Tag(StringRef original_):
|
|
original(original_)
|
|
{}
|
|
StringRef original;
|
|
|
|
friend bool operator< ( Tag const& lhs, Tag const& rhs );
|
|
friend bool operator==( Tag const& lhs, Tag const& rhs );
|
|
};
|
|
|
|
class ITestInvoker;
|
|
|
|
enum class TestCaseProperties : uint8_t {
|
|
None = 0,
|
|
IsHidden = 1 << 1,
|
|
ShouldFail = 1 << 2,
|
|
MayFail = 1 << 3,
|
|
Throws = 1 << 4,
|
|
NonPortable = 1 << 5,
|
|
Benchmark = 1 << 6
|
|
};
|
|
|
|
/**
|
|
* Various metadata about the test case.
|
|
*
|
|
* A test case is uniquely identified by its (class)name and tags
|
|
* combination, with source location being ignored, and other properties
|
|
* being determined from tags.
|
|
*
|
|
* Tags are kept sorted.
|
|
*/
|
|
struct TestCaseInfo : Detail::NonCopyable {
|
|
|
|
TestCaseInfo(StringRef _className,
|
|
NameAndTags const& _tags,
|
|
SourceLineInfo const& _lineInfo);
|
|
|
|
bool isHidden() const;
|
|
bool throws() const;
|
|
bool okToFail() const;
|
|
bool expectedToFail() const;
|
|
|
|
// Adds the tag(s) with test's filename (for the -# flag)
|
|
void addFilenameTag();
|
|
|
|
//! Orders by name, classname and tags
|
|
friend bool operator<( TestCaseInfo const& lhs,
|
|
TestCaseInfo const& rhs );
|
|
|
|
|
|
std::string tagsAsString() const;
|
|
|
|
std::string name;
|
|
StringRef className;
|
|
private:
|
|
std::string backingTags;
|
|
// Internally we copy tags to the backing storage and then add
|
|
// refs to this storage to the tags vector.
|
|
void internalAppendTag(StringRef tagString);
|
|
public:
|
|
std::vector<Tag> tags;
|
|
SourceLineInfo lineInfo;
|
|
TestCaseProperties properties = TestCaseProperties::None;
|
|
};
|
|
|
|
/**
|
|
* Wrapper over the test case information and the test case invoker
|
|
*
|
|
* Does not own either, and is specifically made to be cheap
|
|
* to copy around.
|
|
*/
|
|
class TestCaseHandle {
|
|
TestCaseInfo* m_info;
|
|
ITestInvoker* m_invoker;
|
|
public:
|
|
TestCaseHandle(TestCaseInfo* info, ITestInvoker* invoker) :
|
|
m_info(info), m_invoker(invoker) {}
|
|
|
|
void invoke() const {
|
|
m_invoker->invoke();
|
|
}
|
|
|
|
TestCaseInfo const& getTestCaseInfo() const;
|
|
};
|
|
|
|
Detail::unique_ptr<TestCaseInfo>
|
|
makeTestCaseInfo( StringRef className,
|
|
NameAndTags const& nameAndTags,
|
|
SourceLineInfo const& lineInfo );
|
|
}
|
|
|
|
#ifdef __clang__
|
|
#pragma clang diagnostic pop
|
|
#endif
|
|
|
|
#endif // CATCH_TEST_CASE_INFO_HPP_INCLUDED
|
|
|
|
|
|
#ifndef CATCH_TRANSLATE_EXCEPTION_HPP_INCLUDED
|
|
#define CATCH_TRANSLATE_EXCEPTION_HPP_INCLUDED
|
|
|
|
|
|
|
|
#ifndef CATCH_INTERFACES_EXCEPTION_HPP_INCLUDED
|
|
#define CATCH_INTERFACES_EXCEPTION_HPP_INCLUDED
|
|
|
|
|
|
#include <string>
|
|
#include <vector>
|
|
|
|
namespace Catch {
|
|
using exceptionTranslateFunction = std::string(*)();
|
|
|
|
class IExceptionTranslator;
|
|
using ExceptionTranslators = std::vector<Detail::unique_ptr<IExceptionTranslator const>>;
|
|
|
|
class IExceptionTranslator {
|
|
public:
|
|
virtual ~IExceptionTranslator(); // = default
|
|
virtual std::string translate( ExceptionTranslators::const_iterator it, ExceptionTranslators::const_iterator itEnd ) const = 0;
|
|
};
|
|
|
|
class IExceptionTranslatorRegistry {
|
|
public:
|
|
virtual ~IExceptionTranslatorRegistry(); // = default
|
|
virtual std::string translateActiveException() const = 0;
|
|
};
|
|
|
|
} // namespace Catch
|
|
|
|
#endif // CATCH_INTERFACES_EXCEPTION_HPP_INCLUDED
|
|
|
|
#include <exception>
|
|
|
|
namespace Catch {
|
|
namespace Detail {
|
|
void registerTranslatorImpl(
|
|
Detail::unique_ptr<IExceptionTranslator>&& translator );
|
|
}
|
|
|
|
class ExceptionTranslatorRegistrar {
|
|
template<typename T>
|
|
class ExceptionTranslator : public IExceptionTranslator {
|
|
public:
|
|
|
|
ExceptionTranslator( std::string(*translateFunction)( T const& ) )
|
|
: m_translateFunction( translateFunction )
|
|
{}
|
|
|
|
std::string translate( ExceptionTranslators::const_iterator it, ExceptionTranslators::const_iterator itEnd ) const override {
|
|
#if !defined(CATCH_CONFIG_DISABLE_EXCEPTIONS)
|
|
try {
|
|
if( it == itEnd )
|
|
std::rethrow_exception(std::current_exception());
|
|
else
|
|
return (*it)->translate( it+1, itEnd );
|
|
}
|
|
catch( T const& ex ) {
|
|
return m_translateFunction( ex );
|
|
}
|
|
#else
|
|
return "You should never get here!";
|
|
#endif
|
|
}
|
|
|
|
protected:
|
|
std::string(*m_translateFunction)( T const& );
|
|
};
|
|
|
|
public:
|
|
template<typename T>
|
|
ExceptionTranslatorRegistrar( std::string(*translateFunction)( T const& ) ) {
|
|
Detail::registerTranslatorImpl(
|
|
Detail::make_unique<ExceptionTranslator<T>>(
|
|
translateFunction ) );
|
|
}
|
|
};
|
|
|
|
} // namespace Catch
|
|
|
|
///////////////////////////////////////////////////////////////////////////////
|
|
#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 )
|
|
|
|
#if defined(CATCH_CONFIG_DISABLE)
|
|
#define INTERNAL_CATCH_TRANSLATE_EXCEPTION_NO_REG( translatorName, signature) \
|
|
static std::string translatorName( signature )
|
|
#endif
|
|
|
|
|
|
// This macro is always prefixed
|
|
#if !defined(CATCH_CONFIG_DISABLE)
|
|
#define CATCH_TRANSLATE_EXCEPTION( signature ) INTERNAL_CATCH_TRANSLATE_EXCEPTION( signature )
|
|
#else
|
|
#define CATCH_TRANSLATE_EXCEPTION( signature ) INTERNAL_CATCH_TRANSLATE_EXCEPTION_NO_REG( INTERNAL_CATCH_UNIQUE_NAME( catch_internal_ExceptionTranslator ), signature )
|
|
#endif
|
|
|
|
|
|
#endif // CATCH_TRANSLATE_EXCEPTION_HPP_INCLUDED
|
|
|
|
|
|
#ifndef CATCH_VERSION_HPP_INCLUDED
|
|
#define CATCH_VERSION_HPP_INCLUDED
|
|
|
|
#include <iosfwd>
|
|
|
|
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();
|
|
}
|
|
|
|
#endif // CATCH_VERSION_HPP_INCLUDED
|
|
|
|
|
|
#ifndef CATCH_VERSION_MACROS_HPP_INCLUDED
|
|
#define CATCH_VERSION_MACROS_HPP_INCLUDED
|
|
|
|
#define CATCH_VERSION_MAJOR 3
|
|
#define CATCH_VERSION_MINOR 5
|
|
#define CATCH_VERSION_PATCH 0
|
|
|
|
#endif // CATCH_VERSION_MACROS_HPP_INCLUDED
|
|
|
|
|
|
/** \file
|
|
* This is a convenience header for Catch2's Generator support. It includes
|
|
* **all** of Catch2 headers related to generators.
|
|
*
|
|
* Generally the Catch2 users should use specific includes they need,
|
|
* but this header can be used instead for ease-of-experimentation, or
|
|
* just plain convenience, at the cost of (significantly) increased
|
|
* compilation times.
|
|
*
|
|
* When a new header is added to either the `generators` folder,
|
|
* or to the corresponding internal subfolder, it should be added here.
|
|
*/
|
|
|
|
#ifndef CATCH_GENERATORS_ALL_HPP_INCLUDED
|
|
#define CATCH_GENERATORS_ALL_HPP_INCLUDED
|
|
|
|
|
|
|
|
#ifndef CATCH_GENERATOR_EXCEPTION_HPP_INCLUDED
|
|
#define CATCH_GENERATOR_EXCEPTION_HPP_INCLUDED
|
|
|
|
#include <exception>
|
|
|
|
namespace Catch {
|
|
|
|
// Exception type to be thrown when a Generator runs into an error,
|
|
// e.g. it cannot initialize the first return value based on
|
|
// runtime information
|
|
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;
|
|
};
|
|
|
|
} // end namespace Catch
|
|
|
|
#endif // CATCH_GENERATOR_EXCEPTION_HPP_INCLUDED
|
|
|
|
|
|
#ifndef CATCH_GENERATORS_HPP_INCLUDED
|
|
#define CATCH_GENERATORS_HPP_INCLUDED
|
|
|
|
|
|
|
|
#ifndef CATCH_INTERFACES_GENERATORTRACKER_HPP_INCLUDED
|
|
#define CATCH_INTERFACES_GENERATORTRACKER_HPP_INCLUDED
|
|
|
|
|
|
#include <string>
|
|
|
|
namespace Catch {
|
|
|
|
namespace Generators {
|
|
class GeneratorUntypedBase {
|
|
// Caches result from `toStringImpl`, assume that when it is an
|
|
// empty string, the cache is invalidated.
|
|
mutable std::string m_stringReprCache;
|
|
|
|
// Counts based on `next` returning true
|
|
std::size_t m_currentElementIndex = 0;
|
|
|
|
/**
|
|
* 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;
|
|
|
|
//! Customization point for `currentElementAsString`
|
|
virtual std::string stringifyImpl() const = 0;
|
|
|
|
public:
|
|
GeneratorUntypedBase() = default;
|
|
// Generation of copy ops is deprecated (and Clang will complain)
|
|
// if there is a user destructor defined
|
|
GeneratorUntypedBase(GeneratorUntypedBase const&) = default;
|
|
GeneratorUntypedBase& operator=(GeneratorUntypedBase const&) = default;
|
|
|
|
virtual ~GeneratorUntypedBase(); // = default;
|
|
|
|
/**
|
|
* Attempts to move the generator to the next element
|
|
*
|
|
* Serves as a non-virtual interface to `next`, so that the
|
|
* top level interface can provide sanity checking and shared
|
|
* features.
|
|
*
|
|
* As with `next`, returns true iff the move succeeded and
|
|
* the generator has new valid element to provide.
|
|
*/
|
|
bool countedNext();
|
|
|
|
std::size_t currentElementIndex() const { return m_currentElementIndex; }
|
|
|
|
/**
|
|
* Returns generator's current element as user-friendly string.
|
|
*
|
|
* By default returns string equivalent to calling
|
|
* `Catch::Detail::stringify` on the current element, but generators
|
|
* can customize their implementation as needed.
|
|
*
|
|
* Not thread-safe due to internal caching.
|
|
*
|
|
* The returned ref is valid only until the generator instance
|
|
* is destructed, or it moves onto the next element, whichever
|
|
* comes first.
|
|
*/
|
|
StringRef currentElementAsString() const;
|
|
};
|
|
using GeneratorBasePtr = Catch::Detail::unique_ptr<GeneratorUntypedBase>;
|
|
|
|
} // namespace Generators
|
|
|
|
class IGeneratorTracker {
|
|
public:
|
|
virtual ~IGeneratorTracker(); // = default;
|
|
virtual auto hasGenerator() const -> bool = 0;
|
|
virtual auto getGenerator() const -> Generators::GeneratorBasePtr const& = 0;
|
|
virtual void setGenerator( Generators::GeneratorBasePtr&& generator ) = 0;
|
|
};
|
|
|
|
} // namespace Catch
|
|
|
|
#endif // CATCH_INTERFACES_GENERATORTRACKER_HPP_INCLUDED
|
|
|
|
#include <vector>
|
|
#include <tuple>
|
|
|
|
namespace Catch {
|
|
|
|
namespace Generators {
|
|
|
|
namespace Detail {
|
|
|
|
//! Throws GeneratorException with the provided message
|
|
[[noreturn]]
|
|
void throw_generator_exception(char const * msg);
|
|
|
|
} // end namespace detail
|
|
|
|
template<typename T>
|
|
class IGenerator : public GeneratorUntypedBase {
|
|
std::string stringifyImpl() const override {
|
|
return ::Catch::Detail::stringify( get() );
|
|
}
|
|
|
|
public:
|
|
// 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 <typename T>
|
|
using GeneratorPtr = Catch::Detail::unique_ptr<IGenerator<T>>;
|
|
|
|
template <typename T>
|
|
class GeneratorWrapper final {
|
|
GeneratorPtr<T> m_generator;
|
|
public:
|
|
//! Takes ownership of the passed pointer.
|
|
GeneratorWrapper(IGenerator<T>* generator):
|
|
m_generator(generator) {}
|
|
GeneratorWrapper(GeneratorPtr<T> generator):
|
|
m_generator(CATCH_MOVE(generator)) {}
|
|
|
|
T const& get() const {
|
|
return m_generator->get();
|
|
}
|
|
bool next() {
|
|
return m_generator->countedNext();
|
|
}
|
|
};
|
|
|
|
|
|
template<typename T>
|
|
class SingleValueGenerator final : public IGenerator<T> {
|
|
T m_value;
|
|
public:
|
|
SingleValueGenerator(T const& value) :
|
|
m_value(value)
|
|
{}
|
|
SingleValueGenerator(T&& value):
|
|
m_value(CATCH_MOVE(value))
|
|
{}
|
|
|
|
T const& get() const override {
|
|
return m_value;
|
|
}
|
|
bool next() override {
|
|
return false;
|
|
}
|
|
};
|
|
|
|
template<typename T>
|
|
class FixedValuesGenerator final : public IGenerator<T> {
|
|
static_assert(!std::is_same<T, bool>::value,
|
|
"FixedValuesGenerator does not support bools because of std::vector<bool>"
|
|
"specialization, use SingleValue Generator instead.");
|
|
std::vector<T> m_values;
|
|
size_t m_idx = 0;
|
|
public:
|
|
FixedValuesGenerator( std::initializer_list<T> 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 <typename T, typename DecayedT = std::decay_t<T>>
|
|
GeneratorWrapper<DecayedT> value( T&& value ) {
|
|
return GeneratorWrapper<DecayedT>(
|
|
Catch::Detail::make_unique<SingleValueGenerator<DecayedT>>(
|
|
CATCH_FORWARD( value ) ) );
|
|
}
|
|
template <typename T>
|
|
GeneratorWrapper<T> values(std::initializer_list<T> values) {
|
|
return GeneratorWrapper<T>(Catch::Detail::make_unique<FixedValuesGenerator<T>>(values));
|
|
}
|
|
|
|
template<typename T>
|
|
class Generators : public IGenerator<T> {
|
|
std::vector<GeneratorWrapper<T>> m_generators;
|
|
size_t m_current = 0;
|
|
|
|
void add_generator( GeneratorWrapper<T>&& generator ) {
|
|
m_generators.emplace_back( CATCH_MOVE( generator ) );
|
|
}
|
|
void add_generator( T const& val ) {
|
|
m_generators.emplace_back( value( val ) );
|
|
}
|
|
void add_generator( T&& val ) {
|
|
m_generators.emplace_back( value( CATCH_MOVE( val ) ) );
|
|
}
|
|
template <typename U>
|
|
std::enable_if_t<!std::is_same<std::decay_t<U>, T>::value>
|
|
add_generator( U&& val ) {
|
|
add_generator( T( CATCH_FORWARD( val ) ) );
|
|
}
|
|
|
|
template <typename U> void add_generators( U&& valueOrGenerator ) {
|
|
add_generator( CATCH_FORWARD( valueOrGenerator ) );
|
|
}
|
|
|
|
template <typename U, typename... Gs>
|
|
void add_generators( U&& valueOrGenerator, Gs&&... moreGenerators ) {
|
|
add_generator( CATCH_FORWARD( valueOrGenerator ) );
|
|
add_generators( CATCH_FORWARD( moreGenerators )... );
|
|
}
|
|
|
|
public:
|
|
template <typename... Gs>
|
|
Generators(Gs &&... moreGenerators) {
|
|
m_generators.reserve(sizeof...(Gs));
|
|
add_generators(CATCH_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 <typename... Ts>
|
|
GeneratorWrapper<std::tuple<std::decay_t<Ts>...>>
|
|
table( std::initializer_list<std::tuple<std::decay_t<Ts>...>> tuples ) {
|
|
return values<std::tuple<Ts...>>( tuples );
|
|
}
|
|
|
|
// Tag type to signal that a generator sequence should convert arguments to a specific type
|
|
template <typename T>
|
|
struct as {};
|
|
|
|
template<typename T, typename... Gs>
|
|
auto makeGenerators( GeneratorWrapper<T>&& generator, Gs &&... moreGenerators ) -> Generators<T> {
|
|
return Generators<T>(CATCH_MOVE(generator), CATCH_FORWARD(moreGenerators)...);
|
|
}
|
|
template<typename T>
|
|
auto makeGenerators( GeneratorWrapper<T>&& generator ) -> Generators<T> {
|
|
return Generators<T>(CATCH_MOVE(generator));
|
|
}
|
|
template<typename T, typename... Gs>
|
|
auto makeGenerators( T&& val, Gs &&... moreGenerators ) -> Generators<std::decay_t<T>> {
|
|
return makeGenerators( value( CATCH_FORWARD( val ) ), CATCH_FORWARD( moreGenerators )... );
|
|
}
|
|
template<typename T, typename U, typename... Gs>
|
|
auto makeGenerators( as<T>, U&& val, Gs &&... moreGenerators ) -> Generators<T> {
|
|
return makeGenerators( value( T( CATCH_FORWARD( val ) ) ), CATCH_FORWARD( moreGenerators )... );
|
|
}
|
|
|
|
IGeneratorTracker* acquireGeneratorTracker( StringRef generatorName,
|
|
SourceLineInfo const& lineInfo );
|
|
IGeneratorTracker* createGeneratorTracker( StringRef generatorName,
|
|
SourceLineInfo lineInfo,
|
|
GeneratorBasePtr&& generator );
|
|
|
|
template<typename L>
|
|
auto generate( StringRef generatorName, SourceLineInfo const& lineInfo, L const& generatorExpression ) -> typename decltype(generatorExpression())::type {
|
|
using UnderlyingType = typename decltype(generatorExpression())::type;
|
|
|
|
IGeneratorTracker* tracker = acquireGeneratorTracker( generatorName, lineInfo );
|
|
// Creation of tracker is delayed after generator creation, so
|
|
// that constructing generator can fail without breaking everything.
|
|
if (!tracker) {
|
|
tracker = createGeneratorTracker(
|
|
generatorName,
|
|
lineInfo,
|
|
Catch::Detail::make_unique<Generators<UnderlyingType>>(
|
|
generatorExpression() ) );
|
|
}
|
|
|
|
auto const& generator = static_cast<IGenerator<UnderlyingType> const&>( *tracker->getGenerator() );
|
|
return generator.get();
|
|
}
|
|
|
|
} // namespace Generators
|
|
} // namespace Catch
|
|
|
|
#define CATCH_INTERNAL_GENERATOR_STRINGIZE_IMPL( ... ) #__VA_ARGS__##_catch_sr
|
|
#define CATCH_INTERNAL_GENERATOR_STRINGIZE(...) CATCH_INTERNAL_GENERATOR_STRINGIZE_IMPL(__VA_ARGS__)
|
|
|
|
#define GENERATE( ... ) \
|
|
Catch::Generators::generate( CATCH_INTERNAL_GENERATOR_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( CATCH_INTERNAL_GENERATOR_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( CATCH_INTERNAL_GENERATOR_STRINGIZE(INTERNAL_CATCH_UNIQUE_NAME(generator)), \
|
|
CATCH_INTERNAL_LINEINFO, \
|
|
[&]{ using namespace Catch::Generators; return makeGenerators( __VA_ARGS__ ); } ) //NOLINT(google-build-using-namespace)
|
|
|
|
#endif // CATCH_GENERATORS_HPP_INCLUDED
|
|
|
|
|
|
#ifndef CATCH_GENERATORS_ADAPTERS_HPP_INCLUDED
|
|
#define CATCH_GENERATORS_ADAPTERS_HPP_INCLUDED
|
|
|
|
|
|
#include <cassert>
|
|
|
|
namespace Catch {
|
|
namespace Generators {
|
|
|
|
template <typename T>
|
|
class TakeGenerator final : public IGenerator<T> {
|
|
GeneratorWrapper<T> m_generator;
|
|
size_t m_returned = 0;
|
|
size_t m_target;
|
|
public:
|
|
TakeGenerator(size_t target, GeneratorWrapper<T>&& generator):
|
|
m_generator(CATCH_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 <typename T>
|
|
GeneratorWrapper<T> take(size_t target, GeneratorWrapper<T>&& generator) {
|
|
return GeneratorWrapper<T>(Catch::Detail::make_unique<TakeGenerator<T>>(target, CATCH_MOVE(generator)));
|
|
}
|
|
|
|
|
|
template <typename T, typename Predicate>
|
|
class FilterGenerator final : public IGenerator<T> {
|
|
GeneratorWrapper<T> m_generator;
|
|
Predicate m_predicate;
|
|
public:
|
|
template <typename P = Predicate>
|
|
FilterGenerator(P&& pred, GeneratorWrapper<T>&& generator):
|
|
m_generator(CATCH_MOVE(generator)),
|
|
m_predicate(CATCH_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 = next();
|
|
if (!has_initial_value) {
|
|
Detail::throw_generator_exception("No valid value found in filtered generator");
|
|
}
|
|
}
|
|
}
|
|
|
|
T const& get() const override {
|
|
return m_generator.get();
|
|
}
|
|
|
|
bool next() override {
|
|
bool success = m_generator.next();
|
|
if (!success) {
|
|
return false;
|
|
}
|
|
while (!m_predicate(m_generator.get()) && (success = m_generator.next()) == true);
|
|
return success;
|
|
}
|
|
};
|
|
|
|
|
|
template <typename T, typename Predicate>
|
|
GeneratorWrapper<T> filter(Predicate&& pred, GeneratorWrapper<T>&& generator) {
|
|
return GeneratorWrapper<T>(Catch::Detail::make_unique<FilterGenerator<T, Predicate>>(CATCH_FORWARD(pred), CATCH_MOVE(generator)));
|
|
}
|
|
|
|
template <typename T>
|
|
class RepeatGenerator final : public IGenerator<T> {
|
|
static_assert(!std::is_same<T, bool>::value,
|
|
"RepeatGenerator currently does not support bools"
|
|
"because of std::vector<bool> specialization");
|
|
GeneratorWrapper<T> m_generator;
|
|
mutable std::vector<T> 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<T>&& generator):
|
|
m_generator(CATCH_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 <typename T>
|
|
GeneratorWrapper<T> repeat(size_t repeats, GeneratorWrapper<T>&& generator) {
|
|
return GeneratorWrapper<T>(Catch::Detail::make_unique<RepeatGenerator<T>>(repeats, CATCH_MOVE(generator)));
|
|
}
|
|
|
|
template <typename T, typename U, typename Func>
|
|
class MapGenerator final : public IGenerator<T> {
|
|
// TBD: provide static assert for mapping function, for friendly error message
|
|
GeneratorWrapper<U> m_generator;
|
|
Func m_function;
|
|
// To avoid returning dangling reference, we have to save the values
|
|
T m_cache;
|
|
public:
|
|
template <typename F2 = Func>
|
|
MapGenerator(F2&& function, GeneratorWrapper<U>&& generator) :
|
|
m_generator(CATCH_MOVE(generator)),
|
|
m_function(CATCH_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 <typename Func, typename U, typename T = FunctionReturnType<Func, U>>
|
|
GeneratorWrapper<T> map(Func&& function, GeneratorWrapper<U>&& generator) {
|
|
return GeneratorWrapper<T>(
|
|
Catch::Detail::make_unique<MapGenerator<T, U, Func>>(CATCH_FORWARD(function), CATCH_MOVE(generator))
|
|
);
|
|
}
|
|
|
|
template <typename T, typename U, typename Func>
|
|
GeneratorWrapper<T> map(Func&& function, GeneratorWrapper<U>&& generator) {
|
|
return GeneratorWrapper<T>(
|
|
Catch::Detail::make_unique<MapGenerator<T, U, Func>>(CATCH_FORWARD(function), CATCH_MOVE(generator))
|
|
);
|
|
}
|
|
|
|
template <typename T>
|
|
class ChunkGenerator final : public IGenerator<std::vector<T>> {
|
|
std::vector<T> m_chunk;
|
|
size_t m_chunk_size;
|
|
GeneratorWrapper<T> m_generator;
|
|
bool m_used_up = false;
|
|
public:
|
|
ChunkGenerator(size_t size, GeneratorWrapper<T> generator) :
|
|
m_chunk_size(size), m_generator(CATCH_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()) {
|
|
Detail::throw_generator_exception("Not enough values to initialize the first chunk");
|
|
}
|
|
m_chunk.push_back(m_generator.get());
|
|
}
|
|
}
|
|
}
|
|
std::vector<T> 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 <typename T>
|
|
GeneratorWrapper<std::vector<T>> chunk(size_t size, GeneratorWrapper<T>&& generator) {
|
|
return GeneratorWrapper<std::vector<T>>(
|
|
Catch::Detail::make_unique<ChunkGenerator<T>>(size, CATCH_MOVE(generator))
|
|
);
|
|
}
|
|
|
|
} // namespace Generators
|
|
} // namespace Catch
|
|
|
|
|
|
#endif // CATCH_GENERATORS_ADAPTERS_HPP_INCLUDED
|
|
|
|
|
|
#ifndef CATCH_GENERATORS_RANDOM_HPP_INCLUDED
|
|
#define CATCH_GENERATORS_RANDOM_HPP_INCLUDED
|
|
|
|
|
|
|
|
#ifndef CATCH_RANDOM_NUMBER_GENERATOR_HPP_INCLUDED
|
|
#define CATCH_RANDOM_NUMBER_GENERATOR_HPP_INCLUDED
|
|
|
|
#include <cstdint>
|
|
|
|
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<result_type>(-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
|
|
|
|
#endif // CATCH_RANDOM_NUMBER_GENERATOR_HPP_INCLUDED
|
|
|
|
|
|
|
|
#ifndef CATCH_UNIFORM_INTEGER_DISTRIBUTION_HPP_INCLUDED
|
|
#define CATCH_UNIFORM_INTEGER_DISTRIBUTION_HPP_INCLUDED
|
|
|
|
|
|
|
|
|
|
#ifndef CATCH_RANDOM_INTEGER_HELPERS_HPP_INCLUDED
|
|
#define CATCH_RANDOM_INTEGER_HELPERS_HPP_INCLUDED
|
|
|
|
#include <climits>
|
|
#include <cstddef>
|
|
#include <cstdint>
|
|
#include <type_traits>
|
|
|
|
namespace Catch {
|
|
namespace Detail {
|
|
|
|
template <std::size_t>
|
|
struct SizedUnsignedType;
|
|
#define SizedUnsignedTypeHelper( TYPE ) \
|
|
template <> \
|
|
struct SizedUnsignedType<sizeof( TYPE )> { \
|
|
using type = TYPE; \
|
|
}
|
|
|
|
SizedUnsignedTypeHelper( std::uint8_t );
|
|
SizedUnsignedTypeHelper( std::uint16_t );
|
|
SizedUnsignedTypeHelper( std::uint32_t );
|
|
SizedUnsignedTypeHelper( std::uint64_t );
|
|
#undef SizedUnsignedTypeHelper
|
|
|
|
template <std::size_t sz>
|
|
using SizedUnsignedType_t = typename SizedUnsignedType<sz>::type;
|
|
|
|
template <typename T>
|
|
using DoubleWidthUnsignedType_t = SizedUnsignedType_t<2 * sizeof( T )>;
|
|
|
|
template <typename T>
|
|
struct ExtendedMultResult {
|
|
T upper;
|
|
T lower;
|
|
friend bool operator==( ExtendedMultResult const& lhs,
|
|
ExtendedMultResult const& rhs ) {
|
|
return lhs.upper == rhs.upper && lhs.lower == rhs.lower;
|
|
}
|
|
};
|
|
|
|
// Returns 128 bit result of multiplying lhs and rhs
|
|
constexpr ExtendedMultResult<std::uint64_t>
|
|
extendedMult( std::uint64_t lhs, std::uint64_t rhs ) {
|
|
// We use the simple long multiplication approach for
|
|
// correctness, we can use platform specific builtins
|
|
// for performance later.
|
|
|
|
// Split the lhs and rhs into two 32bit "digits", so that we can
|
|
// do 64 bit arithmetic to handle carry bits.
|
|
// 32b 32b 32b 32b
|
|
// lhs L1 L2
|
|
// * rhs R1 R2
|
|
// ------------------------
|
|
// | R2 * L2 |
|
|
// | R2 * L1 |
|
|
// | R1 * L2 |
|
|
// | R1 * L1 |
|
|
// -------------------------
|
|
// | a | b | c | d |
|
|
|
|
#define CarryBits( x ) ( x >> 32 )
|
|
#define Digits( x ) ( x & 0xFF'FF'FF'FF )
|
|
|
|
auto r2l2 = Digits( rhs ) * Digits( lhs );
|
|
auto r2l1 = Digits( rhs ) * CarryBits( lhs );
|
|
auto r1l2 = CarryBits( rhs ) * Digits( lhs );
|
|
auto r1l1 = CarryBits( rhs ) * CarryBits( lhs );
|
|
|
|
// Sum to columns first
|
|
auto d = Digits( r2l2 );
|
|
auto c = CarryBits( r2l2 ) + Digits( r2l1 ) + Digits( r1l2 );
|
|
auto b = CarryBits( r2l1 ) + CarryBits( r1l2 ) + Digits( r1l1 );
|
|
auto a = CarryBits( r1l1 );
|
|
|
|
// Propagate carries between columns
|
|
c += CarryBits( d );
|
|
b += CarryBits( c );
|
|
a += CarryBits( b );
|
|
|
|
// Remove the used carries
|
|
c = Digits( c );
|
|
b = Digits( b );
|
|
a = Digits( a );
|
|
|
|
#undef CarryBits
|
|
#undef Digits
|
|
|
|
return {
|
|
a << 32 | b, // upper 64 bits
|
|
c << 32 | d // lower 64 bits
|
|
};
|
|
}
|
|
|
|
template <typename UInt>
|
|
constexpr ExtendedMultResult<UInt> extendedMult( UInt lhs, UInt rhs ) {
|
|
static_assert( std::is_unsigned<UInt>::value,
|
|
"extendedMult can only handle unsigned integers" );
|
|
static_assert( sizeof( UInt ) < sizeof( std::uint64_t ),
|
|
"Generic extendedMult can only handle types smaller "
|
|
"than uint64_t" );
|
|
using WideType = DoubleWidthUnsignedType_t<UInt>;
|
|
|
|
auto result = WideType( lhs ) * WideType( rhs );
|
|
return {
|
|
static_cast<UInt>( result >> ( CHAR_BIT * sizeof( UInt ) ) ),
|
|
static_cast<UInt>( result & UInt( -1 ) ) };
|
|
}
|
|
|
|
|
|
template <typename TargetType,
|
|
typename Generator>
|
|
std::enable_if_t<sizeof(typename Generator::result_type) >= sizeof(TargetType),
|
|
TargetType> fillBitsFrom(Generator& gen) {
|
|
using gresult_type = typename Generator::result_type;
|
|
static_assert( std::is_unsigned<TargetType>::value, "Only unsigned integers are supported" );
|
|
static_assert( Generator::min() == 0 &&
|
|
Generator::max() == static_cast<gresult_type>( -1 ),
|
|
"Generator must be able to output all numbers in its result type (effectively it must be a random bit generator)" );
|
|
|
|
// We want to return the top bits from a generator, as they are
|
|
// usually considered higher quality.
|
|
constexpr auto generated_bits = sizeof( gresult_type ) * CHAR_BIT;
|
|
constexpr auto return_bits = sizeof( TargetType ) * CHAR_BIT;
|
|
|
|
return static_cast<TargetType>( gen() >>
|
|
( generated_bits - return_bits) );
|
|
}
|
|
|
|
template <typename TargetType,
|
|
typename Generator>
|
|
std::enable_if_t<sizeof(typename Generator::result_type) < sizeof(TargetType),
|
|
TargetType> fillBitsFrom(Generator& gen) {
|
|
using gresult_type = typename Generator::result_type;
|
|
static_assert( std::is_unsigned<TargetType>::value,
|
|
"Only unsigned integers are supported" );
|
|
static_assert( Generator::min() == 0 &&
|
|
Generator::max() == static_cast<gresult_type>( -1 ),
|
|
"Generator must be able to output all numbers in its result type (effectively it must be a random bit generator)" );
|
|
|
|
constexpr auto generated_bits = sizeof( gresult_type ) * CHAR_BIT;
|
|
constexpr auto return_bits = sizeof( TargetType ) * CHAR_BIT;
|
|
std::size_t filled_bits = 0;
|
|
TargetType ret = 0;
|
|
do {
|
|
ret <<= generated_bits;
|
|
ret |= gen();
|
|
filled_bits += generated_bits;
|
|
} while ( filled_bits < return_bits );
|
|
|
|
return ret;
|
|
}
|
|
|
|
/*
|
|
* Transposes numbers into unsigned type while keeping their ordering
|
|
*
|
|
* This means that signed types are changed so that the ordering is
|
|
* [INT_MIN, ..., -1, 0, ..., INT_MAX], rather than order we would
|
|
* get by simple casting ([0, ..., INT_MAX, INT_MIN, ..., -1])
|
|
*/
|
|
template <typename OriginalType, typename UnsignedType>
|
|
std::enable_if_t<std::is_signed<OriginalType>::value, UnsignedType>
|
|
transposeToNaturalOrder( UnsignedType in ) {
|
|
static_assert(
|
|
sizeof( OriginalType ) == sizeof( UnsignedType ),
|
|
"reordering requires the same sized types on both sides" );
|
|
static_assert( std::is_unsigned<UnsignedType>::value,
|
|
"Input type must be unsigned" );
|
|
// Assuming 2s complement (standardized in current C++), the
|
|
// positive and negative numbers are already internally ordered,
|
|
// and their difference is in the top bit. Swapping it orders
|
|
// them the desired way.
|
|
constexpr auto highest_bit =
|
|
UnsignedType( 1 ) << ( sizeof( UnsignedType ) * CHAR_BIT - 1 );
|
|
return static_cast<UnsignedType>( in ^ highest_bit );
|
|
}
|
|
|
|
|
|
|
|
template <typename OriginalType,
|
|
typename UnsignedType>
|
|
std::enable_if_t<std::is_unsigned<OriginalType>::value, UnsignedType>
|
|
transposeToNaturalOrder(UnsignedType in) {
|
|
static_assert(
|
|
sizeof( OriginalType ) == sizeof( UnsignedType ),
|
|
"reordering requires the same sized types on both sides" );
|
|
static_assert( std::is_unsigned<UnsignedType>::value, "Input type must be unsigned" );
|
|
// No reordering is needed for unsigned -> unsigned
|
|
return in;
|
|
}
|
|
} // namespace Detail
|
|
} // namespace Catch
|
|
|
|
#endif // CATCH_RANDOM_INTEGER_HELPERS_HPP_INCLUDED
|
|
|
|
namespace Catch {
|
|
|
|
namespace Detail {
|
|
// Indirection to enable make_unsigned<bool> behaviour.
|
|
template <typename T>
|
|
struct make_unsigned {
|
|
using type = std::make_unsigned_t<T>;
|
|
};
|
|
|
|
template <>
|
|
struct make_unsigned<bool> {
|
|
using type = uint8_t;
|
|
};
|
|
|
|
template <typename T>
|
|
using make_unsigned_t = typename make_unsigned<T>::type;
|
|
}
|
|
|
|
/**
|
|
* Implementation of uniform distribution on integers.
|
|
*
|
|
* Unlike `std::uniform_int_distribution`, this implementation supports
|
|
* various 1 byte integral types, including bool (but you should not
|
|
* actually use it for bools).
|
|
*
|
|
* The underlying algorithm is based on the one described in "Fast Random
|
|
* Integer Generation in an Interval" by Daniel Lemire, but has been
|
|
* optimized under the assumption of reuse of the same distribution object.
|
|
*/
|
|
template <typename IntegerType>
|
|
class uniform_integer_distribution {
|
|
static_assert(std::is_integral<IntegerType>::value, "...");
|
|
|
|
using UnsignedIntegerType = Detail::make_unsigned_t<IntegerType>;
|
|
|
|
// We store the left range bound converted to internal representation,
|
|
// because it will be used in computation in the () operator.
|
|
UnsignedIntegerType m_a;
|
|
// After initialization, right bound is only used for the b() getter,
|
|
// so we keep it in the original type.
|
|
IntegerType m_b;
|
|
|
|
// How many different values are there in [a, b]. a == b => 1, can be 0 for distribution over all values in the type.
|
|
UnsignedIntegerType m_ab_distance;
|
|
|
|
// We hoisted this out of the main generation function. Technically,
|
|
// this means that using this distribution will be slower than Lemire's
|
|
// algorithm if this distribution instance will be used only few times,
|
|
// but it will be faster if it is used many times. Since Catch2 uses
|
|
// distributions only to implement random generators, we assume that each
|
|
// distribution will be reused many times and this is an optimization.
|
|
UnsignedIntegerType m_rejection_threshold = 0;
|
|
|
|
// Assumes m_b and m_a are already filled
|
|
UnsignedIntegerType computeDistance() const {
|
|
// This overflows and returns 0 if ua == 0 and ub == TYPE_MAX.
|
|
// We handle that later when generating the number.
|
|
return transposeTo(m_b) - m_a + 1;
|
|
}
|
|
|
|
static UnsignedIntegerType computeRejectionThreshold(UnsignedIntegerType ab_distance) {
|
|
// distance == 0 means that we will return all possible values from
|
|
// the type's range, and that we shouldn't reject anything.
|
|
if ( ab_distance == 0 ) { return 0; }
|
|
return ( ~ab_distance + 1 ) % ab_distance;
|
|
}
|
|
|
|
static UnsignedIntegerType transposeTo(IntegerType in) {
|
|
return Detail::transposeToNaturalOrder<IntegerType>(
|
|
static_cast<UnsignedIntegerType>( in ) );
|
|
}
|
|
static IntegerType transposeBack(UnsignedIntegerType in) {
|
|
return static_cast<IntegerType>(
|
|
Detail::transposeToNaturalOrder<IntegerType>(in) );
|
|
}
|
|
|
|
public:
|
|
using result_type = IntegerType;
|
|
|
|
uniform_integer_distribution( IntegerType a, IntegerType b ):
|
|
m_a( transposeTo(a) ),
|
|
m_b( b ),
|
|
m_ab_distance( computeDistance() ),
|
|
m_rejection_threshold( computeRejectionThreshold(m_ab_distance) ) {
|
|
assert( a <= b );
|
|
}
|
|
|
|
template <typename Generator>
|
|
result_type operator()( Generator& g ) {
|
|
// All possible values of result_type are valid.
|
|
if ( m_ab_distance == 0 ) {
|
|
return transposeBack( Detail::fillBitsFrom<UnsignedIntegerType>( g ) );
|
|
}
|
|
|
|
auto random_number = Detail::fillBitsFrom<UnsignedIntegerType>( g );
|
|
auto emul = Detail::extendedMult( random_number, m_ab_distance );
|
|
// Unlike Lemire's algorithm we skip the ab_distance check, since
|
|
// we precomputed the rejection threshold, which is always tighter.
|
|
while (emul.lower < m_rejection_threshold) {
|
|
random_number = Detail::fillBitsFrom<UnsignedIntegerType>( g );
|
|
emul = Detail::extendedMult( random_number, m_ab_distance );
|
|
}
|
|
|
|
return transposeBack(m_a + emul.upper);
|
|
}
|
|
|
|
result_type a() const { return transposeBack(m_a); }
|
|
result_type b() const { return m_b; }
|
|
};
|
|
|
|
} // end namespace Catch
|
|
|
|
#endif // CATCH_UNIFORM_INTEGER_DISTRIBUTION_HPP_INCLUDED
|
|
|
|
|
|
|
|
#ifndef CATCH_UNIFORM_FLOATING_POINT_DISTRIBUTION_HPP_INCLUDED
|
|
#define CATCH_UNIFORM_FLOATING_POINT_DISTRIBUTION_HPP_INCLUDED
|
|
|
|
|
|
|
|
|
|
#ifndef CATCH_RANDOM_FLOATING_POINT_HELPERS_HPP_INCLUDED
|
|
#define CATCH_RANDOM_FLOATING_POINT_HELPERS_HPP_INCLUDED
|
|
|
|
|
|
|
|
#ifndef CATCH_POLYFILLS_HPP_INCLUDED
|
|
#define CATCH_POLYFILLS_HPP_INCLUDED
|
|
|
|
namespace Catch {
|
|
|
|
bool isnan(float f);
|
|
bool isnan(double d);
|
|
|
|
float nextafter(float x, float y);
|
|
double nextafter(double x, double y);
|
|
|
|
}
|
|
|
|
#endif // CATCH_POLYFILLS_HPP_INCLUDED
|
|
|
|
#include <cassert>
|
|
#include <cmath>
|
|
#include <cstdint>
|
|
#include <limits>
|
|
#include <type_traits>
|
|
|
|
namespace Catch {
|
|
|
|
namespace Detail {
|
|
/**
|
|
* Returns the largest magnitude of 1-ULP distance inside the [a, b] range.
|
|
*
|
|
* Assumes `a < b`.
|
|
*/
|
|
template <typename FloatType>
|
|
FloatType gamma(FloatType a, FloatType b) {
|
|
static_assert( std::is_floating_point<FloatType>::value,
|
|
"gamma returns the largest ULP magnitude within "
|
|
"floating point range [a, b]. This only makes sense "
|
|
"for floating point types" );
|
|
assert( a <= b );
|
|
|
|
const auto gamma_up = Catch::nextafter( a, std::numeric_limits<FloatType>::infinity() ) - a;
|
|
const auto gamma_down = b - Catch::nextafter( b, -std::numeric_limits<FloatType>::infinity() );
|
|
|
|
return gamma_up < gamma_down ? gamma_down : gamma_up;
|
|
}
|
|
|
|
template <typename FloatingPoint>
|
|
struct DistanceTypePicker;
|
|
template <>
|
|
struct DistanceTypePicker<float> {
|
|
using type = std::uint32_t;
|
|
};
|
|
template <>
|
|
struct DistanceTypePicker<double> {
|
|
using type = std::uint64_t;
|
|
};
|
|
|
|
template <typename T>
|
|
using DistanceType = typename DistanceTypePicker<T>::type;
|
|
|
|
#if defined( __GNUC__ ) || defined( __clang__ )
|
|
# pragma GCC diagnostic push
|
|
# pragma GCC diagnostic ignored "-Wfloat-equal"
|
|
#endif
|
|
/**
|
|
* Computes the number of equi-distant floats in [a, b]
|
|
*
|
|
* Since not every range can be split into equidistant floats
|
|
* exactly, we actually compute ceil(b/distance - a/distance),
|
|
* because in those cases we want to overcount.
|
|
*
|
|
* Uses modified Dekker's FastTwoSum algorithm to handle rounding.
|
|
*/
|
|
template <typename FloatType>
|
|
DistanceType<FloatType>
|
|
count_equidistant_floats( FloatType a, FloatType b, FloatType distance ) {
|
|
assert( a <= b );
|
|
// We get distance as gamma for our uniform float distribution,
|
|
// so this will round perfectly.
|
|
const auto ag = a / distance;
|
|
const auto bg = b / distance;
|
|
|
|
const auto s = bg - ag;
|
|
const auto err = ( std::fabs( a ) <= std::fabs( b ) )
|
|
? -ag - ( s - bg )
|
|
: bg - ( s + ag );
|
|
const auto ceil_s = static_cast<DistanceType<FloatType>>( std::ceil( s ) );
|
|
|
|
return ( ceil_s != s ) ? ceil_s : ceil_s + ( err > 0 );
|
|
}
|
|
#if defined( __GNUC__ ) || defined( __clang__ )
|
|
# pragma GCC diagnostic pop
|
|
#endif
|
|
|
|
}
|
|
|
|
} // end namespace Catch
|
|
|
|
#endif // CATCH_RANDOM_FLOATING_POINT_HELPERS_HPP_INCLUDED
|
|
|
|
#include <cmath>
|
|
#include <type_traits>
|
|
|
|
namespace Catch {
|
|
|
|
namespace Detail {
|
|
#if defined( __GNUC__ ) || defined( __clang__ )
|
|
# pragma GCC diagnostic push
|
|
# pragma GCC diagnostic ignored "-Wfloat-equal"
|
|
#endif
|
|
// The issue with overflow only happens with maximal ULP and HUGE
|
|
// distance, e.g. when generating numbers in [-inf, inf] for given
|
|
// type. So we only check for the largest possible ULP in the
|
|
// type, and return something that does not overflow to inf in 1 mult.
|
|
constexpr std::uint64_t calculate_max_steps_in_one_go(double gamma) {
|
|
if ( gamma == 1.99584030953472e+292 ) { return 9007199254740991; }
|
|
return static_cast<std::uint64_t>( -1 );
|
|
}
|
|
constexpr std::uint32_t calculate_max_steps_in_one_go(float gamma) {
|
|
if ( gamma == 2.028241e+31f ) { return 16777215; }
|
|
return static_cast<std::uint32_t>( -1 );
|
|
}
|
|
#if defined( __GNUC__ ) || defined( __clang__ )
|
|
# pragma GCC diagnostic pop
|
|
#endif
|
|
}
|
|
|
|
/**
|
|
* Implementation of uniform distribution on floating point numbers.
|
|
*
|
|
* Note that we support only `float` and `double` types, because these
|
|
* usually mean the same thing across different platform. `long double`
|
|
* varies wildly by platform and thus we cannot provide reproducible
|
|
* implementation. Also note that we don't implement all parts of
|
|
* distribution per standard: this distribution is not serializable, nor
|
|
* can the range be arbitrarily reset.
|
|
*
|
|
* The implementation also uses different approach than the one taken by
|
|
* `std::uniform_real_distribution`, where instead of generating a number
|
|
* between [0, 1) and then multiplying the range bounds with it, we first
|
|
* split the [a, b] range into a set of equidistributed floating point
|
|
* numbers, and then use uniform int distribution to pick which one to
|
|
* return.
|
|
*
|
|
* This has the advantage of guaranteeing uniformity (the multiplication
|
|
* method loses uniformity due to rounding when multiplying floats), except
|
|
* for small non-uniformity at one side of the interval, where we have
|
|
* to deal with the fact that not every interval is splittable into
|
|
* equidistributed floats.
|
|
*
|
|
* Based on "Drawing random floating-point numbers from an interval" by
|
|
* Frederic Goualard.
|
|
*/
|
|
template <typename FloatType>
|
|
class uniform_floating_point_distribution {
|
|
static_assert(std::is_floating_point<FloatType>::value, "...");
|
|
static_assert(!std::is_same<FloatType, long double>::value,
|
|
"We do not support long double due to inconsistent behaviour between platforms");
|
|
|
|
using WidthType = Detail::DistanceType<FloatType>;
|
|
|
|
FloatType m_a, m_b;
|
|
FloatType m_ulp_magnitude;
|
|
WidthType m_floats_in_range;
|
|
uniform_integer_distribution<WidthType> m_int_dist;
|
|
|
|
// In specific cases, we can overflow into `inf` when computing the
|
|
// `steps * g` offset. To avoid this, we don't offset by more than this
|
|
// in one multiply + addition.
|
|
WidthType m_max_steps_in_one_go;
|
|
// We don't want to do the magnitude check every call to `operator()`
|
|
bool m_a_has_leq_magnitude;
|
|
|
|
public:
|
|
using result_type = FloatType;
|
|
|
|
uniform_floating_point_distribution( FloatType a, FloatType b ):
|
|
m_a( a ),
|
|
m_b( b ),
|
|
m_ulp_magnitude( Detail::gamma( m_a, m_b ) ),
|
|
m_floats_in_range( Detail::count_equidistant_floats( m_a, m_b, m_ulp_magnitude ) ),
|
|
m_int_dist(0, m_floats_in_range),
|
|
m_max_steps_in_one_go( Detail::calculate_max_steps_in_one_go(m_ulp_magnitude)),
|
|
m_a_has_leq_magnitude(std::fabs(m_a) <= std::fabs(m_b))
|
|
{
|
|
assert( a <= b );
|
|
}
|
|
|
|
template <typename Generator>
|
|
result_type operator()( Generator& g ) {
|
|
WidthType steps = m_int_dist( g );
|
|
if ( m_a_has_leq_magnitude ) {
|
|
if ( steps == m_floats_in_range ) { return m_a; }
|
|
auto b = m_b;
|
|
while (steps > m_max_steps_in_one_go) {
|
|
b -= m_max_steps_in_one_go * m_ulp_magnitude;
|
|
steps -= m_max_steps_in_one_go;
|
|
}
|
|
return b - steps * m_ulp_magnitude;
|
|
} else {
|
|
if ( steps == m_floats_in_range ) { return m_b; }
|
|
auto a = m_a;
|
|
while (steps > m_max_steps_in_one_go) {
|
|
a += m_max_steps_in_one_go * m_ulp_magnitude;
|
|
steps -= m_max_steps_in_one_go;
|
|
}
|
|
return a + steps * m_ulp_magnitude;
|
|
}
|
|
}
|
|
|
|
result_type a() const { return m_a; }
|
|
result_type b() const { return m_b; }
|
|
};
|
|
|
|
} // end namespace Catch
|
|
|
|
#endif // CATCH_UNIFORM_FLOATING_POINT_DISTRIBUTION_HPP_INCLUDED
|
|
|
|
namespace Catch {
|
|
namespace Generators {
|
|
namespace Detail {
|
|
// Returns a suitable seed for a random floating generator based off
|
|
// the primary internal rng. It does so by taking current value from
|
|
// the rng and returning it as the seed.
|
|
std::uint32_t getSeed();
|
|
}
|
|
|
|
template <typename Float>
|
|
class RandomFloatingGenerator final : public IGenerator<Float> {
|
|
Catch::SimplePcg32 m_rng;
|
|
Catch::uniform_floating_point_distribution<Float> m_dist;
|
|
Float m_current_number;
|
|
public:
|
|
RandomFloatingGenerator( Float a, Float b, std::uint32_t seed ):
|
|
m_rng(seed),
|
|
m_dist(a, b) {
|
|
static_cast<void>(next());
|
|
}
|
|
|
|
Float const& get() const override {
|
|
return m_current_number;
|
|
}
|
|
bool next() override {
|
|
m_current_number = m_dist(m_rng);
|
|
return true;
|
|
}
|
|
};
|
|
|
|
template <>
|
|
class RandomFloatingGenerator<long double> final : public IGenerator<long double> {
|
|
// We still rely on <random> for this specialization, but we don't
|
|
// want to drag it into the header.
|
|
struct PImpl;
|
|
Catch::Detail::unique_ptr<PImpl> m_pimpl;
|
|
long double m_current_number;
|
|
|
|
public:
|
|
RandomFloatingGenerator( long double a, long double b, std::uint32_t seed );
|
|
|
|
long double const& get() const override { return m_current_number; }
|
|
bool next() override;
|
|
|
|
~RandomFloatingGenerator() override; // = default
|
|
};
|
|
|
|
template <typename Integer>
|
|
class RandomIntegerGenerator final : public IGenerator<Integer> {
|
|
Catch::SimplePcg32 m_rng;
|
|
Catch::uniform_integer_distribution<Integer> m_dist;
|
|
Integer m_current_number;
|
|
public:
|
|
RandomIntegerGenerator( Integer a, Integer b, std::uint32_t seed ):
|
|
m_rng(seed),
|
|
m_dist(a, b) {
|
|
static_cast<void>(next());
|
|
}
|
|
|
|
Integer const& get() const override {
|
|
return m_current_number;
|
|
}
|
|
bool next() override {
|
|
m_current_number = m_dist(m_rng);
|
|
return true;
|
|
}
|
|
};
|
|
|
|
template <typename T>
|
|
std::enable_if_t<std::is_integral<T>::value, GeneratorWrapper<T>>
|
|
random(T a, T b) {
|
|
return GeneratorWrapper<T>(
|
|
Catch::Detail::make_unique<RandomIntegerGenerator<T>>(a, b, Detail::getSeed())
|
|
);
|
|
}
|
|
|
|
template <typename T>
|
|
std::enable_if_t<std::is_floating_point<T>::value,
|
|
GeneratorWrapper<T>>
|
|
random(T a, T b) {
|
|
return GeneratorWrapper<T>(
|
|
Catch::Detail::make_unique<RandomFloatingGenerator<T>>(a, b, Detail::getSeed())
|
|
);
|
|
}
|
|
|
|
|
|
} // namespace Generators
|
|
} // namespace Catch
|
|
|
|
|
|
#endif // CATCH_GENERATORS_RANDOM_HPP_INCLUDED
|
|
|
|
|
|
#ifndef CATCH_GENERATORS_RANGE_HPP_INCLUDED
|
|
#define CATCH_GENERATORS_RANGE_HPP_INCLUDED
|
|
|
|
|
|
#include <iterator>
|
|
#include <type_traits>
|
|
|
|
namespace Catch {
|
|
namespace Generators {
|
|
|
|
|
|
template <typename T>
|
|
class RangeGenerator final : public IGenerator<T> {
|
|
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 <typename T>
|
|
GeneratorWrapper<T> range(T const& start, T const& end, T const& step) {
|
|
static_assert(std::is_arithmetic<T>::value && !std::is_same<T, bool>::value, "Type must be numeric");
|
|
return GeneratorWrapper<T>(Catch::Detail::make_unique<RangeGenerator<T>>(start, end, step));
|
|
}
|
|
|
|
template <typename T>
|
|
GeneratorWrapper<T> range(T const& start, T const& end) {
|
|
static_assert(std::is_integral<T>::value && !std::is_same<T, bool>::value, "Type must be an integer");
|
|
return GeneratorWrapper<T>(Catch::Detail::make_unique<RangeGenerator<T>>(start, end));
|
|
}
|
|
|
|
|
|
template <typename T>
|
|
class IteratorGenerator final : public IGenerator<T> {
|
|
static_assert(!std::is_same<T, bool>::value,
|
|
"IteratorGenerator currently does not support bools"
|
|
"because of std::vector<bool> specialization");
|
|
|
|
std::vector<T> m_elems;
|
|
size_t m_current = 0;
|
|
public:
|
|
template <typename InputIterator, typename InputSentinel>
|
|
IteratorGenerator(InputIterator first, InputSentinel last):m_elems(first, last) {
|
|
if (m_elems.empty()) {
|
|
Detail::throw_generator_exception("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 <typename InputIterator,
|
|
typename InputSentinel,
|
|
typename ResultType = typename std::iterator_traits<InputIterator>::value_type>
|
|
GeneratorWrapper<ResultType> from_range(InputIterator from, InputSentinel to) {
|
|
return GeneratorWrapper<ResultType>(Catch::Detail::make_unique<IteratorGenerator<ResultType>>(from, to));
|
|
}
|
|
|
|
template <typename Container>
|
|
auto from_range(Container const& cnt) {
|
|
using std::begin;
|
|
using std::end;
|
|
return from_range( begin( cnt ), end( cnt ) );
|
|
}
|
|
|
|
|
|
} // namespace Generators
|
|
} // namespace Catch
|
|
|
|
|
|
#endif // CATCH_GENERATORS_RANGE_HPP_INCLUDED
|
|
|
|
#endif // CATCH_GENERATORS_ALL_HPP_INCLUDED
|
|
|
|
|
|
/** \file
|
|
* This is a convenience header for Catch2's interfaces. It includes
|
|
* **all** of Catch2 headers related to interfaces.
|
|
*
|
|
* Generally the Catch2 users should use specific includes they need,
|
|
* but this header can be used instead for ease-of-experimentation, or
|
|
* just plain convenience, at the cost of somewhat increased compilation
|
|
* times.
|
|
*
|
|
* When a new header is added to either the `interfaces` folder, or to
|
|
* the corresponding internal subfolder, it should be added here.
|
|
*/
|
|
|
|
|
|
#ifndef CATCH_INTERFACES_ALL_HPP_INCLUDED
|
|
#define CATCH_INTERFACES_ALL_HPP_INCLUDED
|
|
|
|
|
|
|
|
#ifndef CATCH_INTERFACES_REPORTER_HPP_INCLUDED
|
|
#define CATCH_INTERFACES_REPORTER_HPP_INCLUDED
|
|
|
|
|
|
|
|
#ifndef CATCH_TEST_RUN_INFO_HPP_INCLUDED
|
|
#define CATCH_TEST_RUN_INFO_HPP_INCLUDED
|
|
|
|
|
|
namespace Catch {
|
|
|
|
struct TestRunInfo {
|
|
constexpr TestRunInfo(StringRef _name) : name(_name) {}
|
|
StringRef name;
|
|
};
|
|
|
|
} // end namespace Catch
|
|
|
|
#endif // CATCH_TEST_RUN_INFO_HPP_INCLUDED
|
|
|
|
#include <map>
|
|
#include <string>
|
|
#include <vector>
|
|
#include <iosfwd>
|
|
|
|
namespace Catch {
|
|
|
|
struct ReporterDescription;
|
|
struct ListenerDescription;
|
|
struct TagInfo;
|
|
struct TestCaseInfo;
|
|
class TestCaseHandle;
|
|
class IConfig;
|
|
class IStream;
|
|
enum class ColourMode : std::uint8_t;
|
|
|
|
struct ReporterConfig {
|
|
ReporterConfig( IConfig const* _fullConfig,
|
|
Detail::unique_ptr<IStream> _stream,
|
|
ColourMode colourMode,
|
|
std::map<std::string, std::string> customOptions );
|
|
|
|
ReporterConfig( ReporterConfig&& ) = default;
|
|
ReporterConfig& operator=( ReporterConfig&& ) = default;
|
|
~ReporterConfig(); // = default
|
|
|
|
Detail::unique_ptr<IStream> takeStream() &&;
|
|
IConfig const* fullConfig() const;
|
|
ColourMode colourMode() const;
|
|
std::map<std::string, std::string> const& customOptions() const;
|
|
|
|
private:
|
|
Detail::unique_ptr<IStream> m_stream;
|
|
IConfig const* m_fullConfig;
|
|
ColourMode m_colourMode;
|
|
std::map<std::string, std::string> m_customOptions;
|
|
};
|
|
|
|
struct AssertionStats {
|
|
AssertionStats( AssertionResult const& _assertionResult,
|
|
std::vector<MessageInfo> const& _infoMessages,
|
|
Totals const& _totals );
|
|
|
|
AssertionStats( AssertionStats const& ) = default;
|
|
AssertionStats( AssertionStats && ) = default;
|
|
AssertionStats& operator = ( AssertionStats const& ) = delete;
|
|
AssertionStats& operator = ( AssertionStats && ) = delete;
|
|
|
|
AssertionResult assertionResult;
|
|
std::vector<MessageInfo> infoMessages;
|
|
Totals totals;
|
|
};
|
|
|
|
struct SectionStats {
|
|
SectionStats( SectionInfo&& _sectionInfo,
|
|
Counts const& _assertions,
|
|
double _durationInSeconds,
|
|
bool _missingAssertions );
|
|
|
|
SectionInfo sectionInfo;
|
|
Counts assertions;
|
|
double durationInSeconds;
|
|
bool missingAssertions;
|
|
};
|
|
|
|
struct TestCaseStats {
|
|
TestCaseStats( TestCaseInfo const& _testInfo,
|
|
Totals const& _totals,
|
|
std::string&& _stdOut,
|
|
std::string&& _stdErr,
|
|
bool _aborting );
|
|
|
|
TestCaseInfo const * testInfo;
|
|
Totals totals;
|
|
std::string stdOut;
|
|
std::string stdErr;
|
|
bool aborting;
|
|
};
|
|
|
|
struct TestRunStats {
|
|
TestRunStats( TestRunInfo const& _runInfo,
|
|
Totals const& _totals,
|
|
bool _aborting );
|
|
|
|
TestRunInfo runInfo;
|
|
Totals totals;
|
|
bool aborting;
|
|
};
|
|
|
|
//! By setting up its preferences, a reporter can modify Catch2's behaviour
|
|
//! in some regards, e.g. it can request Catch2 to capture writes to
|
|
//! stdout/stderr during test execution, and pass them to the reporter.
|
|
struct ReporterPreferences {
|
|
//! Catch2 should redirect writes to stdout and pass them to the
|
|
//! reporter
|
|
bool shouldRedirectStdOut = false;
|
|
//! Catch2 should call `Reporter::assertionEnded` even for passing
|
|
//! assertions
|
|
bool shouldReportAllAssertions = false;
|
|
};
|
|
|
|
/**
|
|
* The common base for all reporters and event listeners
|
|
*
|
|
* Implementing classes must also implement:
|
|
*
|
|
* //! User-friendly description of the reporter/listener type
|
|
* static std::string getDescription()
|
|
*
|
|
* Generally shouldn't be derived from by users of Catch2 directly,
|
|
* instead they should derive from one of the utility bases that
|
|
* derive from this class.
|
|
*/
|
|
class IEventListener {
|
|
protected:
|
|
//! Derived classes can set up their preferences here
|
|
ReporterPreferences m_preferences;
|
|
//! The test run's config as filled in from CLI and defaults
|
|
IConfig const* m_config;
|
|
|
|
public:
|
|
IEventListener( IConfig const* config ): m_config( config ) {}
|
|
|
|
virtual ~IEventListener(); // = default;
|
|
|
|
// Implementing class must also provide the following static methods:
|
|
// static std::string getDescription();
|
|
|
|
ReporterPreferences const& getPreferences() const {
|
|
return m_preferences;
|
|
}
|
|
|
|
//! Called when no test cases match provided test spec
|
|
virtual void noMatchingTestCases( StringRef unmatchedSpec ) = 0;
|
|
//! Called for all invalid test specs from the cli
|
|
virtual void reportInvalidTestSpec( StringRef invalidArgument ) = 0;
|
|
|
|
/**
|
|
* Called once in a testing run before tests are started
|
|
*
|
|
* Not called if tests won't be run (e.g. only listing will happen)
|
|
*/
|
|
virtual void testRunStarting( TestRunInfo const& testRunInfo ) = 0;
|
|
|
|
//! Called _once_ for each TEST_CASE, no matter how many times it is entered
|
|
virtual void testCaseStarting( TestCaseInfo const& testInfo ) = 0;
|
|
//! Called _every time_ a TEST_CASE is entered, including repeats (due to sections)
|
|
virtual void testCasePartialStarting( TestCaseInfo const& testInfo, uint64_t partNumber ) = 0;
|
|
//! Called when a `SECTION` is being entered. Not called for skipped sections
|
|
virtual void sectionStarting( SectionInfo const& sectionInfo ) = 0;
|
|
|
|
//! Called when user-code is being probed before the actual benchmark runs
|
|
virtual void benchmarkPreparing( StringRef benchmarkName ) = 0;
|
|
//! Called after probe but before the user-code is being benchmarked
|
|
virtual void benchmarkStarting( BenchmarkInfo const& benchmarkInfo ) = 0;
|
|
//! Called with the benchmark results if benchmark successfully finishes
|
|
virtual void benchmarkEnded( BenchmarkStats<> const& benchmarkStats ) = 0;
|
|
//! Called if running the benchmarks fails for any reason
|
|
virtual void benchmarkFailed( StringRef benchmarkName ) = 0;
|
|
|
|
//! Called before assertion success/failure is evaluated
|
|
virtual void assertionStarting( AssertionInfo const& assertionInfo ) = 0;
|
|
|
|
//! Called after assertion was fully evaluated
|
|
virtual void assertionEnded( AssertionStats const& assertionStats ) = 0;
|
|
|
|
//! Called after a `SECTION` has finished running
|
|
virtual void sectionEnded( SectionStats const& sectionStats ) = 0;
|
|
//! Called _every time_ a TEST_CASE is entered, including repeats (due to sections)
|
|
virtual void testCasePartialEnded(TestCaseStats const& testCaseStats, uint64_t partNumber ) = 0;
|
|
//! Called _once_ for each TEST_CASE, no matter how many times it is entered
|
|
virtual void testCaseEnded( TestCaseStats const& testCaseStats ) = 0;
|
|
/**
|
|
* Called once after all tests in a testing run are finished
|
|
*
|
|
* Not called if tests weren't run (e.g. only listings happened)
|
|
*/
|
|
virtual void testRunEnded( TestRunStats const& testRunStats ) = 0;
|
|
|
|
/**
|
|
* Called with test cases that are skipped due to the test run aborting.
|
|
* NOT called for test cases that are explicitly skipped using the `SKIP` macro.
|
|
*
|
|
* Deprecated - will be removed in the next major release.
|
|
*/
|
|
virtual void skipTest( TestCaseInfo const& testInfo ) = 0;
|
|
|
|
//! Called if a fatal error (signal/structured exception) occurred
|
|
virtual void fatalErrorEncountered( StringRef error ) = 0;
|
|
|
|
//! Writes out information about provided reporters using reporter-specific format
|
|
virtual void listReporters(std::vector<ReporterDescription> const& descriptions) = 0;
|
|
//! Writes out the provided listeners descriptions using reporter-specific format
|
|
virtual void listListeners(std::vector<ListenerDescription> const& descriptions) = 0;
|
|
//! Writes out information about provided tests using reporter-specific format
|
|
virtual void listTests(std::vector<TestCaseHandle> const& tests) = 0;
|
|
//! Writes out information about the provided tags using reporter-specific format
|
|
virtual void listTags(std::vector<TagInfo> const& tags) = 0;
|
|
};
|
|
using IEventListenerPtr = Detail::unique_ptr<IEventListener>;
|
|
|
|
} // end namespace Catch
|
|
|
|
#endif // CATCH_INTERFACES_REPORTER_HPP_INCLUDED
|
|
|
|
|
|
#ifndef CATCH_INTERFACES_REPORTER_FACTORY_HPP_INCLUDED
|
|
#define CATCH_INTERFACES_REPORTER_FACTORY_HPP_INCLUDED
|
|
|
|
|
|
#include <string>
|
|
|
|
namespace Catch {
|
|
|
|
struct ReporterConfig;
|
|
class IConfig;
|
|
class IEventListener;
|
|
using IEventListenerPtr = Detail::unique_ptr<IEventListener>;
|
|
|
|
|
|
class IReporterFactory {
|
|
public:
|
|
virtual ~IReporterFactory(); // = default
|
|
|
|
virtual IEventListenerPtr
|
|
create( ReporterConfig&& config ) const = 0;
|
|
virtual std::string getDescription() const = 0;
|
|
};
|
|
using IReporterFactoryPtr = Detail::unique_ptr<IReporterFactory>;
|
|
|
|
class EventListenerFactory {
|
|
public:
|
|
virtual ~EventListenerFactory(); // = default
|
|
virtual IEventListenerPtr create( IConfig const* config ) const = 0;
|
|
//! Return a meaningful name for the listener, e.g. its type name
|
|
virtual StringRef getName() const = 0;
|
|
//! Return listener's description if available
|
|
virtual std::string getDescription() const = 0;
|
|
};
|
|
} // namespace Catch
|
|
|
|
#endif // CATCH_INTERFACES_REPORTER_FACTORY_HPP_INCLUDED
|
|
|
|
|
|
#ifndef CATCH_INTERFACES_TAG_ALIAS_REGISTRY_HPP_INCLUDED
|
|
#define CATCH_INTERFACES_TAG_ALIAS_REGISTRY_HPP_INCLUDED
|
|
|
|
#include <string>
|
|
|
|
namespace Catch {
|
|
|
|
struct TagAlias;
|
|
|
|
class ITagAliasRegistry {
|
|
public:
|
|
virtual ~ITagAliasRegistry(); // = default
|
|
// 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
|
|
|
|
#endif // CATCH_INTERFACES_TAG_ALIAS_REGISTRY_HPP_INCLUDED
|
|
|
|
|
|
#ifndef CATCH_INTERFACES_TESTCASE_HPP_INCLUDED
|
|
#define CATCH_INTERFACES_TESTCASE_HPP_INCLUDED
|
|
|
|
#include <vector>
|
|
|
|
namespace Catch {
|
|
|
|
struct TestCaseInfo;
|
|
class TestCaseHandle;
|
|
class IConfig;
|
|
|
|
class ITestCaseRegistry {
|
|
public:
|
|
virtual ~ITestCaseRegistry(); // = default
|
|
// TODO: this exists only for adding filenames to test cases -- let's expose this in a saner way later
|
|
virtual std::vector<TestCaseInfo* > const& getAllInfos() const = 0;
|
|
virtual std::vector<TestCaseHandle> const& getAllTests() const = 0;
|
|
virtual std::vector<TestCaseHandle> const& getAllTestsSorted( IConfig const& config ) const = 0;
|
|
};
|
|
|
|
}
|
|
|
|
#endif // CATCH_INTERFACES_TESTCASE_HPP_INCLUDED
|
|
|
|
#endif // CATCH_INTERFACES_ALL_HPP_INCLUDED
|
|
|
|
|
|
#ifndef CATCH_CASE_INSENSITIVE_COMPARISONS_HPP_INCLUDED
|
|
#define CATCH_CASE_INSENSITIVE_COMPARISONS_HPP_INCLUDED
|
|
|
|
|
|
namespace Catch {
|
|
namespace Detail {
|
|
//! Provides case-insensitive `op<` semantics when called
|
|
struct CaseInsensitiveLess {
|
|
bool operator()( StringRef lhs,
|
|
StringRef rhs ) const;
|
|
};
|
|
|
|
//! Provides case-insensitive `op==` semantics when called
|
|
struct CaseInsensitiveEqualTo {
|
|
bool operator()( StringRef lhs,
|
|
StringRef rhs ) const;
|
|
};
|
|
|
|
} // namespace Detail
|
|
} // namespace Catch
|
|
|
|
#endif // CATCH_CASE_INSENSITIVE_COMPARISONS_HPP_INCLUDED
|
|
|
|
|
|
|
|
/** \file
|
|
* Wrapper for ANDROID_LOGWRITE configuration option
|
|
*
|
|
* We want to default to enabling it when compiled for android, but
|
|
* users of the library should also be able to disable it if they want
|
|
* to.
|
|
*/
|
|
|
|
#ifndef CATCH_CONFIG_ANDROID_LOGWRITE_HPP_INCLUDED
|
|
#define CATCH_CONFIG_ANDROID_LOGWRITE_HPP_INCLUDED
|
|
|
|
|
|
#if defined(__ANDROID__)
|
|
# define CATCH_INTERNAL_CONFIG_ANDROID_LOGWRITE
|
|
#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
|
|
|
|
#endif // CATCH_CONFIG_ANDROID_LOGWRITE_HPP_INCLUDED
|
|
|
|
|
|
|
|
/** \file
|
|
* Wrapper for UNCAUGHT_EXCEPTIONS configuration option
|
|
*
|
|
* For some functionality, Catch2 requires to know whether there is
|
|
* an active exception. Because `std::uncaught_exception` is deprecated
|
|
* in C++17, we want to use `std::uncaught_exceptions` if possible.
|
|
*/
|
|
|
|
#ifndef CATCH_CONFIG_UNCAUGHT_EXCEPTIONS_HPP_INCLUDED
|
|
#define CATCH_CONFIG_UNCAUGHT_EXCEPTIONS_HPP_INCLUDED
|
|
|
|
|
|
#if defined(_MSC_VER)
|
|
# if _MSC_VER >= 1900 // Visual Studio 2015 or newer
|
|
# define CATCH_INTERNAL_CONFIG_CPP17_UNCAUGHT_EXCEPTIONS
|
|
# endif
|
|
#endif
|
|
|
|
|
|
#include <exception>
|
|
|
|
#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_INCLUDED
|
|
|
|
|
|
#ifndef CATCH_CONSOLE_WIDTH_HPP_INCLUDED
|
|
#define CATCH_CONSOLE_WIDTH_HPP_INCLUDED
|
|
|
|
// This include must be kept so that user's configured value for CONSOLE_WIDTH
|
|
// is used before we attempt to provide a default value
|
|
|
|
#ifndef CATCH_CONFIG_CONSOLE_WIDTH
|
|
#define CATCH_CONFIG_CONSOLE_WIDTH 80
|
|
#endif
|
|
|
|
#endif // CATCH_CONSOLE_WIDTH_HPP_INCLUDED
|
|
|
|
|
|
#ifndef CATCH_CONTAINER_NONMEMBERS_HPP_INCLUDED
|
|
#define CATCH_CONTAINER_NONMEMBERS_HPP_INCLUDED
|
|
|
|
|
|
#include <cstddef>
|
|
#include <initializer_list>
|
|
|
|
// We want a simple polyfill over `std::empty`, `std::size` and so on
|
|
// for C++14 or C++ libraries with incomplete support.
|
|
// We also have to handle that MSVC std lib will happily provide these
|
|
// under older standards.
|
|
#if defined(CATCH_CPP17_OR_GREATER) || defined(_MSC_VER)
|
|
|
|
// We are already using this header either way, so there shouldn't
|
|
// be much additional overhead in including it to get the feature
|
|
// test macros
|
|
#include <string>
|
|
|
|
# if !defined(__cpp_lib_nonmember_container_access)
|
|
# define CATCH_CONFIG_POLYFILL_NONMEMBER_CONTAINER_ACCESS
|
|
# endif
|
|
|
|
#else
|
|
#define CATCH_CONFIG_POLYFILL_NONMEMBER_CONTAINER_ACCESS
|
|
#endif
|
|
|
|
|
|
|
|
namespace Catch {
|
|
namespace Detail {
|
|
|
|
#if defined(CATCH_CONFIG_POLYFILL_NONMEMBER_CONTAINER_ACCESS)
|
|
template <typename Container>
|
|
constexpr auto empty(Container const& cont) -> decltype(cont.empty()) {
|
|
return cont.empty();
|
|
}
|
|
template <typename T, std::size_t N>
|
|
constexpr bool empty(const T (&)[N]) noexcept {
|
|
// GCC < 7 does not support the const T(&)[] parameter syntax
|
|
// so we have to ignore the length explicitly
|
|
(void)N;
|
|
return false;
|
|
}
|
|
template <typename T>
|
|
constexpr bool empty(std::initializer_list<T> list) noexcept {
|
|
return list.size() > 0;
|
|
}
|
|
|
|
|
|
template <typename Container>
|
|
constexpr auto size(Container const& cont) -> decltype(cont.size()) {
|
|
return cont.size();
|
|
}
|
|
template <typename T, std::size_t N>
|
|
constexpr std::size_t size(const T(&)[N]) noexcept {
|
|
return N;
|
|
}
|
|
#endif // CATCH_CONFIG_POLYFILL_NONMEMBER_CONTAINER_ACCESS
|
|
|
|
} // end namespace Detail
|
|
} // end namespace Catch
|
|
|
|
|
|
|
|
#endif // CATCH_CONTAINER_NONMEMBERS_HPP_INCLUDED
|
|
|
|
|
|
#ifndef CATCH_DEBUG_CONSOLE_HPP_INCLUDED
|
|
#define CATCH_DEBUG_CONSOLE_HPP_INCLUDED
|
|
|
|
#include <string>
|
|
|
|
namespace Catch {
|
|
void writeToDebugConsole( std::string const& text );
|
|
}
|
|
|
|
#endif // CATCH_DEBUG_CONSOLE_HPP_INCLUDED
|
|
|
|
|
|
#ifndef CATCH_DEBUGGER_HPP_INCLUDED
|
|
#define CATCH_DEBUGGER_HPP_INCLUDED
|
|
|
|
|
|
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 0xd43e0000")
|
|
#elif defined(__POWERPC__)
|
|
#define CATCH_TRAP() __asm__("li r0, 20\nsc\nnop\nli r0, 37\nli r4, 2\nsc\nnop\n" \
|
|
: : : "memory","r0","r3","r4" ) /* NOLINT */
|
|
#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 <signal.h>
|
|
|
|
#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
|
|
|
|
#endif // CATCH_DEBUGGER_HPP_INCLUDED
|
|
|
|
|
|
#ifndef CATCH_ENFORCE_HPP_INCLUDED
|
|
#define CATCH_ENFORCE_HPP_INCLUDED
|
|
|
|
|
|
#include <exception>
|
|
|
|
namespace Catch {
|
|
#if !defined(CATCH_CONFIG_DISABLE_EXCEPTIONS)
|
|
template <typename Ex>
|
|
[[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)
|
|
|
|
|
|
#endif // CATCH_ENFORCE_HPP_INCLUDED
|
|
|
|
|
|
#ifndef CATCH_ENUM_VALUES_REGISTRY_HPP_INCLUDED
|
|
#define CATCH_ENUM_VALUES_REGISTRY_HPP_INCLUDED
|
|
|
|
|
|
#include <vector>
|
|
|
|
namespace Catch {
|
|
|
|
namespace Detail {
|
|
|
|
Catch::Detail::unique_ptr<EnumInfo> makeEnumInfo( StringRef enumName, StringRef allValueNames, std::vector<int> const& values );
|
|
|
|
class EnumValuesRegistry : public IMutableEnumValuesRegistry {
|
|
|
|
std::vector<Catch::Detail::unique_ptr<EnumInfo>> m_enumInfos;
|
|
|
|
EnumInfo const& registerEnum( StringRef enumName, StringRef allEnums, std::vector<int> const& values) override;
|
|
};
|
|
|
|
std::vector<StringRef> parseEnums( StringRef enums );
|
|
|
|
} // Detail
|
|
|
|
} // Catch
|
|
|
|
#endif // CATCH_ENUM_VALUES_REGISTRY_HPP_INCLUDED
|
|
|
|
|
|
#ifndef CATCH_ERRNO_GUARD_HPP_INCLUDED
|
|
#define CATCH_ERRNO_GUARD_HPP_INCLUDED
|
|
|
|
namespace Catch {
|
|
|
|
//! Simple RAII class that stores the value of `errno`
|
|
//! at construction and restores it at destruction.
|
|
class ErrnoGuard {
|
|
public:
|
|
// Keep these outlined to avoid dragging in macros from <cerrno>
|
|
|
|
ErrnoGuard();
|
|
~ErrnoGuard();
|
|
private:
|
|
int m_oldErrno;
|
|
};
|
|
|
|
}
|
|
|
|
#endif // CATCH_ERRNO_GUARD_HPP_INCLUDED
|
|
|
|
|
|
#ifndef CATCH_EXCEPTION_TRANSLATOR_REGISTRY_HPP_INCLUDED
|
|
#define CATCH_EXCEPTION_TRANSLATOR_REGISTRY_HPP_INCLUDED
|
|
|
|
|
|
#include <vector>
|
|
#include <string>
|
|
|
|
namespace Catch {
|
|
|
|
class ExceptionTranslatorRegistry : public IExceptionTranslatorRegistry {
|
|
public:
|
|
~ExceptionTranslatorRegistry() override;
|
|
void registerTranslator( Detail::unique_ptr<IExceptionTranslator>&& translator );
|
|
std::string translateActiveException() const override;
|
|
|
|
private:
|
|
ExceptionTranslators m_translators;
|
|
};
|
|
}
|
|
|
|
#endif // CATCH_EXCEPTION_TRANSLATOR_REGISTRY_HPP_INCLUDED
|
|
|
|
|
|
#ifndef CATCH_FATAL_CONDITION_HANDLER_HPP_INCLUDED
|
|
#define CATCH_FATAL_CONDITION_HANDLER_HPP_INCLUDED
|
|
|
|
|
|
#include <cassert>
|
|
|
|
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() noexcept;
|
|
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() noexcept {
|
|
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
|
|
|
|
#endif // CATCH_FATAL_CONDITION_HANDLER_HPP_INCLUDED
|
|
|
|
|
|
#ifndef CATCH_FLOATING_POINT_HELPERS_HPP_INCLUDED
|
|
#define CATCH_FLOATING_POINT_HELPERS_HPP_INCLUDED
|
|
|
|
|
|
#include <cassert>
|
|
#include <cmath>
|
|
#include <cstdint>
|
|
#include <utility>
|
|
#include <limits>
|
|
|
|
namespace Catch {
|
|
namespace Detail {
|
|
|
|
uint32_t convertToBits(float f);
|
|
uint64_t convertToBits(double d);
|
|
|
|
// Used when we know we want == comparison of two doubles
|
|
// to centralize warning suppression
|
|
bool directCompare( float lhs, float rhs );
|
|
bool directCompare( double lhs, double rhs );
|
|
|
|
} // end namespace Detail
|
|
|
|
|
|
|
|
#if defined( __GNUC__ ) || defined( __clang__ )
|
|
# pragma GCC diagnostic push
|
|
// We do a bunch of direct compensations of floating point numbers,
|
|
// because we know what we are doing and actually do want the direct
|
|
// comparison behaviour.
|
|
# pragma GCC diagnostic ignored "-Wfloat-equal"
|
|
#endif
|
|
|
|
/**
|
|
* Calculates the ULP distance between two floating point numbers
|
|
*
|
|
* The ULP distance of two floating point numbers is the count of
|
|
* valid floating point numbers representable between them.
|
|
*
|
|
* There are some exceptions between how this function counts the
|
|
* distance, and the interpretation of the standard as implemented.
|
|
* by e.g. `nextafter`. For this function it always holds that:
|
|
* * `(x == y) => ulpDistance(x, y) == 0` (so `ulpDistance(-0, 0) == 0`)
|
|
* * `ulpDistance(maxFinite, INF) == 1`
|
|
* * `ulpDistance(x, -x) == 2 * ulpDistance(x, 0)`
|
|
*
|
|
* \pre `!isnan( lhs )`
|
|
* \pre `!isnan( rhs )`
|
|
* \pre floating point numbers are represented in IEEE-754 format
|
|
*/
|
|
template <typename FP>
|
|
uint64_t ulpDistance( FP lhs, FP rhs ) {
|
|
assert( std::numeric_limits<FP>::is_iec559 &&
|
|
"ulpDistance assumes IEEE-754 format for floating point types" );
|
|
assert( !Catch::isnan( lhs ) &&
|
|
"Distance between NaN and number is not meaningful" );
|
|
assert( !Catch::isnan( rhs ) &&
|
|
"Distance between NaN and number is not meaningful" );
|
|
|
|
// We want X == Y to imply 0 ULP distance even if X and Y aren't
|
|
// bit-equal (-0 and 0), or X - Y != 0 (same sign infinities).
|
|
if ( lhs == rhs ) { return 0; }
|
|
|
|
// We need a properly typed positive zero for type inference.
|
|
static constexpr FP positive_zero{};
|
|
|
|
// We want to ensure that +/- 0 is always represented as positive zero
|
|
if ( lhs == positive_zero ) { lhs = positive_zero; }
|
|
if ( rhs == positive_zero ) { rhs = positive_zero; }
|
|
|
|
// If arguments have different signs, we can handle them by summing
|
|
// how far are they from 0 each.
|
|
if ( std::signbit( lhs ) != std::signbit( rhs ) ) {
|
|
return ulpDistance( std::abs( lhs ), positive_zero ) +
|
|
ulpDistance( std::abs( rhs ), positive_zero );
|
|
}
|
|
|
|
// When both lhs and rhs are of the same sign, we can just
|
|
// read the numbers bitwise as integers, and then subtract them
|
|
// (assuming IEEE).
|
|
uint64_t lc = Detail::convertToBits( lhs );
|
|
uint64_t rc = Detail::convertToBits( rhs );
|
|
|
|
// The ulp distance between two numbers is symmetric, so to avoid
|
|
// dealing with overflows we want the bigger converted number on the lhs
|
|
if ( lc < rc ) {
|
|
std::swap( lc, rc );
|
|
}
|
|
|
|
return lc - rc;
|
|
}
|
|
|
|
#if defined( __GNUC__ ) || defined( __clang__ )
|
|
# pragma GCC diagnostic pop
|
|
#endif
|
|
|
|
|
|
} // end namespace Catch
|
|
|
|
#endif // CATCH_FLOATING_POINT_HELPERS_HPP_INCLUDED
|
|
|
|
|
|
#ifndef CATCH_GETENV_HPP_INCLUDED
|
|
#define CATCH_GETENV_HPP_INCLUDED
|
|
|
|
namespace Catch {
|
|
namespace Detail {
|
|
|
|
//! Wrapper over `std::getenv` that compiles on UWP (and always returns nullptr there)
|
|
char const* getEnv(char const* varName);
|
|
|
|
}
|
|
}
|
|
|
|
#endif // CATCH_GETENV_HPP_INCLUDED
|
|
|
|
|
|
#ifndef CATCH_IS_PERMUTATION_HPP_INCLUDED
|
|
#define CATCH_IS_PERMUTATION_HPP_INCLUDED
|
|
|
|
#include <algorithm>
|
|
#include <iterator>
|
|
|
|
namespace Catch {
|
|
namespace Detail {
|
|
|
|
template <typename ForwardIter,
|
|
typename Sentinel,
|
|
typename T,
|
|
typename Comparator>
|
|
ForwardIter find_sentinel( ForwardIter start,
|
|
Sentinel sentinel,
|
|
T const& value,
|
|
Comparator cmp ) {
|
|
while ( start != sentinel ) {
|
|
if ( cmp( *start, value ) ) { break; }
|
|
++start;
|
|
}
|
|
return start;
|
|
}
|
|
|
|
template <typename ForwardIter,
|
|
typename Sentinel,
|
|
typename T,
|
|
typename Comparator>
|
|
std::ptrdiff_t count_sentinel( ForwardIter start,
|
|
Sentinel sentinel,
|
|
T const& value,
|
|
Comparator cmp ) {
|
|
std::ptrdiff_t count = 0;
|
|
while ( start != sentinel ) {
|
|
if ( cmp( *start, value ) ) { ++count; }
|
|
++start;
|
|
}
|
|
return count;
|
|
}
|
|
|
|
template <typename ForwardIter, typename Sentinel>
|
|
std::enable_if_t<!std::is_same<ForwardIter, Sentinel>::value,
|
|
std::ptrdiff_t>
|
|
sentinel_distance( ForwardIter iter, const Sentinel sentinel ) {
|
|
std::ptrdiff_t dist = 0;
|
|
while ( iter != sentinel ) {
|
|
++iter;
|
|
++dist;
|
|
}
|
|
return dist;
|
|
}
|
|
|
|
template <typename ForwardIter>
|
|
std::ptrdiff_t sentinel_distance( ForwardIter first,
|
|
ForwardIter last ) {
|
|
return std::distance( first, last );
|
|
}
|
|
|
|
template <typename ForwardIter1,
|
|
typename Sentinel1,
|
|
typename ForwardIter2,
|
|
typename Sentinel2,
|
|
typename Comparator>
|
|
bool check_element_counts( ForwardIter1 first_1,
|
|
const Sentinel1 end_1,
|
|
ForwardIter2 first_2,
|
|
const Sentinel2 end_2,
|
|
Comparator cmp ) {
|
|
auto cursor = first_1;
|
|
while ( cursor != end_1 ) {
|
|
if ( find_sentinel( first_1, cursor, *cursor, cmp ) ==
|
|
cursor ) {
|
|
// we haven't checked this element yet
|
|
const auto count_in_range_2 =
|
|
count_sentinel( first_2, end_2, *cursor, cmp );
|
|
// Not a single instance in 2nd range, so it cannot be a
|
|
// permutation of 1st range
|
|
if ( count_in_range_2 == 0 ) { return false; }
|
|
|
|
const auto count_in_range_1 =
|
|
count_sentinel( cursor, end_1, *cursor, cmp );
|
|
if ( count_in_range_1 != count_in_range_2 ) {
|
|
return false;
|
|
}
|
|
}
|
|
|
|
++cursor;
|
|
}
|
|
|
|
return true;
|
|
}
|
|
|
|
template <typename ForwardIter1,
|
|
typename Sentinel1,
|
|
typename ForwardIter2,
|
|
typename Sentinel2,
|
|
typename Comparator>
|
|
bool is_permutation( ForwardIter1 first_1,
|
|
const Sentinel1 end_1,
|
|
ForwardIter2 first_2,
|
|
const Sentinel2 end_2,
|
|
Comparator cmp ) {
|
|
// TODO: no optimization for stronger iterators, because we would also have to constrain on sentinel vs not sentinel types
|
|
// TODO: Comparator has to be "both sides", e.g. a == b => b == a
|
|
// This skips shared prefix of the two ranges
|
|
while (first_1 != end_1 && first_2 != end_2 && cmp(*first_1, *first_2)) {
|
|
++first_1;
|
|
++first_2;
|
|
}
|
|
|
|
// We need to handle case where at least one of the ranges has no more elements
|
|
if (first_1 == end_1 || first_2 == end_2) {
|
|
return first_1 == end_1 && first_2 == end_2;
|
|
}
|
|
|
|
// pair counting is n**2, so we pay linear walk to compare the sizes first
|
|
auto dist_1 = sentinel_distance( first_1, end_1 );
|
|
auto dist_2 = sentinel_distance( first_2, end_2 );
|
|
|
|
if (dist_1 != dist_2) { return false; }
|
|
|
|
// Since we do not try to handle stronger iterators pair (e.g.
|
|
// bidir) optimally, the only thing left to do is to check counts in
|
|
// the remaining ranges.
|
|
return check_element_counts( first_1, end_1, first_2, end_2, cmp );
|
|
}
|
|
|
|
} // namespace Detail
|
|
} // namespace Catch
|
|
|
|
#endif // CATCH_IS_PERMUTATION_HPP_INCLUDED
|
|
|
|
|
|
#ifndef CATCH_ISTREAM_HPP_INCLUDED
|
|
#define CATCH_ISTREAM_HPP_INCLUDED
|
|
|
|
|
|
#include <iosfwd>
|
|
#include <cstddef>
|
|
#include <ostream>
|
|
#include <string>
|
|
|
|
namespace Catch {
|
|
|
|
class IStream {
|
|
public:
|
|
virtual ~IStream(); // = default
|
|
virtual std::ostream& stream() = 0;
|
|
/**
|
|
* Best guess on whether the instance is writing to a console (e.g. via stdout/stderr)
|
|
*
|
|
* This is useful for e.g. Win32 colour support, because the Win32
|
|
* API manipulates console directly, unlike POSIX escape codes,
|
|
* that can be written anywhere.
|
|
*
|
|
* Due to variety of ways to change where the stdout/stderr is
|
|
* _actually_ being written, users should always assume that
|
|
* the answer might be wrong.
|
|
*/
|
|
virtual bool isConsole() const { return false; }
|
|
};
|
|
|
|
/**
|
|
* Creates a stream wrapper that writes to specific file.
|
|
*
|
|
* Also recognizes 4 special filenames
|
|
* * `-` for stdout
|
|
* * `%stdout` for stdout
|
|
* * `%stderr` for stderr
|
|
* * `%debug` for platform specific debugging output
|
|
*
|
|
* \throws if passed an unrecognized %-prefixed stream
|
|
*/
|
|
auto makeStream( std::string const& filename ) -> Detail::unique_ptr<IStream>;
|
|
|
|
}
|
|
|
|
#endif // CATCH_STREAM_HPP_INCLUDED
|
|
|
|
|
|
#ifndef CATCH_JSONWRITER_HPP_INCLUDED
|
|
#define CATCH_JSONWRITER_HPP_INCLUDED
|
|
|
|
|
|
#include <cstdint>
|
|
#include <sstream>
|
|
|
|
namespace Catch {
|
|
class JsonObjectWriter;
|
|
class JsonArrayWriter;
|
|
|
|
struct JsonUtils {
|
|
static void indent( std::ostream& os, std::uint64_t level );
|
|
static void appendCommaNewline( std::ostream& os,
|
|
bool& should_comma,
|
|
std::uint64_t level );
|
|
};
|
|
|
|
class JsonValueWriter {
|
|
public:
|
|
JsonValueWriter( std::ostream& os );
|
|
JsonValueWriter( std::ostream& os, std::uint64_t indent_level );
|
|
|
|
JsonObjectWriter writeObject() &&;
|
|
JsonArrayWriter writeArray() &&;
|
|
|
|
template <typename T>
|
|
void write( T const& value ) && {
|
|
writeImpl( value, !std::is_arithmetic<T>::value );
|
|
}
|
|
void write( StringRef value ) &&;
|
|
void write( bool value ) &&;
|
|
|
|
private:
|
|
void writeImpl( StringRef value, bool quote );
|
|
|
|
// Without this SFINAE, this overload is a better match
|
|
// for `std::string`, `char const*`, `char const[N]` args.
|
|
// While it would still work, it would cause code bloat
|
|
// and multiple iteration over the strings
|
|
template <typename T,
|
|
typename = typename std::enable_if_t<
|
|
!std::is_convertible<T, StringRef>::value>>
|
|
void writeImpl( T const& value, bool quote_value ) {
|
|
m_sstream << value;
|
|
writeImpl( m_sstream.str(), quote_value );
|
|
}
|
|
|
|
std::ostream& m_os;
|
|
std::stringstream m_sstream;
|
|
std::uint64_t m_indent_level;
|
|
};
|
|
|
|
class JsonObjectWriter {
|
|
public:
|
|
JsonObjectWriter( std::ostream& os );
|
|
JsonObjectWriter( std::ostream& os, std::uint64_t indent_level );
|
|
|
|
JsonObjectWriter( JsonObjectWriter&& source );
|
|
JsonObjectWriter& operator=( JsonObjectWriter&& source ) = delete;
|
|
|
|
~JsonObjectWriter();
|
|
|
|
JsonValueWriter write( StringRef key );
|
|
|
|
private:
|
|
std::ostream& m_os;
|
|
std::uint64_t m_indent_level;
|
|
bool m_should_comma = false;
|
|
bool m_active = true;
|
|
};
|
|
|
|
class JsonArrayWriter {
|
|
public:
|
|
JsonArrayWriter( std::ostream& os );
|
|
JsonArrayWriter( std::ostream& os, std::uint64_t indent_level );
|
|
|
|
JsonArrayWriter( JsonArrayWriter&& source );
|
|
JsonArrayWriter& operator=( JsonArrayWriter&& source ) = delete;
|
|
|
|
~JsonArrayWriter();
|
|
|
|
JsonObjectWriter writeObject();
|
|
JsonArrayWriter writeArray();
|
|
|
|
template <typename T>
|
|
JsonArrayWriter& write( T const& value ) {
|
|
return writeImpl( value );
|
|
}
|
|
|
|
JsonArrayWriter& write( bool value );
|
|
|
|
private:
|
|
template <typename T>
|
|
JsonArrayWriter& writeImpl( T const& value ) {
|
|
JsonUtils::appendCommaNewline(
|
|
m_os, m_should_comma, m_indent_level + 1 );
|
|
JsonValueWriter{ m_os }.write( value );
|
|
|
|
return *this;
|
|
}
|
|
|
|
std::ostream& m_os;
|
|
std::uint64_t m_indent_level;
|
|
bool m_should_comma = false;
|
|
bool m_active = true;
|
|
};
|
|
|
|
} // namespace Catch
|
|
|
|
#endif // CATCH_JSONWRITER_HPP_INCLUDED
|
|
|
|
|
|
#ifndef CATCH_LEAK_DETECTOR_HPP_INCLUDED
|
|
#define CATCH_LEAK_DETECTOR_HPP_INCLUDED
|
|
|
|
namespace Catch {
|
|
|
|
struct LeakDetector {
|
|
LeakDetector();
|
|
~LeakDetector();
|
|
};
|
|
|
|
}
|
|
#endif // CATCH_LEAK_DETECTOR_HPP_INCLUDED
|
|
|
|
|
|
#ifndef CATCH_LIST_HPP_INCLUDED
|
|
#define CATCH_LIST_HPP_INCLUDED
|
|
|
|
|
|
#include <set>
|
|
#include <string>
|
|
|
|
|
|
namespace Catch {
|
|
|
|
class IEventListener;
|
|
class Config;
|
|
|
|
|
|
struct ReporterDescription {
|
|
std::string name, description;
|
|
};
|
|
struct ListenerDescription {
|
|
StringRef name;
|
|
std::string description;
|
|
};
|
|
|
|
struct TagInfo {
|
|
void add(StringRef spelling);
|
|
std::string all() const;
|
|
|
|
std::set<StringRef> spellings;
|
|
std::size_t count = 0;
|
|
};
|
|
|
|
bool list( IEventListener& reporter, Config const& config );
|
|
|
|
} // end namespace Catch
|
|
|
|
#endif // CATCH_LIST_HPP_INCLUDED
|
|
|
|
|
|
#ifndef CATCH_OUTPUT_REDIRECT_HPP_INCLUDED
|
|
#define CATCH_OUTPUT_REDIRECT_HPP_INCLUDED
|
|
|
|
|
|
#include <cstdio>
|
|
#include <iosfwd>
|
|
#include <string>
|
|
|
|
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 // CATCH_OUTPUT_REDIRECT_HPP_INCLUDED
|
|
|
|
|
|
#ifndef CATCH_PARSE_NUMBERS_HPP_INCLUDED
|
|
#define CATCH_PARSE_NUMBERS_HPP_INCLUDED
|
|
|
|
|
|
#include <string>
|
|
|
|
namespace Catch {
|
|
|
|
/**
|
|
* Parses unsigned int from the input, using provided base
|
|
*
|
|
* Effectively a wrapper around std::stoul but with better error checking
|
|
* e.g. "-1" is rejected, instead of being parsed as UINT_MAX.
|
|
*/
|
|
Optional<unsigned int> parseUInt(std::string const& input, int base = 10);
|
|
}
|
|
|
|
#endif // CATCH_PARSE_NUMBERS_HPP_INCLUDED
|
|
|
|
|
|
#ifndef CATCH_REPORTER_REGISTRY_HPP_INCLUDED
|
|
#define CATCH_REPORTER_REGISTRY_HPP_INCLUDED
|
|
|
|
|
|
#include <map>
|
|
#include <string>
|
|
#include <vector>
|
|
|
|
namespace Catch {
|
|
|
|
class IEventListener;
|
|
using IEventListenerPtr = Detail::unique_ptr<IEventListener>;
|
|
class IReporterFactory;
|
|
using IReporterFactoryPtr = Detail::unique_ptr<IReporterFactory>;
|
|
struct ReporterConfig;
|
|
class EventListenerFactory;
|
|
|
|
class ReporterRegistry {
|
|
struct ReporterRegistryImpl;
|
|
Detail::unique_ptr<ReporterRegistryImpl> m_impl;
|
|
|
|
public:
|
|
ReporterRegistry();
|
|
~ReporterRegistry(); // = default;
|
|
|
|
IEventListenerPtr create( std::string const& name,
|
|
ReporterConfig&& config ) const;
|
|
|
|
void registerReporter( std::string const& name,
|
|
IReporterFactoryPtr factory );
|
|
|
|
void
|
|
registerListener( Detail::unique_ptr<EventListenerFactory> factory );
|
|
|
|
std::map<std::string,
|
|
IReporterFactoryPtr,
|
|
Detail::CaseInsensitiveLess> const&
|
|
getFactories() const;
|
|
|
|
std::vector<Detail::unique_ptr<EventListenerFactory>> const&
|
|
getListeners() const;
|
|
};
|
|
|
|
} // end namespace Catch
|
|
|
|
#endif // CATCH_REPORTER_REGISTRY_HPP_INCLUDED
|
|
|
|
|
|
#ifndef CATCH_RUN_CONTEXT_HPP_INCLUDED
|
|
#define CATCH_RUN_CONTEXT_HPP_INCLUDED
|
|
|
|
|
|
|
|
#ifndef CATCH_TEST_CASE_TRACKER_HPP_INCLUDED
|
|
#define CATCH_TEST_CASE_TRACKER_HPP_INCLUDED
|
|
|
|
|
|
#include <string>
|
|
#include <vector>
|
|
|
|
namespace Catch {
|
|
namespace TestCaseTracking {
|
|
|
|
struct NameAndLocation {
|
|
std::string name;
|
|
SourceLineInfo location;
|
|
|
|
NameAndLocation( std::string&& _name, SourceLineInfo const& _location );
|
|
friend bool operator==(NameAndLocation const& lhs, NameAndLocation const& rhs) {
|
|
// This is a very cheap check that should have a very high hit rate.
|
|
// If we get to SourceLineInfo::operator==, we will redo it, but the
|
|
// cost of repeating is trivial at that point (we will be paying
|
|
// multiple strcmp/memcmps at that point).
|
|
if ( lhs.location.line != rhs.location.line ) { return false; }
|
|
return lhs.name == rhs.name && lhs.location == rhs.location;
|
|
}
|
|
friend bool operator!=(NameAndLocation const& lhs,
|
|
NameAndLocation const& rhs) {
|
|
return !( lhs == rhs );
|
|
}
|
|
};
|
|
|
|
/**
|
|
* This is a variant of `NameAndLocation` that does not own the name string
|
|
*
|
|
* This avoids extra allocations when trying to locate a tracker by its
|
|
* name and location, as long as we make sure that trackers only keep
|
|
* around the owning variant.
|
|
*/
|
|
struct NameAndLocationRef {
|
|
StringRef name;
|
|
SourceLineInfo location;
|
|
|
|
constexpr NameAndLocationRef( StringRef name_,
|
|
SourceLineInfo location_ ):
|
|
name( name_ ), location( location_ ) {}
|
|
|
|
friend bool operator==( NameAndLocation const& lhs,
|
|
NameAndLocationRef const& rhs ) {
|
|
// This is a very cheap check that should have a very high hit rate.
|
|
// If we get to SourceLineInfo::operator==, we will redo it, but the
|
|
// cost of repeating is trivial at that point (we will be paying
|
|
// multiple strcmp/memcmps at that point).
|
|
if ( lhs.location.line != rhs.location.line ) { return false; }
|
|
return StringRef( lhs.name ) == rhs.name &&
|
|
lhs.location == rhs.location;
|
|
}
|
|
friend bool operator==( NameAndLocationRef const& lhs,
|
|
NameAndLocation const& rhs ) {
|
|
return rhs == lhs;
|
|
}
|
|
};
|
|
|
|
class ITracker;
|
|
|
|
using ITrackerPtr = Catch::Detail::unique_ptr<ITracker>;
|
|
|
|
class ITracker {
|
|
NameAndLocation m_nameAndLocation;
|
|
|
|
using Children = std::vector<ITrackerPtr>;
|
|
|
|
protected:
|
|
enum CycleState {
|
|
NotStarted,
|
|
Executing,
|
|
ExecutingChildren,
|
|
NeedsAnotherRun,
|
|
CompletedSuccessfully,
|
|
Failed
|
|
};
|
|
|
|
ITracker* m_parent = nullptr;
|
|
Children m_children;
|
|
CycleState m_runState = NotStarted;
|
|
|
|
public:
|
|
ITracker( NameAndLocation&& nameAndLoc, ITracker* parent ):
|
|
m_nameAndLocation( CATCH_MOVE(nameAndLoc) ),
|
|
m_parent( parent )
|
|
{}
|
|
|
|
|
|
// static queries
|
|
NameAndLocation const& nameAndLocation() const {
|
|
return m_nameAndLocation;
|
|
}
|
|
ITracker* parent() const {
|
|
return m_parent;
|
|
}
|
|
|
|
virtual ~ITracker(); // = default
|
|
|
|
|
|
// dynamic queries
|
|
|
|
//! Returns true if tracker run to completion (successfully or not)
|
|
virtual bool isComplete() const = 0;
|
|
//! Returns true if tracker run to completion successfully
|
|
bool isSuccessfullyCompleted() const {
|
|
return m_runState == CompletedSuccessfully;
|
|
}
|
|
//! Returns true if tracker has started but hasn't been completed
|
|
bool isOpen() const;
|
|
//! Returns true iff tracker has started
|
|
bool hasStarted() const;
|
|
|
|
// actions
|
|
virtual void close() = 0; // Successfully complete
|
|
virtual void fail() = 0;
|
|
void markAsNeedingAnotherRun();
|
|
|
|
//! Register a nested ITracker
|
|
void addChild( ITrackerPtr&& child );
|
|
/**
|
|
* Returns ptr to specific child if register with this tracker.
|
|
*
|
|
* Returns nullptr if not found.
|
|
*/
|
|
ITracker* findChild( NameAndLocationRef const& nameAndLocation );
|
|
//! Have any children been added?
|
|
bool hasChildren() const {
|
|
return !m_children.empty();
|
|
}
|
|
|
|
|
|
//! Marks tracker as executing a child, doing se recursively up the tree
|
|
void openChild();
|
|
|
|
/**
|
|
* Returns true if the instance is a section tracker
|
|
*
|
|
* Subclasses should override to true if they are, replaces RTTI
|
|
* for internal debug checks.
|
|
*/
|
|
virtual bool isSectionTracker() const;
|
|
/**
|
|
* Returns true if the instance is a generator tracker
|
|
*
|
|
* Subclasses should override to true if they are, replaces RTTI
|
|
* for internal debug checks.
|
|
*/
|
|
virtual bool isGeneratorTracker() const;
|
|
};
|
|
|
|
class TrackerContext {
|
|
|
|
enum RunState {
|
|
NotStarted,
|
|
Executing,
|
|
CompletedCycle
|
|
};
|
|
|
|
ITrackerPtr m_rootTracker;
|
|
ITracker* m_currentTracker = nullptr;
|
|
RunState m_runState = NotStarted;
|
|
|
|
public:
|
|
|
|
ITracker& startRun();
|
|
|
|
void startCycle() {
|
|
m_currentTracker = m_rootTracker.get();
|
|
m_runState = Executing;
|
|
}
|
|
void completeCycle();
|
|
|
|
bool completedCycle() const;
|
|
ITracker& currentTracker() { return *m_currentTracker; }
|
|
void setCurrentTracker( ITracker* tracker );
|
|
};
|
|
|
|
class TrackerBase : public ITracker {
|
|
protected:
|
|
|
|
TrackerContext& m_ctx;
|
|
|
|
public:
|
|
TrackerBase( NameAndLocation&& nameAndLocation, TrackerContext& ctx, ITracker* parent );
|
|
|
|
bool isComplete() const override;
|
|
|
|
void open();
|
|
|
|
void close() override;
|
|
void fail() override;
|
|
|
|
private:
|
|
void moveToParent();
|
|
void moveToThis();
|
|
};
|
|
|
|
class SectionTracker : public TrackerBase {
|
|
std::vector<StringRef> m_filters;
|
|
// Note that lifetime-wise we piggy back off the name stored in the `ITracker` parent`.
|
|
// Currently it allocates owns the name, so this is safe. If it is later refactored
|
|
// to not own the name, the name still has to outlive the `ITracker` parent, so
|
|
// this should still be safe.
|
|
StringRef m_trimmed_name;
|
|
public:
|
|
SectionTracker( NameAndLocation&& nameAndLocation, TrackerContext& ctx, ITracker* parent );
|
|
|
|
bool isSectionTracker() const override;
|
|
|
|
bool isComplete() const override;
|
|
|
|
static SectionTracker& acquire( TrackerContext& ctx, NameAndLocationRef const& nameAndLocation );
|
|
|
|
void tryOpen();
|
|
|
|
void addInitialFilters( std::vector<std::string> const& filters );
|
|
void addNextFilters( std::vector<StringRef> const& filters );
|
|
//! Returns filters active in this tracker
|
|
std::vector<StringRef> const& getFilters() const { return m_filters; }
|
|
//! Returns whitespace-trimmed name of the tracked section
|
|
StringRef trimmedName() const;
|
|
};
|
|
|
|
} // namespace TestCaseTracking
|
|
|
|
using TestCaseTracking::ITracker;
|
|
using TestCaseTracking::TrackerContext;
|
|
using TestCaseTracking::SectionTracker;
|
|
|
|
} // namespace Catch
|
|
|
|
#endif // CATCH_TEST_CASE_TRACKER_HPP_INCLUDED
|
|
|
|
#include <string>
|
|
|
|
namespace Catch {
|
|
|
|
class IGeneratorTracker;
|
|
class IConfig;
|
|
class IEventListener;
|
|
using IEventListenerPtr = Detail::unique_ptr<IEventListener>;
|
|
|
|
///////////////////////////////////////////////////////////////////////////
|
|
|
|
class RunContext final : public IResultCapture {
|
|
|
|
public:
|
|
RunContext( RunContext const& ) = delete;
|
|
RunContext& operator =( RunContext const& ) = delete;
|
|
|
|
explicit RunContext( IConfig const* _config, IEventListenerPtr&& reporter );
|
|
|
|
~RunContext() override;
|
|
|
|
Totals runTest(TestCaseHandle const& testCase);
|
|
|
|
public: // IResultCapture
|
|
|
|
// Assertion handlers
|
|
void handleExpr
|
|
( AssertionInfo const& info,
|
|
ITransientExpression const& expr,
|
|
AssertionReaction& reaction ) override;
|
|
void handleMessage
|
|
( AssertionInfo const& info,
|
|
ResultWas::OfType resultType,
|
|
StringRef message,
|
|
AssertionReaction& reaction ) override;
|
|
void handleUnexpectedExceptionNotThrown
|
|
( AssertionInfo const& info,
|
|
AssertionReaction& reaction ) override;
|
|
void handleUnexpectedInflightException
|
|
( AssertionInfo const& info,
|
|
std::string&& message,
|
|
AssertionReaction& reaction ) override;
|
|
void handleIncomplete
|
|
( AssertionInfo const& info ) override;
|
|
void handleNonExpr
|
|
( AssertionInfo const &info,
|
|
ResultWas::OfType resultType,
|
|
AssertionReaction &reaction ) override;
|
|
|
|
void notifyAssertionStarted( AssertionInfo const& info ) override;
|
|
bool sectionStarted( StringRef sectionName,
|
|
SourceLineInfo const& sectionLineInfo,
|
|
Counts& assertions ) override;
|
|
|
|
void sectionEnded( SectionEndInfo&& endInfo ) override;
|
|
void sectionEndedEarly( SectionEndInfo&& endInfo ) override;
|
|
|
|
IGeneratorTracker*
|
|
acquireGeneratorTracker( StringRef generatorName,
|
|
SourceLineInfo const& lineInfo ) override;
|
|
IGeneratorTracker* createGeneratorTracker(
|
|
StringRef generatorName,
|
|
SourceLineInfo lineInfo,
|
|
Generators::GeneratorBasePtr&& generator ) override;
|
|
|
|
|
|
void benchmarkPreparing( StringRef name ) override;
|
|
void benchmarkStarting( BenchmarkInfo const& info ) override;
|
|
void benchmarkEnded( BenchmarkStats<> const& stats ) override;
|
|
void benchmarkFailed( StringRef error ) override;
|
|
|
|
void pushScopedMessage( MessageInfo const& message ) override;
|
|
void popScopedMessage( MessageInfo const& message ) override;
|
|
|
|
void emplaceUnscopedMessage( MessageBuilder&& 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;
|
|
|
|
private:
|
|
|
|
void runCurrentTest( std::string& redirectedCout, std::string& redirectedCerr );
|
|
void invokeActiveTestCase();
|
|
|
|
void resetAssertionInfo();
|
|
bool testForMissingAssertions( Counts& assertions );
|
|
|
|
void assertionEnded( AssertionResult&& result );
|
|
void reportExpr
|
|
( AssertionInfo const &info,
|
|
ResultWas::OfType resultType,
|
|
ITransientExpression const *expr,
|
|
bool negated );
|
|
|
|
void populateReaction( AssertionReaction& reaction );
|
|
|
|
private:
|
|
|
|
void handleUnfinishedSections();
|
|
|
|
TestRunInfo m_runInfo;
|
|
TestCaseHandle const* m_activeTestCase = nullptr;
|
|
ITracker* m_testCaseTracker = nullptr;
|
|
Optional<AssertionResult> m_lastResult;
|
|
|
|
IConfig const* m_config;
|
|
Totals m_totals;
|
|
IEventListenerPtr m_reporter;
|
|
std::vector<MessageInfo> m_messages;
|
|
std::vector<ScopedMessage> m_messageScopes; /* Keeps owners of so-called unscoped messages. */
|
|
AssertionInfo m_lastAssertionInfo;
|
|
std::vector<SectionEndInfo> m_unfinishedSections;
|
|
std::vector<ITracker*> 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
|
|
|
|
#endif // CATCH_RUN_CONTEXT_HPP_INCLUDED
|
|
|
|
|
|
#ifndef CATCH_SHARDING_HPP_INCLUDED
|
|
#define CATCH_SHARDING_HPP_INCLUDED
|
|
|
|
|
|
#include <cmath>
|
|
#include <algorithm>
|
|
|
|
namespace Catch {
|
|
|
|
template<typename Container>
|
|
Container createShard(Container const& container, std::size_t const shardCount, std::size_t const shardIndex) {
|
|
assert(shardCount > shardIndex);
|
|
|
|
if (shardCount == 1) {
|
|
return container;
|
|
}
|
|
|
|
const std::size_t totalTestCount = container.size();
|
|
|
|
const std::size_t shardSize = totalTestCount / shardCount;
|
|
const std::size_t leftoverTests = totalTestCount % shardCount;
|
|
|
|
const std::size_t startIndex = shardIndex * shardSize + (std::min)(shardIndex, leftoverTests);
|
|
const std::size_t endIndex = (shardIndex + 1) * shardSize + (std::min)(shardIndex + 1, leftoverTests);
|
|
|
|
auto startIterator = std::next(container.begin(), static_cast<std::ptrdiff_t>(startIndex));
|
|
auto endIterator = std::next(container.begin(), static_cast<std::ptrdiff_t>(endIndex));
|
|
|
|
return Container(startIterator, endIterator);
|
|
}
|
|
|
|
}
|
|
|
|
#endif // CATCH_SHARDING_HPP_INCLUDED
|
|
|
|
|
|
#ifndef CATCH_SINGLETONS_HPP_INCLUDED
|
|
#define CATCH_SINGLETONS_HPP_INCLUDED
|
|
|
|
namespace Catch {
|
|
|
|
struct ISingleton {
|
|
virtual ~ISingleton(); // = default
|
|
};
|
|
|
|
|
|
void addSingleton( ISingleton* singleton );
|
|
void cleanupSingletons();
|
|
|
|
|
|
template<typename SingletonImplT, typename InterfaceT = SingletonImplT, typename MutableInterfaceT = InterfaceT>
|
|
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
|
|
|
|
#endif // CATCH_SINGLETONS_HPP_INCLUDED
|
|
|
|
|
|
#ifndef CATCH_STARTUP_EXCEPTION_REGISTRY_HPP_INCLUDED
|
|
#define CATCH_STARTUP_EXCEPTION_REGISTRY_HPP_INCLUDED
|
|
|
|
|
|
#include <vector>
|
|
#include <exception>
|
|
|
|
namespace Catch {
|
|
|
|
class StartupExceptionRegistry {
|
|
#if !defined(CATCH_CONFIG_DISABLE_EXCEPTIONS)
|
|
public:
|
|
void add(std::exception_ptr const& exception) noexcept;
|
|
std::vector<std::exception_ptr> const& getExceptions() const noexcept;
|
|
private:
|
|
std::vector<std::exception_ptr> m_exceptions;
|
|
#endif
|
|
};
|
|
|
|
} // end namespace Catch
|
|
|
|
#endif // CATCH_STARTUP_EXCEPTION_REGISTRY_HPP_INCLUDED
|
|
|
|
|
|
|
|
#ifndef CATCH_STDSTREAMS_HPP_INCLUDED
|
|
#define CATCH_STDSTREAMS_HPP_INCLUDED
|
|
|
|
#include <iosfwd>
|
|
|
|
namespace Catch {
|
|
|
|
std::ostream& cout();
|
|
std::ostream& cerr();
|
|
std::ostream& clog();
|
|
|
|
} // namespace Catch
|
|
|
|
#endif
|
|
|
|
|
|
#ifndef CATCH_STRING_MANIP_HPP_INCLUDED
|
|
#define CATCH_STRING_MANIP_HPP_INCLUDED
|
|
|
|
|
|
#include <cstdint>
|
|
#include <string>
|
|
#include <iosfwd>
|
|
#include <vector>
|
|
|
|
namespace Catch {
|
|
|
|
bool startsWith( std::string const& s, std::string const& prefix );
|
|
bool startsWith( StringRef 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 );
|
|
char toLower( char c );
|
|
//! 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<StringRef> splitStringRef( StringRef str, char delimiter );
|
|
bool replaceInPlace( std::string& str, std::string const& replaceThis, std::string const& withThis );
|
|
|
|
/**
|
|
* Helper for streaming a "count [maybe-plural-of-label]" human-friendly string
|
|
*
|
|
* Usage example:
|
|
* ```cpp
|
|
* std::cout << "Found " << pluralise(count, "error") << '\n';
|
|
* ```
|
|
*
|
|
* **Important:** The provided string must outlive the instance
|
|
*/
|
|
class pluralise {
|
|
std::uint64_t m_count;
|
|
StringRef m_label;
|
|
|
|
public:
|
|
constexpr pluralise(std::uint64_t count, StringRef label):
|
|
m_count(count),
|
|
m_label(label)
|
|
{}
|
|
|
|
friend std::ostream& operator << ( std::ostream& os, pluralise const& pluraliser );
|
|
};
|
|
}
|
|
|
|
#endif // CATCH_STRING_MANIP_HPP_INCLUDED
|
|
|
|
|
|
#ifndef CATCH_TAG_ALIAS_REGISTRY_HPP_INCLUDED
|
|
#define CATCH_TAG_ALIAS_REGISTRY_HPP_INCLUDED
|
|
|
|
|
|
#include <map>
|
|
#include <string>
|
|
|
|
namespace Catch {
|
|
struct SourceLineInfo;
|
|
|
|
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<std::string, TagAlias> m_registry;
|
|
};
|
|
|
|
} // end namespace Catch
|
|
|
|
#endif // CATCH_TAG_ALIAS_REGISTRY_HPP_INCLUDED
|
|
|
|
|
|
#ifndef CATCH_TEST_CASE_INFO_HASHER_HPP_INCLUDED
|
|
#define CATCH_TEST_CASE_INFO_HASHER_HPP_INCLUDED
|
|
|
|
#include <cstdint>
|
|
|
|
namespace Catch {
|
|
|
|
struct TestCaseInfo;
|
|
|
|
class TestCaseInfoHasher {
|
|
public:
|
|
using hash_t = std::uint64_t;
|
|
TestCaseInfoHasher( hash_t seed );
|
|
uint32_t operator()( TestCaseInfo const& t ) const;
|
|
|
|
private:
|
|
hash_t m_seed;
|
|
};
|
|
|
|
} // namespace Catch
|
|
|
|
#endif /* CATCH_TEST_CASE_INFO_HASHER_HPP_INCLUDED */
|
|
|
|
|
|
#ifndef CATCH_TEST_CASE_REGISTRY_IMPL_HPP_INCLUDED
|
|
#define CATCH_TEST_CASE_REGISTRY_IMPL_HPP_INCLUDED
|
|
|
|
|
|
#include <vector>
|
|
|
|
namespace Catch {
|
|
|
|
class IConfig;
|
|
class ITestInvoker;
|
|
class TestCaseHandle;
|
|
class TestSpec;
|
|
|
|
std::vector<TestCaseHandle> sortTests( IConfig const& config, std::vector<TestCaseHandle> const& unsortedTestCases );
|
|
|
|
bool isThrowSafe( TestCaseHandle const& testCase, IConfig const& config );
|
|
|
|
std::vector<TestCaseHandle> filterTests( std::vector<TestCaseHandle> const& testCases, TestSpec const& testSpec, IConfig const& config );
|
|
std::vector<TestCaseHandle> const& getAllTestCasesSorted( IConfig const& config );
|
|
|
|
class TestRegistry : public ITestCaseRegistry {
|
|
public:
|
|
void registerTest( Detail::unique_ptr<TestCaseInfo> testInfo, Detail::unique_ptr<ITestInvoker> testInvoker );
|
|
|
|
std::vector<TestCaseInfo*> const& getAllInfos() const override;
|
|
std::vector<TestCaseHandle> const& getAllTests() const override;
|
|
std::vector<TestCaseHandle> const& getAllTestsSorted( IConfig const& config ) const override;
|
|
|
|
private:
|
|
std::vector<Detail::unique_ptr<TestCaseInfo>> m_owned_test_infos;
|
|
// Keeps a materialized vector for `getAllInfos`.
|
|
// We should get rid of that eventually (see interface note)
|
|
std::vector<TestCaseInfo*> m_viewed_test_infos;
|
|
|
|
std::vector<Detail::unique_ptr<ITestInvoker>> m_invokers;
|
|
std::vector<TestCaseHandle> m_handles;
|
|
mutable TestRunOrder m_currentSortOrder = TestRunOrder::Declared;
|
|
mutable std::vector<TestCaseHandle> m_sortedFunctions;
|
|
};
|
|
|
|
///////////////////////////////////////////////////////////////////////////
|
|
|
|
|
|
} // end namespace Catch
|
|
|
|
|
|
#endif // CATCH_TEST_CASE_REGISTRY_IMPL_HPP_INCLUDED
|
|
|
|
|
|
#ifndef CATCH_TEST_SPEC_PARSER_HPP_INCLUDED
|
|
#define CATCH_TEST_SPEC_PARSER_HPP_INCLUDED
|
|
|
|
#ifdef __clang__
|
|
#pragma clang diagnostic push
|
|
#pragma clang diagnostic ignored "-Wpadded"
|
|
#endif
|
|
|
|
|
|
#include <vector>
|
|
#include <string>
|
|
|
|
namespace Catch {
|
|
|
|
class ITagAliasRegistry;
|
|
|
|
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<std::size_t> 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++;
|
|
}
|
|
|
|
};
|
|
|
|
} // namespace Catch
|
|
|
|
#ifdef __clang__
|
|
#pragma clang diagnostic pop
|
|
#endif
|
|
|
|
#endif // CATCH_TEST_SPEC_PARSER_HPP_INCLUDED
|
|
|
|
|
|
#ifndef CATCH_TEXTFLOW_HPP_INCLUDED
|
|
#define CATCH_TEXTFLOW_HPP_INCLUDED
|
|
|
|
#include <cassert>
|
|
#include <string>
|
|
#include <vector>
|
|
|
|
namespace Catch {
|
|
namespace TextFlow {
|
|
|
|
class Columns;
|
|
|
|
/**
|
|
* Represents a column of text with specific width and indentation
|
|
*
|
|
* When written out to a stream, it will perform linebreaking
|
|
* of the provided text so that the written lines fit within
|
|
* target width.
|
|
*/
|
|
class Column {
|
|
// String to be written out
|
|
std::string m_string;
|
|
// Width of the column for linebreaking
|
|
size_t m_width = CATCH_CONFIG_CONSOLE_WIDTH - 1;
|
|
// Indentation of other lines (including first if initial indent is unset)
|
|
size_t m_indent = 0;
|
|
// Indentation of the first line
|
|
size_t m_initialIndent = std::string::npos;
|
|
|
|
public:
|
|
/**
|
|
* Iterates "lines" in `Column` and return sthem
|
|
*/
|
|
class const_iterator {
|
|
friend Column;
|
|
struct EndTag {};
|
|
|
|
Column const& m_column;
|
|
// Where does the current line start?
|
|
size_t m_lineStart = 0;
|
|
// How long should the current line be?
|
|
size_t m_lineLength = 0;
|
|
// How far have we checked the string to iterate?
|
|
size_t m_parsedTo = 0;
|
|
// Should a '-' be appended to the line?
|
|
bool m_addHyphen = false;
|
|
|
|
const_iterator( Column const& column, EndTag ):
|
|
m_column( column ), m_lineStart( m_column.m_string.size() ) {}
|
|
|
|
// Calculates the length of the current line
|
|
void calcLength();
|
|
|
|
// Returns current indentation width
|
|
size_t indentSize() const;
|
|
|
|
// Creates an indented and (optionally) suffixed string from
|
|
// current iterator position, indentation and length.
|
|
std::string addIndentAndSuffix( size_t position,
|
|
size_t length ) const;
|
|
|
|
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 const_iterator( Column const& column );
|
|
|
|
std::string operator*() const;
|
|
|
|
const_iterator& operator++();
|
|
const_iterator operator++( int );
|
|
|
|
bool operator==( const_iterator const& other ) const {
|
|
return m_lineStart == other.m_lineStart && &m_column == &other.m_column;
|
|
}
|
|
bool operator!=( const_iterator const& other ) const {
|
|
return !operator==( other );
|
|
}
|
|
};
|
|
using iterator = const_iterator;
|
|
|
|
explicit Column( std::string const& text ): m_string( text ) {}
|
|
|
|
Column& width( size_t newWidth ) {
|
|
assert( newWidth > 0 );
|
|
m_width = newWidth;
|
|
return *this;
|
|
}
|
|
Column& indent( size_t newIndent ) {
|
|
m_indent = newIndent;
|
|
return *this;
|
|
}
|
|
Column& initialIndent( size_t newIndent ) {
|
|
m_initialIndent = newIndent;
|
|
return *this;
|
|
}
|
|
|
|
size_t width() const { return m_width; }
|
|
const_iterator begin() const { return const_iterator( *this ); }
|
|
const_iterator end() const { return { *this, const_iterator::EndTag{} }; }
|
|
|
|
friend std::ostream& operator<<( std::ostream& os,
|
|
Column const& col );
|
|
|
|
Columns operator+( Column const& other );
|
|
};
|
|
|
|
//! Creates a column that serves as an empty space of specific width
|
|
Column Spacer( size_t spaceWidth );
|
|
|
|
class Columns {
|
|
std::vector<Column> m_columns;
|
|
|
|
public:
|
|
class iterator {
|
|
friend Columns;
|
|
struct EndTag {};
|
|
|
|
std::vector<Column> const& m_columns;
|
|
std::vector<Column::const_iterator> m_iterators;
|
|
size_t m_activeIterators;
|
|
|
|
iterator( Columns const& columns, EndTag );
|
|
|
|
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 );
|
|
|
|
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;
|
|
}
|
|
std::string operator*() const;
|
|
iterator& operator++();
|
|
iterator operator++( int );
|
|
};
|
|
using const_iterator = iterator;
|
|
|
|
iterator begin() const { return iterator( *this ); }
|
|
iterator end() const { return { *this, iterator::EndTag() }; }
|
|
|
|
Columns& operator+=( Column const& col );
|
|
Columns operator+( Column const& col );
|
|
|
|
friend std::ostream& operator<<( std::ostream& os,
|
|
Columns const& cols );
|
|
};
|
|
|
|
} // namespace TextFlow
|
|
} // namespace Catch
|
|
#endif // CATCH_TEXTFLOW_HPP_INCLUDED
|
|
|
|
|
|
#ifndef CATCH_TO_STRING_HPP_INCLUDED
|
|
#define CATCH_TO_STRING_HPP_INCLUDED
|
|
|
|
#include <string>
|
|
|
|
|
|
namespace Catch {
|
|
template <typename T>
|
|
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
|
|
|
|
#endif // CATCH_TO_STRING_HPP_INCLUDED
|
|
|
|
|
|
#ifndef CATCH_UNCAUGHT_EXCEPTIONS_HPP_INCLUDED
|
|
#define CATCH_UNCAUGHT_EXCEPTIONS_HPP_INCLUDED
|
|
|
|
namespace Catch {
|
|
bool uncaught_exceptions();
|
|
} // end namespace Catch
|
|
|
|
#endif // CATCH_UNCAUGHT_EXCEPTIONS_HPP_INCLUDED
|
|
|
|
|
|
#ifndef CATCH_XMLWRITER_HPP_INCLUDED
|
|
#define CATCH_XMLWRITER_HPP_INCLUDED
|
|
|
|
|
|
#include <iosfwd>
|
|
#include <vector>
|
|
|
|
namespace Catch {
|
|
enum class XmlFormatting {
|
|
None = 0x00,
|
|
Indent = 0x01,
|
|
Newline = 0x02,
|
|
};
|
|
|
|
XmlFormatting operator | (XmlFormatting lhs, XmlFormatting rhs);
|
|
XmlFormatting operator & (XmlFormatting lhs, XmlFormatting rhs);
|
|
|
|
/**
|
|
* Helper for XML-encoding text (escaping angle brackets, quotes, etc)
|
|
*
|
|
* Note: doesn't take ownership of passed strings, and thus the
|
|
* encoded string must outlive the encoding instance.
|
|
*/
|
|
class XmlEncode {
|
|
public:
|
|
enum ForWhat { ForTextNodes, ForAttributes };
|
|
|
|
XmlEncode( StringRef str, ForWhat forWhat = ForTextNodes );
|
|
|
|
void encodeTo( std::ostream& os ) const;
|
|
|
|
friend std::ostream& operator << ( std::ostream& os, XmlEncode const& xmlEncode );
|
|
|
|
private:
|
|
StringRef 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( StringRef text,
|
|
XmlFormatting fmt = XmlFormatting::Newline |
|
|
XmlFormatting::Indent );
|
|
|
|
ScopedElement& writeAttribute( StringRef name,
|
|
StringRef attribute );
|
|
template <typename T,
|
|
// Without this SFINAE, this overload is a better match
|
|
// for `std::string`, `char const*`, `char const[N]` args.
|
|
// While it would still work, it would cause code bloat
|
|
// and multiple iteration over the strings
|
|
typename = typename std::enable_if_t<
|
|
!std::is_convertible<T, StringRef>::value>>
|
|
ScopedElement& writeAttribute( StringRef name,
|
|
T const& attribute ) {
|
|
m_writer->writeAttribute( name, attribute );
|
|
return *this;
|
|
}
|
|
|
|
private:
|
|
XmlWriter* m_writer = nullptr;
|
|
XmlFormatting m_fmt;
|
|
};
|
|
|
|
XmlWriter( std::ostream& os );
|
|
~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);
|
|
|
|
//! The attribute content is XML-encoded
|
|
XmlWriter& writeAttribute( StringRef name, StringRef attribute );
|
|
|
|
//! Writes the attribute as "true/false"
|
|
XmlWriter& writeAttribute( StringRef name, bool attribute );
|
|
|
|
//! The attribute content is XML-encoded
|
|
XmlWriter& writeAttribute( StringRef name, char const* attribute );
|
|
|
|
//! The attribute value must provide op<<(ostream&, T). The resulting
|
|
//! serialization is XML-encoded
|
|
template <typename T,
|
|
// Without this SFINAE, this overload is a better match
|
|
// for `std::string`, `char const*`, `char const[N]` args.
|
|
// While it would still work, it would cause code bloat
|
|
// and multiple iteration over the strings
|
|
typename = typename std::enable_if_t<
|
|
!std::is_convertible<T, StringRef>::value>>
|
|
XmlWriter& writeAttribute( StringRef name, T const& attribute ) {
|
|
ReusableStringStream rss;
|
|
rss << attribute;
|
|
return writeAttribute( name, rss.str() );
|
|
}
|
|
|
|
//! Writes escaped `text` in a element
|
|
XmlWriter& writeText( StringRef text,
|
|
XmlFormatting fmt = XmlFormatting::Newline |
|
|
XmlFormatting::Indent );
|
|
|
|
//! Writes XML comment as "<!-- text -->"
|
|
XmlWriter& writeComment( StringRef text,
|
|
XmlFormatting fmt = XmlFormatting::Newline |
|
|
XmlFormatting::Indent );
|
|
|
|
void writeStylesheetRef( StringRef url );
|
|
|
|
void ensureTagClosed();
|
|
|
|
private:
|
|
|
|
void applyFormatting(XmlFormatting fmt);
|
|
|
|
void writeDeclaration();
|
|
|
|
void newlineIfNecessary();
|
|
|
|
bool m_tagIsOpen = false;
|
|
bool m_needsNewline = false;
|
|
std::vector<std::string> m_tags;
|
|
std::string m_indent;
|
|
std::ostream& m_os;
|
|
};
|
|
|
|
}
|
|
|
|
#endif // CATCH_XMLWRITER_HPP_INCLUDED
|
|
|
|
|
|
/** \file
|
|
* This is a convenience header for Catch2's Matcher support. It includes
|
|
* **all** of Catch2 headers related to matchers.
|
|
*
|
|
* Generally the Catch2 users should use specific includes they need,
|
|
* but this header can be used instead for ease-of-experimentation, or
|
|
* just plain convenience, at the cost of increased compilation times.
|
|
*
|
|
* When a new header is added to either the `matchers` folder, or to
|
|
* the corresponding internal subfolder, it should be added here.
|
|
*/
|
|
|
|
#ifndef CATCH_MATCHERS_ALL_HPP_INCLUDED
|
|
#define CATCH_MATCHERS_ALL_HPP_INCLUDED
|
|
|
|
|
|
|
|
#ifndef CATCH_MATCHERS_HPP_INCLUDED
|
|
#define CATCH_MATCHERS_HPP_INCLUDED
|
|
|
|
|
|
|
|
#ifndef CATCH_MATCHERS_IMPL_HPP_INCLUDED
|
|
#define CATCH_MATCHERS_IMPL_HPP_INCLUDED
|
|
|
|
|
|
#include <string>
|
|
|
|
namespace Catch {
|
|
|
|
template<typename ArgT, typename MatcherT>
|
|
class MatchExpr : public ITransientExpression {
|
|
ArgT && m_arg;
|
|
MatcherT const& m_matcher;
|
|
public:
|
|
MatchExpr( ArgT && arg, MatcherT const& matcher )
|
|
: ITransientExpression{ true, matcher.match( arg ) }, // not forwarding arg here on purpose
|
|
m_arg( CATCH_FORWARD(arg) ),
|
|
m_matcher( matcher )
|
|
{}
|
|
|
|
void streamReconstructedExpression( std::ostream& os ) const override {
|
|
os << Catch::Detail::stringify( m_arg )
|
|
<< ' '
|
|
<< m_matcher.toString();
|
|
}
|
|
};
|
|
|
|
namespace Matchers {
|
|
template <typename ArgT>
|
|
class MatcherBase;
|
|
}
|
|
|
|
using StringMatcher = Matchers::MatcherBase<std::string>;
|
|
|
|
void handleExceptionMatchExpr( AssertionHandler& handler, StringMatcher const& matcher );
|
|
|
|
template<typename ArgT, typename MatcherT>
|
|
auto makeMatchExpr( ArgT && arg, MatcherT const& matcher ) -> MatchExpr<ArgT, MatcherT> {
|
|
return MatchExpr<ArgT, MatcherT>( CATCH_FORWARD(arg), matcher );
|
|
}
|
|
|
|
} // 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 ) ); \
|
|
} 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<void>(__VA_ARGS__ ); \
|
|
catchAssertionHandler.handleUnexpectedExceptionNotThrown(); \
|
|
} \
|
|
catch( exceptionType const& ex ) { \
|
|
catchAssertionHandler.handleExpr( Catch::makeMatchExpr( ex, matcher ) ); \
|
|
} \
|
|
catch( ... ) { \
|
|
catchAssertionHandler.handleUnexpectedInflightException(); \
|
|
} \
|
|
else \
|
|
catchAssertionHandler.handleThrowingCallSkipped(); \
|
|
INTERNAL_CATCH_REACT( catchAssertionHandler ) \
|
|
} while( false )
|
|
|
|
|
|
#endif // CATCH_MATCHERS_IMPL_HPP_INCLUDED
|
|
|
|
#include <string>
|
|
#include <vector>
|
|
|
|
namespace Catch {
|
|
namespace Matchers {
|
|
|
|
class MatcherUntypedBase {
|
|
public:
|
|
MatcherUntypedBase() = default;
|
|
|
|
MatcherUntypedBase(MatcherUntypedBase const&) = default;
|
|
MatcherUntypedBase(MatcherUntypedBase&&) = default;
|
|
|
|
MatcherUntypedBase& operator = (MatcherUntypedBase const&) = delete;
|
|
MatcherUntypedBase& operator = (MatcherUntypedBase&&) = delete;
|
|
|
|
std::string toString() const;
|
|
|
|
protected:
|
|
virtual ~MatcherUntypedBase(); // = default;
|
|
virtual std::string describe() const = 0;
|
|
mutable std::string m_cachedToString;
|
|
};
|
|
|
|
|
|
template<typename T>
|
|
class MatcherBase : public MatcherUntypedBase {
|
|
public:
|
|
virtual bool match( T const& arg ) const = 0;
|
|
};
|
|
|
|
namespace Detail {
|
|
|
|
template<typename ArgT>
|
|
class MatchAllOf final : public MatcherBase<ArgT> {
|
|
std::vector<MatcherBase<ArgT> const*> m_matchers;
|
|
|
|
public:
|
|
MatchAllOf() = default;
|
|
MatchAllOf(MatchAllOf const&) = delete;
|
|
MatchAllOf& operator=(MatchAllOf const&) = delete;
|
|
MatchAllOf(MatchAllOf&&) = default;
|
|
MatchAllOf& operator=(MatchAllOf&&) = default;
|
|
|
|
|
|
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;
|
|
}
|
|
|
|
friend MatchAllOf operator&& (MatchAllOf&& lhs, MatcherBase<ArgT> const& rhs) {
|
|
lhs.m_matchers.push_back(&rhs);
|
|
return CATCH_MOVE(lhs);
|
|
}
|
|
friend MatchAllOf operator&& (MatcherBase<ArgT> const& lhs, MatchAllOf&& rhs) {
|
|
rhs.m_matchers.insert(rhs.m_matchers.begin(), &lhs);
|
|
return CATCH_MOVE(rhs);
|
|
}
|
|
};
|
|
|
|
//! lvalue overload is intentionally deleted, users should
|
|
//! not be trying to compose stored composition matchers
|
|
template<typename ArgT>
|
|
MatchAllOf<ArgT> operator&& (MatchAllOf<ArgT> const& lhs, MatcherBase<ArgT> const& rhs) = delete;
|
|
//! lvalue overload is intentionally deleted, users should
|
|
//! not be trying to compose stored composition matchers
|
|
template<typename ArgT>
|
|
MatchAllOf<ArgT> operator&& (MatcherBase<ArgT> const& lhs, MatchAllOf<ArgT> const& rhs) = delete;
|
|
|
|
template<typename ArgT>
|
|
class MatchAnyOf final : public MatcherBase<ArgT> {
|
|
std::vector<MatcherBase<ArgT> const*> m_matchers;
|
|
public:
|
|
MatchAnyOf() = default;
|
|
MatchAnyOf(MatchAnyOf const&) = delete;
|
|
MatchAnyOf& operator=(MatchAnyOf const&) = delete;
|
|
MatchAnyOf(MatchAnyOf&&) = default;
|
|
MatchAnyOf& operator=(MatchAnyOf&&) = default;
|
|
|
|
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;
|
|
}
|
|
|
|
friend MatchAnyOf operator|| (MatchAnyOf&& lhs, MatcherBase<ArgT> const& rhs) {
|
|
lhs.m_matchers.push_back(&rhs);
|
|
return CATCH_MOVE(lhs);
|
|
}
|
|
friend MatchAnyOf operator|| (MatcherBase<ArgT> const& lhs, MatchAnyOf&& rhs) {
|
|
rhs.m_matchers.insert(rhs.m_matchers.begin(), &lhs);
|
|
return CATCH_MOVE(rhs);
|
|
}
|
|
};
|
|
|
|
//! lvalue overload is intentionally deleted, users should
|
|
//! not be trying to compose stored composition matchers
|
|
template<typename ArgT>
|
|
MatchAnyOf<ArgT> operator|| (MatchAnyOf<ArgT> const& lhs, MatcherBase<ArgT> const& rhs) = delete;
|
|
//! lvalue overload is intentionally deleted, users should
|
|
//! not be trying to compose stored composition matchers
|
|
template<typename ArgT>
|
|
MatchAnyOf<ArgT> operator|| (MatcherBase<ArgT> const& lhs, MatchAnyOf<ArgT> const& rhs) = delete;
|
|
|
|
template<typename ArgT>
|
|
class MatchNotOf final : public MatcherBase<ArgT> {
|
|
MatcherBase<ArgT> const& m_underlyingMatcher;
|
|
|
|
public:
|
|
explicit MatchNotOf( MatcherBase<ArgT> 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();
|
|
}
|
|
};
|
|
|
|
} // namespace Detail
|
|
|
|
template <typename T>
|
|
Detail::MatchAllOf<T> operator&& (MatcherBase<T> const& lhs, MatcherBase<T> const& rhs) {
|
|
return Detail::MatchAllOf<T>{} && lhs && rhs;
|
|
}
|
|
template <typename T>
|
|
Detail::MatchAnyOf<T> operator|| (MatcherBase<T> const& lhs, MatcherBase<T> const& rhs) {
|
|
return Detail::MatchAnyOf<T>{} || lhs || rhs;
|
|
}
|
|
|
|
template <typename T>
|
|
Detail::MatchNotOf<T> operator! (MatcherBase<T> const& matcher) {
|
|
return Detail::MatchNotOf<T>{ matcher };
|
|
}
|
|
|
|
|
|
} // namespace Matchers
|
|
} // namespace Catch
|
|
|
|
|
|
#if defined(CATCH_CONFIG_PREFIX_ALL) && !defined(CATCH_CONFIG_DISABLE)
|
|
#define CATCH_REQUIRE_THROWS_WITH( expr, matcher ) INTERNAL_CATCH_THROWS_STR_MATCHES( "CATCH_REQUIRE_THROWS_WITH", Catch::ResultDisposition::Normal, matcher, expr )
|
|
#define CATCH_REQUIRE_THROWS_MATCHES( expr, exceptionType, matcher ) INTERNAL_CATCH_THROWS_MATCHES( "CATCH_REQUIRE_THROWS_MATCHES", exceptionType, Catch::ResultDisposition::Normal, matcher, expr )
|
|
|
|
#define CATCH_CHECK_THROWS_WITH( expr, matcher ) INTERNAL_CATCH_THROWS_STR_MATCHES( "CATCH_CHECK_THROWS_WITH", Catch::ResultDisposition::ContinueOnFailure, matcher, expr )
|
|
#define CATCH_CHECK_THROWS_MATCHES( expr, exceptionType, matcher ) INTERNAL_CATCH_THROWS_MATCHES( "CATCH_CHECK_THROWS_MATCHES", exceptionType, Catch::ResultDisposition::ContinueOnFailure, matcher, expr )
|
|
|
|
#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 )
|
|
|
|
#elif defined(CATCH_CONFIG_PREFIX_ALL) && defined(CATCH_CONFIG_DISABLE)
|
|
|
|
#define CATCH_REQUIRE_THROWS_WITH( expr, matcher ) (void)(0)
|
|
#define CATCH_REQUIRE_THROWS_MATCHES( expr, exceptionType, matcher ) (void)(0)
|
|
|
|
#define CATCH_CHECK_THROWS_WITH( expr, matcher ) (void)(0)
|
|
#define CATCH_CHECK_THROWS_MATCHES( expr, exceptionType, matcher ) (void)(0)
|
|
|
|
#define CATCH_CHECK_THAT( arg, matcher ) (void)(0)
|
|
#define CATCH_REQUIRE_THAT( arg, matcher ) (void)(0)
|
|
|
|
#elif !defined(CATCH_CONFIG_PREFIX_ALL) && !defined(CATCH_CONFIG_DISABLE)
|
|
|
|
#define REQUIRE_THROWS_WITH( expr, matcher ) INTERNAL_CATCH_THROWS_STR_MATCHES( "REQUIRE_THROWS_WITH", Catch::ResultDisposition::Normal, matcher, expr )
|
|
#define REQUIRE_THROWS_MATCHES( expr, exceptionType, matcher ) INTERNAL_CATCH_THROWS_MATCHES( "REQUIRE_THROWS_MATCHES", exceptionType, Catch::ResultDisposition::Normal, matcher, expr )
|
|
|
|
#define CHECK_THROWS_WITH( expr, matcher ) INTERNAL_CATCH_THROWS_STR_MATCHES( "CHECK_THROWS_WITH", Catch::ResultDisposition::ContinueOnFailure, matcher, expr )
|
|
#define CHECK_THROWS_MATCHES( expr, exceptionType, matcher ) INTERNAL_CATCH_THROWS_MATCHES( "CHECK_THROWS_MATCHES", exceptionType, Catch::ResultDisposition::ContinueOnFailure, matcher, expr )
|
|
|
|
#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 )
|
|
|
|
#elif !defined(CATCH_CONFIG_PREFIX_ALL) && defined(CATCH_CONFIG_DISABLE)
|
|
|
|
#define REQUIRE_THROWS_WITH( expr, matcher ) (void)(0)
|
|
#define REQUIRE_THROWS_MATCHES( expr, exceptionType, matcher ) (void)(0)
|
|
|
|
#define CHECK_THROWS_WITH( expr, matcher ) (void)(0)
|
|
#define CHECK_THROWS_MATCHES( expr, exceptionType, matcher ) (void)(0)
|
|
|
|
#define CHECK_THAT( arg, matcher ) (void)(0)
|
|
#define REQUIRE_THAT( arg, matcher ) (void)(0)
|
|
|
|
#endif // end of user facing macro declarations
|
|
|
|
#endif // CATCH_MATCHERS_HPP_INCLUDED
|
|
|
|
|
|
#ifndef CATCH_MATCHERS_CONTAINER_PROPERTIES_HPP_INCLUDED
|
|
#define CATCH_MATCHERS_CONTAINER_PROPERTIES_HPP_INCLUDED
|
|
|
|
|
|
|
|
#ifndef CATCH_MATCHERS_TEMPLATED_HPP_INCLUDED
|
|
#define CATCH_MATCHERS_TEMPLATED_HPP_INCLUDED
|
|
|
|
|
|
#include <array>
|
|
#include <algorithm>
|
|
#include <string>
|
|
#include <type_traits>
|
|
|
|
namespace Catch {
|
|
namespace Matchers {
|
|
class MatcherGenericBase : public MatcherUntypedBase {
|
|
public:
|
|
MatcherGenericBase() = default;
|
|
~MatcherGenericBase() override; // = default;
|
|
|
|
MatcherGenericBase(MatcherGenericBase const&) = default;
|
|
MatcherGenericBase(MatcherGenericBase&&) = default;
|
|
|
|
MatcherGenericBase& operator=(MatcherGenericBase const&) = delete;
|
|
MatcherGenericBase& operator=(MatcherGenericBase&&) = delete;
|
|
};
|
|
|
|
|
|
namespace Detail {
|
|
template<std::size_t N, std::size_t M>
|
|
std::array<void const*, N + M> array_cat(std::array<void const*, N> && lhs, std::array<void const*, M> && rhs) {
|
|
std::array<void const*, N + M> arr{};
|
|
std::copy_n(lhs.begin(), N, arr.begin());
|
|
std::copy_n(rhs.begin(), M, arr.begin() + N);
|
|
return arr;
|
|
}
|
|
|
|
template<std::size_t N>
|
|
std::array<void const*, N+1> array_cat(std::array<void const*, N> && lhs, void const* rhs) {
|
|
std::array<void const*, N+1> arr{};
|
|
std::copy_n(lhs.begin(), N, arr.begin());
|
|
arr[N] = rhs;
|
|
return arr;
|
|
}
|
|
|
|
template<std::size_t N>
|
|
std::array<void const*, N+1> array_cat(void const* lhs, std::array<void const*, N> && rhs) {
|
|
std::array<void const*, N + 1> arr{ {lhs} };
|
|
std::copy_n(rhs.begin(), N, arr.begin() + 1);
|
|
return arr;
|
|
}
|
|
|
|
template<typename T>
|
|
using is_generic_matcher = std::is_base_of<
|
|
Catch::Matchers::MatcherGenericBase,
|
|
std::remove_cv_t<std::remove_reference_t<T>>
|
|
>;
|
|
|
|
template<typename... Ts>
|
|
using are_generic_matchers = Catch::Detail::conjunction<is_generic_matcher<Ts>...>;
|
|
|
|
template<typename T>
|
|
using is_matcher = std::is_base_of<
|
|
Catch::Matchers::MatcherUntypedBase,
|
|
std::remove_cv_t<std::remove_reference_t<T>>
|
|
>;
|
|
|
|
|
|
template<std::size_t N, typename Arg>
|
|
bool match_all_of(Arg&&, std::array<void const*, N> const&, std::index_sequence<>) {
|
|
return true;
|
|
}
|
|
|
|
template<typename T, typename... MatcherTs, std::size_t N, typename Arg, std::size_t Idx, std::size_t... Indices>
|
|
bool match_all_of(Arg&& arg, std::array<void const*, N> const& matchers, std::index_sequence<Idx, Indices...>) {
|
|
return static_cast<T const*>(matchers[Idx])->match(arg) && match_all_of<MatcherTs...>(arg, matchers, std::index_sequence<Indices...>{});
|
|
}
|
|
|
|
|
|
template<std::size_t N, typename Arg>
|
|
bool match_any_of(Arg&&, std::array<void const*, N> const&, std::index_sequence<>) {
|
|
return false;
|
|
}
|
|
|
|
template<typename T, typename... MatcherTs, std::size_t N, typename Arg, std::size_t Idx, std::size_t... Indices>
|
|
bool match_any_of(Arg&& arg, std::array<void const*, N> const& matchers, std::index_sequence<Idx, Indices...>) {
|
|
return static_cast<T const*>(matchers[Idx])->match(arg) || match_any_of<MatcherTs...>(arg, matchers, std::index_sequence<Indices...>{});
|
|
}
|
|
|
|
std::string describe_multi_matcher(StringRef combine, std::string const* descriptions_begin, std::string const* descriptions_end);
|
|
|
|
template<typename... MatcherTs, std::size_t... Idx>
|
|
std::string describe_multi_matcher(StringRef combine, std::array<void const*, sizeof...(MatcherTs)> const& matchers, std::index_sequence<Idx...>) {
|
|
std::array<std::string, sizeof...(MatcherTs)> descriptions {{
|
|
static_cast<MatcherTs const*>(matchers[Idx])->toString()...
|
|
}};
|
|
|
|
return describe_multi_matcher(combine, descriptions.data(), descriptions.data() + descriptions.size());
|
|
}
|
|
|
|
|
|
template<typename... MatcherTs>
|
|
class MatchAllOfGeneric final : public MatcherGenericBase {
|
|
public:
|
|
MatchAllOfGeneric(MatchAllOfGeneric const&) = delete;
|
|
MatchAllOfGeneric& operator=(MatchAllOfGeneric const&) = delete;
|
|
MatchAllOfGeneric(MatchAllOfGeneric&&) = default;
|
|
MatchAllOfGeneric& operator=(MatchAllOfGeneric&&) = default;
|
|
|
|
MatchAllOfGeneric(MatcherTs const&... matchers) : m_matchers{ {std::addressof(matchers)...} } {}
|
|
explicit MatchAllOfGeneric(std::array<void const*, sizeof...(MatcherTs)> matchers) : m_matchers{matchers} {}
|
|
|
|
template<typename Arg>
|
|
bool match(Arg&& arg) const {
|
|
return match_all_of<MatcherTs...>(arg, m_matchers, std::index_sequence_for<MatcherTs...>{});
|
|
}
|
|
|
|
std::string describe() const override {
|
|
return describe_multi_matcher<MatcherTs...>(" and "_sr, m_matchers, std::index_sequence_for<MatcherTs...>{});
|
|
}
|
|
|
|
// Has to be public to enable the concatenating operators
|
|
// below, because they are not friend of the RHS, only LHS,
|
|
// and thus cannot access private fields of RHS
|
|
std::array<void const*, sizeof...( MatcherTs )> m_matchers;
|
|
|
|
|
|
//! Avoids type nesting for `GenericAllOf && GenericAllOf` case
|
|
template<typename... MatchersRHS>
|
|
friend
|
|
MatchAllOfGeneric<MatcherTs..., MatchersRHS...> operator && (
|
|
MatchAllOfGeneric<MatcherTs...>&& lhs,
|
|
MatchAllOfGeneric<MatchersRHS...>&& rhs) {
|
|
return MatchAllOfGeneric<MatcherTs..., MatchersRHS...>{array_cat(CATCH_MOVE(lhs.m_matchers), CATCH_MOVE(rhs.m_matchers))};
|
|
}
|
|
|
|
//! Avoids type nesting for `GenericAllOf && some matcher` case
|
|
template<typename MatcherRHS>
|
|
friend std::enable_if_t<is_matcher<MatcherRHS>::value,
|
|
MatchAllOfGeneric<MatcherTs..., MatcherRHS>> operator && (
|
|
MatchAllOfGeneric<MatcherTs...>&& lhs,
|
|
MatcherRHS const& rhs) {
|
|
return MatchAllOfGeneric<MatcherTs..., MatcherRHS>{array_cat(CATCH_MOVE(lhs.m_matchers), static_cast<void const*>(&rhs))};
|
|
}
|
|
|
|
//! Avoids type nesting for `some matcher && GenericAllOf` case
|
|
template<typename MatcherLHS>
|
|
friend std::enable_if_t<is_matcher<MatcherLHS>::value,
|
|
MatchAllOfGeneric<MatcherLHS, MatcherTs...>> operator && (
|
|
MatcherLHS const& lhs,
|
|
MatchAllOfGeneric<MatcherTs...>&& rhs) {
|
|
return MatchAllOfGeneric<MatcherLHS, MatcherTs...>{array_cat(static_cast<void const*>(std::addressof(lhs)), CATCH_MOVE(rhs.m_matchers))};
|
|
}
|
|
};
|
|
|
|
|
|
template<typename... MatcherTs>
|
|
class MatchAnyOfGeneric final : public MatcherGenericBase {
|
|
public:
|
|
MatchAnyOfGeneric(MatchAnyOfGeneric const&) = delete;
|
|
MatchAnyOfGeneric& operator=(MatchAnyOfGeneric const&) = delete;
|
|
MatchAnyOfGeneric(MatchAnyOfGeneric&&) = default;
|
|
MatchAnyOfGeneric& operator=(MatchAnyOfGeneric&&) = default;
|
|
|
|
MatchAnyOfGeneric(MatcherTs const&... matchers) : m_matchers{ {std::addressof(matchers)...} } {}
|
|
explicit MatchAnyOfGeneric(std::array<void const*, sizeof...(MatcherTs)> matchers) : m_matchers{matchers} {}
|
|
|
|
template<typename Arg>
|
|
bool match(Arg&& arg) const {
|
|
return match_any_of<MatcherTs...>(arg, m_matchers, std::index_sequence_for<MatcherTs...>{});
|
|
}
|
|
|
|
std::string describe() const override {
|
|
return describe_multi_matcher<MatcherTs...>(" or "_sr, m_matchers, std::index_sequence_for<MatcherTs...>{});
|
|
}
|
|
|
|
|
|
// Has to be public to enable the concatenating operators
|
|
// below, because they are not friend of the RHS, only LHS,
|
|
// and thus cannot access private fields of RHS
|
|
std::array<void const*, sizeof...( MatcherTs )> m_matchers;
|
|
|
|
//! Avoids type nesting for `GenericAnyOf || GenericAnyOf` case
|
|
template<typename... MatchersRHS>
|
|
friend MatchAnyOfGeneric<MatcherTs..., MatchersRHS...> operator || (
|
|
MatchAnyOfGeneric<MatcherTs...>&& lhs,
|
|
MatchAnyOfGeneric<MatchersRHS...>&& rhs) {
|
|
return MatchAnyOfGeneric<MatcherTs..., MatchersRHS...>{array_cat(CATCH_MOVE(lhs.m_matchers), CATCH_MOVE(rhs.m_matchers))};
|
|
}
|
|
|
|
//! Avoids type nesting for `GenericAnyOf || some matcher` case
|
|
template<typename MatcherRHS>
|
|
friend std::enable_if_t<is_matcher<MatcherRHS>::value,
|
|
MatchAnyOfGeneric<MatcherTs..., MatcherRHS>> operator || (
|
|
MatchAnyOfGeneric<MatcherTs...>&& lhs,
|
|
MatcherRHS const& rhs) {
|
|
return MatchAnyOfGeneric<MatcherTs..., MatcherRHS>{array_cat(CATCH_MOVE(lhs.m_matchers), static_cast<void const*>(std::addressof(rhs)))};
|
|
}
|
|
|
|
//! Avoids type nesting for `some matcher || GenericAnyOf` case
|
|
template<typename MatcherLHS>
|
|
friend std::enable_if_t<is_matcher<MatcherLHS>::value,
|
|
MatchAnyOfGeneric<MatcherLHS, MatcherTs...>> operator || (
|
|
MatcherLHS const& lhs,
|
|
MatchAnyOfGeneric<MatcherTs...>&& rhs) {
|
|
return MatchAnyOfGeneric<MatcherLHS, MatcherTs...>{array_cat(static_cast<void const*>(std::addressof(lhs)), CATCH_MOVE(rhs.m_matchers))};
|
|
}
|
|
};
|
|
|
|
|
|
template<typename MatcherT>
|
|
class MatchNotOfGeneric final : public MatcherGenericBase {
|
|
MatcherT const& m_matcher;
|
|
|
|
public:
|
|
MatchNotOfGeneric(MatchNotOfGeneric const&) = delete;
|
|
MatchNotOfGeneric& operator=(MatchNotOfGeneric const&) = delete;
|
|
MatchNotOfGeneric(MatchNotOfGeneric&&) = default;
|
|
MatchNotOfGeneric& operator=(MatchNotOfGeneric&&) = default;
|
|
|
|
explicit MatchNotOfGeneric(MatcherT const& matcher) : m_matcher{matcher} {}
|
|
|
|
template<typename Arg>
|
|
bool match(Arg&& arg) const {
|
|
return !m_matcher.match(arg);
|
|
}
|
|
|
|
std::string describe() const override {
|
|
return "not " + m_matcher.toString();
|
|
}
|
|
|
|
//! Negating negation can just unwrap and return underlying matcher
|
|
friend MatcherT const& operator ! (MatchNotOfGeneric<MatcherT> const& matcher) {
|
|
return matcher.m_matcher;
|
|
}
|
|
};
|
|
} // namespace Detail
|
|
|
|
|
|
// compose only generic matchers
|
|
template<typename MatcherLHS, typename MatcherRHS>
|
|
std::enable_if_t<Detail::are_generic_matchers<MatcherLHS, MatcherRHS>::value, Detail::MatchAllOfGeneric<MatcherLHS, MatcherRHS>>
|
|
operator && (MatcherLHS const& lhs, MatcherRHS const& rhs) {
|
|
return { lhs, rhs };
|
|
}
|
|
|
|
template<typename MatcherLHS, typename MatcherRHS>
|
|
std::enable_if_t<Detail::are_generic_matchers<MatcherLHS, MatcherRHS>::value, Detail::MatchAnyOfGeneric<MatcherLHS, MatcherRHS>>
|
|
operator || (MatcherLHS const& lhs, MatcherRHS const& rhs) {
|
|
return { lhs, rhs };
|
|
}
|
|
|
|
//! Wrap provided generic matcher in generic negator
|
|
template<typename MatcherT>
|
|
std::enable_if_t<Detail::is_generic_matcher<MatcherT>::value, Detail::MatchNotOfGeneric<MatcherT>>
|
|
operator ! (MatcherT const& matcher) {
|
|
return Detail::MatchNotOfGeneric<MatcherT>{matcher};
|
|
}
|
|
|
|
|
|
// compose mixed generic and non-generic matchers
|
|
template<typename MatcherLHS, typename ArgRHS>
|
|
std::enable_if_t<Detail::is_generic_matcher<MatcherLHS>::value, Detail::MatchAllOfGeneric<MatcherLHS, MatcherBase<ArgRHS>>>
|
|
operator && (MatcherLHS const& lhs, MatcherBase<ArgRHS> const& rhs) {
|
|
return { lhs, rhs };
|
|
}
|
|
|
|
template<typename ArgLHS, typename MatcherRHS>
|
|
std::enable_if_t<Detail::is_generic_matcher<MatcherRHS>::value, Detail::MatchAllOfGeneric<MatcherBase<ArgLHS>, MatcherRHS>>
|
|
operator && (MatcherBase<ArgLHS> const& lhs, MatcherRHS const& rhs) {
|
|
return { lhs, rhs };
|
|
}
|
|
|
|
template<typename MatcherLHS, typename ArgRHS>
|
|
std::enable_if_t<Detail::is_generic_matcher<MatcherLHS>::value, Detail::MatchAnyOfGeneric<MatcherLHS, MatcherBase<ArgRHS>>>
|
|
operator || (MatcherLHS const& lhs, MatcherBase<ArgRHS> const& rhs) {
|
|
return { lhs, rhs };
|
|
}
|
|
|
|
template<typename ArgLHS, typename MatcherRHS>
|
|
std::enable_if_t<Detail::is_generic_matcher<MatcherRHS>::value, Detail::MatchAnyOfGeneric<MatcherBase<ArgLHS>, MatcherRHS>>
|
|
operator || (MatcherBase<ArgLHS> const& lhs, MatcherRHS const& rhs) {
|
|
return { lhs, rhs };
|
|
}
|
|
|
|
} // namespace Matchers
|
|
} // namespace Catch
|
|
|
|
#endif // CATCH_MATCHERS_TEMPLATED_HPP_INCLUDED
|
|
|
|
namespace Catch {
|
|
namespace Matchers {
|
|
|
|
class IsEmptyMatcher final : public MatcherGenericBase {
|
|
public:
|
|
template <typename RangeLike>
|
|
bool match(RangeLike&& rng) const {
|
|
#if defined(CATCH_CONFIG_POLYFILL_NONMEMBER_CONTAINER_ACCESS)
|
|
using Catch::Detail::empty;
|
|
#else
|
|
using std::empty;
|
|
#endif
|
|
return empty(rng);
|
|
}
|
|
|
|
std::string describe() const override;
|
|
};
|
|
|
|
class HasSizeMatcher final : public MatcherGenericBase {
|
|
std::size_t m_target_size;
|
|
public:
|
|
explicit HasSizeMatcher(std::size_t target_size):
|
|
m_target_size(target_size)
|
|
{}
|
|
|
|
template <typename RangeLike>
|
|
bool match(RangeLike&& rng) const {
|
|
#if defined(CATCH_CONFIG_POLYFILL_NONMEMBER_CONTAINER_ACCESS)
|
|
using Catch::Detail::size;
|
|
#else
|
|
using std::size;
|
|
#endif
|
|
return size(rng) == m_target_size;
|
|
}
|
|
|
|
std::string describe() const override;
|
|
};
|
|
|
|
template <typename Matcher>
|
|
class SizeMatchesMatcher final : public MatcherGenericBase {
|
|
Matcher m_matcher;
|
|
public:
|
|
explicit SizeMatchesMatcher(Matcher m):
|
|
m_matcher(CATCH_MOVE(m))
|
|
{}
|
|
|
|
template <typename RangeLike>
|
|
bool match(RangeLike&& rng) const {
|
|
#if defined(CATCH_CONFIG_POLYFILL_NONMEMBER_CONTAINER_ACCESS)
|
|
using Catch::Detail::size;
|
|
#else
|
|
using std::size;
|
|
#endif
|
|
return m_matcher.match(size(rng));
|
|
}
|
|
|
|
std::string describe() const override {
|
|
return "size matches " + m_matcher.describe();
|
|
}
|
|
};
|
|
|
|
|
|
//! Creates a matcher that accepts empty ranges/containers
|
|
IsEmptyMatcher IsEmpty();
|
|
//! Creates a matcher that accepts ranges/containers with specific size
|
|
HasSizeMatcher SizeIs(std::size_t sz);
|
|
template <typename Matcher>
|
|
std::enable_if_t<Detail::is_matcher<Matcher>::value,
|
|
SizeMatchesMatcher<Matcher>> SizeIs(Matcher&& m) {
|
|
return SizeMatchesMatcher<Matcher>{CATCH_FORWARD(m)};
|
|
}
|
|
|
|
} // end namespace Matchers
|
|
} // end namespace Catch
|
|
|
|
#endif // CATCH_MATCHERS_CONTAINER_PROPERTIES_HPP_INCLUDED
|
|
|
|
|
|
#ifndef CATCH_MATCHERS_CONTAINS_HPP_INCLUDED
|
|
#define CATCH_MATCHERS_CONTAINS_HPP_INCLUDED
|
|
|
|
|
|
#include <algorithm>
|
|
#include <functional>
|
|
|
|
namespace Catch {
|
|
namespace Matchers {
|
|
//! Matcher for checking that an element in range is equal to specific element
|
|
template <typename T, typename Equality>
|
|
class ContainsElementMatcher final : public MatcherGenericBase {
|
|
T m_desired;
|
|
Equality m_eq;
|
|
public:
|
|
template <typename T2, typename Equality2>
|
|
ContainsElementMatcher(T2&& target, Equality2&& predicate):
|
|
m_desired(CATCH_FORWARD(target)),
|
|
m_eq(CATCH_FORWARD(predicate))
|
|
{}
|
|
|
|
std::string describe() const override {
|
|
return "contains element " + Catch::Detail::stringify(m_desired);
|
|
}
|
|
|
|
template <typename RangeLike>
|
|
bool match( RangeLike&& rng ) const {
|
|
for ( auto&& elem : rng ) {
|
|
if ( m_eq( elem, m_desired ) ) { return true; }
|
|
}
|
|
return false;
|
|
}
|
|
};
|
|
|
|
//! Meta-matcher for checking that an element in a range matches a specific matcher
|
|
template <typename Matcher>
|
|
class ContainsMatcherMatcher final : public MatcherGenericBase {
|
|
Matcher m_matcher;
|
|
public:
|
|
// Note that we do a copy+move to avoid having to SFINAE this
|
|
// constructor (and also avoid some perfect forwarding failure
|
|
// cases)
|
|
ContainsMatcherMatcher(Matcher matcher):
|
|
m_matcher(CATCH_MOVE(matcher))
|
|
{}
|
|
|
|
template <typename RangeLike>
|
|
bool match(RangeLike&& rng) const {
|
|
for (auto&& elem : rng) {
|
|
if (m_matcher.match(elem)) {
|
|
return true;
|
|
}
|
|
}
|
|
return false;
|
|
}
|
|
|
|
std::string describe() const override {
|
|
return "contains element matching " + m_matcher.describe();
|
|
}
|
|
};
|
|
|
|
/**
|
|
* Creates a matcher that checks whether a range contains a specific element.
|
|
*
|
|
* Uses `std::equal_to` to do the comparison
|
|
*/
|
|
template <typename T>
|
|
std::enable_if_t<!Detail::is_matcher<T>::value,
|
|
ContainsElementMatcher<T, std::equal_to<>>> Contains(T&& elem) {
|
|
return { CATCH_FORWARD(elem), std::equal_to<>{} };
|
|
}
|
|
|
|
//! Creates a matcher that checks whether a range contains element matching a matcher
|
|
template <typename Matcher>
|
|
std::enable_if_t<Detail::is_matcher<Matcher>::value,
|
|
ContainsMatcherMatcher<Matcher>> Contains(Matcher&& matcher) {
|
|
return { CATCH_FORWARD(matcher) };
|
|
}
|
|
|
|
/**
|
|
* Creates a matcher that checks whether a range contains a specific element.
|
|
*
|
|
* Uses `eq` to do the comparisons, the element is provided on the rhs
|
|
*/
|
|
template <typename T, typename Equality>
|
|
ContainsElementMatcher<T, Equality> Contains(T&& elem, Equality&& eq) {
|
|
return { CATCH_FORWARD(elem), CATCH_FORWARD(eq) };
|
|
}
|
|
|
|
}
|
|
}
|
|
|
|
#endif // CATCH_MATCHERS_CONTAINS_HPP_INCLUDED
|
|
|
|
|
|
#ifndef CATCH_MATCHERS_EXCEPTION_HPP_INCLUDED
|
|
#define CATCH_MATCHERS_EXCEPTION_HPP_INCLUDED
|
|
|
|
|
|
namespace Catch {
|
|
namespace Matchers {
|
|
|
|
class ExceptionMessageMatcher final : public MatcherBase<std::exception> {
|
|
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;
|
|
};
|
|
|
|
//! Creates a matcher that checks whether a std derived exception has the provided message
|
|
ExceptionMessageMatcher Message(std::string const& message);
|
|
|
|
template <typename StringMatcherType>
|
|
class ExceptionMessageMatchesMatcher final
|
|
: public MatcherBase<std::exception> {
|
|
StringMatcherType m_matcher;
|
|
|
|
public:
|
|
ExceptionMessageMatchesMatcher( StringMatcherType matcher ):
|
|
m_matcher( CATCH_MOVE( matcher ) ) {}
|
|
|
|
bool match( std::exception const& ex ) const override {
|
|
return m_matcher.match( ex.what() );
|
|
}
|
|
|
|
std::string describe() const override {
|
|
return " matches \"" + m_matcher.describe() + '"';
|
|
}
|
|
};
|
|
|
|
//! Creates a matcher that checks whether a message from an std derived
|
|
//! exception matches a provided matcher
|
|
template <typename StringMatcherType>
|
|
ExceptionMessageMatchesMatcher<StringMatcherType>
|
|
MessageMatches( StringMatcherType&& matcher ) {
|
|
return { CATCH_FORWARD( matcher ) };
|
|
}
|
|
|
|
} // namespace Matchers
|
|
} // namespace Catch
|
|
|
|
#endif // CATCH_MATCHERS_EXCEPTION_HPP_INCLUDED
|
|
|
|
|
|
#ifndef CATCH_MATCHERS_FLOATING_POINT_HPP_INCLUDED
|
|
#define CATCH_MATCHERS_FLOATING_POINT_HPP_INCLUDED
|
|
|
|
|
|
namespace Catch {
|
|
namespace Matchers {
|
|
|
|
namespace Detail {
|
|
enum class FloatingPointKind : uint8_t;
|
|
}
|
|
|
|
class WithinAbsMatcher final : public MatcherBase<double> {
|
|
public:
|
|
WithinAbsMatcher(double target, double margin);
|
|
bool match(double const& matchee) const override;
|
|
std::string describe() const override;
|
|
private:
|
|
double m_target;
|
|
double m_margin;
|
|
};
|
|
|
|
//! Creates a matcher that accepts numbers within certain range of target
|
|
WithinAbsMatcher WithinAbs( double target, double margin );
|
|
|
|
|
|
|
|
class WithinUlpsMatcher final : public MatcherBase<double> {
|
|
public:
|
|
WithinUlpsMatcher( double target,
|
|
uint64_t ulps,
|
|
Detail::FloatingPointKind baseType );
|
|
bool match(double const& matchee) const override;
|
|
std::string describe() const override;
|
|
private:
|
|
double m_target;
|
|
uint64_t m_ulps;
|
|
Detail::FloatingPointKind m_type;
|
|
};
|
|
|
|
//! Creates a matcher that accepts doubles within certain ULP range of target
|
|
WithinUlpsMatcher WithinULP(double target, uint64_t maxUlpDiff);
|
|
//! Creates a matcher that accepts floats within certain ULP range of target
|
|
WithinUlpsMatcher WithinULP(float target, uint64_t maxUlpDiff);
|
|
|
|
|
|
|
|
// 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.
|
|
class WithinRelMatcher final : public MatcherBase<double> {
|
|
public:
|
|
WithinRelMatcher( double target, double epsilon );
|
|
bool match(double const& matchee) const override;
|
|
std::string describe() const override;
|
|
private:
|
|
double m_target;
|
|
double m_epsilon;
|
|
};
|
|
|
|
//! Creates a matcher that accepts doubles within certain relative range of target
|
|
WithinRelMatcher WithinRel(double target, double eps);
|
|
//! Creates a matcher that accepts doubles within 100*DBL_EPS relative range of target
|
|
WithinRelMatcher WithinRel(double target);
|
|
//! Creates a matcher that accepts doubles within certain relative range of target
|
|
WithinRelMatcher WithinRel(float target, float eps);
|
|
//! Creates a matcher that accepts floats within 100*FLT_EPS relative range of target
|
|
WithinRelMatcher WithinRel(float target);
|
|
|
|
|
|
|
|
class IsNaNMatcher final : public MatcherBase<double> {
|
|
public:
|
|
IsNaNMatcher() = default;
|
|
bool match( double const& matchee ) const override;
|
|
std::string describe() const override;
|
|
};
|
|
|
|
IsNaNMatcher IsNaN();
|
|
|
|
} // namespace Matchers
|
|
} // namespace Catch
|
|
|
|
#endif // CATCH_MATCHERS_FLOATING_POINT_HPP_INCLUDED
|
|
|
|
|
|
#ifndef CATCH_MATCHERS_PREDICATE_HPP_INCLUDED
|
|
#define CATCH_MATCHERS_PREDICATE_HPP_INCLUDED
|
|
|
|
|
|
#include <string>
|
|
|
|
namespace Catch {
|
|
namespace Matchers {
|
|
|
|
namespace Detail {
|
|
std::string finalizeDescription(const std::string& desc);
|
|
} // namespace Detail
|
|
|
|
template <typename T, typename Predicate>
|
|
class PredicateMatcher final : public MatcherBase<T> {
|
|
Predicate m_predicate;
|
|
std::string m_description;
|
|
public:
|
|
|
|
PredicateMatcher(Predicate&& elem, std::string const& descr)
|
|
:m_predicate(CATCH_FORWARD(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;
|
|
}
|
|
};
|
|
|
|
/**
|
|
* Creates a matcher that calls delegates `match` to the provided predicate.
|
|
*
|
|
* The user has to explicitly specify the argument type to the matcher
|
|
*/
|
|
template<typename T, typename Pred>
|
|
PredicateMatcher<T, Pred> Predicate(Pred&& predicate, std::string const& description = "") {
|
|
static_assert(is_callable<Pred(T)>::value, "Predicate not callable with argument T");
|
|
static_assert(std::is_same<bool, FunctionReturnType<Pred, T>>::value, "Predicate does not return bool");
|
|
return PredicateMatcher<T, Pred>(CATCH_FORWARD(predicate), description);
|
|
}
|
|
|
|
} // namespace Matchers
|
|
} // namespace Catch
|
|
|
|
#endif // CATCH_MATCHERS_PREDICATE_HPP_INCLUDED
|
|
|
|
|
|
#ifndef CATCH_MATCHERS_QUANTIFIERS_HPP_INCLUDED
|
|
#define CATCH_MATCHERS_QUANTIFIERS_HPP_INCLUDED
|
|
|
|
|
|
namespace Catch {
|
|
namespace Matchers {
|
|
// Matcher for checking that all elements in range matches a given matcher.
|
|
template <typename Matcher>
|
|
class AllMatchMatcher final : public MatcherGenericBase {
|
|
Matcher m_matcher;
|
|
public:
|
|
AllMatchMatcher(Matcher matcher):
|
|
m_matcher(CATCH_MOVE(matcher))
|
|
{}
|
|
|
|
std::string describe() const override {
|
|
return "all match " + m_matcher.describe();
|
|
}
|
|
|
|
template <typename RangeLike>
|
|
bool match(RangeLike&& rng) const {
|
|
for (auto&& elem : rng) {
|
|
if (!m_matcher.match(elem)) {
|
|
return false;
|
|
}
|
|
}
|
|
return true;
|
|
}
|
|
};
|
|
|
|
// Matcher for checking that no element in range matches a given matcher.
|
|
template <typename Matcher>
|
|
class NoneMatchMatcher final : public MatcherGenericBase {
|
|
Matcher m_matcher;
|
|
public:
|
|
NoneMatchMatcher(Matcher matcher):
|
|
m_matcher(CATCH_MOVE(matcher))
|
|
{}
|
|
|
|
std::string describe() const override {
|
|
return "none match " + m_matcher.describe();
|
|
}
|
|
|
|
template <typename RangeLike>
|
|
bool match(RangeLike&& rng) const {
|
|
for (auto&& elem : rng) {
|
|
if (m_matcher.match(elem)) {
|
|
return false;
|
|
}
|
|
}
|
|
return true;
|
|
}
|
|
};
|
|
|
|
// Matcher for checking that at least one element in range matches a given matcher.
|
|
template <typename Matcher>
|
|
class AnyMatchMatcher final : public MatcherGenericBase {
|
|
Matcher m_matcher;
|
|
public:
|
|
AnyMatchMatcher(Matcher matcher):
|
|
m_matcher(CATCH_MOVE(matcher))
|
|
{}
|
|
|
|
std::string describe() const override {
|
|
return "any match " + m_matcher.describe();
|
|
}
|
|
|
|
template <typename RangeLike>
|
|
bool match(RangeLike&& rng) const {
|
|
for (auto&& elem : rng) {
|
|
if (m_matcher.match(elem)) {
|
|
return true;
|
|
}
|
|
}
|
|
return false;
|
|
}
|
|
};
|
|
|
|
// Matcher for checking that all elements in range are true.
|
|
class AllTrueMatcher final : public MatcherGenericBase {
|
|
public:
|
|
std::string describe() const override;
|
|
|
|
template <typename RangeLike>
|
|
bool match(RangeLike&& rng) const {
|
|
for (auto&& elem : rng) {
|
|
if (!elem) {
|
|
return false;
|
|
}
|
|
}
|
|
return true;
|
|
}
|
|
};
|
|
|
|
// Matcher for checking that no element in range is true.
|
|
class NoneTrueMatcher final : public MatcherGenericBase {
|
|
public:
|
|
std::string describe() const override;
|
|
|
|
template <typename RangeLike>
|
|
bool match(RangeLike&& rng) const {
|
|
for (auto&& elem : rng) {
|
|
if (elem) {
|
|
return false;
|
|
}
|
|
}
|
|
return true;
|
|
}
|
|
};
|
|
|
|
// Matcher for checking that any element in range is true.
|
|
class AnyTrueMatcher final : public MatcherGenericBase {
|
|
public:
|
|
std::string describe() const override;
|
|
|
|
template <typename RangeLike>
|
|
bool match(RangeLike&& rng) const {
|
|
for (auto&& elem : rng) {
|
|
if (elem) {
|
|
return true;
|
|
}
|
|
}
|
|
return false;
|
|
}
|
|
};
|
|
|
|
// Creates a matcher that checks whether all elements in a range match a matcher
|
|
template <typename Matcher>
|
|
AllMatchMatcher<Matcher> AllMatch(Matcher&& matcher) {
|
|
return { CATCH_FORWARD(matcher) };
|
|
}
|
|
|
|
// Creates a matcher that checks whether no element in a range matches a matcher.
|
|
template <typename Matcher>
|
|
NoneMatchMatcher<Matcher> NoneMatch(Matcher&& matcher) {
|
|
return { CATCH_FORWARD(matcher) };
|
|
}
|
|
|
|
// Creates a matcher that checks whether any element in a range matches a matcher.
|
|
template <typename Matcher>
|
|
AnyMatchMatcher<Matcher> AnyMatch(Matcher&& matcher) {
|
|
return { CATCH_FORWARD(matcher) };
|
|
}
|
|
|
|
// Creates a matcher that checks whether all elements in a range are true
|
|
AllTrueMatcher AllTrue();
|
|
|
|
// Creates a matcher that checks whether no element in a range is true
|
|
NoneTrueMatcher NoneTrue();
|
|
|
|
// Creates a matcher that checks whether any element in a range is true
|
|
AnyTrueMatcher AnyTrue();
|
|
}
|
|
}
|
|
|
|
#endif // CATCH_MATCHERS_QUANTIFIERS_HPP_INCLUDED
|
|
|
|
|
|
#ifndef CATCH_MATCHERS_RANGE_EQUALS_HPP_INCLUDED
|
|
#define CATCH_MATCHERS_RANGE_EQUALS_HPP_INCLUDED
|
|
|
|
|
|
#include <algorithm>
|
|
#include <utility>
|
|
|
|
namespace Catch {
|
|
namespace Matchers {
|
|
|
|
/**
|
|
* Matcher for checking that an element contains the same
|
|
* elements in the same order
|
|
*/
|
|
template <typename TargetRangeLike, typename Equality>
|
|
class RangeEqualsMatcher final : public MatcherGenericBase {
|
|
TargetRangeLike m_desired;
|
|
Equality m_predicate;
|
|
|
|
public:
|
|
template <typename TargetRangeLike2, typename Equality2>
|
|
RangeEqualsMatcher( TargetRangeLike2&& range,
|
|
Equality2&& predicate ):
|
|
m_desired( CATCH_FORWARD( range ) ),
|
|
m_predicate( CATCH_FORWARD( predicate ) ) {}
|
|
|
|
template <typename RangeLike>
|
|
bool match( RangeLike&& rng ) const {
|
|
auto rng_start = begin( rng );
|
|
const auto rng_end = end( rng );
|
|
auto target_start = begin( m_desired );
|
|
const auto target_end = end( m_desired );
|
|
|
|
while (rng_start != rng_end && target_start != target_end) {
|
|
if (!m_predicate(*rng_start, *target_start)) {
|
|
return false;
|
|
}
|
|
++rng_start;
|
|
++target_start;
|
|
}
|
|
return rng_start == rng_end && target_start == target_end;
|
|
}
|
|
|
|
std::string describe() const override {
|
|
return "elements are " + Catch::Detail::stringify( m_desired );
|
|
}
|
|
};
|
|
|
|
/**
|
|
* Matcher for checking that an element contains the same
|
|
* elements (but not necessarily in the same order)
|
|
*/
|
|
template <typename TargetRangeLike, typename Equality>
|
|
class UnorderedRangeEqualsMatcher final : public MatcherGenericBase {
|
|
TargetRangeLike m_desired;
|
|
Equality m_predicate;
|
|
|
|
public:
|
|
template <typename TargetRangeLike2, typename Equality2>
|
|
UnorderedRangeEqualsMatcher( TargetRangeLike2&& range,
|
|
Equality2&& predicate ):
|
|
m_desired( CATCH_FORWARD( range ) ),
|
|
m_predicate( CATCH_FORWARD( predicate ) ) {}
|
|
|
|
template <typename RangeLike>
|
|
bool match( RangeLike&& rng ) const {
|
|
using std::begin;
|
|
using std::end;
|
|
return Catch::Detail::is_permutation( begin( m_desired ),
|
|
end( m_desired ),
|
|
begin( rng ),
|
|
end( rng ),
|
|
m_predicate );
|
|
}
|
|
|
|
std::string describe() const override {
|
|
return "unordered elements are " +
|
|
::Catch::Detail::stringify( m_desired );
|
|
}
|
|
};
|
|
|
|
/**
|
|
* Creates a matcher that checks if all elements in a range are equal
|
|
* to all elements in another range.
|
|
*
|
|
* Uses `std::equal_to` to do the comparison
|
|
*/
|
|
template <typename RangeLike>
|
|
std::enable_if_t<!Detail::is_matcher<RangeLike>::value,
|
|
RangeEqualsMatcher<RangeLike, std::equal_to<>>>
|
|
RangeEquals( RangeLike&& range ) {
|
|
return { CATCH_FORWARD( range ), std::equal_to<>{} };
|
|
}
|
|
|
|
/**
|
|
* Creates a matcher that checks if all elements in a range are equal
|
|
* to all elements in another range.
|
|
*
|
|
* Uses to provided predicate `predicate` to do the comparisons
|
|
*/
|
|
template <typename RangeLike, typename Equality>
|
|
RangeEqualsMatcher<RangeLike, Equality>
|
|
RangeEquals( RangeLike&& range, Equality&& predicate ) {
|
|
return { CATCH_FORWARD( range ), CATCH_FORWARD( predicate ) };
|
|
}
|
|
|
|
/**
|
|
* Creates a matcher that checks if all elements in a range are equal
|
|
* to all elements in another range, in some permutation
|
|
*
|
|
* Uses `std::equal_to` to do the comparison
|
|
*/
|
|
template <typename RangeLike>
|
|
std::enable_if_t<
|
|
!Detail::is_matcher<RangeLike>::value,
|
|
UnorderedRangeEqualsMatcher<RangeLike, std::equal_to<>>>
|
|
UnorderedRangeEquals( RangeLike&& range ) {
|
|
return { CATCH_FORWARD( range ), std::equal_to<>{} };
|
|
}
|
|
|
|
/**
|
|
* Creates a matcher that checks if all elements in a range are equal
|
|
* to all elements in another range, in some permutation.
|
|
*
|
|
* Uses to provided predicate `predicate` to do the comparisons
|
|
*/
|
|
template <typename RangeLike, typename Equality>
|
|
UnorderedRangeEqualsMatcher<RangeLike, Equality>
|
|
UnorderedRangeEquals( RangeLike&& range, Equality&& predicate ) {
|
|
return { CATCH_FORWARD( range ), CATCH_FORWARD( predicate ) };
|
|
}
|
|
} // namespace Matchers
|
|
} // namespace Catch
|
|
|
|
#endif // CATCH_MATCHERS_RANGE_EQUALS_HPP_INCLUDED
|
|
|
|
|
|
#ifndef CATCH_MATCHERS_STRING_HPP_INCLUDED
|
|
#define CATCH_MATCHERS_STRING_HPP_INCLUDED
|
|
|
|
|
|
#include <string>
|
|
|
|
namespace Catch {
|
|
namespace Matchers {
|
|
|
|
struct CasedString {
|
|
CasedString( std::string const& str, CaseSensitive caseSensitivity );
|
|
std::string adjustString( std::string const& str ) const;
|
|
StringRef caseSensitivitySuffix() const;
|
|
|
|
CaseSensitive m_caseSensitivity;
|
|
std::string m_str;
|
|
};
|
|
|
|
class StringMatcherBase : public MatcherBase<std::string> {
|
|
protected:
|
|
CasedString m_comparator;
|
|
StringRef m_operation;
|
|
|
|
public:
|
|
StringMatcherBase( StringRef operation,
|
|
CasedString const& comparator );
|
|
std::string describe() const override;
|
|
};
|
|
|
|
class StringEqualsMatcher final : public StringMatcherBase {
|
|
public:
|
|
StringEqualsMatcher( CasedString const& comparator );
|
|
bool match( std::string const& source ) const override;
|
|
};
|
|
class StringContainsMatcher final : public StringMatcherBase {
|
|
public:
|
|
StringContainsMatcher( CasedString const& comparator );
|
|
bool match( std::string const& source ) const override;
|
|
};
|
|
class StartsWithMatcher final : public StringMatcherBase {
|
|
public:
|
|
StartsWithMatcher( CasedString const& comparator );
|
|
bool match( std::string const& source ) const override;
|
|
};
|
|
class EndsWithMatcher final : public StringMatcherBase {
|
|
public:
|
|
EndsWithMatcher( CasedString const& comparator );
|
|
bool match( std::string const& source ) const override;
|
|
};
|
|
|
|
class RegexMatcher final : public MatcherBase<std::string> {
|
|
std::string m_regex;
|
|
CaseSensitive m_caseSensitivity;
|
|
|
|
public:
|
|
RegexMatcher( std::string regex, CaseSensitive caseSensitivity );
|
|
bool match( std::string const& matchee ) const override;
|
|
std::string describe() const override;
|
|
};
|
|
|
|
//! Creates matcher that accepts strings that are exactly equal to `str`
|
|
StringEqualsMatcher Equals( std::string const& str, CaseSensitive caseSensitivity = CaseSensitive::Yes );
|
|
//! Creates matcher that accepts strings that contain `str`
|
|
StringContainsMatcher ContainsSubstring( std::string const& str, CaseSensitive caseSensitivity = CaseSensitive::Yes );
|
|
//! Creates matcher that accepts strings that _end_ with `str`
|
|
EndsWithMatcher EndsWith( std::string const& str, CaseSensitive caseSensitivity = CaseSensitive::Yes );
|
|
//! Creates matcher that accepts strings that _start_ with `str`
|
|
StartsWithMatcher StartsWith( std::string const& str, CaseSensitive caseSensitivity = CaseSensitive::Yes );
|
|
//! Creates matcher that accepts strings matching `regex`
|
|
RegexMatcher Matches( std::string const& regex, CaseSensitive caseSensitivity = CaseSensitive::Yes );
|
|
|
|
} // namespace Matchers
|
|
} // namespace Catch
|
|
|
|
#endif // CATCH_MATCHERS_STRING_HPP_INCLUDED
|
|
|
|
|
|
#ifndef CATCH_MATCHERS_VECTOR_HPP_INCLUDED
|
|
#define CATCH_MATCHERS_VECTOR_HPP_INCLUDED
|
|
|
|
|
|
#include <algorithm>
|
|
|
|
namespace Catch {
|
|
namespace Matchers {
|
|
|
|
template<typename T, typename Alloc>
|
|
class VectorContainsElementMatcher final : public MatcherBase<std::vector<T, Alloc>> {
|
|
T const& m_comparator;
|
|
|
|
public:
|
|
VectorContainsElementMatcher(T const& comparator):
|
|
m_comparator(comparator)
|
|
{}
|
|
|
|
bool match(std::vector<T, Alloc> 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 );
|
|
}
|
|
};
|
|
|
|
template<typename T, typename AllocComp, typename AllocMatch>
|
|
class ContainsMatcher final : public MatcherBase<std::vector<T, AllocMatch>> {
|
|
std::vector<T, AllocComp> const& m_comparator;
|
|
|
|
public:
|
|
ContainsMatcher(std::vector<T, AllocComp> const& comparator):
|
|
m_comparator( comparator )
|
|
{}
|
|
|
|
bool match(std::vector<T, AllocMatch> 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 );
|
|
}
|
|
};
|
|
|
|
template<typename T, typename AllocComp, typename AllocMatch>
|
|
class EqualsMatcher final : public MatcherBase<std::vector<T, AllocMatch>> {
|
|
std::vector<T, AllocComp> const& m_comparator;
|
|
|
|
public:
|
|
EqualsMatcher(std::vector<T, AllocComp> const& comparator):
|
|
m_comparator( comparator )
|
|
{}
|
|
|
|
bool match(std::vector<T, AllocMatch> 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<T> 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 );
|
|
}
|
|
};
|
|
|
|
template<typename T, typename AllocComp, typename AllocMatch>
|
|
class ApproxMatcher final : public MatcherBase<std::vector<T, AllocMatch>> {
|
|
std::vector<T, AllocComp> const& m_comparator;
|
|
mutable Catch::Approx approx = Catch::Approx::custom();
|
|
|
|
public:
|
|
ApproxMatcher(std::vector<T, AllocComp> const& comparator):
|
|
m_comparator( comparator )
|
|
{}
|
|
|
|
bool match(std::vector<T, AllocMatch> 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 <typename = std::enable_if_t<std::is_constructible<double, T>::value>>
|
|
ApproxMatcher& epsilon( T const& newEpsilon ) {
|
|
approx.epsilon(static_cast<double>(newEpsilon));
|
|
return *this;
|
|
}
|
|
template <typename = std::enable_if_t<std::is_constructible<double, T>::value>>
|
|
ApproxMatcher& margin( T const& newMargin ) {
|
|
approx.margin(static_cast<double>(newMargin));
|
|
return *this;
|
|
}
|
|
template <typename = std::enable_if_t<std::is_constructible<double, T>::value>>
|
|
ApproxMatcher& scale( T const& newScale ) {
|
|
approx.scale(static_cast<double>(newScale));
|
|
return *this;
|
|
}
|
|
};
|
|
|
|
template<typename T, typename AllocComp, typename AllocMatch>
|
|
class UnorderedEqualsMatcher final : public MatcherBase<std::vector<T, AllocMatch>> {
|
|
std::vector<T, AllocComp> const& m_target;
|
|
|
|
public:
|
|
UnorderedEqualsMatcher(std::vector<T, AllocComp> const& target):
|
|
m_target(target)
|
|
{}
|
|
bool match(std::vector<T, AllocMatch> 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);
|
|
}
|
|
};
|
|
|
|
|
|
// The following functions create the actual matcher objects.
|
|
// This allows the types to be inferred
|
|
|
|
//! Creates a matcher that matches vectors that contain all elements in `comparator`
|
|
template<typename T, typename AllocComp = std::allocator<T>, typename AllocMatch = AllocComp>
|
|
ContainsMatcher<T, AllocComp, AllocMatch> Contains( std::vector<T, AllocComp> const& comparator ) {
|
|
return ContainsMatcher<T, AllocComp, AllocMatch>(comparator);
|
|
}
|
|
|
|
//! Creates a matcher that matches vectors that contain `comparator` as an element
|
|
template<typename T, typename Alloc = std::allocator<T>>
|
|
VectorContainsElementMatcher<T, Alloc> VectorContains( T const& comparator ) {
|
|
return VectorContainsElementMatcher<T, Alloc>(comparator);
|
|
}
|
|
|
|
//! Creates a matcher that matches vectors that are exactly equal to `comparator`
|
|
template<typename T, typename AllocComp = std::allocator<T>, typename AllocMatch = AllocComp>
|
|
EqualsMatcher<T, AllocComp, AllocMatch> Equals( std::vector<T, AllocComp> const& comparator ) {
|
|
return EqualsMatcher<T, AllocComp, AllocMatch>(comparator);
|
|
}
|
|
|
|
//! Creates a matcher that matches vectors that `comparator` as an element
|
|
template<typename T, typename AllocComp = std::allocator<T>, typename AllocMatch = AllocComp>
|
|
ApproxMatcher<T, AllocComp, AllocMatch> Approx( std::vector<T, AllocComp> const& comparator ) {
|
|
return ApproxMatcher<T, AllocComp, AllocMatch>(comparator);
|
|
}
|
|
|
|
//! Creates a matcher that matches vectors that is equal to `target` modulo permutation
|
|
template<typename T, typename AllocComp = std::allocator<T>, typename AllocMatch = AllocComp>
|
|
UnorderedEqualsMatcher<T, AllocComp, AllocMatch> UnorderedEquals(std::vector<T, AllocComp> const& target) {
|
|
return UnorderedEqualsMatcher<T, AllocComp, AllocMatch>(target);
|
|
}
|
|
|
|
} // namespace Matchers
|
|
} // namespace Catch
|
|
|
|
#endif // CATCH_MATCHERS_VECTOR_HPP_INCLUDED
|
|
|
|
#endif // CATCH_MATCHERS_ALL_HPP_INCLUDED
|
|
|
|
|
|
/** \file
|
|
* This is a convenience header for Catch2's Reporter support. It includes
|
|
* **all** of Catch2 headers related to reporters, including all reporters.
|
|
*
|
|
* Generally the Catch2 users should use specific includes they need,
|
|
* but this header can be used instead for ease-of-experimentation, or
|
|
* just plain convenience, at the cost of (significantly) increased
|
|
* compilation times.
|
|
*
|
|
* When a new header (reporter) is added to either the `reporter` folder,
|
|
* or to the corresponding internal subfolder, it should be added here.
|
|
*/
|
|
|
|
#ifndef CATCH_REPORTERS_ALL_HPP_INCLUDED
|
|
#define CATCH_REPORTERS_ALL_HPP_INCLUDED
|
|
|
|
|
|
|
|
#ifndef CATCH_REPORTER_AUTOMAKE_HPP_INCLUDED
|
|
#define CATCH_REPORTER_AUTOMAKE_HPP_INCLUDED
|
|
|
|
|
|
|
|
#ifndef CATCH_REPORTER_STREAMING_BASE_HPP_INCLUDED
|
|
#define CATCH_REPORTER_STREAMING_BASE_HPP_INCLUDED
|
|
|
|
|
|
|
|
#ifndef CATCH_REPORTER_COMMON_BASE_HPP_INCLUDED
|
|
#define CATCH_REPORTER_COMMON_BASE_HPP_INCLUDED
|
|
|
|
|
|
#include <map>
|
|
#include <string>
|
|
|
|
namespace Catch {
|
|
class ColourImpl;
|
|
|
|
/**
|
|
* This is the base class for all reporters.
|
|
*
|
|
* If are writing a reporter, you must derive from this type, or one
|
|
* of the helper reporter bases that are derived from this type.
|
|
*
|
|
* ReporterBase centralizes handling of various common tasks in reporters,
|
|
* like storing the right stream for the reporters to write to, and
|
|
* providing the default implementation of the different listing events.
|
|
*/
|
|
class ReporterBase : public IEventListener {
|
|
protected:
|
|
//! The stream wrapper as passed to us by outside code
|
|
Detail::unique_ptr<IStream> m_wrapped_stream;
|
|
//! Cached output stream from `m_wrapped_stream` to reduce
|
|
//! number of indirect calls needed to write output.
|
|
std::ostream& m_stream;
|
|
//! Colour implementation this reporter was configured for
|
|
Detail::unique_ptr<ColourImpl> m_colour;
|
|
//! The custom reporter options user passed down to the reporter
|
|
std::map<std::string, std::string> m_customOptions;
|
|
|
|
public:
|
|
ReporterBase( ReporterConfig&& config );
|
|
~ReporterBase() override; // = default;
|
|
|
|
/**
|
|
* Provides a simple default listing of reporters.
|
|
*
|
|
* Should look roughly like the reporter listing in v2 and earlier
|
|
* versions of Catch2.
|
|
*/
|
|
void listReporters(
|
|
std::vector<ReporterDescription> const& descriptions ) override;
|
|
/**
|
|
* Provides a simple default listing of listeners
|
|
*
|
|
* Looks similarly to listing of reporters, but with listener type
|
|
* instead of reporter name.
|
|
*/
|
|
void listListeners(
|
|
std::vector<ListenerDescription> const& descriptions ) override;
|
|
/**
|
|
* Provides a simple default listing of tests.
|
|
*
|
|
* Should look roughly like the test listing in v2 and earlier versions
|
|
* of Catch2. Especially supports low-verbosity listing that mimics the
|
|
* old `--list-test-names-only` output.
|
|
*/
|
|
void listTests( std::vector<TestCaseHandle> const& tests ) override;
|
|
/**
|
|
* Provides a simple default listing of tags.
|
|
*
|
|
* Should look roughly like the tag listing in v2 and earlier versions
|
|
* of Catch2.
|
|
*/
|
|
void listTags( std::vector<TagInfo> const& tags ) override;
|
|
};
|
|
} // namespace Catch
|
|
|
|
#endif // CATCH_REPORTER_COMMON_BASE_HPP_INCLUDED
|
|
|
|
#include <vector>
|
|
|
|
namespace Catch {
|
|
|
|
class StreamingReporterBase : public ReporterBase {
|
|
public:
|
|
// GCC5 compat: we cannot use inherited constructor, because it
|
|
// doesn't implement backport of P0136
|
|
StreamingReporterBase(ReporterConfig&& _config):
|
|
ReporterBase(CATCH_MOVE(_config))
|
|
{}
|
|
~StreamingReporterBase() override;
|
|
|
|
void benchmarkPreparing( StringRef ) override {}
|
|
void benchmarkStarting( BenchmarkInfo const& ) override {}
|
|
void benchmarkEnded( BenchmarkStats<> const& ) override {}
|
|
void benchmarkFailed( StringRef ) override {}
|
|
|
|
void fatalErrorEncountered( StringRef /*error*/ ) override {}
|
|
void noMatchingTestCases( StringRef /*unmatchedSpec*/ ) override {}
|
|
void reportInvalidTestSpec( StringRef /*invalidArgument*/ ) override {}
|
|
|
|
void testRunStarting( TestRunInfo const& _testRunInfo ) override;
|
|
|
|
void testCaseStarting(TestCaseInfo const& _testInfo) override {
|
|
currentTestCaseInfo = &_testInfo;
|
|
}
|
|
void testCasePartialStarting( TestCaseInfo const&, uint64_t ) override {}
|
|
void sectionStarting(SectionInfo const& _sectionInfo) override {
|
|
m_sectionStack.push_back(_sectionInfo);
|
|
}
|
|
|
|
void assertionStarting( AssertionInfo const& ) override {}
|
|
void assertionEnded( AssertionStats const& ) override {}
|
|
|
|
void sectionEnded(SectionStats const& /* _sectionStats */) override {
|
|
m_sectionStack.pop_back();
|
|
}
|
|
void testCasePartialEnded( TestCaseStats const&, uint64_t ) override {}
|
|
void testCaseEnded(TestCaseStats const& /* _testCaseStats */) override {
|
|
currentTestCaseInfo = nullptr;
|
|
}
|
|
void testRunEnded( TestRunStats const& /* _testRunStats */ ) override;
|
|
|
|
void skipTest(TestCaseInfo const&) override {
|
|
// Don't do anything with this by default.
|
|
// It can optionally be overridden in the derived class.
|
|
}
|
|
|
|
protected:
|
|
TestRunInfo currentTestRunInfo{ "test run has not started yet"_sr };
|
|
TestCaseInfo const* currentTestCaseInfo = nullptr;
|
|
|
|
//! Stack of all _active_ sections in the _current_ test case
|
|
std::vector<SectionInfo> m_sectionStack;
|
|
};
|
|
|
|
} // end namespace Catch
|
|
|
|
#endif // CATCH_REPORTER_STREAMING_BASE_HPP_INCLUDED
|
|
|
|
#include <string>
|
|
|
|
namespace Catch {
|
|
|
|
class AutomakeReporter final : public StreamingReporterBase {
|
|
public:
|
|
// GCC5 compat: we cannot use inherited constructor, because it
|
|
// doesn't implement backport of P0136
|
|
AutomakeReporter(ReporterConfig&& _config):
|
|
StreamingReporterBase(CATCH_MOVE(_config))
|
|
{}
|
|
~AutomakeReporter() override;
|
|
|
|
static std::string getDescription() {
|
|
using namespace std::string_literals;
|
|
return "Reports test results in the format of Automake .trs files"s;
|
|
}
|
|
|
|
void testCaseEnded(TestCaseStats const& _testCaseStats) override;
|
|
void skipTest(TestCaseInfo const& testInfo) override;
|
|
};
|
|
|
|
} // end namespace Catch
|
|
|
|
#endif // CATCH_REPORTER_AUTOMAKE_HPP_INCLUDED
|
|
|
|
|
|
#ifndef CATCH_REPORTER_COMPACT_HPP_INCLUDED
|
|
#define CATCH_REPORTER_COMPACT_HPP_INCLUDED
|
|
|
|
|
|
|
|
|
|
namespace Catch {
|
|
|
|
class CompactReporter final : public StreamingReporterBase {
|
|
public:
|
|
using StreamingReporterBase::StreamingReporterBase;
|
|
|
|
~CompactReporter() override;
|
|
|
|
static std::string getDescription();
|
|
|
|
void noMatchingTestCases( StringRef unmatchedSpec ) override;
|
|
|
|
void testRunStarting( TestRunInfo const& _testInfo ) override;
|
|
|
|
void assertionEnded(AssertionStats const& _assertionStats) override;
|
|
|
|
void sectionEnded(SectionStats const& _sectionStats) override;
|
|
|
|
void testRunEnded(TestRunStats const& _testRunStats) override;
|
|
|
|
};
|
|
|
|
} // end namespace Catch
|
|
|
|
#endif // CATCH_REPORTER_COMPACT_HPP_INCLUDED
|
|
|
|
|
|
#ifndef CATCH_REPORTER_CONSOLE_HPP_INCLUDED
|
|
#define CATCH_REPORTER_CONSOLE_HPP_INCLUDED
|
|
|
|
|
|
namespace Catch {
|
|
// Fwd decls
|
|
class TablePrinter;
|
|
|
|
class ConsoleReporter final : public StreamingReporterBase {
|
|
Detail::unique_ptr<TablePrinter> m_tablePrinter;
|
|
|
|
public:
|
|
ConsoleReporter(ReporterConfig&& config);
|
|
~ConsoleReporter() override;
|
|
static std::string getDescription();
|
|
|
|
void noMatchingTestCases( StringRef unmatchedSpec ) override;
|
|
void reportInvalidTestSpec( StringRef arg ) override;
|
|
|
|
void assertionStarting(AssertionInfo const&) override;
|
|
|
|
void assertionEnded(AssertionStats const& _assertionStats) override;
|
|
|
|
void sectionStarting(SectionInfo const& _sectionInfo) override;
|
|
void sectionEnded(SectionStats const& _sectionStats) override;
|
|
|
|
void benchmarkPreparing( StringRef name ) override;
|
|
void benchmarkStarting(BenchmarkInfo const& info) override;
|
|
void benchmarkEnded(BenchmarkStats<> const& stats) override;
|
|
void benchmarkFailed( StringRef error ) override;
|
|
|
|
void testCaseEnded(TestCaseStats const& _testCaseStats) override;
|
|
void testRunEnded(TestRunStats const& _testRunStats) override;
|
|
void testRunStarting(TestRunInfo const& _testRunInfo) override;
|
|
|
|
private:
|
|
void lazyPrint();
|
|
|
|
void lazyPrintWithoutClosingBenchmarkTable();
|
|
void lazyPrintRunInfo();
|
|
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 printTotalsDivider(Totals const& totals);
|
|
|
|
bool m_headerPrinted = false;
|
|
bool m_testRunInfoPrinted = false;
|
|
};
|
|
|
|
} // end namespace Catch
|
|
|
|
#endif // CATCH_REPORTER_CONSOLE_HPP_INCLUDED
|
|
|
|
|
|
#ifndef CATCH_REPORTER_CUMULATIVE_BASE_HPP_INCLUDED
|
|
#define CATCH_REPORTER_CUMULATIVE_BASE_HPP_INCLUDED
|
|
|
|
|
|
#include <string>
|
|
#include <vector>
|
|
|
|
namespace Catch {
|
|
|
|
namespace Detail {
|
|
|
|
//! Represents either an assertion or a benchmark result to be handled by cumulative reporter later
|
|
class AssertionOrBenchmarkResult {
|
|
// This should really be a variant, but this is much faster
|
|
// to write and the data layout here is already terrible
|
|
// enough that we do not have to care about the object size.
|
|
Optional<AssertionStats> m_assertion;
|
|
Optional<BenchmarkStats<>> m_benchmark;
|
|
public:
|
|
AssertionOrBenchmarkResult(AssertionStats const& assertion);
|
|
AssertionOrBenchmarkResult(BenchmarkStats<> const& benchmark);
|
|
|
|
bool isAssertion() const;
|
|
bool isBenchmark() const;
|
|
|
|
AssertionStats const& asAssertion() const;
|
|
BenchmarkStats<> const& asBenchmark() const;
|
|
};
|
|
}
|
|
|
|
/**
|
|
* Utility base for reporters that need to handle all results at once
|
|
*
|
|
* It stores tree of all test cases, sections and assertions, and after the
|
|
* test run is finished, calls into `testRunEndedCumulative` to pass the
|
|
* control to the deriving class.
|
|
*
|
|
* If you are deriving from this class and override any testing related
|
|
* member functions, you should first call into the base's implementation to
|
|
* avoid breaking the tree construction.
|
|
*
|
|
* Due to the way this base functions, it has to expand assertions up-front,
|
|
* even if they are later unused (e.g. because the deriving reporter does
|
|
* not report successful assertions, or because the deriving reporter does
|
|
* not use assertion expansion at all). Derived classes can use two
|
|
* customization points, `m_shouldStoreSuccesfulAssertions` and
|
|
* `m_shouldStoreFailedAssertions`, to disable the expansion and gain extra
|
|
* performance. **Accessing the assertion expansions if it wasn't stored is
|
|
* UB.**
|
|
*/
|
|
class CumulativeReporterBase : public ReporterBase {
|
|
public:
|
|
template<typename T, typename ChildNodeT>
|
|
struct Node {
|
|
explicit Node( T const& _value ) : value( _value ) {}
|
|
|
|
using ChildNodes = std::vector<Detail::unique_ptr<ChildNodeT>>;
|
|
T value;
|
|
ChildNodes children;
|
|
};
|
|
struct SectionNode {
|
|
explicit SectionNode(SectionStats const& _stats) : stats(_stats) {}
|
|
|
|
bool operator == (SectionNode const& other) const {
|
|
return stats.sectionInfo.lineInfo == other.stats.sectionInfo.lineInfo;
|
|
}
|
|
|
|
bool hasAnyAssertions() const;
|
|
|
|
SectionStats stats;
|
|
std::vector<Detail::unique_ptr<SectionNode>> childSections;
|
|
std::vector<Detail::AssertionOrBenchmarkResult> assertionsAndBenchmarks;
|
|
std::string stdOut;
|
|
std::string stdErr;
|
|
};
|
|
|
|
|
|
using TestCaseNode = Node<TestCaseStats, SectionNode>;
|
|
using TestRunNode = Node<TestRunStats, TestCaseNode>;
|
|
|
|
// GCC5 compat: we cannot use inherited constructor, because it
|
|
// doesn't implement backport of P0136
|
|
CumulativeReporterBase(ReporterConfig&& _config):
|
|
ReporterBase(CATCH_MOVE(_config))
|
|
{}
|
|
~CumulativeReporterBase() override;
|
|
|
|
void benchmarkPreparing( StringRef ) override {}
|
|
void benchmarkStarting( BenchmarkInfo const& ) override {}
|
|
void benchmarkEnded( BenchmarkStats<> const& benchmarkStats ) override;
|
|
void benchmarkFailed( StringRef ) override {}
|
|
|
|
void noMatchingTestCases( StringRef ) override {}
|
|
void reportInvalidTestSpec( StringRef ) override {}
|
|
void fatalErrorEncountered( StringRef /*error*/ ) override {}
|
|
|
|
void testRunStarting( TestRunInfo const& ) override {}
|
|
|
|
void testCaseStarting( TestCaseInfo const& ) override {}
|
|
void testCasePartialStarting( TestCaseInfo const&, uint64_t ) override {}
|
|
void sectionStarting( SectionInfo const& sectionInfo ) override;
|
|
|
|
void assertionStarting( AssertionInfo const& ) override {}
|
|
|
|
void assertionEnded( AssertionStats const& assertionStats ) override;
|
|
void sectionEnded( SectionStats const& sectionStats ) override;
|
|
void testCasePartialEnded( TestCaseStats const&, uint64_t ) override {}
|
|
void testCaseEnded( TestCaseStats const& testCaseStats ) override;
|
|
void testRunEnded( TestRunStats const& testRunStats ) override;
|
|
//! Customization point: called after last test finishes (testRunEnded has been handled)
|
|
virtual void testRunEndedCumulative() = 0;
|
|
|
|
void skipTest(TestCaseInfo const&) override {}
|
|
|
|
protected:
|
|
//! Should the cumulative base store the assertion expansion for successful assertions?
|
|
bool m_shouldStoreSuccesfulAssertions = true;
|
|
//! Should the cumulative base store the assertion expansion for failed assertions?
|
|
bool m_shouldStoreFailedAssertions = true;
|
|
|
|
// We need lazy construction here. We should probably refactor it
|
|
// later, after the events are redone.
|
|
//! The root node of the test run tree.
|
|
Detail::unique_ptr<TestRunNode> m_testRun;
|
|
|
|
private:
|
|
// Note: We rely on pointer identity being stable, which is why
|
|
// we store pointers to the nodes rather than the values.
|
|
std::vector<Detail::unique_ptr<TestCaseNode>> m_testCases;
|
|
// Root section of the _current_ test case
|
|
Detail::unique_ptr<SectionNode> m_rootSection;
|
|
// Deepest section of the _current_ test case
|
|
SectionNode* m_deepestSection = nullptr;
|
|
// Stack of _active_ sections in the _current_ test case
|
|
std::vector<SectionNode*> m_sectionStack;
|
|
};
|
|
|
|
} // end namespace Catch
|
|
|
|
#endif // CATCH_REPORTER_CUMULATIVE_BASE_HPP_INCLUDED
|
|
|
|
|
|
#ifndef CATCH_REPORTER_EVENT_LISTENER_HPP_INCLUDED
|
|
#define CATCH_REPORTER_EVENT_LISTENER_HPP_INCLUDED
|
|
|
|
|
|
namespace Catch {
|
|
|
|
/**
|
|
* Base class to simplify implementing listeners.
|
|
*
|
|
* Provides empty default implementation for all IEventListener member
|
|
* functions, so that a listener implementation can pick which
|
|
* member functions it actually cares about.
|
|
*/
|
|
class EventListenerBase : public IEventListener {
|
|
public:
|
|
using IEventListener::IEventListener;
|
|
|
|
void reportInvalidTestSpec( StringRef unmatchedSpec ) override;
|
|
void fatalErrorEncountered( StringRef error ) override;
|
|
|
|
void benchmarkPreparing( StringRef name ) override;
|
|
void benchmarkStarting( BenchmarkInfo const& benchmarkInfo ) override;
|
|
void benchmarkEnded( BenchmarkStats<> const& benchmarkStats ) override;
|
|
void benchmarkFailed( StringRef error ) override;
|
|
|
|
void assertionStarting( AssertionInfo const& assertionInfo ) override;
|
|
void assertionEnded( AssertionStats const& assertionStats ) override;
|
|
|
|
void listReporters(
|
|
std::vector<ReporterDescription> const& descriptions ) override;
|
|
void listListeners(
|
|
std::vector<ListenerDescription> const& descriptions ) override;
|
|
void listTests( std::vector<TestCaseHandle> const& tests ) override;
|
|
void listTags( std::vector<TagInfo> const& tagInfos ) override;
|
|
|
|
void noMatchingTestCases( StringRef unmatchedSpec ) override;
|
|
void testRunStarting( TestRunInfo const& testRunInfo ) override;
|
|
void testCaseStarting( TestCaseInfo const& testInfo ) override;
|
|
void testCasePartialStarting( TestCaseInfo const& testInfo,
|
|
uint64_t partNumber ) override;
|
|
void sectionStarting( SectionInfo const& sectionInfo ) override;
|
|
void sectionEnded( SectionStats const& sectionStats ) override;
|
|
void testCasePartialEnded( TestCaseStats const& testCaseStats,
|
|
uint64_t partNumber ) override;
|
|
void testCaseEnded( TestCaseStats const& testCaseStats ) override;
|
|
void testRunEnded( TestRunStats const& testRunStats ) override;
|
|
void skipTest( TestCaseInfo const& testInfo ) override;
|
|
};
|
|
|
|
} // end namespace Catch
|
|
|
|
#endif // CATCH_REPORTER_EVENT_LISTENER_HPP_INCLUDED
|
|
|
|
|
|
#ifndef CATCH_REPORTER_HELPERS_HPP_INCLUDED
|
|
#define CATCH_REPORTER_HELPERS_HPP_INCLUDED
|
|
|
|
#include <iosfwd>
|
|
#include <string>
|
|
#include <vector>
|
|
|
|
|
|
namespace Catch {
|
|
|
|
class IConfig;
|
|
class TestCaseHandle;
|
|
class ColourImpl;
|
|
|
|
// Returns double formatted as %.3f (format expected on output)
|
|
std::string getFormattedDuration( double duration );
|
|
|
|
//! Should the reporter show duration of test given current configuration?
|
|
bool shouldShowDuration( IConfig const& config, double duration );
|
|
|
|
std::string serializeFilters( std::vector<std::string> const& filters );
|
|
|
|
struct lineOfChars {
|
|
char c;
|
|
constexpr lineOfChars( char c_ ): c( c_ ) {}
|
|
|
|
friend std::ostream& operator<<( std::ostream& out, lineOfChars value );
|
|
};
|
|
|
|
/**
|
|
* Lists reporter descriptions to the provided stream in user-friendly
|
|
* format
|
|
*
|
|
* Used as the default listing implementation by the first party reporter
|
|
* bases. The output should be backwards compatible with the output of
|
|
* Catch2 v2 binaries.
|
|
*/
|
|
void
|
|
defaultListReporters( std::ostream& out,
|
|
std::vector<ReporterDescription> const& descriptions,
|
|
Verbosity verbosity );
|
|
|
|
/**
|
|
* Lists listeners descriptions to the provided stream in user-friendly
|
|
* format
|
|
*/
|
|
void defaultListListeners( std::ostream& out,
|
|
std::vector<ListenerDescription> const& descriptions );
|
|
|
|
/**
|
|
* Lists tag information to the provided stream in user-friendly format
|
|
*
|
|
* Used as the default listing implementation by the first party reporter
|
|
* bases. The output should be backwards compatible with the output of
|
|
* Catch2 v2 binaries.
|
|
*/
|
|
void defaultListTags( std::ostream& out, std::vector<TagInfo> const& tags, bool isFiltered );
|
|
|
|
/**
|
|
* Lists test case information to the provided stream in user-friendly
|
|
* format
|
|
*
|
|
* Used as the default listing implementation by the first party reporter
|
|
* bases. The output is backwards compatible with the output of Catch2
|
|
* v2 binaries, and also supports the format specific to the old
|
|
* `--list-test-names-only` option, for people who used it in integrations.
|
|
*/
|
|
void defaultListTests( std::ostream& out,
|
|
ColourImpl* streamColour,
|
|
std::vector<TestCaseHandle> const& tests,
|
|
bool isFiltered,
|
|
Verbosity verbosity );
|
|
|
|
/**
|
|
* Prints test run totals to the provided stream in user-friendly format
|
|
*
|
|
* Used by the console and compact reporters.
|
|
*/
|
|
void printTestRunTotals( std::ostream& stream,
|
|
ColourImpl& streamColour,
|
|
Totals const& totals );
|
|
|
|
} // end namespace Catch
|
|
|
|
#endif // CATCH_REPORTER_HELPERS_HPP_INCLUDED
|
|
|
|
|
|
|
|
#ifndef CATCH_REPORTER_JSON_HPP_INCLUDED
|
|
#define CATCH_REPORTER_JSON_HPP_INCLUDED
|
|
|
|
|
|
#include <stack>
|
|
|
|
namespace Catch {
|
|
class JsonReporter : public StreamingReporterBase {
|
|
public:
|
|
JsonReporter( ReporterConfig&& config );
|
|
|
|
~JsonReporter() override;
|
|
|
|
static std::string getDescription();
|
|
|
|
public: // StreamingReporterBase
|
|
void testRunStarting( TestRunInfo const& runInfo ) override;
|
|
void testRunEnded( TestRunStats const& runStats ) override;
|
|
|
|
void testCaseStarting( TestCaseInfo const& tcInfo ) override;
|
|
void testCaseEnded( TestCaseStats const& tcStats ) override;
|
|
|
|
void testCasePartialStarting( TestCaseInfo const& tcInfo,
|
|
uint64_t index ) override;
|
|
void testCasePartialEnded( TestCaseStats const& tcStats,
|
|
uint64_t index ) override;
|
|
|
|
void sectionStarting( SectionInfo const& sectionInfo ) override;
|
|
void sectionEnded( SectionStats const& sectionStats ) override;
|
|
|
|
void assertionStarting( AssertionInfo const& assertionInfo ) override;
|
|
void assertionEnded( AssertionStats const& assertionStats ) override;
|
|
|
|
//void testRunEndedCumulative() override;
|
|
|
|
void benchmarkPreparing( StringRef name ) override;
|
|
void benchmarkStarting( BenchmarkInfo const& ) override;
|
|
void benchmarkEnded( BenchmarkStats<> const& ) override;
|
|
void benchmarkFailed( StringRef error ) override;
|
|
|
|
void listReporters(
|
|
std::vector<ReporterDescription> const& descriptions ) override;
|
|
void listListeners(
|
|
std::vector<ListenerDescription> const& descriptions ) override;
|
|
void listTests( std::vector<TestCaseHandle> const& tests ) override;
|
|
void listTags( std::vector<TagInfo> const& tags ) override;
|
|
|
|
private:
|
|
Timer m_testCaseTimer;
|
|
enum class Writer {
|
|
Object,
|
|
Array
|
|
};
|
|
|
|
JsonArrayWriter& startArray();
|
|
JsonArrayWriter& startArray( StringRef key );
|
|
|
|
JsonObjectWriter& startObject();
|
|
JsonObjectWriter& startObject( StringRef key );
|
|
|
|
void endObject();
|
|
void endArray();
|
|
|
|
bool isInside( Writer writer );
|
|
|
|
void startListing();
|
|
void endListing();
|
|
|
|
// Invariant:
|
|
// When m_writers is not empty and its top element is
|
|
// - Writer::Object, then m_objectWriters is not be empty
|
|
// - Writer::Array, then m_arrayWriters shall not be empty
|
|
std::stack<JsonObjectWriter> m_objectWriters{};
|
|
std::stack<JsonArrayWriter> m_arrayWriters{};
|
|
std::stack<Writer> m_writers{};
|
|
|
|
bool m_startedListing = false;
|
|
|
|
// std::size_t m_sectionDepth = 0;
|
|
// std::size_t m_sectionStarted = 0;
|
|
};
|
|
} // namespace Catch
|
|
|
|
#endif // CATCH_REPORTER_JSON_HPP_INCLUDED
|
|
|
|
|
|
#ifndef CATCH_REPORTER_JUNIT_HPP_INCLUDED
|
|
#define CATCH_REPORTER_JUNIT_HPP_INCLUDED
|
|
|
|
|
|
|
|
namespace Catch {
|
|
|
|
class JunitReporter final : public CumulativeReporterBase {
|
|
public:
|
|
JunitReporter(ReporterConfig&& _config);
|
|
|
|
static std::string getDescription();
|
|
|
|
void testRunStarting(TestRunInfo const& runInfo) override;
|
|
|
|
void testCaseStarting(TestCaseInfo const& testCaseInfo) override;
|
|
void assertionEnded(AssertionStats const& assertionStats) override;
|
|
|
|
void testCaseEnded(TestCaseStats const& testCaseStats) override;
|
|
|
|
void testRunEndedCumulative() override;
|
|
|
|
private:
|
|
void writeRun(TestRunNode const& testRunNode, double suiteTime);
|
|
|
|
void writeTestCase(TestCaseNode const& testCaseNode);
|
|
|
|
void writeSection( std::string const& className,
|
|
std::string const& rootName,
|
|
SectionNode const& sectionNode,
|
|
bool testOkToFail );
|
|
|
|
void writeAssertions(SectionNode const& sectionNode);
|
|
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
|
|
|
|
#endif // CATCH_REPORTER_JUNIT_HPP_INCLUDED
|
|
|
|
|
|
#ifndef CATCH_REPORTER_MULTI_HPP_INCLUDED
|
|
#define CATCH_REPORTER_MULTI_HPP_INCLUDED
|
|
|
|
|
|
namespace Catch {
|
|
|
|
class MultiReporter final : public IEventListener {
|
|
/*
|
|
* Stores all added reporters and listeners
|
|
*
|
|
* All Listeners are stored before all reporters, and individual
|
|
* listeners/reporters are stored in order of insertion.
|
|
*/
|
|
std::vector<IEventListenerPtr> m_reporterLikes;
|
|
bool m_haveNoncapturingReporters = false;
|
|
|
|
// Keep track of how many listeners we have already inserted,
|
|
// so that we can insert them into the main vector at the right place
|
|
size_t m_insertedListeners = 0;
|
|
|
|
void updatePreferences(IEventListener const& reporterish);
|
|
|
|
public:
|
|
using IEventListener::IEventListener;
|
|
|
|
void addListener( IEventListenerPtr&& listener );
|
|
void addReporter( IEventListenerPtr&& reporter );
|
|
|
|
public: // IEventListener
|
|
|
|
void noMatchingTestCases( StringRef unmatchedSpec ) override;
|
|
void fatalErrorEncountered( StringRef error ) override;
|
|
void reportInvalidTestSpec( StringRef arg ) override;
|
|
|
|
void benchmarkPreparing( StringRef name ) override;
|
|
void benchmarkStarting( BenchmarkInfo const& benchmarkInfo ) override;
|
|
void benchmarkEnded( BenchmarkStats<> const& benchmarkStats ) override;
|
|
void benchmarkFailed( StringRef error ) override;
|
|
|
|
void testRunStarting( TestRunInfo const& testRunInfo ) override;
|
|
void testCaseStarting( TestCaseInfo const& testInfo ) override;
|
|
void testCasePartialStarting(TestCaseInfo const& testInfo, uint64_t partNumber) override;
|
|
void sectionStarting( SectionInfo const& sectionInfo ) override;
|
|
void assertionStarting( AssertionInfo const& assertionInfo ) override;
|
|
|
|
void assertionEnded( AssertionStats const& assertionStats ) override;
|
|
void sectionEnded( SectionStats const& sectionStats ) override;
|
|
void testCasePartialEnded(TestCaseStats const& testInfo, uint64_t partNumber) override;
|
|
void testCaseEnded( TestCaseStats const& testCaseStats ) override;
|
|
void testRunEnded( TestRunStats const& testRunStats ) override;
|
|
|
|
void skipTest( TestCaseInfo const& testInfo ) override;
|
|
|
|
void listReporters(std::vector<ReporterDescription> const& descriptions) override;
|
|
void listListeners(std::vector<ListenerDescription> const& descriptions) override;
|
|
void listTests(std::vector<TestCaseHandle> const& tests) override;
|
|
void listTags(std::vector<TagInfo> const& tags) override;
|
|
|
|
|
|
};
|
|
|
|
} // end namespace Catch
|
|
|
|
#endif // CATCH_REPORTER_MULTI_HPP_INCLUDED
|
|
|
|
|
|
#ifndef CATCH_REPORTER_REGISTRARS_HPP_INCLUDED
|
|
#define CATCH_REPORTER_REGISTRARS_HPP_INCLUDED
|
|
|
|
|
|
#include <type_traits>
|
|
|
|
namespace Catch {
|
|
|
|
namespace Detail {
|
|
|
|
template <typename T, typename = void>
|
|
struct has_description : std::false_type {};
|
|
|
|
template <typename T>
|
|
struct has_description<
|
|
T,
|
|
void_t<decltype( T::getDescription() )>>
|
|
: std::true_type {};
|
|
|
|
//! Indirection for reporter registration, so that the error handling is
|
|
//! independent on the reporter's concrete type
|
|
void registerReporterImpl( std::string const& name,
|
|
IReporterFactoryPtr reporterPtr );
|
|
//! Actually registers the factory, independent on listener's concrete type
|
|
void registerListenerImpl( Detail::unique_ptr<EventListenerFactory> listenerFactory );
|
|
} // namespace Detail
|
|
|
|
class IEventListener;
|
|
using IEventListenerPtr = Detail::unique_ptr<IEventListener>;
|
|
|
|
template <typename T>
|
|
class ReporterFactory : public IReporterFactory {
|
|
|
|
IEventListenerPtr create( ReporterConfig&& config ) const override {
|
|
return Detail::make_unique<T>( CATCH_MOVE(config) );
|
|
}
|
|
|
|
std::string getDescription() const override {
|
|
return T::getDescription();
|
|
}
|
|
};
|
|
|
|
|
|
template<typename T>
|
|
class ReporterRegistrar {
|
|
public:
|
|
explicit ReporterRegistrar( std::string const& name ) {
|
|
registerReporterImpl( name,
|
|
Detail::make_unique<ReporterFactory<T>>() );
|
|
}
|
|
};
|
|
|
|
template<typename T>
|
|
class ListenerRegistrar {
|
|
|
|
class TypedListenerFactory : public EventListenerFactory {
|
|
StringRef m_listenerName;
|
|
|
|
std::string getDescriptionImpl( std::true_type ) const {
|
|
return T::getDescription();
|
|
}
|
|
|
|
std::string getDescriptionImpl( std::false_type ) const {
|
|
return "(No description provided)";
|
|
}
|
|
|
|
public:
|
|
TypedListenerFactory( StringRef listenerName ):
|
|
m_listenerName( listenerName ) {}
|
|
|
|
IEventListenerPtr create( IConfig const* config ) const override {
|
|
return Detail::make_unique<T>( config );
|
|
}
|
|
|
|
StringRef getName() const override {
|
|
return m_listenerName;
|
|
}
|
|
|
|
std::string getDescription() const override {
|
|
return getDescriptionImpl( Detail::has_description<T>{} );
|
|
}
|
|
};
|
|
|
|
public:
|
|
ListenerRegistrar(StringRef listenerName) {
|
|
registerListenerImpl( Detail::make_unique<TypedListenerFactory>(listenerName) );
|
|
}
|
|
};
|
|
}
|
|
|
|
#if !defined(CATCH_CONFIG_DISABLE)
|
|
|
|
# define CATCH_REGISTER_REPORTER( name, reporterType ) \
|
|
CATCH_INTERNAL_START_WARNINGS_SUPPRESSION \
|
|
CATCH_INTERNAL_SUPPRESS_GLOBALS_WARNINGS \
|
|
namespace { \
|
|
Catch::ReporterRegistrar<reporterType> INTERNAL_CATCH_UNIQUE_NAME( \
|
|
catch_internal_RegistrarFor )( name ); \
|
|
} \
|
|
CATCH_INTERNAL_STOP_WARNINGS_SUPPRESSION
|
|
|
|
# define CATCH_REGISTER_LISTENER( listenerType ) \
|
|
CATCH_INTERNAL_START_WARNINGS_SUPPRESSION \
|
|
CATCH_INTERNAL_SUPPRESS_GLOBALS_WARNINGS \
|
|
namespace { \
|
|
Catch::ListenerRegistrar<listenerType> INTERNAL_CATCH_UNIQUE_NAME( \
|
|
catch_internal_RegistrarFor )( #listenerType##_catch_sr ); \
|
|
} \
|
|
CATCH_INTERNAL_STOP_WARNINGS_SUPPRESSION
|
|
|
|
#else // CATCH_CONFIG_DISABLE
|
|
|
|
#define CATCH_REGISTER_REPORTER(name, reporterType)
|
|
#define CATCH_REGISTER_LISTENER(listenerType)
|
|
|
|
#endif // CATCH_CONFIG_DISABLE
|
|
|
|
#endif // CATCH_REPORTER_REGISTRARS_HPP_INCLUDED
|
|
|
|
|
|
#ifndef CATCH_REPORTER_SONARQUBE_HPP_INCLUDED
|
|
#define CATCH_REPORTER_SONARQUBE_HPP_INCLUDED
|
|
|
|
|
|
|
|
namespace Catch {
|
|
|
|
class SonarQubeReporter final : public CumulativeReporterBase {
|
|
public:
|
|
SonarQubeReporter(ReporterConfig&& config)
|
|
: CumulativeReporterBase(CATCH_MOVE(config))
|
|
, xml(m_stream) {
|
|
m_preferences.shouldRedirectStdOut = true;
|
|
m_preferences.shouldReportAllAssertions = true;
|
|
m_shouldStoreSuccesfulAssertions = false;
|
|
}
|
|
|
|
static std::string getDescription() {
|
|
using namespace std::string_literals;
|
|
return "Reports test results in the Generic Test Data SonarQube XML format"s;
|
|
}
|
|
|
|
void testRunStarting( TestRunInfo const& testRunInfo ) override;
|
|
|
|
void testRunEndedCumulative() override {
|
|
writeRun( *m_testRun );
|
|
xml.endElement();
|
|
}
|
|
|
|
void writeRun( TestRunNode const& groupNode );
|
|
|
|
void writeTestFile(StringRef filename, std::vector<TestCaseNode const*> const& testCaseNodes);
|
|
|
|
void writeTestCase(TestCaseNode const& testCaseNode);
|
|
|
|
void writeSection(std::string const& rootName, SectionNode const& sectionNode, bool okToFail);
|
|
|
|
void writeAssertions(SectionNode const& sectionNode, bool okToFail);
|
|
|
|
void writeAssertion(AssertionStats const& stats, bool okToFail);
|
|
|
|
private:
|
|
XmlWriter xml;
|
|
};
|
|
|
|
|
|
} // end namespace Catch
|
|
|
|
#endif // CATCH_REPORTER_SONARQUBE_HPP_INCLUDED
|
|
|
|
|
|
#ifndef CATCH_REPORTER_TAP_HPP_INCLUDED
|
|
#define CATCH_REPORTER_TAP_HPP_INCLUDED
|
|
|
|
|
|
namespace Catch {
|
|
|
|
class TAPReporter final : public StreamingReporterBase {
|
|
public:
|
|
TAPReporter( ReporterConfig&& config ):
|
|
StreamingReporterBase( CATCH_MOVE(config) ) {
|
|
m_preferences.shouldReportAllAssertions = true;
|
|
}
|
|
|
|
static std::string getDescription() {
|
|
using namespace std::string_literals;
|
|
return "Reports test results in TAP format, suitable for test harnesses"s;
|
|
}
|
|
|
|
void testRunStarting( TestRunInfo const& testInfo ) override;
|
|
|
|
void noMatchingTestCases( StringRef unmatchedSpec ) override;
|
|
|
|
void assertionEnded(AssertionStats const& _assertionStats) override;
|
|
|
|
void testRunEnded(TestRunStats const& _testRunStats) override;
|
|
|
|
private:
|
|
std::size_t counter = 0;
|
|
};
|
|
|
|
} // end namespace Catch
|
|
|
|
#endif // CATCH_REPORTER_TAP_HPP_INCLUDED
|
|
|
|
|
|
#ifndef CATCH_REPORTER_TEAMCITY_HPP_INCLUDED
|
|
#define CATCH_REPORTER_TEAMCITY_HPP_INCLUDED
|
|
|
|
|
|
#include <cstring>
|
|
|
|
#ifdef __clang__
|
|
# pragma clang diagnostic push
|
|
# pragma clang diagnostic ignored "-Wpadded"
|
|
#endif
|
|
|
|
namespace Catch {
|
|
|
|
class TeamCityReporter final : public StreamingReporterBase {
|
|
public:
|
|
TeamCityReporter( ReporterConfig&& _config )
|
|
: StreamingReporterBase( CATCH_MOVE(_config) )
|
|
{
|
|
m_preferences.shouldRedirectStdOut = true;
|
|
}
|
|
|
|
~TeamCityReporter() override;
|
|
|
|
static std::string getDescription() {
|
|
using namespace std::string_literals;
|
|
return "Reports test results as TeamCity service messages"s;
|
|
}
|
|
|
|
void testRunStarting( TestRunInfo const& groupInfo ) override;
|
|
void testRunEnded( TestRunStats const& testGroupStats ) override;
|
|
|
|
|
|
void assertionEnded(AssertionStats const& assertionStats) override;
|
|
|
|
void sectionStarting(SectionInfo const& sectionInfo) override {
|
|
m_headerPrintedForThisSection = false;
|
|
StreamingReporterBase::sectionStarting( sectionInfo );
|
|
}
|
|
|
|
void testCaseStarting(TestCaseInfo const& testInfo) override;
|
|
|
|
void testCaseEnded(TestCaseStats const& testCaseStats) override;
|
|
|
|
private:
|
|
void printSectionHeader(std::ostream& os);
|
|
|
|
bool m_headerPrintedForThisSection = false;
|
|
Timer m_testTimer;
|
|
};
|
|
|
|
} // end namespace Catch
|
|
|
|
#ifdef __clang__
|
|
# pragma clang diagnostic pop
|
|
#endif
|
|
|
|
#endif // CATCH_REPORTER_TEAMCITY_HPP_INCLUDED
|
|
|
|
|
|
#ifndef CATCH_REPORTER_XML_HPP_INCLUDED
|
|
#define CATCH_REPORTER_XML_HPP_INCLUDED
|
|
|
|
|
|
|
|
|
|
namespace Catch {
|
|
class XmlReporter : public StreamingReporterBase {
|
|
public:
|
|
XmlReporter(ReporterConfig&& _config);
|
|
|
|
~XmlReporter() override;
|
|
|
|
static std::string getDescription();
|
|
|
|
virtual std::string getStylesheetRef() const;
|
|
|
|
void writeSourceInfo(SourceLineInfo const& sourceInfo);
|
|
|
|
public: // StreamingReporterBase
|
|
|
|
void testRunStarting(TestRunInfo const& testInfo) override;
|
|
|
|
void testCaseStarting(TestCaseInfo const& testInfo) override;
|
|
|
|
void sectionStarting(SectionInfo const& sectionInfo) override;
|
|
|
|
void assertionStarting(AssertionInfo const&) override;
|
|
|
|
void assertionEnded(AssertionStats const& assertionStats) override;
|
|
|
|
void sectionEnded(SectionStats const& sectionStats) override;
|
|
|
|
void testCaseEnded(TestCaseStats const& testCaseStats) override;
|
|
|
|
void testRunEnded(TestRunStats const& testRunStats) override;
|
|
|
|
void benchmarkPreparing( StringRef name ) override;
|
|
void benchmarkStarting(BenchmarkInfo const&) override;
|
|
void benchmarkEnded(BenchmarkStats<> const&) override;
|
|
void benchmarkFailed( StringRef error ) override;
|
|
|
|
void listReporters(std::vector<ReporterDescription> const& descriptions) override;
|
|
void listListeners(std::vector<ListenerDescription> const& descriptions) override;
|
|
void listTests(std::vector<TestCaseHandle> const& tests) override;
|
|
void listTags(std::vector<TagInfo> const& tags) override;
|
|
|
|
private:
|
|
Timer m_testCaseTimer;
|
|
XmlWriter m_xml;
|
|
int m_sectionDepth = 0;
|
|
};
|
|
|
|
} // end namespace Catch
|
|
|
|
#endif // CATCH_REPORTER_XML_HPP_INCLUDED
|
|
|
|
#endif // CATCH_REPORTERS_ALL_HPP_INCLUDED
|
|
|
|
#endif // CATCH_ALL_HPP_INCLUDED
|
|
#endif // CATCH_AMALGAMATED_HPP_INCLUDED
|