diff options
author | 2019-10-13 13:50:38 +0200 | |
---|---|---|
committer | 2019-10-13 07:50:38 -0400 | |
commit | 0837e7451a84f95c29dbdb9bd6b8b931fee1635d (patch) | |
tree | 626bcbd250a7fbebdf5958a288d2f438d4f9fb5a /3rdparty/bx | |
parent | c913ccb59d713ce0b91135520719ac5385e54358 (diff) |
WIP: sync bgfx, bx and bimg with latest upstream (#5723)
* Sync with bgfx upstream revision b91d0b6
* Sync with bx upstream revision d60912b
* Sync with bimg upstream revision bd81f60
* Add astc-codec decoder
* Rename VertexDecl to VertexLayout
* Rename UniformType enum Int1 to Sampler.
* Add NVN stub
* Fix unused-const-variable error on macOS
* Drop redundant explicit language parameters
buildoptions_cpp are only applied to c++ files and buildoptions_objcpp are only
applied to objective c++ files. As such, hardcoding -x offers no benefit while
preventing overrides (such as one needed by 3rdparty/bgfx/src/renderer_vk.cpp on
macOS) from working.
* Re-introduce -x c++ in places where C code is compiled as C++ to prevent clang from throwing a warning
* Build bgfx as Objective-C++ on macOS
It is needed due to included headers
* Enable Direct3D12 and Vulkan bgfx rendering backends
* Enable building of spirv shaders
* Properly escape /c in cmd call
* Comment out dx12 bgfx renderer
* Honor VERBOSE setting during shaders build
* Only invert hlsl shader XYZ_TO_sRGB matrix for opengl
* Add spirv shaders
* OpenGL ES needs transposed matrix too
* Metal needs transposed matrix as well
Diffstat (limited to '3rdparty/bx')
138 files changed, 16771 insertions, 9847 deletions
diff --git a/3rdparty/bx/.editorconfig b/3rdparty/bx/.editorconfig index e9070240b79..3a55382bae2 100644 --- a/3rdparty/bx/.editorconfig +++ b/3rdparty/bx/.editorconfig @@ -1,9 +1,10 @@ root = true [*] -indent_style = tab -indent_size = 4 -end_of_line = lf +charset = utf-8 +indent_style = tab +indent_size = 4 +end_of_line = lf max_line_length = 100 insert_final_newline = true trim_trailing_whitespace = true diff --git a/3rdparty/bx/.travis.yml b/3rdparty/bx/.travis.yml index 5f5538002bb..54bf844abf2 100644 --- a/3rdparty/bx/.travis.yml +++ b/3rdparty/bx/.travis.yml @@ -11,12 +11,12 @@ addons: sources: - ubuntu-toolchain-r-test packages: - - gcc-5 - - g++-5 + - gcc-8 + - g++-8 - clang script: - - if [ "$TRAVIS_OS_NAME" == "linux" ]; then make test CXX="g++-5" CC="gcc-5"; fi + - if [ "$TRAVIS_OS_NAME" == "linux" ]; then make test CXX="g++-8" CC="gcc-8"; fi - if [ "$TRAVIS_OS_NAME" == "osx" ]; then make test; fi branches: diff --git a/3rdparty/bx/3rdparty/catch/catch.hpp b/3rdparty/bx/3rdparty/catch/catch.hpp index 2a09fd193a0..9b14505916c 100644 --- a/3rdparty/bx/3rdparty/catch/catch.hpp +++ b/3rdparty/bx/3rdparty/catch/catch.hpp @@ -1,17 +1,21 @@ /* - * Catch v1.7.2 - * Generated: 2017-02-13 15:57:33.350226 + * Catch v2.9.0 + * Generated: 2019-06-16 10:41:32.122746 * ---------------------------------------------------------- * This file has been merged from multiple headers. Please don't edit it directly - * Copyright (c) 2012 Two Blue Cubes Ltd. All rights reserved. + * Copyright (c) 2019 Two Blue Cubes Ltd. All rights reserved. * * Distributed under the Boost Software License, Version 1.0. (See accompanying * file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) */ #ifndef TWOBLUECUBES_SINGLE_INCLUDE_CATCH_HPP_INCLUDED #define TWOBLUECUBES_SINGLE_INCLUDE_CATCH_HPP_INCLUDED +// start catch.hpp -#define TWOBLUECUBES_CATCH_HPP_INCLUDED + +#define CATCH_VERSION_MAJOR 2 +#define CATCH_VERSION_MINOR 9 +#define CATCH_VERSION_PATCH 0 #ifdef __clang__ # pragma clang system_header @@ -19,34 +23,66 @@ # pragma GCC system_header #endif -// #included from: internal/catch_suppress_warnings.h +// start catch_suppress_warnings.h #ifdef __clang__ # ifdef __ICC // icpc defines the __clang__ macro # pragma warning(push) # pragma warning(disable: 161 1682) # else // __ICC -# pragma clang diagnostic ignored "-Wglobal-constructors" -# pragma clang diagnostic ignored "-Wvariadic-macros" -# pragma clang diagnostic ignored "-Wc99-extensions" -# pragma clang diagnostic ignored "-Wunused-variable" # pragma clang diagnostic push # pragma clang diagnostic ignored "-Wpadded" -# pragma clang diagnostic ignored "-Wc++98-compat" -# pragma clang diagnostic ignored "-Wc++98-compat-pedantic" # pragma clang diagnostic ignored "-Wswitch-enum" # pragma clang diagnostic ignored "-Wcovered-switch-default" # endif #elif defined __GNUC__ -# pragma GCC diagnostic ignored "-Wvariadic-macros" -# pragma GCC diagnostic ignored "-Wunused-variable" + // Because REQUIREs trigger GCC's -Wparentheses, and because still + // supported version of g++ have only buggy support for _Pragmas, + // Wparentheses have to be suppressed globally. +# pragma GCC diagnostic ignored "-Wparentheses" // See #674 for details + # pragma GCC diagnostic push +# pragma GCC diagnostic ignored "-Wunused-variable" # pragma GCC diagnostic ignored "-Wpadded" #endif +// end catch_suppress_warnings.h #if defined(CATCH_CONFIG_MAIN) || defined(CATCH_CONFIG_RUNNER) # define CATCH_IMPL +# define CATCH_CONFIG_ALL_PARTS +#endif + +// In the impl file, we want to have access to all parts of the headers +// Can also be used to sanely support PCHs +#if defined(CATCH_CONFIG_ALL_PARTS) +# define CATCH_CONFIG_EXTERNAL_INTERFACES +# if defined(CATCH_CONFIG_DISABLE_MATCHERS) +# undef CATCH_CONFIG_DISABLE_MATCHERS +# endif +# if !defined(CATCH_CONFIG_ENABLE_CHRONO_STRINGMAKER) +# define CATCH_CONFIG_ENABLE_CHRONO_STRINGMAKER +# endif +#endif + +#if !defined(CATCH_CONFIG_IMPL_ONLY) +// start catch_platform.h + +#ifdef __APPLE__ +# include <TargetConditionals.h> +# if TARGET_OS_OSX == 1 +# define CATCH_PLATFORM_MAC +# elif TARGET_OS_IPHONE == 1 +# define CATCH_PLATFORM_IPHONE +# endif + +#elif defined(linux) || defined(__linux) || defined(__linux__) +# define CATCH_PLATFORM_LINUX + +#elif defined(WIN32) || defined(__WIN32__) || defined(_WIN32) || defined(_MSC_VER) || defined(__MINGW32__) +# define CATCH_PLATFORM_WINDOWS #endif +// end catch_platform.h + #ifdef CATCH_IMPL # ifndef CLARA_CONFIG_MAIN # define CLARA_CONFIG_MAIN_NOT_DEFINED @@ -54,277 +90,314 @@ # endif #endif -// #included from: internal/catch_notimplemented_exception.h -#define TWOBLUECUBES_CATCH_NOTIMPLEMENTED_EXCEPTION_H_INCLUDED +// start catch_user_interfaces.h + +namespace Catch { + unsigned int rngSeed(); +} + +// end catch_user_interfaces.h +// start catch_tag_alias_autoregistrar.h -// #included from: catch_common.h -#define TWOBLUECUBES_CATCH_COMMON_H_INCLUDED +// start catch_common.h -// #included from: catch_compiler_capabilities.h -#define TWOBLUECUBES_CATCH_COMPILER_CAPABILITIES_HPP_INCLUDED +// start catch_compiler_capabilities.h -// Detect a number of compiler features - mostly C++11/14 conformance - by compiler +// Detect a number of compiler features - by compiler // The following features are defined: // -// CATCH_CONFIG_CPP11_NULLPTR : is nullptr supported? -// CATCH_CONFIG_CPP11_NOEXCEPT : is noexcept supported? -// CATCH_CONFIG_CPP11_GENERATED_METHODS : The delete and default keywords for compiler generated methods -// CATCH_CONFIG_CPP11_IS_ENUM : std::is_enum is supported? -// CATCH_CONFIG_CPP11_TUPLE : std::tuple is supported -// CATCH_CONFIG_CPP11_LONG_LONG : is long long supported? -// CATCH_CONFIG_CPP11_OVERRIDE : is override supported? -// CATCH_CONFIG_CPP11_UNIQUE_PTR : is unique_ptr supported (otherwise use auto_ptr) -// CATCH_CONFIG_CPP11_SHUFFLE : is std::shuffle supported? -// CATCH_CONFIG_CPP11_TYPE_TRAITS : are type_traits and enable_if supported? - -// CATCH_CONFIG_CPP11_OR_GREATER : Is C++11 supported? - -// CATCH_CONFIG_VARIADIC_MACROS : are variadic macros supported? // CATCH_CONFIG_COUNTER : is the __COUNTER__ macro supported? // CATCH_CONFIG_WINDOWS_SEH : is Windows SEH supported? +// CATCH_CONFIG_POSIX_SIGNALS : are POSIX signals supported? +// CATCH_CONFIG_DISABLE_EXCEPTIONS : Are exceptions enabled? // **************** // Note to maintainers: if new toggles are added please document them // in configuration.md, too // **************** // In general each macro has a _NO_<feature name> form -// (e.g. CATCH_CONFIG_CPP11_NO_NULLPTR) which disables the feature. +// (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. -// All the C++11 features can be disabled with CATCH_CONFIG_NO_CPP11 - #ifdef __cplusplus -# if __cplusplus >= 201103L -# define CATCH_CPP11_OR_GREATER +# if (__cplusplus >= 201402L) || (defined(_MSVC_LANG) && _MSVC_LANG >= 201402L) +# define CATCH_CPP14_OR_GREATER # endif -# if __cplusplus >= 201402L -# define CATCH_CPP14_OR_GREATER +# if (__cplusplus >= 201703L) || (defined(_MSVC_LANG) && _MSVC_LANG >= 201703L) +# define CATCH_CPP17_OR_GREATER # endif #endif -#ifdef __clang__ - -# if __has_feature(cxx_nullptr) -# define CATCH_INTERNAL_CONFIG_CPP11_NULLPTR -# endif +#if defined(CATCH_CPP17_OR_GREATER) +# define CATCH_INTERNAL_CONFIG_CPP17_UNCAUGHT_EXCEPTIONS +#endif -# if __has_feature(cxx_noexcept) -# define CATCH_INTERNAL_CONFIG_CPP11_NOEXCEPT -# endif +#ifdef __clang__ -# if defined(CATCH_CPP11_OR_GREATER) -# define CATCH_INTERNAL_SUPPRESS_PARENTHESES_WARNINGS _Pragma( "clang diagnostic ignored \"-Wparentheses\"" ) -# endif +# define CATCH_INTERNAL_SUPPRESS_GLOBALS_WARNINGS \ + _Pragma( "clang diagnostic push" ) \ + _Pragma( "clang diagnostic ignored \"-Wexit-time-destructors\"" ) \ + _Pragma( "clang diagnostic ignored \"-Wglobal-constructors\"") +# define CATCH_INTERNAL_UNSUPPRESS_GLOBALS_WARNINGS \ + _Pragma( "clang diagnostic pop" ) + +# define CATCH_INTERNAL_SUPPRESS_PARENTHESES_WARNINGS \ + _Pragma( "clang diagnostic push" ) \ + _Pragma( "clang diagnostic ignored \"-Wparentheses\"" ) +# define CATCH_INTERNAL_UNSUPPRESS_PARENTHESES_WARNINGS \ + _Pragma( "clang diagnostic pop" ) + +# define CATCH_INTERNAL_SUPPRESS_UNUSED_WARNINGS \ + _Pragma( "clang diagnostic push" ) \ + _Pragma( "clang diagnostic ignored \"-Wunused-variable\"" ) +# define CATCH_INTERNAL_UNSUPPRESS_UNUSED_WARNINGS \ + _Pragma( "clang diagnostic pop" ) + +# define CATCH_INTERNAL_SUPPRESS_ZERO_VARIADIC_WARNINGS \ + _Pragma( "clang diagnostic push" ) \ + _Pragma( "clang diagnostic ignored \"-Wgnu-zero-variadic-macro-arguments\"" ) +# define CATCH_INTERNAL_UNSUPPRESS_ZERO_VARIADIC_WARNINGS \ + _Pragma( "clang diagnostic pop" ) #endif // __clang__ //////////////////////////////////////////////////////////////////////////////// -// Borland -#ifdef __BORLANDC__ - -#endif // __BORLANDC__ +// Assume that non-Windows platforms support posix signals by default +#if !defined(CATCH_PLATFORM_WINDOWS) + #define CATCH_INTERNAL_CONFIG_POSIX_SIGNALS +#endif //////////////////////////////////////////////////////////////////////////////// -// EDG -#ifdef __EDG_VERSION__ +// We know some environments not to support full POSIX signals +#if defined(__CYGWIN__) || defined(__QNX__) || defined(__EMSCRIPTEN__) || defined(__DJGPP__) + #define CATCH_INTERNAL_CONFIG_NO_POSIX_SIGNALS +#endif -#endif // __EDG_VERSION__ +#ifdef __OS400__ +# define CATCH_INTERNAL_CONFIG_NO_POSIX_SIGNALS +# define CATCH_CONFIG_COLOUR_NONE +#endif //////////////////////////////////////////////////////////////////////////////// -// Digital Mars -#ifdef __DMC__ +// Android somehow still does not support std::to_string +#if defined(__ANDROID__) +# define CATCH_INTERNAL_CONFIG_NO_CPP11_TO_STRING +#endif -#endif // __DMC__ +//////////////////////////////////////////////////////////////////////////////// +// Not all Windows environments support SEH properly +#if defined(__MINGW32__) +# define CATCH_INTERNAL_CONFIG_NO_WINDOWS_SEH +#endif //////////////////////////////////////////////////////////////////////////////// -// GCC -#ifdef __GNUC__ +// PS4 +#if defined(__ORBIS__) +# define CATCH_INTERNAL_CONFIG_NO_NEW_CAPTURE +#endif -# if __GNUC__ == 4 && __GNUC_MINOR__ >= 6 && defined(__GXX_EXPERIMENTAL_CXX0X__) -# define CATCH_INTERNAL_CONFIG_CPP11_NULLPTR -# endif +//////////////////////////////////////////////////////////////////////////////// +// Cygwin +#ifdef __CYGWIN__ -# if !defined(CATCH_INTERNAL_SUPPRESS_PARENTHESES_WARNINGS) && defined(CATCH_CPP11_OR_GREATER) -# define CATCH_INTERNAL_SUPPRESS_PARENTHESES_WARNINGS _Pragma( "GCC diagnostic ignored \"-Wparentheses\"" ) -# endif +// 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)) -// - otherwise more recent versions define __cplusplus >= 201103L -// and will get picked up below +# define CATCH_INTERNAL_CONFIG_NO_CPP11_TO_STRING -#endif // __GNUC__ +# endif +#endif // __CYGWIN__ //////////////////////////////////////////////////////////////////////////////// // Visual C++ #ifdef _MSC_VER -#define CATCH_INTERNAL_CONFIG_WINDOWS_SEH +# if _MSC_VER >= 1900 // Visual Studio 2015 or newer +# define CATCH_INTERNAL_CONFIG_CPP17_UNCAUGHT_EXCEPTIONS +# endif -#if (_MSC_VER >= 1600) -# define CATCH_INTERNAL_CONFIG_CPP11_NULLPTR -# define CATCH_INTERNAL_CONFIG_CPP11_UNIQUE_PTR -#endif +// Universal Windows platform does not support SEH +// Or console colours (or console at all...) +# if defined(WINAPI_FAMILY) && (WINAPI_FAMILY == WINAPI_FAMILY_APP) +# define CATCH_CONFIG_COLOUR_NONE +# else +# define CATCH_INTERNAL_CONFIG_WINDOWS_SEH +# endif -#if (_MSC_VER >= 1900 ) // (VC++ 13 (VS2015)) -#define CATCH_INTERNAL_CONFIG_CPP11_NOEXCEPT -#define CATCH_INTERNAL_CONFIG_CPP11_GENERATED_METHODS -#define CATCH_INTERNAL_CONFIG_CPP11_SHUFFLE -#define CATCH_INTERNAL_CONFIG_CPP11_TYPE_TRAITS -#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(_MSVC_TRADITIONAL) || (defined(_MSVC_TRADITIONAL) && _MSVC_TRADITIONAL) +# define CATCH_INTERNAL_CONFIG_TRADITIONAL_MSVC_PREPROCESSOR +# endif +#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 //////////////////////////////////////////////////////////////////////////////// - -// Use variadic macros if the compiler supports them -#if ( defined _MSC_VER && _MSC_VER > 1400 && !defined __EDGE__) || \ - ( defined __WAVE__ && __WAVE_HAS_VARIADICS ) || \ - ( defined __GNUC__ && __GNUC__ >= 3 ) || \ - ( !defined __cplusplus && __STDC_VERSION__ >= 199901L || __cplusplus >= 201103L ) - -#define CATCH_INTERNAL_CONFIG_VARIADIC_MACROS - +// 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 -// Use __COUNTER__ if the compiler supports it -#if ( defined _MSC_VER && _MSC_VER >= 1300 ) || \ - ( defined __GNUC__ && __GNUC__ >= 4 && __GNUC_MINOR__ >= 3 ) || \ - ( defined __clang__ && __clang_major__ >= 3 ) - -#define CATCH_INTERNAL_CONFIG_COUNTER +//////////////////////////////////////////////////////////////////////////////// +// DJGPP +#ifdef __DJGPP__ +# define CATCH_INTERNAL_CONFIG_NO_WCHAR +#endif // __DJGPP__ +//////////////////////////////////////////////////////////////////////////////// +// Embarcadero C++Build +#if defined(__BORLANDC__) + #define CATCH_INTERNAL_CONFIG_POLYFILL_ISNAN #endif //////////////////////////////////////////////////////////////////////////////// -// C++ language feature support - -// catch all support for C++11 -#if defined(CATCH_CPP11_OR_GREATER) - -# if !defined(CATCH_INTERNAL_CONFIG_CPP11_NULLPTR) -# define CATCH_INTERNAL_CONFIG_CPP11_NULLPTR -# endif - -# ifndef CATCH_INTERNAL_CONFIG_CPP11_NOEXCEPT -# define CATCH_INTERNAL_CONFIG_CPP11_NOEXCEPT -# endif - -# ifndef CATCH_INTERNAL_CONFIG_CPP11_GENERATED_METHODS -# define CATCH_INTERNAL_CONFIG_CPP11_GENERATED_METHODS -# endif - -# ifndef CATCH_INTERNAL_CONFIG_CPP11_IS_ENUM -# define CATCH_INTERNAL_CONFIG_CPP11_IS_ENUM -# endif - -# ifndef CATCH_INTERNAL_CONFIG_CPP11_TUPLE -# define CATCH_INTERNAL_CONFIG_CPP11_TUPLE -# endif -# ifndef CATCH_INTERNAL_CONFIG_VARIADIC_MACROS -# define CATCH_INTERNAL_CONFIG_VARIADIC_MACROS -# endif +// Use of __COUNTER__ is suppressed during code analysis in +// CLion/AppCode 2017.2.x and former, because __COUNTER__ is not properly +// handled by it. +// Otherwise all supported compilers support COUNTER macro, +// but user still might want to turn it off +#if ( !defined(__JETBRAINS_IDE__) || __JETBRAINS_IDE__ >= 20170300L ) + #define CATCH_INTERNAL_CONFIG_COUNTER +#endif -# if !defined(CATCH_INTERNAL_CONFIG_CPP11_LONG_LONG) -# define CATCH_INTERNAL_CONFIG_CPP11_LONG_LONG -# endif +//////////////////////////////////////////////////////////////////////////////// +// Check if string_view is available and usable +// The check is split apart to work around v140 (VS2015) preprocessor issue... +#if defined(__has_include) +#if __has_include(<string_view>) && defined(CATCH_CPP17_OR_GREATER) +# define CATCH_INTERNAL_CONFIG_CPP17_STRING_VIEW +#endif +#endif -# if !defined(CATCH_INTERNAL_CONFIG_CPP11_OVERRIDE) -# define CATCH_INTERNAL_CONFIG_CPP11_OVERRIDE -# endif -# if !defined(CATCH_INTERNAL_CONFIG_CPP11_UNIQUE_PTR) -# define CATCH_INTERNAL_CONFIG_CPP11_UNIQUE_PTR -# endif -# if !defined(CATCH_INTERNAL_CONFIG_CPP11_SHUFFLE) -# define CATCH_INTERNAL_CONFIG_CPP11_SHUFFLE -# endif -# if !defined(CATCH_INTERNAL_CONFIG_CPP11_TYPE_TRAITS) -# define CATCH_INTERNAL_CONFIG_CPP11_TYPE_TRAITS -# endif +//////////////////////////////////////////////////////////////////////////////// +// Check if optional is available and usable +#if defined(__has_include) +# if __has_include(<optional>) && defined(CATCH_CPP17_OR_GREATER) +# define CATCH_INTERNAL_CONFIG_CPP17_OPTIONAL +# endif // __has_include(<optional>) && defined(CATCH_CPP17_OR_GREATER) +#endif // __has_include -#endif // __cplusplus >= 201103L +//////////////////////////////////////////////////////////////////////////////// +// Check if variant is available and usable +#if defined(__has_include) +# 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 // __has_include -// Now set the actual defines based on the above + anything the user has configured -#if defined(CATCH_INTERNAL_CONFIG_CPP11_NULLPTR) && !defined(CATCH_CONFIG_CPP11_NO_NULLPTR) && !defined(CATCH_CONFIG_CPP11_NULLPTR) && !defined(CATCH_CONFIG_NO_CPP11) -# define CATCH_CONFIG_CPP11_NULLPTR +#if defined(CATCH_INTERNAL_CONFIG_COUNTER) && !defined(CATCH_CONFIG_NO_COUNTER) && !defined(CATCH_CONFIG_COUNTER) +# define CATCH_CONFIG_COUNTER #endif -#if defined(CATCH_INTERNAL_CONFIG_CPP11_NOEXCEPT) && !defined(CATCH_CONFIG_CPP11_NO_NOEXCEPT) && !defined(CATCH_CONFIG_CPP11_NOEXCEPT) && !defined(CATCH_CONFIG_NO_CPP11) -# define CATCH_CONFIG_CPP11_NOEXCEPT +#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 -#if defined(CATCH_INTERNAL_CONFIG_CPP11_GENERATED_METHODS) && !defined(CATCH_CONFIG_CPP11_NO_GENERATED_METHODS) && !defined(CATCH_CONFIG_CPP11_GENERATED_METHODS) && !defined(CATCH_CONFIG_NO_CPP11) -# define CATCH_CONFIG_CPP11_GENERATED_METHODS +// 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_CPP11_IS_ENUM) && !defined(CATCH_CONFIG_CPP11_NO_IS_ENUM) && !defined(CATCH_CONFIG_CPP11_IS_ENUM) && !defined(CATCH_CONFIG_NO_CPP11) -# define CATCH_CONFIG_CPP11_IS_ENUM +// This is set by default, because we assume that compilers with no wchar_t support are just rare exceptions. +#if !defined(CATCH_INTERNAL_CONFIG_NO_WCHAR) && !defined(CATCH_CONFIG_NO_WCHAR) && !defined(CATCH_CONFIG_WCHAR) +# define CATCH_CONFIG_WCHAR #endif -#if defined(CATCH_INTERNAL_CONFIG_CPP11_TUPLE) && !defined(CATCH_CONFIG_CPP11_NO_TUPLE) && !defined(CATCH_CONFIG_CPP11_TUPLE) && !defined(CATCH_CONFIG_NO_CPP11) -# define CATCH_CONFIG_CPP11_TUPLE + +#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_VARIADIC_MACROS) && !defined(CATCH_CONFIG_NO_VARIADIC_MACROS) && !defined(CATCH_CONFIG_VARIADIC_MACROS) -# define CATCH_CONFIG_VARIADIC_MACROS + +#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_CPP11_LONG_LONG) && !defined(CATCH_CONFIG_CPP11_NO_LONG_LONG) && !defined(CATCH_CONFIG_CPP11_LONG_LONG) && !defined(CATCH_CONFIG_NO_CPP11) -# define CATCH_CONFIG_CPP11_LONG_LONG + +#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 -#if defined(CATCH_INTERNAL_CONFIG_CPP11_OVERRIDE) && !defined(CATCH_CONFIG_CPP11_NO_OVERRIDE) && !defined(CATCH_CONFIG_CPP11_OVERRIDE) && !defined(CATCH_CONFIG_NO_CPP11) -# define CATCH_CONFIG_CPP11_OVERRIDE + +#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_CPP11_UNIQUE_PTR) && !defined(CATCH_CONFIG_CPP11_NO_UNIQUE_PTR) && !defined(CATCH_CONFIG_CPP11_UNIQUE_PTR) && !defined(CATCH_CONFIG_NO_CPP11) -# define CATCH_CONFIG_CPP11_UNIQUE_PTR + +#if defined(CATCH_INTERNAL_CONFIG_CPP17_VARIANT) && !defined(CATCH_CONFIG_NO_CPP17_VARIANT) && !defined(CATCH_CONFIG_CPP17_VARIANT) +# define CATCH_CONFIG_CPP17_VARIANT #endif -// Use of __COUNTER__ is suppressed if __JETBRAINS_IDE__ is #defined (meaning we're being parsed by a JetBrains IDE for -// analytics) because, at time of writing, __COUNTER__ is not properly handled by it. -// This does not affect compilation -#if defined(CATCH_INTERNAL_CONFIG_COUNTER) && !defined(CATCH_CONFIG_NO_COUNTER) && !defined(CATCH_CONFIG_COUNTER) && !defined(__JETBRAINS_IDE__) -# define CATCH_CONFIG_COUNTER + +#if defined(CATCH_CONFIG_EXPERIMENTAL_REDIRECT) +# define CATCH_INTERNAL_CONFIG_NEW_CAPTURE #endif -#if defined(CATCH_INTERNAL_CONFIG_CPP11_SHUFFLE) && !defined(CATCH_CONFIG_CPP11_NO_SHUFFLE) && !defined(CATCH_CONFIG_CPP11_SHUFFLE) && !defined(CATCH_CONFIG_NO_CPP11) -# define CATCH_CONFIG_CPP11_SHUFFLE + +#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_CPP11_TYPE_TRAITS) && !defined(CATCH_CONFIG_CPP11_NO_TYPE_TRAITS) && !defined(CATCH_CONFIG_CPP11_TYPE_TRAITS) && !defined(CATCH_CONFIG_NO_CPP11) -# define CATCH_CONFIG_CPP11_TYPE_TRAITS -# endif -#if defined(CATCH_INTERNAL_CONFIG_WINDOWS_SEH) && !defined(CATCH_CONFIG_NO_WINDOWS_SEH) && !defined(CATCH_CONFIG_WINDOWS_SEH) -# define CATCH_CONFIG_WINDOWS_SEH + +#if !defined(CATCH_INTERNAL_CONFIG_EXCEPTIONS_ENABLED) && !defined(CATCH_CONFIG_DISABLE_EXCEPTIONS) +# define CATCH_CONFIG_DISABLE_EXCEPTIONS #endif -#if !defined(CATCH_INTERNAL_SUPPRESS_PARENTHESES_WARNINGS) -# define CATCH_INTERNAL_SUPPRESS_PARENTHESES_WARNINGS +#if defined(CATCH_INTERNAL_CONFIG_POLYFILL_ISNAN) && !defined(CATCH_CONFIG_NO_POLYFILL_ISNAN) && !defined(CATCH_CONFIG_POLYFILL_ISNAN) +# define CATCH_CONFIG_POLYFILL_ISNAN #endif -// noexcept support: -#if defined(CATCH_CONFIG_CPP11_NOEXCEPT) && !defined(CATCH_NOEXCEPT) -# define CATCH_NOEXCEPT noexcept -# define CATCH_NOEXCEPT_IS(x) noexcept(x) -#else -# define CATCH_NOEXCEPT throw() -# define CATCH_NOEXCEPT_IS(x) +#if defined(CATCH_INTERNAL_CONFIG_USE_ASYNC) && !defined(CATCH_CONFIG_NO_USE_ASYNC) && !defined(CATCH_CONFIG_USE_ASYNC) +# define CATCH_CONFIG_USE_ASYNC #endif -// nullptr support -#ifdef CATCH_CONFIG_CPP11_NULLPTR -# define CATCH_NULL nullptr -#else -# define CATCH_NULL NULL +#if !defined(CATCH_INTERNAL_SUPPRESS_PARENTHESES_WARNINGS) +# define CATCH_INTERNAL_SUPPRESS_PARENTHESES_WARNINGS +# define CATCH_INTERNAL_UNSUPPRESS_PARENTHESES_WARNINGS +#endif +#if !defined(CATCH_INTERNAL_SUPPRESS_GLOBALS_WARNINGS) +# define CATCH_INTERNAL_SUPPRESS_GLOBALS_WARNINGS +# define CATCH_INTERNAL_UNSUPPRESS_GLOBALS_WARNINGS +#endif +#if !defined(CATCH_INTERNAL_SUPPRESS_UNUSED_WARNINGS) +# define CATCH_INTERNAL_SUPPRESS_UNUSED_WARNINGS +# define CATCH_INTERNAL_UNSUPPRESS_UNUSED_WARNINGS +#endif +#if !defined(CATCH_INTERNAL_SUPPRESS_ZERO_VARIADIC_WARNINGS) +# define CATCH_INTERNAL_SUPPRESS_ZERO_VARIADIC_WARNINGS +# define CATCH_INTERNAL_UNSUPPRESS_ZERO_VARIADIC_WARNINGS #endif -// override support -#ifdef CATCH_CONFIG_CPP11_OVERRIDE -# define CATCH_OVERRIDE override +#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_OVERRIDE +#define CATCH_TRY try +#define CATCH_CATCH_ALL catch (...) +#define CATCH_CATCH_ANON(type) catch (type) #endif -// unique_ptr support -#ifdef CATCH_CONFIG_CPP11_UNIQUE_PTR -# define CATCH_AUTO_PTR( T ) std::unique_ptr<T> -#else -# define CATCH_AUTO_PTR( T ) std::auto_ptr<T> +#if defined(CATCH_INTERNAL_CONFIG_TRADITIONAL_MSVC_PREPROCESSOR) && !defined(CATCH_CONFIG_NO_TRADITIONAL_MSVC_PREPROCESSOR) && !defined(CATCH_CONFIG_TRADITIONAL_MSVC_PREPROCESSOR) +#define CATCH_CONFIG_TRADITIONAL_MSVC_PREPROCESSOR #endif +// end catch_compiler_capabilities.h #define INTERNAL_CATCH_UNIQUE_NAME_LINE2( name, line ) name##line #define INTERNAL_CATCH_UNIQUE_NAME_LINE( name, line ) INTERNAL_CATCH_UNIQUE_NAME_LINE2( name, line ) #ifdef CATCH_CONFIG_COUNTER @@ -333,95 +406,48 @@ # define INTERNAL_CATCH_UNIQUE_NAME( name ) INTERNAL_CATCH_UNIQUE_NAME_LINE( name, __LINE__ ) #endif -#define INTERNAL_CATCH_STRINGIFY2( expr ) #expr -#define INTERNAL_CATCH_STRINGIFY( expr ) INTERNAL_CATCH_STRINGIFY2( expr ) +#include <iosfwd> +#include <string> +#include <cstdint> -#include <sstream> -#include <algorithm> +// We need a dummy global operator<< so we can bring it into Catch namespace later +struct Catch_global_namespace_dummy {}; +std::ostream& operator<<(std::ostream&, Catch_global_namespace_dummy); namespace Catch { - struct IConfig; - struct CaseSensitive { enum Choice { Yes, No }; }; class NonCopyable { -#ifdef CATCH_CONFIG_CPP11_GENERATED_METHODS NonCopyable( NonCopyable const& ) = delete; NonCopyable( NonCopyable && ) = delete; NonCopyable& operator = ( NonCopyable const& ) = delete; NonCopyable& operator = ( NonCopyable && ) = delete; -#else - NonCopyable( NonCopyable const& info ); - NonCopyable& operator = ( NonCopyable const& ); -#endif protected: - NonCopyable() {} + NonCopyable(); virtual ~NonCopyable(); }; - class SafeBool { - public: - typedef void (SafeBool::*type)() const; - - static type makeSafe( bool value ) { - return value ? &SafeBool::trueValue : 0; - } - private: - void trueValue() const {} - }; - - template<typename ContainerT> - inline void deleteAll( ContainerT& container ) { - typename ContainerT::const_iterator it = container.begin(); - typename ContainerT::const_iterator itEnd = container.end(); - for(; it != itEnd; ++it ) - delete *it; - } - template<typename AssociativeContainerT> - inline void deleteAllValues( AssociativeContainerT& container ) { - typename AssociativeContainerT::const_iterator it = container.begin(); - typename AssociativeContainerT::const_iterator itEnd = container.end(); - for(; it != itEnd; ++it ) - delete it->second; - } - - bool startsWith( std::string const& s, std::string const& prefix ); - bool startsWith( std::string const& s, char prefix ); - bool endsWith( std::string const& s, std::string const& suffix ); - bool endsWith( std::string const& s, char suffix ); - bool contains( std::string const& s, std::string const& infix ); - void toLowerInPlace( std::string& s ); - std::string toLower( std::string const& s ); - std::string trim( std::string const& str ); - bool replaceInPlace( std::string& str, std::string const& replaceThis, std::string const& withThis ); - - struct pluralise { - pluralise( std::size_t count, std::string const& label ); - - friend std::ostream& operator << ( std::ostream& os, pluralise const& pluraliser ); + struct SourceLineInfo { - std::size_t m_count; - std::string m_label; - }; + SourceLineInfo() = delete; + SourceLineInfo( char const* _file, std::size_t _line ) noexcept + : file( _file ), + line( _line ) + {} - struct SourceLineInfo { + SourceLineInfo( SourceLineInfo const& other ) = default; + SourceLineInfo& operator = ( SourceLineInfo const& ) = default; + SourceLineInfo( SourceLineInfo&& ) noexcept = default; + SourceLineInfo& operator = ( SourceLineInfo&& ) noexcept = default; - SourceLineInfo(); - SourceLineInfo( char const* _file, std::size_t _line ); -# ifdef CATCH_CONFIG_CPP11_GENERATED_METHODS - SourceLineInfo(SourceLineInfo const& other) = default; - SourceLineInfo( SourceLineInfo && ) = default; - SourceLineInfo& operator = ( SourceLineInfo const& ) = default; - SourceLineInfo& operator = ( SourceLineInfo && ) = default; -# endif - bool empty() const; - bool operator == ( SourceLineInfo const& other ) const; - bool operator < ( SourceLineInfo const& other ) const; + bool empty() const noexcept; + bool operator == ( SourceLineInfo const& other ) const noexcept; + bool operator < ( SourceLineInfo const& other ) const noexcept; char const* file; std::size_t line; @@ -429,24 +455,17 @@ namespace Catch { std::ostream& operator << ( std::ostream& os, SourceLineInfo const& info ); - // This is just here to avoid compiler warnings with macro constants and boolean literals - inline bool isTrue( bool value ){ return value; } - inline bool alwaysTrue() { return true; } - inline bool alwaysFalse() { return false; } - - void throwLogicError( std::string const& message, SourceLineInfo const& locationInfo ); - - void seedRng( IConfig const& config ); - unsigned int rngSeed(); + // Bring in operator<< from global namespace into Catch namespace + // This is necessary because the overload of operator<< above makes + // lookup stop at namespace Catch + using ::operator<<; // Use this in variadic streaming macros to allow // >> +StreamEndStop // as well as // >> stuff +StreamEndStop struct StreamEndStop { - std::string operator+() { - return std::string(); - } + std::string operator+() const; }; template<typename T> T const& operator + ( T const& value, StreamEndStop ) { @@ -454,347 +473,833 @@ namespace Catch { } } -#define CATCH_INTERNAL_LINEINFO ::Catch::SourceLineInfo( __FILE__, static_cast<std::size_t>( __LINE__ ) ) -#define CATCH_INTERNAL_ERROR( msg ) ::Catch::throwLogicError( msg, CATCH_INTERNAL_LINEINFO ); +#define CATCH_INTERNAL_LINEINFO \ + ::Catch::SourceLineInfo( __FILE__, static_cast<std::size_t>( __LINE__ ) ) +// end catch_common.h namespace Catch { - class NotImplementedException : public std::exception - { - public: - NotImplementedException( SourceLineInfo const& lineInfo ); - NotImplementedException( NotImplementedException const& ) {} - - virtual ~NotImplementedException() CATCH_NOEXCEPT {} - - virtual const char* what() const CATCH_NOEXCEPT; - - private: - std::string m_what; - SourceLineInfo m_lineInfo; + struct RegistrarForTagAliases { + RegistrarForTagAliases( char const* alias, char const* tag, SourceLineInfo const& lineInfo ); }; } // end namespace Catch -/////////////////////////////////////////////////////////////////////////////// -#define CATCH_NOT_IMPLEMENTED throw Catch::NotImplementedException( CATCH_INTERNAL_LINEINFO ) +#define CATCH_REGISTER_TAG_ALIAS( alias, spec ) \ + CATCH_INTERNAL_SUPPRESS_GLOBALS_WARNINGS \ + namespace{ Catch::RegistrarForTagAliases INTERNAL_CATCH_UNIQUE_NAME( AutoRegisterTagAlias )( alias, spec, CATCH_INTERNAL_LINEINFO ); } \ + CATCH_INTERNAL_UNSUPPRESS_GLOBALS_WARNINGS -// #included from: internal/catch_context.h -#define TWOBLUECUBES_CATCH_CONTEXT_H_INCLUDED +// end catch_tag_alias_autoregistrar.h +// start catch_test_registry.h -// #included from: catch_interfaces_generators.h -#define TWOBLUECUBES_CATCH_INTERFACES_GENERATORS_H_INCLUDED +// start catch_interfaces_testcase.h -#include <string> +#include <vector> namespace Catch { - struct IGeneratorInfo { - virtual ~IGeneratorInfo(); - virtual bool moveNext() = 0; - virtual std::size_t getCurrentIndex() const = 0; + class TestSpec; + + struct ITestInvoker { + virtual void invoke () const = 0; + virtual ~ITestInvoker(); }; - struct IGeneratorsForTest { - virtual ~IGeneratorsForTest(); + class TestCase; + struct IConfig; - virtual IGeneratorInfo& getGeneratorInfo( std::string const& fileInfo, std::size_t size ) = 0; - virtual bool moveNext() = 0; + struct ITestCaseRegistry { + virtual ~ITestCaseRegistry(); + virtual std::vector<TestCase> const& getAllTests() const = 0; + virtual std::vector<TestCase> const& getAllTestsSorted( IConfig const& config ) const = 0; }; - IGeneratorsForTest* createGeneratorsForTest(); + bool matchTest( TestCase const& testCase, TestSpec const& testSpec, IConfig const& config ); + std::vector<TestCase> filterTests( std::vector<TestCase> const& testCases, TestSpec const& testSpec, IConfig const& config ); + std::vector<TestCase> const& getAllTestCasesSorted( IConfig const& config ); -} // end namespace Catch +} -// #included from: catch_ptr.hpp -#define TWOBLUECUBES_CATCH_PTR_HPP_INCLUDED +// end catch_interfaces_testcase.h +// start catch_stringref.h -#ifdef __clang__ -#pragma clang diagnostic push -#pragma clang diagnostic ignored "-Wpadded" -#endif +#include <cstddef> +#include <string> +#include <iosfwd> namespace Catch { - // An intrusive reference counting smart pointer. - // T must implement addRef() and release() methods - // typically implementing the IShared interface - template<typename T> - class Ptr { + /// 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. c_str() must return a null terminated + /// string, however, and so the StringRef will internally take ownership + /// (taking a copy), if necessary. In theory this ownership is not externally + /// visible - but it does mean (substring) StringRefs should not be shared between + /// threads. + class StringRef { public: - Ptr() : m_p( CATCH_NULL ){} - Ptr( T* p ) : m_p( p ){ - if( m_p ) - m_p->addRef(); - } - Ptr( Ptr const& other ) : m_p( other.m_p ){ - if( m_p ) - m_p->addRef(); - } - ~Ptr(){ - if( m_p ) - m_p->release(); - } - void reset() { - if( m_p ) - m_p->release(); - m_p = CATCH_NULL; + using size_type = std::size_t; + + private: + friend struct StringRefTestAccess; + + char const* m_start; + size_type m_size; + + char* m_data = nullptr; + + void takeOwnership(); + + static constexpr char const* const s_empty = ""; + + public: // construction/ assignment + StringRef() noexcept + : StringRef( s_empty, 0 ) + {} + + StringRef( StringRef const& other ) noexcept + : m_start( other.m_start ), + m_size( other.m_size ) + {} + + StringRef( StringRef&& other ) noexcept + : m_start( other.m_start ), + m_size( other.m_size ), + m_data( other.m_data ) + { + other.m_data = nullptr; } - Ptr& operator = ( T* p ){ - Ptr temp( p ); - swap( temp ); - return *this; + + StringRef( char const* rawChars ) noexcept; + + 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() ) + {} + + ~StringRef() noexcept { + delete[] m_data; } - Ptr& operator = ( Ptr const& other ){ - Ptr temp( other ); - swap( temp ); + + auto operator = ( StringRef const &other ) noexcept -> StringRef& { + delete[] m_data; + m_data = nullptr; + m_start = other.m_start; + m_size = other.m_size; return *this; } - void swap( Ptr& other ) { std::swap( m_p, other.m_p ); } - T* get() const{ return m_p; } - T& operator*() const { return *m_p; } - T* operator->() const { return m_p; } - bool operator !() const { return m_p == CATCH_NULL; } - operator SafeBool::type() const { return SafeBool::makeSafe( m_p != CATCH_NULL ); } - private: - T* m_p; - }; + operator std::string() const; - struct IShared : NonCopyable { - virtual ~IShared(); - virtual void addRef() const = 0; - virtual void release() const = 0; - }; + void swap( StringRef& other ) noexcept; - template<typename T = IShared> - struct SharedImpl : T { + public: // operators + auto operator == ( StringRef const& other ) const noexcept -> bool; + auto operator != ( StringRef const& other ) const noexcept -> bool; - SharedImpl() : m_rc( 0 ){} + auto operator[] ( size_type index ) const noexcept -> char; - virtual void addRef() const { - ++m_rc; + public: // named queries + auto empty() const noexcept -> bool { + return m_size == 0; } - virtual void release() const { - if( --m_rc == 0 ) - delete this; + auto size() const noexcept -> size_type { + return m_size; } - mutable unsigned int m_rc; - }; + auto numberOfCharacters() const noexcept -> size_type; + auto c_str() const -> char const*; -} // end namespace Catch + public: // substrings and searches + auto substr( size_type start, size_type size ) const noexcept -> StringRef; -#ifdef __clang__ -#pragma clang diagnostic pop -#endif + // Returns the current start pointer. + // Note that the pointer can change when if the StringRef is a substring + auto currentData() const noexcept -> char const*; -namespace Catch { + private: // ownership queries - may not be consistent between calls + auto isOwned() const noexcept -> bool; + auto isSubstring() const noexcept -> bool; + }; - class TestCase; - class Stream; - struct IResultCapture; - struct IRunner; - struct IGeneratorsForTest; - struct IConfig; + auto operator + ( StringRef const& lhs, StringRef const& rhs ) -> std::string; + auto operator + ( StringRef const& lhs, char const* rhs ) -> std::string; + auto operator + ( char const* lhs, StringRef const& rhs ) -> std::string; - struct IContext - { - virtual ~IContext(); + auto operator += ( std::string& lhs, StringRef const& sr ) -> std::string&; + auto operator << ( std::ostream& os, StringRef const& sr ) -> std::ostream&; - virtual IResultCapture* getResultCapture() = 0; - virtual IRunner* getRunner() = 0; - virtual size_t getGeneratorIndex( std::string const& fileInfo, size_t totalSize ) = 0; - virtual bool advanceGeneratorsForCurrentTest() = 0; - virtual Ptr<IConfig const> getConfig() const = 0; - }; - - struct IMutableContext : IContext - { - virtual ~IMutableContext(); - virtual void setResultCapture( IResultCapture* resultCapture ) = 0; - virtual void setRunner( IRunner* runner ) = 0; - virtual void setConfig( Ptr<IConfig const> const& config ) = 0; - }; + inline auto operator "" _sr( char const* rawChars, std::size_t size ) noexcept -> StringRef { + return StringRef( rawChars, size ); + } - IContext& getCurrentContext(); - IMutableContext& getCurrentMutableContext(); - void cleanUpContext(); - Stream createStream( std::string const& streamName ); +} // namespace Catch +inline auto operator "" _catch_sr( char const* rawChars, std::size_t size ) noexcept -> Catch::StringRef { + return Catch::StringRef( rawChars, size ); } -// #included from: internal/catch_test_registry.hpp -#define TWOBLUECUBES_CATCH_TEST_REGISTRY_HPP_INCLUDED +// end catch_stringref.h +// start catch_type_traits.hpp -// #included from: catch_interfaces_testcase.h -#define TWOBLUECUBES_CATCH_INTERFACES_TESTCASE_H_INCLUDED -#include <vector> - -namespace Catch { +#include <type_traits> - class TestSpec; +namespace Catch{ - struct ITestCase : IShared { - virtual void invoke () const = 0; - protected: - virtual ~ITestCase(); - }; +#ifdef CATCH_CPP17_OR_GREATER + template <typename...> + inline constexpr auto is_unique = std::true_type{}; - class TestCase; - struct IConfig; + template <typename T, typename... Rest> + inline constexpr auto is_unique<T, Rest...> = std::bool_constant< + (!std::is_same_v<T, Rest> && ...) && is_unique<Rest...> + >{}; +#else - struct ITestCaseRegistry { - virtual ~ITestCaseRegistry(); - virtual std::vector<TestCase> const& getAllTests() const = 0; - virtual std::vector<TestCase> const& getAllTestsSorted( IConfig const& config ) const = 0; - }; +template <typename...> +struct is_unique : std::true_type{}; - bool matchTest( TestCase const& testCase, TestSpec const& testSpec, IConfig const& config ); - std::vector<TestCase> filterTests( std::vector<TestCase> const& testCases, TestSpec const& testSpec, IConfig const& config ); - std::vector<TestCase> const& getAllTestCasesSorted( IConfig const& config ); +template <typename T0, typename T1, typename... Rest> +struct is_unique<T0, T1, Rest...> : std::integral_constant +<bool, + !std::is_same<T0, T1>::value + && is_unique<T0, Rest...>::value + && is_unique<T1, Rest...>::value +>{}; +#endif } -namespace Catch { +// end catch_type_traits.hpp +// start catch_preprocessor.hpp -template<typename C> -class MethodTestCase : public SharedImpl<ITestCase> { -public: - MethodTestCase( void (C::*method)() ) : m_method( method ) {} +#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__))) - virtual void invoke() const { - C obj; - (obj.*m_method)(); - } +#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 -private: - virtual ~MethodTestCase() {} +#define CATCH_REC_END(...) +#define CATCH_REC_OUT + +#define CATCH_EMPTY() +#define CATCH_DEFER(id) id CATCH_EMPTY() + +#define CATCH_REC_GET_END2() 0, CATCH_REC_END +#define CATCH_REC_GET_END1(...) CATCH_REC_GET_END2 +#define CATCH_REC_GET_END(...) CATCH_REC_GET_END1 +#define CATCH_REC_NEXT0(test, next, ...) next CATCH_REC_OUT +#define CATCH_REC_NEXT1(test, next) CATCH_DEFER ( CATCH_REC_NEXT0 ) ( test, next, 0) +#define CATCH_REC_NEXT(test, next) CATCH_REC_NEXT1(CATCH_REC_GET_END test, next) + +#define CATCH_REC_LIST0(f, x, peek, ...) , f(x) CATCH_DEFER ( CATCH_REC_NEXT(peek, CATCH_REC_LIST1) ) ( f, peek, __VA_ARGS__ ) +#define CATCH_REC_LIST1(f, x, peek, ...) , f(x) CATCH_DEFER ( CATCH_REC_NEXT(peek, CATCH_REC_LIST0) ) ( f, peek, __VA_ARGS__ ) +#define CATCH_REC_LIST2(f, x, peek, ...) f(x) CATCH_DEFER ( CATCH_REC_NEXT(peek, CATCH_REC_LIST1) ) ( f, peek, __VA_ARGS__ ) + +#define CATCH_REC_LIST0_UD(f, userdata, x, peek, ...) , f(userdata, x) CATCH_DEFER ( CATCH_REC_NEXT(peek, CATCH_REC_LIST1_UD) ) ( f, userdata, peek, __VA_ARGS__ ) +#define CATCH_REC_LIST1_UD(f, userdata, x, peek, ...) , f(userdata, x) CATCH_DEFER ( CATCH_REC_NEXT(peek, CATCH_REC_LIST0_UD) ) ( f, userdata, peek, __VA_ARGS__ ) +#define CATCH_REC_LIST2_UD(f, userdata, x, peek, ...) f(userdata, x) CATCH_DEFER ( CATCH_REC_NEXT(peek, CATCH_REC_LIST1_UD) ) ( f, userdata, peek, __VA_ARGS__ ) + +// Applies the function macro `f` to each of the remaining parameters, inserts commas between the results, +// and passes userdata as the first parameter to each invocation, +// e.g. CATCH_REC_LIST_UD(f, x, a, b, c) evaluates to f(x, a), f(x, b), f(x, c) +#define CATCH_REC_LIST_UD(f, userdata, ...) CATCH_RECURSE(CATCH_REC_LIST2_UD(f, userdata, __VA_ARGS__, ()()(), ()()(), ()()(), 0)) + +#define CATCH_REC_LIST(f, ...) CATCH_RECURSE(CATCH_REC_LIST2(f, __VA_ARGS__, ()()(), ()()(), ()()(), 0)) + +#define INTERNAL_CATCH_EXPAND1(param) INTERNAL_CATCH_EXPAND2(param) +#define INTERNAL_CATCH_EXPAND2(...) INTERNAL_CATCH_NO## __VA_ARGS__ +#define INTERNAL_CATCH_DEF(...) INTERNAL_CATCH_DEF __VA_ARGS__ +#define INTERNAL_CATCH_NOINTERNAL_CATCH_DEF +#define INTERNAL_CATCH_STRINGIZE(...) INTERNAL_CATCH_STRINGIZE2(__VA_ARGS__) +#ifndef CATCH_CONFIG_TRADITIONAL_MSVC_PREPROCESSOR +#define INTERNAL_CATCH_STRINGIZE2(...) #__VA_ARGS__ +#define INTERNAL_CATCH_STRINGIZE_WITHOUT_PARENS(param) INTERNAL_CATCH_STRINGIZE(INTERNAL_CATCH_REMOVE_PARENS(param)) +#else +// MSVC is adding extra space and needs another indirection to expand INTERNAL_CATCH_NOINTERNAL_CATCH_DEF +#define INTERNAL_CATCH_STRINGIZE2(...) INTERNAL_CATCH_STRINGIZE3(__VA_ARGS__) +#define INTERNAL_CATCH_STRINGIZE3(...) #__VA_ARGS__ +#define INTERNAL_CATCH_STRINGIZE_WITHOUT_PARENS(param) (INTERNAL_CATCH_STRINGIZE(INTERNAL_CATCH_REMOVE_PARENS(param)) + 1) +#endif - void (C::*m_method)(); -}; +#define INTERNAL_CATCH_MAKE_NAMESPACE2(...) ns_##__VA_ARGS__ +#define INTERNAL_CATCH_MAKE_NAMESPACE(name) INTERNAL_CATCH_MAKE_NAMESPACE2(name) -typedef void(*TestFunction)(); +#define INTERNAL_CATCH_REMOVE_PARENS(...) INTERNAL_CATCH_EXPAND1(INTERNAL_CATCH_DEF __VA_ARGS__) -struct NameAndDesc { - NameAndDesc( const char* _name = "", const char* _description= "" ) - : name( _name ), description( _description ) - {} +#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, _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 L1, typename...E1, template<typename...> class L2, typename...E2> \ + constexpr auto append(L1<E1...>, L2<E2...>) noexcept -> L1<E1...,E2...> { return {}; }\ + template< template<typename...> class L1, typename...E1, template<typename...> class L2, typename...E2, typename...Rest>\ + constexpr auto append(L1<E1...>, L2<E2...>, Rest...) noexcept -> decltype(append(L1<E1...,E2...>{}, Rest{}...)) { return {}; }\ + template< template<typename...> class L1, typename...E1, typename...Rest>\ + constexpr auto append(L1<E1...>, TypeList<mpl_::na>, Rest...) noexcept -> L1<E1...> { return {}; }\ + \ + template< template<typename...> class Container, template<typename...> class List, typename...elems>\ + constexpr auto rewrap(List<elems...>) noexcept -> TypeList<Container<elems...>> { return {}; }\ + template< template<typename...> class Container, template<typename...> class List, class...Elems, typename...Elements>\ + constexpr auto rewrap(List<Elems...>,Elements...) noexcept -> decltype(append(TypeList<Container<Elems...>>{}, rewrap<Container>(Elements{}...))) { return {}; }\ + \ + template<template <typename...> class Final, template< typename...> class...Containers, typename...Types>\ + constexpr auto create(TypeList<Types...>) noexcept -> decltype(append(Final<>{}, rewrap<Containers>(Types{}...)...)) { return {}; }\ + template<template <typename...> class Final, template <typename...> class List, typename...Ts>\ + constexpr auto convert(List<Ts...>) noexcept -> decltype(append(Final<>{},TypeList<Ts>{}...)) { return {}; } + +#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 Container, template<INTERNAL_CATCH_REMOVE_PARENS(signature)> class List, INTERNAL_CATCH_REMOVE_PARENS(signature)>\ + constexpr auto rewrap(List<__VA_ARGS__>) noexcept -> TypeList<Container<__VA_ARGS__>> { return {}; }\ + template< template<INTERNAL_CATCH_REMOVE_PARENS(signature)> class Container, template<INTERNAL_CATCH_REMOVE_PARENS(signature)> class List, INTERNAL_CATCH_REMOVE_PARENS(signature), typename...Elements>\ + constexpr auto rewrap(List<__VA_ARGS__>,Elements...elems) noexcept -> decltype(append(TypeList<Container<__VA_ARGS__>>{}, rewrap<Container>(elems...))) { return {}; }\ + template<template <typename...> class Final, template<INTERNAL_CATCH_REMOVE_PARENS(signature)> class...Containers, typename...Types>\ + constexpr auto create(TypeList<Types...>) noexcept -> decltype(append(Final<>{}, rewrap<Containers>(Types{}...)...)) { return {}; } + +#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 + +// end catch_preprocessor.hpp +// start catch_meta.hpp + + +#include <type_traits> - const char* name; - const char* description; +namespace Catch { +template<typename T> +struct always_false : std::false_type {}; + +template <typename> struct true_given : std::true_type {}; +struct is_callable_tester { + template <typename Fun, typename... Args> + true_given<decltype(std::declval<Fun>()(std::declval<Args>()...))> static test(int); + template <typename...> + std::false_type static test(...); }; -void registerTestCase - ( ITestCase* testCase, - char const* className, - NameAndDesc const& nameAndDesc, - SourceLineInfo const& lineInfo ); +template <typename T> +struct is_callable; -struct AutoReg { +template <typename Fun, typename... Args> +struct is_callable<Fun(Args...)> : decltype(is_callable_tester::test<Fun, Args...>(0)) {}; - AutoReg - ( TestFunction function, - SourceLineInfo const& lineInfo, - NameAndDesc const& nameAndDesc ); +} // namespace Catch + +namespace mpl_{ + struct na; +} + +// end catch_meta.hpp +namespace Catch { - template<typename C> - AutoReg - ( void (C::*method)(), - char const* className, - NameAndDesc const& nameAndDesc, - SourceLineInfo const& lineInfo ) { +template<typename C> +class TestInvokerAsMethod : public ITestInvoker { + void (C::*m_testAsMethod)(); +public: + TestInvokerAsMethod( void (C::*testAsMethod)() ) noexcept : m_testAsMethod( testAsMethod ) {} - registerTestCase - ( new MethodTestCase<C>( method ), - className, - nameAndDesc, - lineInfo ); + void invoke() const override { + C obj; + (obj.*m_testAsMethod)(); } +}; - ~AutoReg(); +auto makeTestInvoker( void(*testAsFunction)() ) noexcept -> ITestInvoker*; -private: - AutoReg( AutoReg const& ); - void operator= ( AutoReg const& ); +template<typename C> +auto makeTestInvoker( void (C::*testAsMethod)() ) noexcept -> ITestInvoker* { + return new(std::nothrow) TestInvokerAsMethod<C>( testAsMethod ); +} + +struct NameAndTags { + NameAndTags( StringRef const& name_ = StringRef(), StringRef const& tags_ = StringRef() ) noexcept; + StringRef name; + StringRef tags; }; -void registerTestCaseFunction - ( TestFunction function, - SourceLineInfo const& lineInfo, - NameAndDesc const& nameAndDesc ); +struct AutoReg : NonCopyable { + AutoReg( ITestInvoker* invoker, SourceLineInfo const& lineInfo, StringRef const& classOrMethod, NameAndTags const& nameAndTags ) noexcept; + ~AutoReg(); +}; } // end namespace Catch -#ifdef CATCH_CONFIG_VARIADIC_MACROS +#if defined(CATCH_CONFIG_DISABLE) + #define INTERNAL_CATCH_TESTCASE_NO_REGISTRATION( TestName, ... ) \ + static void TestName() + #define INTERNAL_CATCH_TESTCASE_METHOD_NO_REGISTRATION( TestName, ClassName, ... ) \ + namespace{ \ + struct TestName : INTERNAL_CATCH_REMOVE_PARENS(ClassName) { \ + void test(); \ + }; \ + } \ + void TestName::test() + #define INTERNAL_CATCH_TEMPLATE_TEST_CASE_NO_REGISTRATION_2( TestName, TestFunc, Name, Tags, Signature, ... ) \ + INTERNAL_CATCH_DEFINE_SIG_TEST(TestFunc, INTERNAL_CATCH_REMOVE_PARENS(Signature)) + #define INTERNAL_CATCH_TEMPLATE_TEST_CASE_METHOD_NO_REGISTRATION_2( TestNameClass, TestName, ClassName, Name, Tags, Signature, ... ) \ + namespace{ \ + namespace INTERNAL_CATCH_MAKE_NAMESPACE(TestName) { \ + INTERNAL_CATCH_DECLARE_SIG_TEST_METHOD(TestName, ClassName, INTERNAL_CATCH_REMOVE_PARENS(Signature));\ + } \ + } \ + INTERNAL_CATCH_DEFINE_SIG_TEST_METHOD(TestName, INTERNAL_CATCH_REMOVE_PARENS(Signature)) + + #ifndef CATCH_CONFIG_TRADITIONAL_MSVC_PREPROCESSOR + #define INTERNAL_CATCH_TEMPLATE_TEST_CASE_NO_REGISTRATION(Name, Tags, ...) \ + INTERNAL_CATCH_TEMPLATE_TEST_CASE_NO_REGISTRATION_2( INTERNAL_CATCH_UNIQUE_NAME( ____C_A_T_C_H____T_E_M_P_L_A_T_E____T_E_S_T____ ), INTERNAL_CATCH_UNIQUE_NAME( ____C_A_T_C_H____T_E_M_P_L_A_T_E____T_E_S_T____F_U_N_C____ ), Name, Tags, typename TestType, __VA_ARGS__ ) + #else + #define INTERNAL_CATCH_TEMPLATE_TEST_CASE_NO_REGISTRATION(Name, Tags, ...) \ + INTERNAL_CATCH_EXPAND_VARGS( INTERNAL_CATCH_TEMPLATE_TEST_CASE_NO_REGISTRATION_2( INTERNAL_CATCH_UNIQUE_NAME( ____C_A_T_C_H____T_E_M_P_L_A_T_E____T_E_S_T____ ), INTERNAL_CATCH_UNIQUE_NAME( ____C_A_T_C_H____T_E_M_P_L_A_T_E____T_E_S_T____F_U_N_C____ ), Name, Tags, typename TestType, __VA_ARGS__ ) ) + #endif + + #ifndef CATCH_CONFIG_TRADITIONAL_MSVC_PREPROCESSOR + #define INTERNAL_CATCH_TEMPLATE_TEST_CASE_SIG_NO_REGISTRATION(Name, Tags, Signature, ...) \ + INTERNAL_CATCH_TEMPLATE_TEST_CASE_NO_REGISTRATION_2( INTERNAL_CATCH_UNIQUE_NAME( ____C_A_T_C_H____T_E_M_P_L_A_T_E____T_E_S_T____ ), INTERNAL_CATCH_UNIQUE_NAME( ____C_A_T_C_H____T_E_M_P_L_A_T_E____T_E_S_T____F_U_N_C____ ), Name, Tags, Signature, __VA_ARGS__ ) + #else + #define INTERNAL_CATCH_TEMPLATE_TEST_CASE_SIG_NO_REGISTRATION(Name, Tags, Signature, ...) \ + INTERNAL_CATCH_EXPAND_VARGS( INTERNAL_CATCH_TEMPLATE_TEST_CASE_NO_REGISTRATION_2( INTERNAL_CATCH_UNIQUE_NAME( ____C_A_T_C_H____T_E_M_P_L_A_T_E____T_E_S_T____ ), INTERNAL_CATCH_UNIQUE_NAME( ____C_A_T_C_H____T_E_M_P_L_A_T_E____T_E_S_T____F_U_N_C____ ), Name, Tags, Signature, __VA_ARGS__ ) ) + #endif + + #ifndef CATCH_CONFIG_TRADITIONAL_MSVC_PREPROCESSOR + #define INTERNAL_CATCH_TEMPLATE_TEST_CASE_METHOD_NO_REGISTRATION( ClassName, Name, Tags,... ) \ + INTERNAL_CATCH_TEMPLATE_TEST_CASE_METHOD_NO_REGISTRATION_2( INTERNAL_CATCH_UNIQUE_NAME( ____C_A_T_C_H____T_E_M_P_L_A_T_E____T_E_S_T____C_L_A_S_S____ ), INTERNAL_CATCH_UNIQUE_NAME( ____C_A_T_C_H____T_E_M_P_L_A_T_E____T_E_S_T____ ) , ClassName, Name, Tags, typename T, __VA_ARGS__ ) + #else + #define INTERNAL_CATCH_TEMPLATE_TEST_CASE_METHOD_NO_REGISTRATION( ClassName, Name, Tags,... ) \ + INTERNAL_CATCH_EXPAND_VARGS( INTERNAL_CATCH_TEMPLATE_TEST_CASE_METHOD_NO_REGISTRATION_2( INTERNAL_CATCH_UNIQUE_NAME( ____C_A_T_C_H____T_E_M_P_L_A_T_E____T_E_S_T____C_L_A_S_S____ ), INTERNAL_CATCH_UNIQUE_NAME( ____C_A_T_C_H____T_E_M_P_L_A_T_E____T_E_S_T____ ) , ClassName, Name, Tags, typename T, __VA_ARGS__ ) ) + #endif + + #ifndef CATCH_CONFIG_TRADITIONAL_MSVC_PREPROCESSOR + #define INTERNAL_CATCH_TEMPLATE_TEST_CASE_METHOD_SIG_NO_REGISTRATION( ClassName, Name, Tags, Signature, ... ) \ + INTERNAL_CATCH_TEMPLATE_TEST_CASE_METHOD_NO_REGISTRATION_2( INTERNAL_CATCH_UNIQUE_NAME( ____C_A_T_C_H____T_E_M_P_L_A_T_E____T_E_S_T____C_L_A_S_S____ ), INTERNAL_CATCH_UNIQUE_NAME( ____C_A_T_C_H____T_E_M_P_L_A_T_E____T_E_S_T____ ) , ClassName, Name, Tags, Signature, __VA_ARGS__ ) + #else + #define INTERNAL_CATCH_TEMPLATE_TEST_CASE_METHOD_SIG_NO_REGISTRATION( ClassName, Name, Tags, Signature, ... ) \ + INTERNAL_CATCH_EXPAND_VARGS( INTERNAL_CATCH_TEMPLATE_TEST_CASE_METHOD_NO_REGISTRATION_2( INTERNAL_CATCH_UNIQUE_NAME( ____C_A_T_C_H____T_E_M_P_L_A_T_E____T_E_S_T____C_L_A_S_S____ ), INTERNAL_CATCH_UNIQUE_NAME( ____C_A_T_C_H____T_E_M_P_L_A_T_E____T_E_S_T____ ) , ClassName, Name, Tags, Signature, __VA_ARGS__ ) ) + #endif +#endif + /////////////////////////////////////////////////////////////////////////////// #define INTERNAL_CATCH_TESTCASE2( TestName, ... ) \ static void TestName(); \ - namespace{ Catch::AutoReg INTERNAL_CATCH_UNIQUE_NAME( autoRegistrar )( &TestName, CATCH_INTERNAL_LINEINFO, Catch::NameAndDesc( __VA_ARGS__ ) ); }\ + CATCH_INTERNAL_SUPPRESS_GLOBALS_WARNINGS \ + namespace{ Catch::AutoReg INTERNAL_CATCH_UNIQUE_NAME( autoRegistrar )( Catch::makeTestInvoker( &TestName ), CATCH_INTERNAL_LINEINFO, Catch::StringRef(), Catch::NameAndTags{ __VA_ARGS__ } ); } /* NOLINT */ \ + CATCH_INTERNAL_UNSUPPRESS_GLOBALS_WARNINGS \ static void TestName() #define INTERNAL_CATCH_TESTCASE( ... ) \ INTERNAL_CATCH_TESTCASE2( INTERNAL_CATCH_UNIQUE_NAME( ____C_A_T_C_H____T_E_S_T____ ), __VA_ARGS__ ) /////////////////////////////////////////////////////////////////////////////// #define INTERNAL_CATCH_METHOD_AS_TEST_CASE( QualifiedMethod, ... ) \ - namespace{ Catch::AutoReg INTERNAL_CATCH_UNIQUE_NAME( autoRegistrar )( &QualifiedMethod, "&" #QualifiedMethod, Catch::NameAndDesc( __VA_ARGS__ ), CATCH_INTERNAL_LINEINFO ); } + CATCH_INTERNAL_SUPPRESS_GLOBALS_WARNINGS \ + namespace{ Catch::AutoReg INTERNAL_CATCH_UNIQUE_NAME( autoRegistrar )( Catch::makeTestInvoker( &QualifiedMethod ), CATCH_INTERNAL_LINEINFO, "&" #QualifiedMethod, Catch::NameAndTags{ __VA_ARGS__ } ); } /* NOLINT */ \ + CATCH_INTERNAL_UNSUPPRESS_GLOBALS_WARNINGS /////////////////////////////////////////////////////////////////////////////// #define INTERNAL_CATCH_TEST_CASE_METHOD2( TestName, ClassName, ... )\ + CATCH_INTERNAL_SUPPRESS_GLOBALS_WARNINGS \ namespace{ \ - struct TestName : ClassName{ \ + struct TestName : INTERNAL_CATCH_REMOVE_PARENS(ClassName) { \ void test(); \ }; \ - Catch::AutoReg INTERNAL_CATCH_UNIQUE_NAME( autoRegistrar ) ( &TestName::test, #ClassName, Catch::NameAndDesc( __VA_ARGS__ ), CATCH_INTERNAL_LINEINFO ); \ + Catch::AutoReg INTERNAL_CATCH_UNIQUE_NAME( autoRegistrar ) ( Catch::makeTestInvoker( &TestName::test ), CATCH_INTERNAL_LINEINFO, #ClassName, Catch::NameAndTags{ __VA_ARGS__ } ); /* NOLINT */ \ } \ + CATCH_INTERNAL_UNSUPPRESS_GLOBALS_WARNINGS \ void TestName::test() #define INTERNAL_CATCH_TEST_CASE_METHOD( ClassName, ... ) \ INTERNAL_CATCH_TEST_CASE_METHOD2( INTERNAL_CATCH_UNIQUE_NAME( ____C_A_T_C_H____T_E_S_T____ ), ClassName, __VA_ARGS__ ) /////////////////////////////////////////////////////////////////////////////// #define INTERNAL_CATCH_REGISTER_TESTCASE( Function, ... ) \ - Catch::AutoReg( Function, CATCH_INTERNAL_LINEINFO, Catch::NameAndDesc( __VA_ARGS__ ) ); + CATCH_INTERNAL_SUPPRESS_GLOBALS_WARNINGS \ + Catch::AutoReg INTERNAL_CATCH_UNIQUE_NAME( autoRegistrar )( Catch::makeTestInvoker( Function ), CATCH_INTERNAL_LINEINFO, Catch::StringRef(), Catch::NameAndTags{ __VA_ARGS__ } ); /* NOLINT */ \ + CATCH_INTERNAL_UNSUPPRESS_GLOBALS_WARNINGS -#else /////////////////////////////////////////////////////////////////////////////// - #define INTERNAL_CATCH_TESTCASE2( TestName, Name, Desc ) \ - static void TestName(); \ - namespace{ Catch::AutoReg INTERNAL_CATCH_UNIQUE_NAME( autoRegistrar )( &TestName, CATCH_INTERNAL_LINEINFO, Catch::NameAndDesc( Name, Desc ) ); }\ - static void TestName() - #define INTERNAL_CATCH_TESTCASE( Name, Desc ) \ - INTERNAL_CATCH_TESTCASE2( INTERNAL_CATCH_UNIQUE_NAME( ____C_A_T_C_H____T_E_S_T____ ), Name, Desc ) + #define INTERNAL_CATCH_TEMPLATE_TEST_CASE_2(TestName, TestFunc, Name, Tags, Signature, ... )\ + CATCH_INTERNAL_SUPPRESS_GLOBALS_WARNINGS \ + CATCH_INTERNAL_SUPPRESS_ZERO_VARIADIC_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(){\ + int index = 0; \ + constexpr char const* tmpl_types[] = {CATCH_REC_LIST(INTERNAL_CATCH_STRINGIZE_WITHOUT_PARENS, __VA_ARGS__)};\ + using expander = int[];\ + (void)expander{(reg_test(Types{}, Catch::NameAndTags{ Name " - " + std::string(tmpl_types[index]), Tags } ), index++, 0)... };/* NOLINT */ \ + }\ + };\ + static int INTERNAL_CATCH_UNIQUE_NAME( globalRegistrar ) = [](){\ + TestName<INTERNAL_CATCH_MAKE_TYPE_LISTS_FROM_TYPES(__VA_ARGS__)>();\ + return 0;\ + }();\ + }\ + }\ + CATCH_INTERNAL_UNSUPPRESS_GLOBALS_WARNINGS \ + CATCH_INTERNAL_UNSUPPRESS_ZERO_VARIADIC_WARNINGS \ + INTERNAL_CATCH_DEFINE_SIG_TEST(TestFunc,INTERNAL_CATCH_REMOVE_PARENS(Signature)) + +#ifndef CATCH_CONFIG_TRADITIONAL_MSVC_PREPROCESSOR + #define INTERNAL_CATCH_TEMPLATE_TEST_CASE(Name, Tags, ...) \ + INTERNAL_CATCH_TEMPLATE_TEST_CASE_2( INTERNAL_CATCH_UNIQUE_NAME( ____C_A_T_C_H____T_E_M_P_L_A_T_E____T_E_S_T____ ), INTERNAL_CATCH_UNIQUE_NAME( ____C_A_T_C_H____T_E_M_P_L_A_T_E____T_E_S_T____F_U_N_C____ ), Name, Tags, typename TestType, __VA_ARGS__ ) +#else + #define INTERNAL_CATCH_TEMPLATE_TEST_CASE(Name, Tags, ...) \ + INTERNAL_CATCH_EXPAND_VARGS( INTERNAL_CATCH_TEMPLATE_TEST_CASE_2( INTERNAL_CATCH_UNIQUE_NAME( ____C_A_T_C_H____T_E_M_P_L_A_T_E____T_E_S_T____ ), INTERNAL_CATCH_UNIQUE_NAME( ____C_A_T_C_H____T_E_M_P_L_A_T_E____T_E_S_T____F_U_N_C____ ), Name, Tags, typename TestType, __VA_ARGS__ ) ) +#endif - /////////////////////////////////////////////////////////////////////////////// - #define INTERNAL_CATCH_METHOD_AS_TEST_CASE( QualifiedMethod, Name, Desc ) \ - namespace{ Catch::AutoReg INTERNAL_CATCH_UNIQUE_NAME( autoRegistrar )( &QualifiedMethod, "&" #QualifiedMethod, Catch::NameAndDesc( Name, Desc ), CATCH_INTERNAL_LINEINFO ); } +#ifndef CATCH_CONFIG_TRADITIONAL_MSVC_PREPROCESSOR + #define INTERNAL_CATCH_TEMPLATE_TEST_CASE_SIG(Name, Tags, Signature, ...) \ + INTERNAL_CATCH_TEMPLATE_TEST_CASE_2( INTERNAL_CATCH_UNIQUE_NAME( ____C_A_T_C_H____T_E_M_P_L_A_T_E____T_E_S_T____ ), INTERNAL_CATCH_UNIQUE_NAME( ____C_A_T_C_H____T_E_M_P_L_A_T_E____T_E_S_T____F_U_N_C____ ), Name, Tags, Signature, __VA_ARGS__ ) +#else + #define INTERNAL_CATCH_TEMPLATE_TEST_CASE_SIG(Name, Tags, Signature, ...) \ + INTERNAL_CATCH_EXPAND_VARGS( INTERNAL_CATCH_TEMPLATE_TEST_CASE_2( INTERNAL_CATCH_UNIQUE_NAME( ____C_A_T_C_H____T_E_M_P_L_A_T_E____T_E_S_T____ ), INTERNAL_CATCH_UNIQUE_NAME( ____C_A_T_C_H____T_E_M_P_L_A_T_E____T_E_S_T____F_U_N_C____ ), Name, Tags, Signature, __VA_ARGS__ ) ) +#endif - /////////////////////////////////////////////////////////////////////////////// - #define INTERNAL_CATCH_TEST_CASE_METHOD2( TestCaseName, ClassName, TestName, Desc )\ - namespace{ \ - struct TestCaseName : ClassName{ \ - void test(); \ - }; \ - Catch::AutoReg INTERNAL_CATCH_UNIQUE_NAME( autoRegistrar ) ( &TestCaseName::test, #ClassName, Catch::NameAndDesc( TestName, Desc ), CATCH_INTERNAL_LINEINFO ); \ - } \ - void TestCaseName::test() - #define INTERNAL_CATCH_TEST_CASE_METHOD( ClassName, TestName, Desc )\ - INTERNAL_CATCH_TEST_CASE_METHOD2( INTERNAL_CATCH_UNIQUE_NAME( ____C_A_T_C_H____T_E_S_T____ ), ClassName, TestName, Desc ) + #define INTERNAL_CATCH_TEMPLATE_PRODUCT_TEST_CASE2(TestName, TestFuncName, Name, Tags, Signature, TmplTypes, TypesList) \ + CATCH_INTERNAL_SUPPRESS_GLOBALS_WARNINGS \ + CATCH_INTERNAL_SUPPRESS_ZERO_VARIADIC_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() { \ + int index = 0; \ + using expander = int[]; \ + constexpr char const* tmpl_types[] = {CATCH_REC_LIST(INTERNAL_CATCH_STRINGIZE_WITHOUT_PARENS, INTERNAL_CATCH_REMOVE_PARENS(TmplTypes))};\ + constexpr char const* types_list[] = {CATCH_REC_LIST(INTERNAL_CATCH_STRINGIZE_WITHOUT_PARENS, INTERNAL_CATCH_REMOVE_PARENS(TypesList))};\ + constexpr auto num_types = sizeof(types_list) / sizeof(types_list[0]);\ + (void)expander{(Catch::AutoReg( Catch::makeTestInvoker( &TestFuncName<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++, 0)... };/* NOLINT */\ + } \ + }; \ + static int INTERNAL_CATCH_UNIQUE_NAME( globalRegistrar ) = [](){ \ + using TestInit = decltype(create<TestName, INTERNAL_CATCH_REMOVE_PARENS(TmplTypes)>(TypeList<INTERNAL_CATCH_MAKE_TYPE_LISTS_FROM_TYPES(INTERNAL_CATCH_REMOVE_PARENS(TypesList))>{})); \ + TestInit t; \ + t.reg_tests(); \ + return 0; \ + }(); \ + } \ + } \ + CATCH_INTERNAL_UNSUPPRESS_GLOBALS_WARNINGS \ + CATCH_INTERNAL_UNSUPPRESS_ZERO_VARIADIC_WARNINGS \ + 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( ____C_A_T_C_H____T_E_M_P_L_A_T_E____T_E_S_T____ ), INTERNAL_CATCH_UNIQUE_NAME( ____C_A_T_C_H____T_E_M_P_L_A_T_E____T_E_S_T____F_U_N_C____ ), Name, Tags, typename T,__VA_ARGS__) +#else + #define INTERNAL_CATCH_TEMPLATE_PRODUCT_TEST_CASE(Name, Tags, ...)\ + INTERNAL_CATCH_EXPAND_VARGS( INTERNAL_CATCH_TEMPLATE_PRODUCT_TEST_CASE2( INTERNAL_CATCH_UNIQUE_NAME( ____C_A_T_C_H____T_E_M_P_L_A_T_E____T_E_S_T____ ), INTERNAL_CATCH_UNIQUE_NAME( ____C_A_T_C_H____T_E_M_P_L_A_T_E____T_E_S_T____F_U_N_C____ ), Name, Tags, typename T, __VA_ARGS__ ) ) +#endif - /////////////////////////////////////////////////////////////////////////////// - #define INTERNAL_CATCH_REGISTER_TESTCASE( Function, Name, Desc ) \ - Catch::AutoReg( Function, CATCH_INTERNAL_LINEINFO, Catch::NameAndDesc( Name, Desc ) ); +#ifndef CATCH_CONFIG_TRADITIONAL_MSVC_PREPROCESSOR + #define INTERNAL_CATCH_TEMPLATE_PRODUCT_TEST_CASE_SIG(Name, Tags, Signature, ...)\ + INTERNAL_CATCH_TEMPLATE_PRODUCT_TEST_CASE2(INTERNAL_CATCH_UNIQUE_NAME( ____C_A_T_C_H____T_E_M_P_L_A_T_E____T_E_S_T____ ), INTERNAL_CATCH_UNIQUE_NAME( ____C_A_T_C_H____T_E_M_P_L_A_T_E____T_E_S_T____F_U_N_C____ ), Name, Tags, Signature, __VA_ARGS__) +#else + #define INTERNAL_CATCH_TEMPLATE_PRODUCT_TEST_CASE_SIG(Name, Tags, Signature, ...)\ + INTERNAL_CATCH_EXPAND_VARGS( INTERNAL_CATCH_TEMPLATE_PRODUCT_TEST_CASE2( INTERNAL_CATCH_UNIQUE_NAME( ____C_A_T_C_H____T_E_M_P_L_A_T_E____T_E_S_T____ ), INTERNAL_CATCH_UNIQUE_NAME( ____C_A_T_C_H____T_E_M_P_L_A_T_E____T_E_S_T____F_U_N_C____ ), Name, Tags, Signature, __VA_ARGS__ ) ) #endif -// #included from: internal/catch_capture.hpp -#define TWOBLUECUBES_CATCH_CAPTURE_HPP_INCLUDED + #define INTERNAL_CATCH_TEMPLATE_LIST_TEST_CASE_2(TestName, TestFunc, Name, Tags, TmplList)\ + CATCH_INTERNAL_SUPPRESS_GLOBALS_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() { \ + int index = 0; \ + using expander = int[]; \ + (void)expander{(Catch::AutoReg( Catch::makeTestInvoker( &TestFunc<Types> ), CATCH_INTERNAL_LINEINFO, Catch::StringRef(), Catch::NameAndTags{ Name " - " + std::string(INTERNAL_CATCH_STRINGIZE(TmplList)) + " - " + std::to_string(index), Tags } ), index++, 0)... };/* NOLINT */\ + } \ + };\ + static int INTERNAL_CATCH_UNIQUE_NAME( globalRegistrar ) = [](){ \ + using TestInit = decltype(convert<TestName>(TmplList {})); \ + TestInit t; \ + t.reg_tests(); \ + return 0; \ + }(); \ + }}\ + CATCH_INTERNAL_UNSUPPRESS_GLOBALS_WARNINGS \ + 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( ____C_A_T_C_H____T_E_M_P_L_A_T_E____T_E_S_T____ ), INTERNAL_CATCH_UNIQUE_NAME( ____C_A_T_C_H____T_E_M_P_L_A_T_E____T_E_S_T____F_U_N_C____ ), Name, Tags, TmplList ) + + #define INTERNAL_CATCH_TEMPLATE_TEST_CASE_METHOD_2( TestNameClass, TestName, ClassName, Name, Tags, Signature, ... ) \ + CATCH_INTERNAL_SUPPRESS_GLOBALS_WARNINGS \ + CATCH_INTERNAL_SUPPRESS_ZERO_VARIADIC_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(){\ + int index = 0; \ + constexpr char const* tmpl_types[] = {CATCH_REC_LIST(INTERNAL_CATCH_STRINGIZE_WITHOUT_PARENS, __VA_ARGS__)};\ + using expander = int[];\ + (void)expander{(reg_test(Types{}, #ClassName, Catch::NameAndTags{ Name " - " + std::string(tmpl_types[index]), Tags } ), index++, 0)... };/* NOLINT */ \ + }\ + };\ + static int INTERNAL_CATCH_UNIQUE_NAME( globalRegistrar ) = [](){\ + TestNameClass<INTERNAL_CATCH_MAKE_TYPE_LISTS_FROM_TYPES(__VA_ARGS__)>();\ + return 0;\ + }();\ + }\ + }\ + CATCH_INTERNAL_UNSUPPRESS_GLOBALS_WARNINGS\ + CATCH_INTERNAL_UNSUPPRESS_ZERO_VARIADIC_WARNINGS\ + INTERNAL_CATCH_DEFINE_SIG_TEST_METHOD(TestName, INTERNAL_CATCH_REMOVE_PARENS(Signature)) + +#ifndef CATCH_CONFIG_TRADITIONAL_MSVC_PREPROCESSOR + #define INTERNAL_CATCH_TEMPLATE_TEST_CASE_METHOD( ClassName, Name, Tags,... ) \ + INTERNAL_CATCH_TEMPLATE_TEST_CASE_METHOD_2( INTERNAL_CATCH_UNIQUE_NAME( ____C_A_T_C_H____T_E_M_P_L_A_T_E____T_E_S_T____C_L_A_S_S____ ), INTERNAL_CATCH_UNIQUE_NAME( ____C_A_T_C_H____T_E_M_P_L_A_T_E____T_E_S_T____ ) , ClassName, Name, Tags, typename T, __VA_ARGS__ ) +#else + #define INTERNAL_CATCH_TEMPLATE_TEST_CASE_METHOD( ClassName, Name, Tags,... ) \ + INTERNAL_CATCH_EXPAND_VARGS( INTERNAL_CATCH_TEMPLATE_TEST_CASE_METHOD_2( INTERNAL_CATCH_UNIQUE_NAME( ____C_A_T_C_H____T_E_M_P_L_A_T_E____T_E_S_T____C_L_A_S_S____ ), INTERNAL_CATCH_UNIQUE_NAME( ____C_A_T_C_H____T_E_M_P_L_A_T_E____T_E_S_T____ ) , ClassName, Name, Tags, typename T, __VA_ARGS__ ) ) +#endif -// #included from: catch_result_builder.h -#define TWOBLUECUBES_CATCH_RESULT_BUILDER_H_INCLUDED +#ifndef CATCH_CONFIG_TRADITIONAL_MSVC_PREPROCESSOR + #define INTERNAL_CATCH_TEMPLATE_TEST_CASE_METHOD_SIG( ClassName, Name, Tags, Signature, ... ) \ + INTERNAL_CATCH_TEMPLATE_TEST_CASE_METHOD_2( INTERNAL_CATCH_UNIQUE_NAME( ____C_A_T_C_H____T_E_M_P_L_A_T_E____T_E_S_T____C_L_A_S_S____ ), INTERNAL_CATCH_UNIQUE_NAME( ____C_A_T_C_H____T_E_M_P_L_A_T_E____T_E_S_T____ ) , ClassName, Name, Tags, Signature, __VA_ARGS__ ) +#else + #define INTERNAL_CATCH_TEMPLATE_TEST_CASE_METHOD_SIG( ClassName, Name, Tags, Signature, ... ) \ + INTERNAL_CATCH_EXPAND_VARGS( INTERNAL_CATCH_TEMPLATE_TEST_CASE_METHOD_2( INTERNAL_CATCH_UNIQUE_NAME( ____C_A_T_C_H____T_E_M_P_L_A_T_E____T_E_S_T____C_L_A_S_S____ ), INTERNAL_CATCH_UNIQUE_NAME( ____C_A_T_C_H____T_E_M_P_L_A_T_E____T_E_S_T____ ) , ClassName, Name, Tags, Signature, __VA_ARGS__ ) ) +#endif -// #included from: catch_result_type.h -#define TWOBLUECUBES_CATCH_RESULT_TYPE_H_INCLUDED + #define INTERNAL_CATCH_TEMPLATE_PRODUCT_TEST_CASE_METHOD_2(TestNameClass, TestName, ClassName, Name, Tags, Signature, TmplTypes, TypesList)\ + CATCH_INTERNAL_SUPPRESS_GLOBALS_WARNINGS \ + CATCH_INTERNAL_SUPPRESS_ZERO_VARIADIC_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(){\ + int index = 0;\ + using expander = int[];\ + constexpr char const* tmpl_types[] = {CATCH_REC_LIST(INTERNAL_CATCH_STRINGIZE_WITHOUT_PARENS, INTERNAL_CATCH_REMOVE_PARENS(TmplTypes))};\ + constexpr char const* types_list[] = {CATCH_REC_LIST(INTERNAL_CATCH_STRINGIZE_WITHOUT_PARENS, INTERNAL_CATCH_REMOVE_PARENS(TypesList))};\ + constexpr auto num_types = sizeof(types_list) / sizeof(types_list[0]);\ + (void)expander{(Catch::AutoReg( Catch::makeTestInvoker( &TestName<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++, 0)... };/* NOLINT */ \ + }\ + };\ + static int INTERNAL_CATCH_UNIQUE_NAME( globalRegistrar ) = [](){\ + using TestInit = decltype(create<TestNameClass, INTERNAL_CATCH_REMOVE_PARENS(TmplTypes)>(TypeList<INTERNAL_CATCH_MAKE_TYPE_LISTS_FROM_TYPES(INTERNAL_CATCH_REMOVE_PARENS(TypesList))>{}));\ + TestInit t;\ + t.reg_tests();\ + return 0;\ + }(); \ + }\ + }\ + CATCH_INTERNAL_UNSUPPRESS_GLOBALS_WARNINGS \ + CATCH_INTERNAL_UNSUPPRESS_ZERO_VARIADIC_WARNINGS \ + 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( ____C_A_T_C_H____T_E_M_P_L_A_T_E____T_E_S_T____ ), INTERNAL_CATCH_UNIQUE_NAME( ____C_A_T_C_H____T_E_M_P_L_A_T_E____T_E_S_T____F_U_N_C____ ), ClassName, Name, Tags, typename T, __VA_ARGS__ ) +#else + #define INTERNAL_CATCH_TEMPLATE_PRODUCT_TEST_CASE_METHOD( ClassName, Name, Tags, ... )\ + INTERNAL_CATCH_EXPAND_VARGS( INTERNAL_CATCH_TEMPLATE_PRODUCT_TEST_CASE_METHOD_2( INTERNAL_CATCH_UNIQUE_NAME( ____C_A_T_C_H____T_E_M_P_L_A_T_E____T_E_S_T____ ), INTERNAL_CATCH_UNIQUE_NAME( ____C_A_T_C_H____T_E_M_P_L_A_T_E____T_E_S_T____F_U_N_C____ ), ClassName, Name, Tags, typename T,__VA_ARGS__ ) ) +#endif + +#ifndef CATCH_CONFIG_TRADITIONAL_MSVC_PREPROCESSOR + #define INTERNAL_CATCH_TEMPLATE_PRODUCT_TEST_CASE_METHOD_SIG( ClassName, Name, Tags, Signature, ... )\ + INTERNAL_CATCH_TEMPLATE_PRODUCT_TEST_CASE_METHOD_2( INTERNAL_CATCH_UNIQUE_NAME( ____C_A_T_C_H____T_E_M_P_L_A_T_E____T_E_S_T____ ), INTERNAL_CATCH_UNIQUE_NAME( ____C_A_T_C_H____T_E_M_P_L_A_T_E____T_E_S_T____F_U_N_C____ ), ClassName, Name, Tags, Signature, __VA_ARGS__ ) +#else + #define INTERNAL_CATCH_TEMPLATE_PRODUCT_TEST_CASE_METHOD_SIG( ClassName, Name, Tags, Signature, ... )\ + INTERNAL_CATCH_EXPAND_VARGS( INTERNAL_CATCH_TEMPLATE_PRODUCT_TEST_CASE_METHOD_2( INTERNAL_CATCH_UNIQUE_NAME( ____C_A_T_C_H____T_E_M_P_L_A_T_E____T_E_S_T____ ), INTERNAL_CATCH_UNIQUE_NAME( ____C_A_T_C_H____T_E_M_P_L_A_T_E____T_E_S_T____F_U_N_C____ ), ClassName, Name, Tags, Signature,__VA_ARGS__ ) ) +#endif + + #define INTERNAL_CATCH_TEMPLATE_LIST_TEST_CASE_METHOD_2( TestNameClass, TestName, ClassName, Name, Tags, TmplList) \ + CATCH_INTERNAL_SUPPRESS_GLOBALS_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(){\ + int index = 0;\ + using expander = int[];\ + (void)expander{(Catch::AutoReg( Catch::makeTestInvoker( &TestName<Types>::test ), CATCH_INTERNAL_LINEINFO, #ClassName, Catch::NameAndTags{ Name " - " + std::string(INTERNAL_CATCH_STRINGIZE(TmplList)) + " - " + std::to_string(index), Tags } ), index++, 0)... };/* NOLINT */ \ + }\ + };\ + static int INTERNAL_CATCH_UNIQUE_NAME( globalRegistrar ) = [](){\ + using TestInit = decltype(convert<TestNameClass>(TmplList {}));\ + TestInit t;\ + t.reg_tests();\ + return 0;\ + }(); \ + }}\ + CATCH_INTERNAL_UNSUPPRESS_GLOBALS_WARNINGS \ + 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( ____C_A_T_C_H____T_E_M_P_L_A_T_E____T_E_S_T____ ), INTERNAL_CATCH_UNIQUE_NAME( ____C_A_T_C_H____T_E_M_P_L_A_T_E____T_E_S_T____F_U_N_C____ ), ClassName, Name, Tags, TmplList ) + +// end catch_test_registry.h +// start catch_capture.hpp + +// start catch_assertionhandler.h + +// start catch_assertioninfo.h + +// start catch_result_type.h namespace Catch { @@ -819,12 +1324,8 @@ namespace Catch { }; }; - inline bool isOk( ResultWas::OfType resultType ) { - return ( resultType & ResultWas::FailureBit ) == 0; - } - inline bool isJustInfo( int flags ) { - return flags == ResultWas::Info; - } + bool isOk( ResultWas::OfType resultType ); + bool isJustInfo( int flags ); // ResultDisposition::Flags enum struct ResultDisposition { enum Flags { @@ -835,1241 +1336,1226 @@ namespace Catch { SuppressFail = 0x08 // Failures are reported but do not fail the test }; }; - inline ResultDisposition::Flags operator | ( ResultDisposition::Flags lhs, ResultDisposition::Flags rhs ) { - return static_cast<ResultDisposition::Flags>( static_cast<int>( lhs ) | static_cast<int>( rhs ) ); - } + ResultDisposition::Flags operator | ( ResultDisposition::Flags lhs, ResultDisposition::Flags rhs ); - inline bool shouldContinueOnFailure( int flags ) { return ( flags & ResultDisposition::ContinueOnFailure ) != 0; } - inline bool isFalseTest( int flags ) { return ( flags & ResultDisposition::FalseTest ) != 0; } - inline bool shouldSuppressFailure( int flags ) { return ( flags & ResultDisposition::SuppressFail ) != 0; } + bool shouldContinueOnFailure( int flags ); + inline bool isFalseTest( int flags ) { return ( flags & ResultDisposition::FalseTest ) != 0; } + bool shouldSuppressFailure( int flags ); } // end namespace Catch -// #included from: catch_assertionresult.h -#define TWOBLUECUBES_CATCH_ASSERTIONRESULT_H_INCLUDED - -#include <string> - +// end catch_result_type.h namespace Catch { - struct STATIC_ASSERT_Expression_Too_Complex_Please_Rewrite_As_Binary_Comparison; - - struct DecomposedExpression - { - virtual ~DecomposedExpression() {} - virtual bool isBinaryExpression() const { - return false; - } - virtual void reconstructExpression( std::string& dest ) const = 0; - - // Only simple binary comparisons can be decomposed. - // If more complex check is required then wrap sub-expressions in parentheses. - template<typename T> STATIC_ASSERT_Expression_Too_Complex_Please_Rewrite_As_Binary_Comparison& operator + ( T const& ); - template<typename T> STATIC_ASSERT_Expression_Too_Complex_Please_Rewrite_As_Binary_Comparison& operator - ( T const& ); - template<typename T> STATIC_ASSERT_Expression_Too_Complex_Please_Rewrite_As_Binary_Comparison& operator * ( T const& ); - template<typename T> STATIC_ASSERT_Expression_Too_Complex_Please_Rewrite_As_Binary_Comparison& operator / ( T const& ); - template<typename T> STATIC_ASSERT_Expression_Too_Complex_Please_Rewrite_As_Binary_Comparison& operator % ( T const& ); - template<typename T> STATIC_ASSERT_Expression_Too_Complex_Please_Rewrite_As_Binary_Comparison& operator && ( T const& ); - template<typename T> STATIC_ASSERT_Expression_Too_Complex_Please_Rewrite_As_Binary_Comparison& operator || ( T const& ); - }; - struct AssertionInfo { - AssertionInfo() {} - AssertionInfo( std::string const& _macroName, - SourceLineInfo const& _lineInfo, - std::string const& _capturedExpression, - ResultDisposition::Flags _resultDisposition ); - - std::string macroName; + StringRef macroName; SourceLineInfo lineInfo; - std::string capturedExpression; + StringRef capturedExpression; ResultDisposition::Flags resultDisposition; - }; - - struct AssertionResultData - { - AssertionResultData() : decomposedExpression( CATCH_NULL ) - , resultType( ResultWas::Unknown ) - , negated( false ) - , parenthesized( false ) {} - - void negate( bool parenthesize ) { - negated = !negated; - parenthesized = parenthesize; - if( resultType == ResultWas::Ok ) - resultType = ResultWas::ExpressionFailed; - else if( resultType == ResultWas::ExpressionFailed ) - resultType = ResultWas::Ok; - } - - std::string const& reconstructExpression() const { - if( decomposedExpression != CATCH_NULL ) { - decomposedExpression->reconstructExpression( reconstructedExpression ); - if( parenthesized ) { - reconstructedExpression.insert( 0, 1, '(' ); - reconstructedExpression.append( 1, ')' ); - } - if( negated ) { - reconstructedExpression.insert( 0, 1, '!' ); - } - decomposedExpression = CATCH_NULL; - } - return reconstructedExpression; - } - mutable DecomposedExpression const* decomposedExpression; - mutable std::string reconstructedExpression; - std::string message; - ResultWas::OfType resultType; - bool negated; - bool parenthesized; + // We want to delete this constructor but a compiler bug in 4.8 means + // the struct is then treated as non-aggregate + //AssertionInfo() = delete; }; - class AssertionResult { - public: - AssertionResult(); - AssertionResult( AssertionInfo const& info, AssertionResultData const& data ); - ~AssertionResult(); -# ifdef CATCH_CONFIG_CPP11_GENERATED_METHODS - AssertionResult( AssertionResult const& ) = default; - AssertionResult( AssertionResult && ) = default; - AssertionResult& operator = ( AssertionResult const& ) = default; - AssertionResult& operator = ( AssertionResult && ) = default; -# endif +} // end namespace Catch - bool isOk() const; - bool succeeded() const; - ResultWas::OfType getResultType() const; - bool hasExpression() const; - bool hasMessage() const; - std::string getExpression() const; - std::string getExpressionInMacro() const; - bool hasExpandedExpression() const; - std::string getExpandedExpression() const; - std::string getMessage() const; - SourceLineInfo getSourceInfo() const; - std::string getTestMacroName() const; - void discardDecomposedExpression() const; - void expandDecomposedExpression() const; +// end catch_assertioninfo.h +// start catch_decomposer.h - protected: - AssertionInfo m_info; - AssertionResultData m_resultData; - }; +// start catch_tostring.h -} // end namespace Catch +#include <vector> +#include <cstddef> +#include <type_traits> +#include <string> +// start catch_stream.h -// #included from: catch_matchers.hpp -#define TWOBLUECUBES_CATCH_MATCHERS_HPP_INCLUDED +#include <iosfwd> +#include <cstddef> +#include <ostream> namespace Catch { -namespace Matchers { - namespace Impl { - - namespace Generic { - template<typename ExpressionT> class AllOf; - template<typename ExpressionT> class AnyOf; - template<typename ExpressionT> class Not; - } - template<typename ExpressionT> - struct Matcher : SharedImpl<IShared> - { - typedef ExpressionT ExpressionType; + std::ostream& cout(); + std::ostream& cerr(); + std::ostream& clog(); - virtual ~Matcher() {} - virtual Ptr<Matcher> clone() const = 0; - virtual bool match( ExpressionT const& expr ) const = 0; - virtual std::string toString() const = 0; + class StringRef; - Generic::AllOf<ExpressionT> operator && ( Matcher<ExpressionT> const& other ) const; - Generic::AnyOf<ExpressionT> operator || ( Matcher<ExpressionT> const& other ) const; - Generic::Not<ExpressionT> operator ! () const; + struct IStream { + virtual ~IStream(); + virtual std::ostream& stream() const = 0; }; - template<typename DerivedT, typename ExpressionT> - struct MatcherImpl : Matcher<ExpressionT> { + auto makeStream( StringRef const &filename ) -> IStream const*; - virtual Ptr<Matcher<ExpressionT> > clone() const { - return Ptr<Matcher<ExpressionT> >( new DerivedT( static_cast<DerivedT const&>( *this ) ) ); - } - }; + class ReusableStringStream { + std::size_t m_index; + std::ostream* m_oss; + public: + ReusableStringStream(); + ~ReusableStringStream(); - namespace Generic { - template<typename ExpressionT> - class Not : public MatcherImpl<Not<ExpressionT>, ExpressionT> { - public: - explicit Not( Matcher<ExpressionT> const& matcher ) : m_matcher(matcher.clone()) {} - Not( Not const& other ) : m_matcher( other.m_matcher ) {} + auto str() const -> std::string; - virtual bool match( ExpressionT const& expr ) const CATCH_OVERRIDE { - return !m_matcher->match( expr ); - } + template<typename T> + auto operator << ( T const& value ) -> ReusableStringStream& { + *m_oss << value; + return *this; + } + auto get() -> std::ostream& { return *m_oss; } + }; +} - virtual std::string toString() const CATCH_OVERRIDE { - return "not " + m_matcher->toString(); - } - private: - Ptr< Matcher<ExpressionT> > m_matcher; - }; +// end catch_stream.h +// start catch_interfaces_enum_values_registry.h - template<typename ExpressionT> - class AllOf : public MatcherImpl<AllOf<ExpressionT>, ExpressionT> { - public: +#include <vector> - AllOf() {} - AllOf( AllOf const& other ) : m_matchers( other.m_matchers ) {} +namespace Catch { - AllOf& add( Matcher<ExpressionT> const& matcher ) { - m_matchers.push_back( matcher.clone() ); - return *this; - } - virtual bool match( ExpressionT const& expr ) const - { - for( std::size_t i = 0; i < m_matchers.size(); ++i ) - if( !m_matchers[i]->match( expr ) ) - return false; - return true; - } - virtual std::string toString() const { - std::ostringstream oss; - oss << "( "; - for( std::size_t i = 0; i < m_matchers.size(); ++i ) { - if( i != 0 ) - oss << " and "; - oss << m_matchers[i]->toString(); - } - oss << " )"; - return oss.str(); - } + namespace Detail { + struct EnumInfo { + StringRef m_name; + std::vector<std::pair<int, std::string>> m_values; - AllOf operator && ( Matcher<ExpressionT> const& other ) const { - AllOf allOfExpr( *this ); - allOfExpr.add( other ); - return allOfExpr; - } + ~EnumInfo(); - private: - std::vector<Ptr<Matcher<ExpressionT> > > m_matchers; + StringRef lookup( int value ) const; }; + } // namespace Detail - template<typename ExpressionT> - class AnyOf : public MatcherImpl<AnyOf<ExpressionT>, ExpressionT> { - public: + struct IMutableEnumValuesRegistry { + virtual ~IMutableEnumValuesRegistry(); - AnyOf() {} - AnyOf( AnyOf const& other ) : m_matchers( other.m_matchers ) {} + virtual Detail::EnumInfo const& registerEnum( StringRef enumName, StringRef allEnums, std::vector<int> const& values ) = 0; - AnyOf& add( Matcher<ExpressionT> const& matcher ) { - m_matchers.push_back( matcher.clone() ); - return *this; - } - virtual bool match( ExpressionT const& expr ) const - { - for( std::size_t i = 0; i < m_matchers.size(); ++i ) - if( m_matchers[i]->match( expr ) ) - return true; - return false; - } - virtual std::string toString() const { - std::ostringstream oss; - oss << "( "; - for( std::size_t i = 0; i < m_matchers.size(); ++i ) { - if( i != 0 ) - oss << " or "; - oss << m_matchers[i]->toString(); - } - oss << " )"; - return oss.str(); - } - - AnyOf operator || ( Matcher<ExpressionT> const& other ) const { - AnyOf anyOfExpr( *this ); - anyOfExpr.add( other ); - return anyOfExpr; - } + template<typename E> + Detail::EnumInfo const& registerEnum( StringRef enumName, StringRef allEnums, std::initializer_list<E> values ) { + std::vector<int> intValues; + intValues.reserve( values.size() ); + for( auto enumValue : values ) + intValues.push_back( static_cast<int>( enumValue ) ); + return registerEnum( enumName, allEnums, intValues ); + } + }; - private: - std::vector<Ptr<Matcher<ExpressionT> > > m_matchers; - }; +} // Catch - } // namespace Generic +// end catch_interfaces_enum_values_registry.h - template<typename ExpressionT> - Generic::AllOf<ExpressionT> Matcher<ExpressionT>::operator && ( Matcher<ExpressionT> const& other ) const { - Generic::AllOf<ExpressionT> allOfExpr; - allOfExpr.add( *this ); - allOfExpr.add( other ); - return allOfExpr; - } +#ifdef CATCH_CONFIG_CPP17_STRING_VIEW +#include <string_view> +#endif - template<typename ExpressionT> - Generic::AnyOf<ExpressionT> Matcher<ExpressionT>::operator || ( Matcher<ExpressionT> const& other ) const { - Generic::AnyOf<ExpressionT> anyOfExpr; - anyOfExpr.add( *this ); - anyOfExpr.add( other ); - return anyOfExpr; - } +#ifdef __OBJC__ +// start catch_objc_arc.hpp - template<typename ExpressionT> - Generic::Not<ExpressionT> Matcher<ExpressionT>::operator ! () const { - return Generic::Not<ExpressionT>( *this ); - } +#import <Foundation/Foundation.h> - namespace StdString { +#ifdef __has_feature +#define CATCH_ARC_ENABLED __has_feature(objc_arc) +#else +#define CATCH_ARC_ENABLED 0 +#endif - inline std::string makeString( std::string const& str ) { return str; } - inline std::string makeString( const char* str ) { return str ? std::string( str ) : std::string(); } +void arcSafeRelease( NSObject* obj ); +id performOptionalSelector( id obj, SEL sel ); - struct CasedString - { - CasedString( std::string const& str, CaseSensitive::Choice caseSensitivity ) - : m_caseSensitivity( caseSensitivity ), - m_str( adjustString( str ) ) - {} - std::string adjustString( std::string const& str ) const { - return m_caseSensitivity == CaseSensitive::No - ? toLower( str ) - : str; +#if !CATCH_ARC_ENABLED +inline void arcSafeRelease( NSObject* obj ) { + [obj release]; +} +inline id performOptionalSelector( id obj, SEL sel ) { + if( [obj respondsToSelector: sel] ) + return [obj performSelector: sel]; + return nil; +} +#define CATCH_UNSAFE_UNRETAINED +#define CATCH_ARC_STRONG +#else +inline void arcSafeRelease( NSObject* ){} +inline id performOptionalSelector( id obj, SEL sel ) { +#ifdef __clang__ +#pragma clang diagnostic push +#pragma clang diagnostic ignored "-Warc-performSelector-leaks" +#endif + if( [obj respondsToSelector: sel] ) + return [obj performSelector: sel]; +#ifdef __clang__ +#pragma clang diagnostic pop +#endif + return nil; +} +#define CATCH_UNSAFE_UNRETAINED __unsafe_unretained +#define CATCH_ARC_STRONG __strong +#endif - } - std::string toStringSuffix() const - { - return m_caseSensitivity == CaseSensitive::No - ? " (case insensitive)" - : std::string(); - } - CaseSensitive::Choice m_caseSensitivity; - std::string m_str; - }; +// end catch_objc_arc.hpp +#endif - struct Equals : MatcherImpl<Equals, std::string> { - Equals( std::string const& str, CaseSensitive::Choice caseSensitivity = CaseSensitive::Yes ) - : m_data( str, caseSensitivity ) - {} - Equals( Equals const& other ) : m_data( other.m_data ){} +#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 - virtual ~Equals(); +namespace Catch { + namespace Detail { - virtual bool match( std::string const& expr ) const { - return m_data.m_str == m_data.adjustString( expr );; - } - virtual std::string toString() const { - return "equals: \"" + m_data.m_str + '"' + m_data.toStringSuffix(); - } + extern const std::string unprintableString; - CasedString m_data; - }; + std::string rawMemoryToString( const void *object, std::size_t size ); - struct Contains : MatcherImpl<Contains, std::string> { - Contains( std::string const& substr, CaseSensitive::Choice caseSensitivity = CaseSensitive::Yes ) - : m_data( substr, caseSensitivity ){} - Contains( Contains const& other ) : m_data( other.m_data ){} + template<typename T> + std::string rawMemoryToString( const T& object ) { + return rawMemoryToString( &object, sizeof(object) ); + } - virtual ~Contains(); + template<typename T> + class IsStreamInsertable { + template<typename SS, typename TT> + static auto test(int) + -> decltype(std::declval<SS&>() << std::declval<TT>(), std::true_type()); - virtual bool match( std::string const& expr ) const { - return m_data.adjustString( expr ).find( m_data.m_str ) != std::string::npos; - } - virtual std::string toString() const { - return "contains: \"" + m_data.m_str + '"' + m_data.toStringSuffix(); - } + template<typename, typename> + static auto test(...)->std::false_type; - CasedString m_data; + public: + static const bool value = decltype(test<std::ostream, const T&>(0))::value; }; - struct StartsWith : MatcherImpl<StartsWith, std::string> { - StartsWith( std::string const& substr, CaseSensitive::Choice caseSensitivity = CaseSensitive::Yes ) - : m_data( substr, caseSensitivity ){} + template<typename E> + std::string convertUnknownEnumToString( E e ); - StartsWith( StartsWith const& other ) : m_data( other.m_data ){} + template<typename T> + typename std::enable_if< + !std::is_enum<T>::value && !std::is_base_of<std::exception, T>::value, + std::string>::type convertUnstreamable( T const& ) { + return Detail::unprintableString; + } + template<typename T> + typename std::enable_if< + !std::is_enum<T>::value && std::is_base_of<std::exception, T>::value, + std::string>::type convertUnstreamable(T const& ex) { + return ex.what(); + } - virtual ~StartsWith(); + template<typename T> + typename std::enable_if< + std::is_enum<T>::value + , std::string>::type convertUnstreamable( T const& value ) { + return convertUnknownEnumToString( value ); + } - virtual bool match( std::string const& expr ) const { - return startsWith( m_data.adjustString( expr ), m_data.m_str ); - } - virtual std::string toString() const { - return "starts with: \"" + m_data.m_str + '"' + m_data.toStringSuffix(); - } +#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 - CasedString m_data; - }; + } // namespace Detail - struct EndsWith : MatcherImpl<EndsWith, std::string> { - EndsWith( std::string const& substr, CaseSensitive::Choice caseSensitivity = CaseSensitive::Yes ) - : m_data( substr, caseSensitivity ){} - EndsWith( EndsWith const& other ) : m_data( other.m_data ){} + // If we decide for C++14, change these to enable_if_ts + template <typename T, typename = void> + struct StringMaker { + template <typename Fake = T> + static + typename std::enable_if<::Catch::Detail::IsStreamInsertable<Fake>::value, std::string>::type + convert(const Fake& value) { + ReusableStringStream rss; + // NB: call using the function-like syntax to avoid ambiguity with + // user-defined templated operator<< under clang. + rss.operator<<(value); + return rss.str(); + } + + template <typename Fake = T> + static + typename std::enable_if<!::Catch::Detail::IsStreamInsertable<Fake>::value, std::string>::type + convert( const Fake& value ) { +#if !defined(CATCH_CONFIG_FALLBACK_STRINGIFIER) + return Detail::convertUnstreamable(value); +#else + return CATCH_CONFIG_FALLBACK_STRINGIFIER(value); +#endif + } + }; - virtual ~EndsWith(); + namespace Detail { - virtual bool match( std::string const& expr ) const { - return endsWith( m_data.adjustString( expr ), m_data.m_str ); - } - virtual std::string toString() const { - return "ends with: \"" + m_data.m_str + '"' + m_data.toStringSuffix(); - } + // 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<typename std::remove_cv<typename std::remove_reference<T>::type>::type>::convert(e); + } - CasedString m_data; - }; - } // namespace StdString - } // namespace Impl + template<typename E> + std::string convertUnknownEnumToString( E e ) { + return ::Catch::Detail::stringify(static_cast<typename std::underlying_type<E>::type>(e)); + } - // The following functions create the actual matcher objects. - // This allows the types to be inferred - template<typename ExpressionT> - inline Impl::Generic::Not<ExpressionT> Not( Impl::Matcher<ExpressionT> const& m ) { - return Impl::Generic::Not<ExpressionT>( m ); - } +#if defined(_MANAGED) + template <typename T> + std::string stringify( T^ e ) { + return ::Catch::StringMaker<T^>::convert(e); + } +#endif - template<typename ExpressionT> - inline Impl::Generic::AllOf<ExpressionT> AllOf( Impl::Matcher<ExpressionT> const& m1, - Impl::Matcher<ExpressionT> const& m2 ) { - return Impl::Generic::AllOf<ExpressionT>().add( m1 ).add( m2 ); - } - template<typename ExpressionT> - inline Impl::Generic::AllOf<ExpressionT> AllOf( Impl::Matcher<ExpressionT> const& m1, - Impl::Matcher<ExpressionT> const& m2, - Impl::Matcher<ExpressionT> const& m3 ) { - return Impl::Generic::AllOf<ExpressionT>().add( m1 ).add( m2 ).add( m3 ); - } - template<typename ExpressionT> - inline Impl::Generic::AnyOf<ExpressionT> AnyOf( Impl::Matcher<ExpressionT> const& m1, - Impl::Matcher<ExpressionT> const& m2 ) { - return Impl::Generic::AnyOf<ExpressionT>().add( m1 ).add( m2 ); - } - template<typename ExpressionT> - inline Impl::Generic::AnyOf<ExpressionT> AnyOf( Impl::Matcher<ExpressionT> const& m1, - Impl::Matcher<ExpressionT> const& m2, - Impl::Matcher<ExpressionT> const& m3 ) { - return Impl::Generic::AnyOf<ExpressionT>().add( m1 ).add( m2 ).add( m3 ); - } + } // namespace Detail - inline Impl::StdString::Equals Equals( std::string const& str, CaseSensitive::Choice caseSensitivity = CaseSensitive::Yes ) { - return Impl::StdString::Equals( str, caseSensitivity ); - } - inline Impl::StdString::Equals Equals( const char* str, CaseSensitive::Choice caseSensitivity = CaseSensitive::Yes ) { - return Impl::StdString::Equals( Impl::StdString::makeString( str ), caseSensitivity ); - } - inline Impl::StdString::Contains Contains( std::string const& substr, CaseSensitive::Choice caseSensitivity = CaseSensitive::Yes ) { - return Impl::StdString::Contains( substr, caseSensitivity ); - } - inline Impl::StdString::Contains Contains( const char* substr, CaseSensitive::Choice caseSensitivity = CaseSensitive::Yes ) { - return Impl::StdString::Contains( Impl::StdString::makeString( substr ), caseSensitivity ); - } - inline Impl::StdString::StartsWith StartsWith( std::string const& substr ) { - return Impl::StdString::StartsWith( substr ); - } - inline Impl::StdString::StartsWith StartsWith( const char* substr ) { - return Impl::StdString::StartsWith( Impl::StdString::makeString( substr ) ); - } - inline Impl::StdString::EndsWith EndsWith( std::string const& substr ) { - return Impl::StdString::EndsWith( substr ); - } - inline Impl::StdString::EndsWith EndsWith( const char* substr ) { - return Impl::StdString::EndsWith( Impl::StdString::makeString( substr ) ); - } + // Some predefined specializations -} // namespace Matchers + template<> + struct StringMaker<std::string> { + static std::string convert(const std::string& str); + }; -using namespace Matchers; +#ifdef CATCH_CONFIG_CPP17_STRING_VIEW + template<> + struct StringMaker<std::string_view> { + static std::string convert(std::string_view str); + }; +#endif -} // namespace Catch + template<> + struct StringMaker<char const *> { + static std::string convert(char const * str); + }; + template<> + struct StringMaker<char *> { + static std::string convert(char * str); + }; -namespace Catch { +#ifdef CATCH_CONFIG_WCHAR + template<> + struct StringMaker<std::wstring> { + static std::string convert(const std::wstring& wstr); + }; - struct TestFailureException{}; +# ifdef CATCH_CONFIG_CPP17_STRING_VIEW + template<> + struct StringMaker<std::wstring_view> { + static std::string convert(std::wstring_view str); + }; +# endif - template<typename T> class ExpressionLhs; + 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 - struct CopyableStream { - CopyableStream() {} - CopyableStream( CopyableStream const& other ) { - oss << other.oss.str(); + // TBD: Should we use `strnlen` to ensure that we don't go out of the buffer, + // while keeping string semantics? + template<int SZ> + struct StringMaker<char[SZ]> { + static std::string convert(char const* str) { + return ::Catch::Detail::stringify(std::string{ str }); } - CopyableStream& operator=( CopyableStream const& other ) { - oss.str(std::string()); - oss << other.oss.str(); - return *this; + }; + template<int SZ> + struct StringMaker<signed char[SZ]> { + static std::string convert(signed char const* str) { + return ::Catch::Detail::stringify(std::string{ reinterpret_cast<char const *>(str) }); } - std::ostringstream oss; }; - - class ResultBuilder : public DecomposedExpression { - public: - ResultBuilder( char const* macroName, - SourceLineInfo const& lineInfo, - char const* capturedExpression, - ResultDisposition::Flags resultDisposition, - char const* secondArg = "" ); - - template<typename T> - ExpressionLhs<T const&> operator <= ( T const& operand ); - ExpressionLhs<bool> operator <= ( bool value ); - - template<typename T> - ResultBuilder& operator << ( T const& value ) { - m_stream.oss << value; - return *this; + template<int SZ> + struct StringMaker<unsigned char[SZ]> { + static std::string convert(unsigned char const* str) { + return ::Catch::Detail::stringify(std::string{ reinterpret_cast<char const *>(str) }); } + }; - ResultBuilder& setResultType( ResultWas::OfType result ); - ResultBuilder& setResultType( bool result ); - - void endExpression( DecomposedExpression const& expr ); - - virtual void reconstructExpression( std::string& dest ) const CATCH_OVERRIDE; - - AssertionResult build() const; - AssertionResult build( DecomposedExpression const& expr ) const; + 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); + }; - void useActiveException( ResultDisposition::Flags resultDisposition = ResultDisposition::Normal ); - void captureResult( ResultWas::OfType resultType ); - void captureExpression(); - void captureExpectedException( std::string const& expectedMessage ); - void captureExpectedException( Matchers::Impl::Matcher<std::string> const& matcher ); - void handleResult( AssertionResult const& result ); - void react(); - bool shouldDebugBreak() const; - bool allowThrows() const; + template<> + struct StringMaker<bool> { + static std::string convert(bool b); + }; - template<typename ArgT, typename MatcherT> - void captureMatch( ArgT const& arg, MatcherT const& matcher, char const* matcherString ); + 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); + }; - private: - AssertionInfo m_assertionInfo; - AssertionResultData m_data; - CopyableStream m_stream; + template<> + struct StringMaker<std::nullptr_t> { + static std::string convert(std::nullptr_t); + }; - bool m_shouldDebugBreak; - bool m_shouldThrow; + template<> + struct StringMaker<float> { + static std::string convert(float value); + static int precision; }; -} // namespace Catch + template<> + struct StringMaker<double> { + static std::string convert(double value); + static int precision; + }; -// Include after due to circular dependency: -// #included from: catch_expression_lhs.hpp -#define TWOBLUECUBES_CATCH_EXPRESSION_LHS_HPP_INCLUDED + 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"; + } + } + }; -// #included from: catch_evaluate.hpp -#define TWOBLUECUBES_CATCH_EVALUATE_HPP_INCLUDED + 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"; + } + } + }; -#ifdef _MSC_VER -#pragma warning(push) -#pragma warning(disable:4389) // '==' : signed/unsigned mismatch +#if defined(_MANAGED) + template <typename T> + struct StringMaker<T^> { + static std::string convert( T^ ref ) { + return ::Catch::Detail::clrReferenceToString(ref); + } + }; #endif -#include <cstddef> + namespace Detail { + template<typename InputIterator> + std::string rangeToString(InputIterator first, InputIterator 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 { -namespace Internal { +#ifdef __OBJC__ + template<> + struct StringMaker<NSString*> { + static std::string convert(NSString * nsstring) { + if (!nsstring) + return "nil"; + return std::string("@") + [nsstring UTF8String]; + } + }; + template<> + struct StringMaker<NSObject*> { + static std::string convert(NSObject* nsObject) { + return ::Catch::Detail::stringify([nsObject description]); + } - enum Operator { - IsEqualTo, - IsNotEqualTo, - IsLessThan, - IsGreaterThan, - IsLessThanOrEqualTo, - IsGreaterThanOrEqualTo }; + namespace Detail { + inline std::string stringify( NSString* nsstring ) { + return StringMaker<NSString*>::convert( nsstring ); + } - template<Operator Op> struct OperatorTraits { static const char* getName(){ return "*error*"; } }; - template<> struct OperatorTraits<IsEqualTo> { static const char* getName(){ return "=="; } }; - template<> struct OperatorTraits<IsNotEqualTo> { static const char* getName(){ return "!="; } }; - template<> struct OperatorTraits<IsLessThan> { static const char* getName(){ return "<"; } }; - template<> struct OperatorTraits<IsGreaterThan> { static const char* getName(){ return ">"; } }; - template<> struct OperatorTraits<IsLessThanOrEqualTo> { static const char* getName(){ return "<="; } }; - template<> struct OperatorTraits<IsGreaterThanOrEqualTo>{ static const char* getName(){ return ">="; } }; + } // namespace Detail +#endif // __OBJC__ - template<typename T> - inline T& opCast(T const& t) { return const_cast<T&>(t); } +} // namespace Catch -// nullptr_t support based on pull request #154 from Konstantin Baumann -#ifdef CATCH_CONFIG_CPP11_NULLPTR - inline std::nullptr_t opCast(std::nullptr_t) { return nullptr; } -#endif // CATCH_CONFIG_CPP11_NULLPTR +////////////////////////////////////////////////////// +// Separate std-lib types stringification, so it can be selectively enabled +// This means that we do not bring in - // So the compare overloads can be operator agnostic we convey the operator as a template - // enum, which is used to specialise an Evaluator for doing the comparison. - template<typename T1, typename T2, Operator Op> - class Evaluator{}; +#if defined(CATCH_CONFIG_ENABLE_ALL_STRINGMAKERS) +# define CATCH_CONFIG_ENABLE_PAIR_STRINGMAKER +# define CATCH_CONFIG_ENABLE_TUPLE_STRINGMAKER +# define CATCH_CONFIG_ENABLE_VARIANT_STRINGMAKER +# define CATCH_CONFIG_ENABLE_CHRONO_STRINGMAKER +# define CATCH_CONFIG_ENABLE_OPTIONAL_STRINGMAKER +#endif +// Separate std::pair specialization +#if defined(CATCH_CONFIG_ENABLE_PAIR_STRINGMAKER) +#include <utility> +namespace Catch { template<typename T1, typename T2> - struct Evaluator<T1, T2, IsEqualTo> { - static bool evaluate( T1 const& lhs, T2 const& rhs) { - return bool( opCast( lhs ) == opCast( rhs ) ); - } - }; - template<typename T1, typename T2> - struct Evaluator<T1, T2, IsNotEqualTo> { - static bool evaluate( T1 const& lhs, T2 const& rhs ) { - return bool( opCast( lhs ) != opCast( rhs ) ); + 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(); } }; - template<typename T1, typename T2> - struct Evaluator<T1, T2, IsLessThan> { - static bool evaluate( T1 const& lhs, T2 const& rhs ) { - return bool( opCast( lhs ) < opCast( rhs ) ); +} +#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) { + ReusableStringStream rss; + if (optional.has_value()) { + rss << ::Catch::Detail::stringify(*optional); + } else { + rss << "{ }"; + } + return rss.str(); } }; - template<typename T1, typename T2> - struct Evaluator<T1, T2, IsGreaterThan> { - static bool evaluate( T1 const& lhs, T2 const& rhs ) { - return bool( opCast( lhs ) > opCast( rhs ) ); +} +#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(); } }; - template<typename T1, typename T2> - struct Evaluator<T1, T2, IsGreaterThanOrEqualTo> { - static bool evaluate( T1 const& lhs, T2 const& rhs ) { - return bool( opCast( lhs ) >= opCast( rhs ) ); +} +#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 T1, typename T2> - struct Evaluator<T1, T2, IsLessThanOrEqualTo> { - static bool evaluate( T1 const& lhs, T2 const& rhs ) { - return bool( opCast( lhs ) <= opCast( rhs ) ); + + 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 - template<Operator Op, typename T1, typename T2> - bool applyEvaluator( T1 const& lhs, T2 const& rhs ) { - return Evaluator<T1, T2, Op>::evaluate( lhs, rhs ); - } - - // This level of indirection allows us to specialise for integer types - // to avoid signed/ unsigned warnings - - // "base" overload - template<Operator Op, typename T1, typename T2> - bool compare( T1 const& lhs, T2 const& rhs ) { - return Evaluator<T1, T2, Op>::evaluate( lhs, rhs ); - } - - // unsigned X to int - template<Operator Op> bool compare( unsigned int lhs, int rhs ) { - return applyEvaluator<Op>( lhs, static_cast<unsigned int>( rhs ) ); - } - template<Operator Op> bool compare( unsigned long lhs, int rhs ) { - return applyEvaluator<Op>( lhs, static_cast<unsigned int>( rhs ) ); - } - template<Operator Op> bool compare( unsigned char lhs, int rhs ) { - return applyEvaluator<Op>( lhs, static_cast<unsigned int>( rhs ) ); - } +namespace Catch { + struct not_this_one {}; // Tag type for detecting which begin/ end are being selected - // unsigned X to long - template<Operator Op> bool compare( unsigned int lhs, long rhs ) { - return applyEvaluator<Op>( lhs, static_cast<unsigned long>( rhs ) ); - } - template<Operator Op> bool compare( unsigned long lhs, long rhs ) { - return applyEvaluator<Op>( lhs, static_cast<unsigned long>( rhs ) ); - } - template<Operator Op> bool compare( unsigned char lhs, long rhs ) { - return applyEvaluator<Op>( lhs, static_cast<unsigned long>( rhs ) ); - } + // Import begin/ end from std here so they are considered alongside the fallback (...) overloads in this namespace + using std::begin; + using std::end; - // int to unsigned X - template<Operator Op> bool compare( int lhs, unsigned int rhs ) { - return applyEvaluator<Op>( static_cast<unsigned int>( lhs ), rhs ); - } - template<Operator Op> bool compare( int lhs, unsigned long rhs ) { - return applyEvaluator<Op>( static_cast<unsigned int>( lhs ), rhs ); - } - template<Operator Op> bool compare( int lhs, unsigned char rhs ) { - return applyEvaluator<Op>( static_cast<unsigned int>( lhs ), rhs ); - } + not_this_one begin( ... ); + not_this_one end( ... ); - // long to unsigned X - template<Operator Op> bool compare( long lhs, unsigned int rhs ) { - return applyEvaluator<Op>( static_cast<unsigned long>( lhs ), rhs ); - } - template<Operator Op> bool compare( long lhs, unsigned long rhs ) { - return applyEvaluator<Op>( static_cast<unsigned long>( lhs ), rhs ); - } - template<Operator Op> bool compare( long lhs, unsigned char rhs ) { - return applyEvaluator<Op>( static_cast<unsigned long>( lhs ), rhs ); - } + template <typename T> + struct is_range { + static const bool value = + !std::is_same<decltype(begin(std::declval<T>())), not_this_one>::value && + !std::is_same<decltype(end(std::declval<T>())), not_this_one>::value; + }; - // pointer to long (when comparing against NULL) - template<Operator Op, typename T> bool compare( long lhs, T* rhs ) { - return Evaluator<T*, T*, Op>::evaluate( reinterpret_cast<T*>( lhs ), rhs ); - } - template<Operator Op, typename T> bool compare( T* lhs, long rhs ) { - return Evaluator<T*, T*, Op>::evaluate( lhs, reinterpret_cast<T*>( rhs ) ); - } +#if defined(_MANAGED) // Managed types are never ranges + template <typename T> + struct is_range<T^> { + static const bool value = false; + }; +#endif - // pointer to int (when comparing against NULL) - template<Operator Op, typename T> bool compare( int lhs, T* rhs ) { - return Evaluator<T*, T*, Op>::evaluate( reinterpret_cast<T*>( lhs ), rhs ); - } - template<Operator Op, typename T> bool compare( T* lhs, int rhs ) { - return Evaluator<T*, T*, Op>::evaluate( lhs, reinterpret_cast<T*>( rhs ) ); + template<typename Range> + std::string rangeToString( Range const& range ) { + return ::Catch::Detail::rangeToString( begin( range ), end( range ) ); } -#ifdef CATCH_CONFIG_CPP11_LONG_LONG - // long long to unsigned X - template<Operator Op> bool compare( long long lhs, unsigned int rhs ) { - return applyEvaluator<Op>( static_cast<unsigned long>( lhs ), rhs ); - } - template<Operator Op> bool compare( long long lhs, unsigned long rhs ) { - return applyEvaluator<Op>( static_cast<unsigned long>( lhs ), rhs ); - } - template<Operator Op> bool compare( long long lhs, unsigned long long rhs ) { - return applyEvaluator<Op>( static_cast<unsigned long>( lhs ), rhs ); - } - template<Operator Op> bool compare( long long lhs, unsigned char rhs ) { - return applyEvaluator<Op>( static_cast<unsigned long>( lhs ), rhs ); + // 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(); } - // unsigned long long to X - template<Operator Op> bool compare( unsigned long long lhs, int rhs ) { - return applyEvaluator<Op>( static_cast<long>( lhs ), rhs ); - } - template<Operator Op> bool compare( unsigned long long lhs, long rhs ) { - return applyEvaluator<Op>( static_cast<long>( lhs ), rhs ); - } - template<Operator Op> bool compare( unsigned long long lhs, long long rhs ) { - return applyEvaluator<Op>( static_cast<long>( lhs ), rhs ); - } - template<Operator Op> bool compare( unsigned long long lhs, char rhs ) { - return applyEvaluator<Op>( static_cast<long>( lhs ), rhs ); - } + template<typename R> + struct StringMaker<R, typename std::enable_if<is_range<R>::value && !::Catch::Detail::IsStreamInsertable<R>::value>::type> { + static std::string convert( R const& range ) { + return rangeToString( range ); + } + }; - // pointer to long long (when comparing against NULL) - template<Operator Op, typename T> bool compare( long long lhs, T* rhs ) { - return Evaluator<T*, T*, Op>::evaluate( reinterpret_cast<T*>( lhs ), rhs ); - } - template<Operator Op, typename T> bool compare( T* lhs, long long rhs ) { - return Evaluator<T*, T*, Op>::evaluate( lhs, reinterpret_cast<T*>( rhs ) ); - } -#endif // CATCH_CONFIG_CPP11_LONG_LONG + template <typename T, int SZ> + struct StringMaker<T[SZ]> { + static std::string convert(T const(&arr)[SZ]) { + return rangeToString(arr); + } + }; -#ifdef CATCH_CONFIG_CPP11_NULLPTR - // pointer to nullptr_t (when comparing against nullptr) - template<Operator Op, typename T> bool compare( std::nullptr_t, T* rhs ) { - return Evaluator<T*, T*, Op>::evaluate( nullptr, rhs ); - } - template<Operator Op, typename T> bool compare( T* lhs, std::nullptr_t ) { - return Evaluator<T*, T*, Op>::evaluate( lhs, nullptr ); - } -#endif // CATCH_CONFIG_CPP11_NULLPTR +} // namespace Catch -} // end of namespace Internal -} // end of namespace Catch +// Separate std::chrono::duration specialization +#if defined(CATCH_CONFIG_ENABLE_CHRONO_STRINGMAKER) +#include <ctime> +#include <ratio> +#include <chrono> -#ifdef _MSC_VER -#pragma warning(pop) -#endif +namespace Catch { -// #included from: catch_tostring.h -#define TWOBLUECUBES_CATCH_TOSTRING_H_INCLUDED +template <class Ratio> +struct ratio_string { + static std::string symbol(); +}; -#include <sstream> -#include <iomanip> -#include <limits> -#include <vector> -#include <cstddef> +template <class Ratio> +std::string ratio_string<Ratio>::symbol() { + Catch::ReusableStringStream rss; + rss << '[' << Ratio::num << '/' + << Ratio::den << ']'; + return rss.str(); +} +template <> +struct ratio_string<std::atto> { + static std::string symbol(); +}; +template <> +struct ratio_string<std::femto> { + static std::string symbol(); +}; +template <> +struct ratio_string<std::pico> { + static std::string symbol(); +}; +template <> +struct ratio_string<std::nano> { + static std::string symbol(); +}; +template <> +struct ratio_string<std::micro> { + static std::string symbol(); +}; +template <> +struct ratio_string<std::milli> { + static std::string symbol(); +}; -#ifdef __OBJC__ -// #included from: catch_objc_arc.hpp -#define TWOBLUECUBES_CATCH_OBJC_ARC_HPP_INCLUDED + //////////// + // 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(); + } + }; -#import <Foundation/Foundation.h> + //////////// + // 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 __has_feature -#define CATCH_ARC_ENABLED __has_feature(objc_arc) +#ifdef _MSC_VER + std::tm timeInfo = {}; + gmtime_s(&timeInfo, &converted); #else -#define CATCH_ARC_ENABLED 0 + std::tm* timeInfo = std::gmtime(&converted); #endif -void arcSafeRelease( NSObject* obj ); -id performOptionalSelector( id obj, SEL sel ); + auto const timeStampSize = sizeof("2017-01-16T17:06:45Z"); + char timeStamp[timeStampSize]; + const char * const fmt = "%Y-%m-%dT%H:%M:%SZ"; -#if !CATCH_ARC_ENABLED -inline void arcSafeRelease( NSObject* obj ) { - [obj release]; -} -inline id performOptionalSelector( id obj, SEL sel ) { - if( [obj respondsToSelector: sel] ) - return [obj performSelector: sel]; - return nil; -} -#define CATCH_UNSAFE_UNRETAINED -#define CATCH_ARC_STRONG +#ifdef _MSC_VER + std::strftime(timeStamp, timeStampSize, fmt, &timeInfo); #else -inline void arcSafeRelease( NSObject* ){} -inline id performOptionalSelector( id obj, SEL sel ) { -#ifdef __clang__ -#pragma clang diagnostic push -#pragma clang diagnostic ignored "-Warc-performSelector-leaks" -#endif - if( [obj respondsToSelector: sel] ) - return [obj performSelector: sel]; -#ifdef __clang__ -#pragma clang diagnostic pop + std::strftime(timeStamp, timeStampSize, fmt, timeInfo); #endif - return nil; + return std::string(timeStamp); + } + }; +} +#endif // CATCH_CONFIG_ENABLE_CHRONO_STRINGMAKER + +#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 enumInfo.lookup( static_cast<int>( value ) ); \ + } \ + }; \ } -#define CATCH_UNSAFE_UNRETAINED __unsafe_unretained -#define CATCH_ARC_STRONG __strong -#endif -#endif +#define CATCH_REGISTER_ENUM( enumName, ... ) INTERNAL_CATCH_REGISTER_ENUM( enumName, __VA_ARGS__ ) -#ifdef CATCH_CONFIG_CPP11_TUPLE -#include <tuple> +#ifdef _MSC_VER +#pragma warning(pop) #endif -#ifdef CATCH_CONFIG_CPP11_IS_ENUM -#include <type_traits> +// end catch_tostring.h +#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 namespace Catch { -// Why we're here. -template<typename T> -std::string toString( T const& value ); + struct ITransientExpression { + auto isBinaryExpression() const -> bool { return m_isBinaryExpression; } + auto getResult() const -> bool { return m_result; } + virtual void streamReconstructedExpression( std::ostream &os ) const = 0; -// Built in overloads + ITransientExpression( bool isBinaryExpression, bool result ) + : m_isBinaryExpression( isBinaryExpression ), + m_result( result ) + {} -std::string toString( std::string const& value ); -std::string toString( std::wstring const& value ); -std::string toString( const char* const value ); -std::string toString( char* const value ); -std::string toString( const wchar_t* const value ); -std::string toString( wchar_t* const value ); -std::string toString( int value ); -std::string toString( unsigned long value ); -std::string toString( unsigned int value ); -std::string toString( const double value ); -std::string toString( const float value ); -std::string toString( bool value ); -std::string toString( char value ); -std::string toString( signed char value ); -std::string toString( unsigned char value ); + // We don't actually need a virtual destructor, but many static analysers + // complain if it's not here :-( + virtual ~ITransientExpression(); -#ifdef CATCH_CONFIG_CPP11_LONG_LONG -std::string toString( long long value ); -std::string toString( unsigned long long value ); -#endif + bool m_isBinaryExpression; + bool m_result; -#ifdef CATCH_CONFIG_CPP11_NULLPTR -std::string toString( std::nullptr_t ); -#endif + }; -#ifdef __OBJC__ - std::string toString( NSString const * const& nsstring ); - std::string toString( NSString * CATCH_ARC_STRONG const& nsstring ); - std::string toString( NSObject* const& nsObject ); -#endif + void formatReconstructedExpression( std::ostream &os, std::string const& lhs, StringRef op, std::string const& rhs ); -namespace Detail { + template<typename LhsT, typename RhsT> + class BinaryExpr : public ITransientExpression { + LhsT m_lhs; + StringRef m_op; + RhsT m_rhs; - extern const std::string unprintableString; + void streamReconstructedExpression( std::ostream &os ) const override { + formatReconstructedExpression + ( os, Catch::Detail::stringify( m_lhs ), m_op, Catch::Detail::stringify( m_rhs ) ); + } - struct BorgType { - template<typename T> BorgType( T const& ); - }; + public: + BinaryExpr( bool comparisonResult, LhsT lhs, StringRef op, RhsT rhs ) + : ITransientExpression{ true, comparisonResult }, + m_lhs( lhs ), + m_op( op ), + m_rhs( rhs ) + {} - struct TrueType { char sizer[1]; }; - struct FalseType { char sizer[2]; }; + 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"); + } - TrueType& testStreamable( std::ostream& ); - FalseType testStreamable( FalseType ); + 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"); + } - FalseType operator<<( std::ostream const&, BorgType const& ); + 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> - struct IsStreamInsertable { - static std::ostream &s; - static T const&t; - enum { value = sizeof( testStreamable(s << t) ) == sizeof( TrueType ) }; - }; + 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"); + } -#if defined(CATCH_CONFIG_CPP11_IS_ENUM) - template<typename T, - bool IsEnum = std::is_enum<T>::value - > - struct EnumStringMaker - { - static std::string convert( T const& ) { return unprintableString; } - }; + 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> - struct EnumStringMaker<T,true> - { - static std::string convert( T const& v ) - { - return ::Catch::toString( - static_cast<typename std::underlying_type<T>::type>(v) - ); + 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"); } - }; -#endif - template<bool C> - struct StringMakerBase { -#if defined(CATCH_CONFIG_CPP11_IS_ENUM) + template<typename T> - static std::string convert( T const& v ) - { - return EnumStringMaker<T>::convert( v ); + 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"); } -#else + template<typename T> - static std::string convert( T const& ) { return unprintableString; } -#endif + 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<> - struct StringMakerBase<true> { - template<typename T> - static std::string convert( T const& _value ) { - std::ostringstream oss; - oss << _value; - return oss.str(); + 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 ) + {} }; - std::string rawMemoryToString( const void *object, std::size_t size ); + // Specialised comparison functions to handle equality comparisons between ints and pointers (NULL deduces as an int) + template<typename LhsT, typename RhsT> + auto compareEqual( LhsT const& lhs, RhsT const& rhs ) -> bool { return static_cast<bool>(lhs == rhs); } + template<typename T> + auto compareEqual( T* const& lhs, int rhs ) -> bool { return lhs == reinterpret_cast<void const*>( rhs ); } + template<typename T> + auto compareEqual( T* const& lhs, long rhs ) -> bool { return lhs == reinterpret_cast<void const*>( rhs ); } + template<typename T> + auto compareEqual( int lhs, T* const& rhs ) -> bool { return reinterpret_cast<void const*>( lhs ) == rhs; } + template<typename T> + auto compareEqual( long lhs, T* const& rhs ) -> bool { return reinterpret_cast<void const*>( lhs ) == rhs; } + template<typename LhsT, typename RhsT> + auto compareNotEqual( LhsT const& lhs, RhsT&& rhs ) -> bool { return static_cast<bool>(lhs != rhs); } template<typename T> - inline std::string rawMemoryToString( const T& object ) { - return rawMemoryToString( &object, sizeof(object) ); - } + auto compareNotEqual( T* const& lhs, int rhs ) -> bool { return lhs != reinterpret_cast<void const*>( rhs ); } + template<typename T> + auto compareNotEqual( T* const& lhs, long rhs ) -> bool { return lhs != reinterpret_cast<void const*>( rhs ); } + template<typename T> + auto compareNotEqual( int lhs, T* const& rhs ) -> bool { return reinterpret_cast<void const*>( lhs ) != rhs; } + template<typename T> + auto compareNotEqual( long lhs, T* const& rhs ) -> bool { return reinterpret_cast<void const*>( lhs ) != rhs; } -} // end namespace Detail + template<typename LhsT> + class ExprLhs { + LhsT m_lhs; + public: + explicit ExprLhs( LhsT lhs ) : m_lhs( lhs ) {} -template<typename T> -struct StringMaker : - Detail::StringMakerBase<Detail::IsStreamInsertable<T>::value> {}; + template<typename RhsT> + auto operator == ( RhsT const& rhs ) -> BinaryExpr<LhsT, RhsT const&> const { + return { compareEqual( m_lhs, rhs ), m_lhs, "==", rhs }; + } + auto operator == ( bool rhs ) -> BinaryExpr<LhsT, bool> const { + return { m_lhs == rhs, m_lhs, "==", rhs }; + } -template<typename T> -struct StringMaker<T*> { - template<typename U> - static std::string convert( U* p ) { - if( !p ) - return "NULL"; - else - return Detail::rawMemoryToString( p ); - } -}; + template<typename RhsT> + auto operator != ( RhsT const& rhs ) -> BinaryExpr<LhsT, RhsT const&> const { + return { compareNotEqual( m_lhs, rhs ), m_lhs, "!=", rhs }; + } + auto operator != ( bool rhs ) -> BinaryExpr<LhsT, bool> const { + return { m_lhs != rhs, m_lhs, "!=", rhs }; + } -template<typename R, typename C> -struct StringMaker<R C::*> { - static std::string convert( R C::* p ) { - if( !p ) - return "NULL"; - else - return Detail::rawMemoryToString( p ); - } -}; + template<typename RhsT> + auto operator > ( RhsT const& rhs ) -> BinaryExpr<LhsT, RhsT const&> const { + return { static_cast<bool>(m_lhs > rhs), m_lhs, ">", rhs }; + } + template<typename RhsT> + auto operator < ( RhsT const& rhs ) -> BinaryExpr<LhsT, RhsT const&> const { + return { static_cast<bool>(m_lhs < rhs), m_lhs, "<", rhs }; + } + template<typename RhsT> + auto operator >= ( RhsT const& rhs ) -> BinaryExpr<LhsT, RhsT const&> const { + return { static_cast<bool>(m_lhs >= rhs), m_lhs, ">=", rhs }; + } + template<typename RhsT> + auto operator <= ( RhsT const& rhs ) -> BinaryExpr<LhsT, RhsT const&> const { + return { static_cast<bool>(m_lhs <= rhs), m_lhs, "<=", rhs }; + } -namespace Detail { - template<typename InputIterator> - std::string rangeToString( InputIterator first, InputIterator last ); -} - -//template<typename T, typename Allocator> -//struct StringMaker<std::vector<T, Allocator> > { -// static std::string convert( std::vector<T,Allocator> const& v ) { -// return Detail::rangeToString( v.begin(), v.end() ); -// } -//}; - -template<typename T, typename Allocator> -std::string toString( std::vector<T,Allocator> const& v ) { - return Detail::rangeToString( v.begin(), v.end() ); -} - -#ifdef CATCH_CONFIG_CPP11_TUPLE - -// toString for tuples -namespace TupleDetail { - template< - typename Tuple, - std::size_t N = 0, - bool = (N < std::tuple_size<Tuple>::value) - > - struct ElementPrinter { - static void print( const Tuple& tuple, std::ostream& os ) - { - os << ( N ? ", " : " " ) - << Catch::toString(std::get<N>(tuple)); - ElementPrinter<Tuple,N+1>::print(tuple,os); - } - }; - - template< - typename Tuple, - std::size_t N - > - struct ElementPrinter<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 ) - { - std::ostringstream os; - os << '{'; - TupleDetail::ElementPrinter<std::tuple<Types...>>::print( tuple, os ); - os << " }"; - return os.str(); - } -}; -#endif // CATCH_CONFIG_CPP11_TUPLE + template<typename RhsT> + auto operator && ( RhsT const& ) -> BinaryExpr<LhsT, RhsT const&> const { + static_assert(always_false<RhsT>::value, + "operator&& is not supported inside assertions, " + "wrap the expression inside parentheses, or decompose it"); + } -namespace Detail { - template<typename T> - std::string makeString( T const& value ) { - return StringMaker<T>::convert( value ); - } -} // end namespace Detail + template<typename RhsT> + auto operator || ( RhsT const& ) -> BinaryExpr<LhsT, RhsT const&> const { + static_assert(always_false<RhsT>::value, + "operator|| is not supported inside assertions, " + "wrap the expression inside parentheses, or decompose it"); + } -/// \brief converts any type to a string -/// -/// The default template forwards on to ostringstream - except when an -/// ostringstream overload does not exist - in which case it attempts to detect -/// that and writes {?}. -/// Overload (not specialise) this template for custom typs that you don't want -/// to provide an ostream overload for. -template<typename T> -std::string toString( T const& value ) { - return StringMaker<T>::convert( value ); -} + auto makeUnaryExpr() const -> UnaryExpr<LhsT> { + return UnaryExpr<LhsT>{ m_lhs }; + } + }; - namespace Detail { - template<typename InputIterator> - std::string rangeToString( InputIterator first, InputIterator last ) { - std::ostringstream oss; - oss << "{ "; - if( first != last ) { - oss << Catch::toString( *first ); - for( ++first ; first != last ; ++first ) - oss << ", " << Catch::toString( *first ); - } - oss << " }"; - return oss.str(); + void handleExpression( ITransientExpression const& expr ); + + template<typename T> + void handleExpression( ExprLhs<T> const& expr ) { + handleExpression( expr.makeUnaryExpr() ); } -} -} // end namespace Catch + struct Decomposer { + template<typename T> + auto operator <= ( T const& lhs ) -> ExprLhs<T const&> { + return ExprLhs<T const&>{ lhs }; + } -namespace Catch { + auto operator <=( bool value ) -> ExprLhs<bool> { + return ExprLhs<bool>{ value }; + } + }; -template<typename LhsT, Internal::Operator Op, typename RhsT> -class BinaryExpression; +} // end namespace Catch -template<typename ArgT, typename MatcherT> -class MatchExpression; +#ifdef _MSC_VER +#pragma warning(pop) +#endif -// Wraps the LHS of an expression and overloads comparison operators -// for also capturing those and RHS (if any) -template<typename T> -class ExpressionLhs : public DecomposedExpression { -public: - ExpressionLhs( ResultBuilder& rb, T lhs ) : m_rb( rb ), m_lhs( lhs ), m_truthy(false) {} +// end catch_decomposer.h +// start catch_interfaces_capture.h - template<typename RhsT> - BinaryExpression<T, Internal::IsEqualTo, RhsT const&> - operator == ( RhsT const& rhs ) { - return captureExpression<Internal::IsEqualTo>( rhs ); - } +#include <string> +#include <chrono> - template<typename RhsT> - BinaryExpression<T, Internal::IsNotEqualTo, RhsT const&> - operator != ( RhsT const& rhs ) { - return captureExpression<Internal::IsNotEqualTo>( rhs ); - } +namespace Catch { - template<typename RhsT> - BinaryExpression<T, Internal::IsLessThan, RhsT const&> - operator < ( RhsT const& rhs ) { - return captureExpression<Internal::IsLessThan>( rhs ); - } + class AssertionResult; + struct AssertionInfo; + struct SectionInfo; + struct SectionEndInfo; + struct MessageInfo; + struct MessageBuilder; + struct Counts; + struct AssertionReaction; + struct SourceLineInfo; - template<typename RhsT> - BinaryExpression<T, Internal::IsGreaterThan, RhsT const&> - operator > ( RhsT const& rhs ) { - return captureExpression<Internal::IsGreaterThan>( rhs ); - } + struct ITransientExpression; + struct IGeneratorTracker; - template<typename RhsT> - BinaryExpression<T, Internal::IsLessThanOrEqualTo, RhsT const&> - operator <= ( RhsT const& rhs ) { - return captureExpression<Internal::IsLessThanOrEqualTo>( rhs ); - } +#if defined(CATCH_CONFIG_ENABLE_BENCHMARKING) + struct BenchmarkInfo; + template <typename Duration = std::chrono::duration<double, std::nano>> + struct BenchmarkStats; +#endif // CATCH_CONFIG_ENABLE_BENCHMARKING - template<typename RhsT> - BinaryExpression<T, Internal::IsGreaterThanOrEqualTo, RhsT const&> - operator >= ( RhsT const& rhs ) { - return captureExpression<Internal::IsGreaterThanOrEqualTo>( rhs ); - } + struct IResultCapture { - BinaryExpression<T, Internal::IsEqualTo, bool> operator == ( bool rhs ) { - return captureExpression<Internal::IsEqualTo>( rhs ); - } + virtual ~IResultCapture(); - BinaryExpression<T, Internal::IsNotEqualTo, bool> operator != ( bool rhs ) { - return captureExpression<Internal::IsNotEqualTo>( rhs ); - } + virtual bool sectionStarted( SectionInfo const& sectionInfo, + Counts& assertions ) = 0; + virtual void sectionEnded( SectionEndInfo const& endInfo ) = 0; + virtual void sectionEndedEarly( SectionEndInfo const& endInfo ) = 0; - void endExpression() { - m_truthy = m_lhs ? true : false; - m_rb - .setResultType( m_truthy ) - .endExpression( *this ); - } + virtual auto acquireGeneratorTracker( SourceLineInfo const& lineInfo ) -> IGeneratorTracker& = 0; - virtual void reconstructExpression( std::string& dest ) const CATCH_OVERRIDE { - dest = Catch::toString( m_truthy ); - } +#if defined(CATCH_CONFIG_ENABLE_BENCHMARKING) + virtual void benchmarkPreparing( std::string const& name ) = 0; + virtual void benchmarkStarting( BenchmarkInfo const& info ) = 0; + virtual void benchmarkEnded( BenchmarkStats<> const& stats ) = 0; + virtual void benchmarkFailed( std::string const& error ) = 0; +#endif // CATCH_CONFIG_ENABLE_BENCHMARKING -private: - template<Internal::Operator Op, typename RhsT> - BinaryExpression<T, Op, RhsT&> captureExpression( RhsT& rhs ) const { - return BinaryExpression<T, Op, RhsT&>( m_rb, m_lhs, rhs ); - } + virtual void pushScopedMessage( MessageInfo const& message ) = 0; + virtual void popScopedMessage( MessageInfo const& message ) = 0; - template<Internal::Operator Op> - BinaryExpression<T, Op, bool> captureExpression( bool rhs ) const { - return BinaryExpression<T, Op, bool>( m_rb, m_lhs, rhs ); - } + virtual void emplaceUnscopedMessage( MessageBuilder const& builder ) = 0; + + virtual void handleFatalErrorCondition( StringRef message ) = 0; + + virtual void handleExpr + ( AssertionInfo const& info, + ITransientExpression const& expr, + AssertionReaction& reaction ) = 0; + virtual void handleMessage + ( AssertionInfo const& info, + ResultWas::OfType resultType, + StringRef const& message, + AssertionReaction& reaction ) = 0; + virtual void handleUnexpectedExceptionNotThrown + ( AssertionInfo const& info, + AssertionReaction& reaction ) = 0; + virtual void handleUnexpectedInflightException + ( AssertionInfo const& info, + std::string const& message, + AssertionReaction& reaction ) = 0; + virtual void handleIncomplete + ( AssertionInfo const& info ) = 0; + virtual void handleNonExpr + ( AssertionInfo const &info, + ResultWas::OfType resultType, + AssertionReaction &reaction ) = 0; + + virtual bool lastAssertionPassed() = 0; + virtual void assertionPassed() = 0; + + // Deprecated, do not use: + virtual std::string getCurrentTestName() const = 0; + virtual const AssertionResult* getLastResult() const = 0; + virtual void exceptionEarlyReported() = 0; + }; -private: - ResultBuilder& m_rb; - T m_lhs; - bool m_truthy; -}; + IResultCapture& getResultCapture(); +} -template<typename LhsT, Internal::Operator Op, typename RhsT> -class BinaryExpression : public DecomposedExpression { -public: - BinaryExpression( ResultBuilder& rb, LhsT lhs, RhsT rhs ) - : m_rb( rb ), m_lhs( lhs ), m_rhs( rhs ) {} +// end catch_interfaces_capture.h +namespace Catch { - void endExpression() const { - m_rb - .setResultType( Internal::compare<Op>( m_lhs, m_rhs ) ) - .endExpression( *this ); - } + struct TestFailureException{}; + struct AssertionResultData; + struct IResultCapture; + class RunContext; - virtual bool isBinaryExpression() const CATCH_OVERRIDE { - return true; - } + class LazyExpression { + friend class AssertionHandler; + friend struct AssertionStats; + friend class RunContext; - virtual void reconstructExpression( std::string& dest ) const CATCH_OVERRIDE { - std::string lhs = Catch::toString( m_lhs ); - std::string rhs = Catch::toString( m_rhs ); - char delim = lhs.size() + rhs.size() < 40 && - lhs.find('\n') == std::string::npos && - rhs.find('\n') == std::string::npos ? ' ' : '\n'; - dest.reserve( 7 + lhs.size() + rhs.size() ); - // 2 for spaces around operator - // 2 for operator - // 2 for parentheses (conditionally added later) - // 1 for negation (conditionally added later) - dest = lhs; - dest += delim; - dest += Internal::OperatorTraits<Op>::getName(); - dest += delim; - dest += rhs; - } + ITransientExpression const* m_transientExpression = nullptr; + bool m_isNegated; + public: + LazyExpression( bool isNegated ); + LazyExpression( LazyExpression const& other ); + LazyExpression& operator = ( LazyExpression const& ) = delete; -private: - ResultBuilder& m_rb; - LhsT m_lhs; - RhsT m_rhs; -}; + explicit operator bool() const; -template<typename ArgT, typename MatcherT> -class MatchExpression : public DecomposedExpression { -public: - MatchExpression( ArgT arg, MatcherT matcher, char const* matcherString ) - : m_arg( arg ), m_matcher( matcher ), m_matcherString( matcherString ) {} + friend auto operator << ( std::ostream& os, LazyExpression const& lazyExpr ) -> std::ostream&; + }; - virtual bool isBinaryExpression() const CATCH_OVERRIDE { - return true; - } + struct AssertionReaction { + bool shouldDebugBreak = false; + bool shouldThrow = false; + }; - virtual void reconstructExpression( std::string& dest ) const CATCH_OVERRIDE { - std::string matcherAsString = m_matcher.toString(); - dest = Catch::toString( m_arg ); - dest += ' '; - if( matcherAsString == Detail::unprintableString ) - dest += m_matcherString; - else - dest += matcherAsString; - } + class AssertionHandler { + AssertionInfo m_assertionInfo; + AssertionReaction m_reaction; + bool m_completed = false; + IResultCapture& m_resultCapture; -private: - ArgT m_arg; - MatcherT m_matcher; - char const* m_matcherString; -}; + public: + AssertionHandler + ( StringRef const& macroName, + SourceLineInfo const& lineInfo, + StringRef capturedExpression, + ResultDisposition::Flags resultDisposition ); + ~AssertionHandler() { + if ( !m_completed ) { + m_resultCapture.handleIncomplete( m_assertionInfo ); + } + } -} // end namespace Catch + template<typename T> + void handleExpr( ExprLhs<T> const& expr ) { + handleExpr( expr.makeUnaryExpr() ); + } + void handleExpr( ITransientExpression const& expr ); + void handleMessage(ResultWas::OfType resultType, StringRef const& message); -namespace Catch { + void handleExceptionThrownAsExpected(); + void handleUnexpectedExceptionNotThrown(); + void handleExceptionNotThrownAsExpected(); + void handleThrowingCallSkipped(); + void handleUnexpectedInflightException(); - template<typename T> - inline ExpressionLhs<T const&> ResultBuilder::operator <= ( T const& operand ) { - return ExpressionLhs<T const&>( *this, operand ); - } + void complete(); + void setCompleted(); - inline ExpressionLhs<bool> ResultBuilder::operator <= ( bool value ) { - return ExpressionLhs<bool>( *this, value ); - } + // query + auto allowThrows() const -> bool; + }; - template<typename ArgT, typename MatcherT> - inline void ResultBuilder::captureMatch( ArgT const& arg, MatcherT const& matcher, - char const* matcherString ) { - MatchExpression<ArgT const&, MatcherT const&> expr( arg, matcher, matcherString ); - setResultType( matcher.match( arg ) ); - endExpression( expr ); - } + void handleExceptionMatchExpr( AssertionHandler& handler, std::string const& str, StringRef const& matcherString ); } // namespace Catch -// #included from: catch_message.h -#define TWOBLUECUBES_CATCH_MESSAGE_H_INCLUDED +// end catch_assertionhandler.h +// start catch_message.h #include <string> +#include <vector> namespace Catch { struct MessageInfo { - MessageInfo( std::string const& _macroName, + MessageInfo( StringRef const& _macroName, SourceLineInfo const& _lineInfo, ResultWas::OfType _type ); - std::string macroName; + StringRef macroName; + std::string message; SourceLineInfo lineInfo; ResultWas::OfType type; - std::string message; unsigned int sequence; - bool operator == ( MessageInfo const& other ) const { - return sequence == other.sequence; - } - bool operator < ( MessageInfo const& other ) const { - return sequence < other.sequence; - } + bool operator == ( MessageInfo const& other ) const; + bool operator < ( MessageInfo const& other ) const; private: static unsigned int globalCount; }; - struct MessageBuilder { - MessageBuilder( std::string const& macroName, + struct MessageStream { + + template<typename T> + MessageStream& operator << ( T const& value ) { + m_stream << value; + return *this; + } + + ReusableStringStream m_stream; + }; + + struct MessageBuilder : MessageStream { + MessageBuilder( StringRef const& macroName, SourceLineInfo const& lineInfo, - ResultWas::OfType type ) - : m_info( macroName, lineInfo, type ) - {} + ResultWas::OfType type ); template<typename T> MessageBuilder& operator << ( T const& value ) { @@ -2078,370 +2564,222 @@ namespace Catch { } MessageInfo m_info; - std::ostringstream m_stream; }; class ScopedMessage { public: - ScopedMessage( MessageBuilder const& builder ); - ScopedMessage( ScopedMessage const& other ); + explicit ScopedMessage( MessageBuilder const& builder ); + ScopedMessage( ScopedMessage& duplicate ) = delete; + ScopedMessage( ScopedMessage&& old ); ~ScopedMessage(); MessageInfo m_info; + bool m_moved; }; -} // end namespace Catch - -// #included from: catch_interfaces_capture.h -#define TWOBLUECUBES_CATCH_INTERFACES_CAPTURE_H_INCLUDED - -#include <string> - -namespace Catch { - - class TestCase; - class AssertionResult; - struct AssertionInfo; - struct SectionInfo; - struct SectionEndInfo; - struct MessageInfo; - class ScopedMessageBuilder; - struct Counts; - - struct IResultCapture { - - virtual ~IResultCapture(); + class Capturer { + std::vector<MessageInfo> m_messages; + IResultCapture& m_resultCapture = getResultCapture(); + size_t m_captured = 0; + public: + Capturer( StringRef macroName, SourceLineInfo const& lineInfo, ResultWas::OfType resultType, StringRef names ); + ~Capturer(); - virtual void assertionEnded( AssertionResult const& result ) = 0; - virtual bool sectionStarted( SectionInfo const& sectionInfo, - Counts& assertions ) = 0; - virtual void sectionEnded( SectionEndInfo const& endInfo ) = 0; - virtual void sectionEndedEarly( SectionEndInfo const& endInfo ) = 0; - virtual void pushScopedMessage( MessageInfo const& message ) = 0; - virtual void popScopedMessage( MessageInfo const& message ) = 0; + void captureValue( size_t index, std::string const& value ); - virtual std::string getCurrentTestName() const = 0; - virtual const AssertionResult* getLastResult() const = 0; + template<typename T> + void captureValues( size_t index, T const& value ) { + captureValue( index, Catch::Detail::stringify( value ) ); + } - virtual void handleFatalErrorCondition( std::string const& message ) = 0; + 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... ); + } }; - IResultCapture& getResultCapture(); -} - -// #included from: catch_debugger.h -#define TWOBLUECUBES_CATCH_DEBUGGER_H_INCLUDED - -// #included from: catch_platform.h -#define TWOBLUECUBES_CATCH_PLATFORM_H_INCLUDED - -#if defined(__MAC_OS_X_VERSION_MIN_REQUIRED) -# define CATCH_PLATFORM_MAC -#elif defined(__IPHONE_OS_VERSION_MIN_REQUIRED) -# define CATCH_PLATFORM_IPHONE -#elif defined(linux) || defined(__linux) || defined(__linux__) -# define CATCH_PLATFORM_LINUX -#elif defined(WIN32) || defined(__WIN32__) || defined(_WIN32) || defined(_MSC_VER) -# define CATCH_PLATFORM_WINDOWS -# if !defined(NOMINMAX) && !defined(CATCH_CONFIG_NO_NOMINMAX) -# define CATCH_DEFINES_NOMINMAX -# endif -# if !defined(WIN32_LEAN_AND_MEAN) && !defined(CATCH_CONFIG_NO_WIN32_LEAN_AND_MEAN) -# define CATCH_DEFINES_WIN32_LEAN_AND_MEAN -# endif -#endif - -#include <string> - -namespace Catch{ - - bool isDebuggerActive(); - void writeToDebugConsole( std::string const& text ); -} - -#ifdef CATCH_PLATFORM_MAC - - // The following code snippet based on: - // http://cocoawithlove.com/2008/03/break-into-debugger.html - #if defined(__ppc64__) || defined(__ppc__) - #define CATCH_TRAP() \ - __asm__("li r0, 20\nsc\nnop\nli r0, 37\nli r4, 2\nsc\nnop\n" \ - : : : "memory","r0","r3","r4" ) - #else - #define CATCH_TRAP() __asm__("int $3\n" : : ) - #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") - #else // Fall back to the generic way. - #include <signal.h> +} // end namespace Catch - #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 +// end catch_message.h +#if !defined(CATCH_CONFIG_DISABLE) -#ifdef CATCH_TRAP - #define CATCH_BREAK_INTO_DEBUGGER() if( Catch::isDebuggerActive() ) { CATCH_TRAP(); } +#if !defined(CATCH_CONFIG_DISABLE_STRINGIFICATION) + #define CATCH_INTERNAL_STRINGIFY(...) #__VA_ARGS__ #else - #define CATCH_BREAK_INTO_DEBUGGER() Catch::alwaysTrue(); -#endif - -// #included from: catch_interfaces_runner.h -#define TWOBLUECUBES_CATCH_INTERFACES_RUNNER_H_INCLUDED - -namespace Catch { - class TestCase; - - struct IRunner { - virtual ~IRunner(); - virtual bool aborting() const = 0; - }; -} - -// #included from: catch_type_traits.hpp -#define TWOBLUECUBES_CATCH_TYPE_TRAITS_HPP_INCLUDED - -#if defined(CATCH_CONFIG_CPP11_TYPE_TRAITS) -#include <type_traits> + #define CATCH_INTERNAL_STRINGIFY(...) "Disabled by CATCH_CONFIG_DISABLE_STRINGIFICATION" #endif -namespace Catch { - -#if defined(CATCH_CONFIG_CPP11_TYPE_TRAITS) - - template <typename T> - using add_lvalue_reference = std::add_lvalue_reference<T>; +#if defined(CATCH_CONFIG_FAST_COMPILE) || defined(CATCH_CONFIG_DISABLE_EXCEPTIONS) - template <typename T> - using add_const = std::add_const<T>; +/////////////////////////////////////////////////////////////////////////////// +// 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 +#else // CATCH_CONFIG_FAST_COMPILE - template <typename T> - struct add_const { - typedef const T type; - }; - - template <typename T> - struct add_lvalue_reference { - typedef T& type; - }; - template <typename T> - struct add_lvalue_reference<T&> { - typedef T& type; - }; - // No && overload, because that is C++11, in which case we have - // proper type_traits implementation from the standard library +#define INTERNAL_CATCH_TRY try +#define INTERNAL_CATCH_CATCH( handler ) catch(...) { handler.handleUnexpectedInflightException(); } #endif -} +#define INTERNAL_CATCH_REACT( handler ) handler.complete(); /////////////////////////////////////////////////////////////////////////////// -// In the event of a failure works out if the debugger needs to be invoked -// and/or an exception thrown and takes appropriate action. -// This needs to be done as a macro so the debugger will stop in the user -// source code rather than in Catch library code -#define INTERNAL_CATCH_REACT( resultBuilder ) \ - if( resultBuilder.shouldDebugBreak() ) CATCH_BREAK_INTO_DEBUGGER(); \ - resultBuilder.react(); - -/////////////////////////////////////////////////////////////////////////////// -#define INTERNAL_CATCH_TEST( expr, resultDisposition, macroName ) \ +#define INTERNAL_CATCH_TEST( macroName, resultDisposition, ... ) \ do { \ - Catch::ResultBuilder __catchResult( macroName, CATCH_INTERNAL_LINEINFO, #expr, resultDisposition ); \ - try { \ + Catch::AssertionHandler catchAssertionHandler( macroName##_catch_sr, CATCH_INTERNAL_LINEINFO, CATCH_INTERNAL_STRINGIFY(__VA_ARGS__), resultDisposition ); \ + INTERNAL_CATCH_TRY { \ CATCH_INTERNAL_SUPPRESS_PARENTHESES_WARNINGS \ - ( __catchResult <= expr ).endExpression(); \ - } \ - catch( ... ) { \ - __catchResult.useActiveException( resultDisposition ); \ - } \ - INTERNAL_CATCH_REACT( __catchResult ) \ - } while( Catch::isTrue( false && static_cast<bool>( !!(expr) ) ) ) // expr here is never evaluated at runtime but it forces the compiler to give it a look + catchAssertionHandler.handleExpr( Catch::Decomposer() <= __VA_ARGS__ ); \ + CATCH_INTERNAL_UNSUPPRESS_PARENTHESES_WARNINGS \ + } INTERNAL_CATCH_CATCH( catchAssertionHandler ) \ + INTERNAL_CATCH_REACT( catchAssertionHandler ) \ + } while( (void)0, (false) && static_cast<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( expr, resultDisposition, macroName ) \ - INTERNAL_CATCH_TEST( expr, resultDisposition, macroName ); \ - if( Catch::getResultCapture().getLastResult()->succeeded() ) +#define INTERNAL_CATCH_IF( macroName, resultDisposition, ... ) \ + INTERNAL_CATCH_TEST( macroName, resultDisposition, __VA_ARGS__ ); \ + if( Catch::getResultCapture().lastAssertionPassed() ) /////////////////////////////////////////////////////////////////////////////// -#define INTERNAL_CATCH_ELSE( expr, resultDisposition, macroName ) \ - INTERNAL_CATCH_TEST( expr, resultDisposition, macroName ); \ - if( !Catch::getResultCapture().getLastResult()->succeeded() ) +#define INTERNAL_CATCH_ELSE( macroName, resultDisposition, ... ) \ + INTERNAL_CATCH_TEST( macroName, resultDisposition, __VA_ARGS__ ); \ + if( !Catch::getResultCapture().lastAssertionPassed() ) /////////////////////////////////////////////////////////////////////////////// -#define INTERNAL_CATCH_NO_THROW( expr, resultDisposition, macroName ) \ +#define INTERNAL_CATCH_NO_THROW( macroName, resultDisposition, ... ) \ do { \ - Catch::ResultBuilder __catchResult( macroName, CATCH_INTERNAL_LINEINFO, #expr, resultDisposition ); \ + Catch::AssertionHandler catchAssertionHandler( macroName##_catch_sr, CATCH_INTERNAL_LINEINFO, CATCH_INTERNAL_STRINGIFY(__VA_ARGS__), resultDisposition ); \ try { \ - static_cast<void>(expr); \ - __catchResult.captureResult( Catch::ResultWas::Ok ); \ + static_cast<void>(__VA_ARGS__); \ + catchAssertionHandler.handleExceptionNotThrownAsExpected(); \ } \ catch( ... ) { \ - __catchResult.useActiveException( resultDisposition ); \ + catchAssertionHandler.handleUnexpectedInflightException(); \ } \ - INTERNAL_CATCH_REACT( __catchResult ) \ - } while( Catch::alwaysFalse() ) + INTERNAL_CATCH_REACT( catchAssertionHandler ) \ + } while( false ) /////////////////////////////////////////////////////////////////////////////// -#define INTERNAL_CATCH_THROWS( expr, resultDisposition, matcher, macroName ) \ +#define INTERNAL_CATCH_THROWS( macroName, resultDisposition, ... ) \ do { \ - Catch::ResultBuilder __catchResult( macroName, CATCH_INTERNAL_LINEINFO, #expr, resultDisposition, #matcher ); \ - if( __catchResult.allowThrows() ) \ + Catch::AssertionHandler catchAssertionHandler( macroName##_catch_sr, CATCH_INTERNAL_LINEINFO, CATCH_INTERNAL_STRINGIFY(__VA_ARGS__), resultDisposition); \ + if( catchAssertionHandler.allowThrows() ) \ try { \ - static_cast<void>(expr); \ - __catchResult.captureResult( Catch::ResultWas::DidntThrowException ); \ + static_cast<void>(__VA_ARGS__); \ + catchAssertionHandler.handleUnexpectedExceptionNotThrown(); \ } \ catch( ... ) { \ - __catchResult.captureExpectedException( matcher ); \ + catchAssertionHandler.handleExceptionThrownAsExpected(); \ } \ else \ - __catchResult.captureResult( Catch::ResultWas::Ok ); \ - INTERNAL_CATCH_REACT( __catchResult ) \ - } while( Catch::alwaysFalse() ) + catchAssertionHandler.handleThrowingCallSkipped(); \ + INTERNAL_CATCH_REACT( catchAssertionHandler ) \ + } while( false ) /////////////////////////////////////////////////////////////////////////////// -#define INTERNAL_CATCH_THROWS_AS( expr, exceptionType, resultDisposition, macroName ) \ +#define INTERNAL_CATCH_THROWS_AS( macroName, exceptionType, resultDisposition, expr ) \ do { \ - Catch::ResultBuilder __catchResult( macroName, CATCH_INTERNAL_LINEINFO, #expr ", " #exceptionType, resultDisposition ); \ - if( __catchResult.allowThrows() ) \ + Catch::AssertionHandler catchAssertionHandler( macroName##_catch_sr, CATCH_INTERNAL_LINEINFO, CATCH_INTERNAL_STRINGIFY(expr) ", " CATCH_INTERNAL_STRINGIFY(exceptionType), resultDisposition ); \ + if( catchAssertionHandler.allowThrows() ) \ try { \ static_cast<void>(expr); \ - __catchResult.captureResult( Catch::ResultWas::DidntThrowException ); \ + catchAssertionHandler.handleUnexpectedExceptionNotThrown(); \ } \ - catch( Catch::add_const<Catch::add_lvalue_reference<exceptionType>::type>::type ) { \ - __catchResult.captureResult( Catch::ResultWas::Ok ); \ + catch( exceptionType const& ) { \ + catchAssertionHandler.handleExceptionThrownAsExpected(); \ } \ catch( ... ) { \ - __catchResult.useActiveException( resultDisposition ); \ + catchAssertionHandler.handleUnexpectedInflightException(); \ } \ else \ - __catchResult.captureResult( Catch::ResultWas::Ok ); \ - INTERNAL_CATCH_REACT( __catchResult ) \ - } while( Catch::alwaysFalse() ) + catchAssertionHandler.handleThrowingCallSkipped(); \ + INTERNAL_CATCH_REACT( catchAssertionHandler ) \ + } while( false ) /////////////////////////////////////////////////////////////////////////////// -#ifdef CATCH_CONFIG_VARIADIC_MACROS - #define INTERNAL_CATCH_MSG( messageType, resultDisposition, macroName, ... ) \ - do { \ - Catch::ResultBuilder __catchResult( macroName, CATCH_INTERNAL_LINEINFO, "", resultDisposition ); \ - __catchResult << __VA_ARGS__ + ::Catch::StreamEndStop(); \ - __catchResult.captureResult( messageType ); \ - INTERNAL_CATCH_REACT( __catchResult ) \ - } while( Catch::alwaysFalse() ) -#else - #define INTERNAL_CATCH_MSG( messageType, resultDisposition, macroName, log ) \ - do { \ - Catch::ResultBuilder __catchResult( macroName, CATCH_INTERNAL_LINEINFO, "", resultDisposition ); \ - __catchResult << log + ::Catch::StreamEndStop(); \ - __catchResult.captureResult( messageType ); \ - INTERNAL_CATCH_REACT( __catchResult ) \ - } while( Catch::alwaysFalse() ) -#endif +#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_INFO( log, macroName ) \ - Catch::ScopedMessage INTERNAL_CATCH_UNIQUE_NAME( scopedMessage ) = Catch::MessageBuilder( macroName, CATCH_INTERNAL_LINEINFO, Catch::ResultWas::Info ) << log; +#define INTERNAL_CATCH_CAPTURE( varName, macroName, ... ) \ + auto varName = Catch::Capturer( macroName, CATCH_INTERNAL_LINEINFO, Catch::ResultWas::Info, #__VA_ARGS__ ); \ + varName.captureValues( 0, __VA_ARGS__ ) /////////////////////////////////////////////////////////////////////////////// -#define INTERNAL_CHECK_THAT( arg, matcher, resultDisposition, macroName ) \ +#define INTERNAL_CATCH_INFO( macroName, log ) \ + Catch::ScopedMessage INTERNAL_CATCH_UNIQUE_NAME( scopedMessage )( Catch::MessageBuilder( macroName##_catch_sr, CATCH_INTERNAL_LINEINFO, Catch::ResultWas::Info ) << log ); + +/////////////////////////////////////////////////////////////////////////////// +#define INTERNAL_CATCH_UNSCOPED_INFO( macroName, log ) \ + Catch::getResultCapture().emplaceUnscopedMessage( Catch::MessageBuilder( macroName##_catch_sr, CATCH_INTERNAL_LINEINFO, Catch::ResultWas::Info ) << log ) + +/////////////////////////////////////////////////////////////////////////////// +// Although this is matcher-based, it can be used with just a string +#define INTERNAL_CATCH_THROWS_STR_MATCHES( macroName, resultDisposition, matcher, ... ) \ do { \ - Catch::ResultBuilder __catchResult( macroName, CATCH_INTERNAL_LINEINFO, #arg ", " #matcher, resultDisposition ); \ - try { \ - __catchResult.captureMatch( arg, matcher, #matcher ); \ - } catch( ... ) { \ - __catchResult.useActiveException( resultDisposition | Catch::ResultDisposition::ContinueOnFailure ); \ - } \ - INTERNAL_CATCH_REACT( __catchResult ) \ - } while( Catch::alwaysFalse() ) + Catch::AssertionHandler catchAssertionHandler( macroName##_catch_sr, CATCH_INTERNAL_LINEINFO, CATCH_INTERNAL_STRINGIFY(__VA_ARGS__) ", " CATCH_INTERNAL_STRINGIFY(matcher), resultDisposition ); \ + if( catchAssertionHandler.allowThrows() ) \ + try { \ + static_cast<void>(__VA_ARGS__); \ + catchAssertionHandler.handleUnexpectedExceptionNotThrown(); \ + } \ + catch( ... ) { \ + Catch::handleExceptionMatchExpr( catchAssertionHandler, matcher, #matcher##_catch_sr ); \ + } \ + else \ + catchAssertionHandler.handleThrowingCallSkipped(); \ + INTERNAL_CATCH_REACT( catchAssertionHandler ) \ + } while( false ) + +#endif // CATCH_CONFIG_DISABLE -// #included from: internal/catch_section.h -#define TWOBLUECUBES_CATCH_SECTION_H_INCLUDED +// end catch_capture.hpp +// start catch_section.h -// #included from: catch_section_info.h -#define TWOBLUECUBES_CATCH_SECTION_INFO_H_INCLUDED +// start catch_section_info.h -// #included from: catch_totals.hpp -#define TWOBLUECUBES_CATCH_TOTALS_HPP_INCLUDED +// start catch_totals.h #include <cstddef> namespace Catch { struct Counts { - Counts() : passed( 0 ), failed( 0 ), failedButOk( 0 ) {} - - Counts operator - ( Counts const& other ) const { - Counts diff; - diff.passed = passed - other.passed; - diff.failed = failed - other.failed; - diff.failedButOk = failedButOk - other.failedButOk; - return diff; - } - Counts& operator += ( Counts const& other ) { - passed += other.passed; - failed += other.failed; - failedButOk += other.failedButOk; - return *this; - } + Counts operator - ( Counts const& other ) const; + Counts& operator += ( Counts const& other ); - std::size_t total() const { - return passed + failed + failedButOk; - } - bool allPassed() const { - return failed == 0 && failedButOk == 0; - } - bool allOk() const { - return failed == 0; - } + std::size_t total() const; + bool allPassed() const; + bool allOk() const; - std::size_t passed; - std::size_t failed; - std::size_t failedButOk; + std::size_t passed = 0; + std::size_t failed = 0; + std::size_t failedButOk = 0; }; struct Totals { - Totals operator - ( Totals const& other ) const { - Totals diff; - diff.assertions = assertions - other.assertions; - diff.testCases = testCases - other.testCases; - return diff; - } + Totals operator - ( Totals const& other ) const; + Totals& operator += ( Totals const& other ); - Totals delta( Totals const& prevTotals ) const { - Totals diff = *this - prevTotals; - if( diff.assertions.failed > 0 ) - ++diff.testCases.failed; - else if( diff.assertions.failedButOk > 0 ) - ++diff.testCases.failedButOk; - else - ++diff.testCases.passed; - return diff; - } - - Totals& operator += ( Totals const& other ) { - assertions += other.assertions; - testCases += other.testCases; - return *this; - } + Totals delta( Totals const& prevTotals ) const; + int error = 0; Counts assertions; Counts testCases; }; } +// end catch_totals.h #include <string> namespace Catch { @@ -2449,19 +2787,20 @@ namespace Catch { struct SectionInfo { SectionInfo ( SourceLineInfo const& _lineInfo, + std::string const& _name ); + + // Deprecated + SectionInfo + ( SourceLineInfo const& _lineInfo, std::string const& _name, - std::string const& _description = std::string() ); + std::string const& ) : SectionInfo( _lineInfo, _name ) {} std::string name; - std::string description; + std::string description; // !Deprecated: this will always be empty SourceLineInfo lineInfo; }; struct SectionEndInfo { - SectionEndInfo( SectionInfo const& _sectionInfo, Counts const& _prevAssertions, double _durationInSeconds ) - : sectionInfo( _sectionInfo ), prevAssertions( _prevAssertions ), durationInSeconds( _durationInSeconds ) - {} - SectionInfo sectionInfo; Counts prevAssertions; double durationInSeconds; @@ -2469,31 +2808,29 @@ namespace Catch { } // end namespace Catch -// #included from: catch_timer.h -#define TWOBLUECUBES_CATCH_TIMER_H_INCLUDED +// end catch_section_info.h +// start catch_timer.h -#ifdef CATCH_PLATFORM_WINDOWS -typedef unsigned long long uint64_t; -#else -#include <stdint.h> -#endif +#include <cstdint> namespace Catch { + auto getCurrentNanosecondsSinceEpoch() -> uint64_t; + auto getEstimatedClockResolution() -> uint64_t; + class Timer { + uint64_t m_nanoseconds = 0; public: - Timer() : m_ticks( 0 ) {} void start(); - unsigned int getElapsedMicroseconds() const; - unsigned int getElapsedMilliseconds() const; - double getElapsedSeconds() const; - - private: - uint64_t m_ticks; + auto getElapsedNanoseconds() const -> uint64_t; + auto getElapsedMicroseconds() const -> uint64_t; + auto getElapsedMilliseconds() const -> unsigned int; + auto getElapsedSeconds() const -> double; }; } // namespace Catch +// end catch_timer.h #include <string> namespace Catch { @@ -2504,7 +2841,7 @@ namespace Catch { ~Section(); // This indicates whether the section should be executed or not - operator bool() const; + explicit operator bool() const; private: SectionInfo m_info; @@ -2517,204 +2854,23 @@ namespace Catch { } // end namespace Catch -#ifdef CATCH_CONFIG_VARIADIC_MACROS - #define INTERNAL_CATCH_SECTION( ... ) \ - if( Catch::Section const& INTERNAL_CATCH_UNIQUE_NAME( catch_internal_Section ) = Catch::SectionInfo( CATCH_INTERNAL_LINEINFO, __VA_ARGS__ ) ) -#else - #define INTERNAL_CATCH_SECTION( name, desc ) \ - if( Catch::Section const& INTERNAL_CATCH_UNIQUE_NAME( catch_internal_Section ) = Catch::SectionInfo( CATCH_INTERNAL_LINEINFO, name, desc ) ) -#endif - -// #included from: internal/catch_generators.hpp -#define TWOBLUECUBES_CATCH_GENERATORS_HPP_INCLUDED - -#include <iterator> -#include <vector> -#include <string> -#include <stdlib.h> - -namespace Catch { - -template<typename T> -struct IGenerator { - virtual ~IGenerator() {} - virtual T getValue( std::size_t index ) const = 0; - virtual std::size_t size () const = 0; -}; - -template<typename T> -class BetweenGenerator : public IGenerator<T> { -public: - BetweenGenerator( T from, T to ) : m_from( from ), m_to( to ){} - - virtual T getValue( std::size_t index ) const { - return m_from+static_cast<int>( index ); - } - - virtual std::size_t size() const { - return static_cast<std::size_t>( 1+m_to-m_from ); - } - -private: - - T m_from; - T m_to; -}; - -template<typename T> -class ValuesGenerator : public IGenerator<T> { -public: - ValuesGenerator(){} - - void add( T value ) { - m_values.push_back( value ); - } - - virtual T getValue( std::size_t index ) const { - return m_values[index]; - } - - virtual std::size_t size() const { - return m_values.size(); - } - -private: - std::vector<T> m_values; -}; - -template<typename T> -class CompositeGenerator { -public: - CompositeGenerator() : m_totalSize( 0 ) {} - - // *** Move semantics, similar to auto_ptr *** - CompositeGenerator( CompositeGenerator& other ) - : m_fileInfo( other.m_fileInfo ), - m_totalSize( 0 ) - { - move( other ); - } - - CompositeGenerator& setFileInfo( const char* fileInfo ) { - m_fileInfo = fileInfo; - return *this; - } - - ~CompositeGenerator() { - deleteAll( m_composed ); - } - - operator T () const { - size_t overallIndex = getCurrentContext().getGeneratorIndex( m_fileInfo, m_totalSize ); - - typename std::vector<const IGenerator<T>*>::const_iterator it = m_composed.begin(); - typename std::vector<const IGenerator<T>*>::const_iterator itEnd = m_composed.end(); - for( size_t index = 0; it != itEnd; ++it ) - { - const IGenerator<T>* generator = *it; - if( overallIndex >= index && overallIndex < index + generator->size() ) - { - return generator->getValue( overallIndex-index ); - } - index += generator->size(); - } - CATCH_INTERNAL_ERROR( "Indexed past end of generated range" ); - return T(); // Suppress spurious "not all control paths return a value" warning in Visual Studio - if you know how to fix this please do so - } - - void add( const IGenerator<T>* generator ) { - m_totalSize += generator->size(); - m_composed.push_back( generator ); - } - - CompositeGenerator& then( CompositeGenerator& other ) { - move( other ); - return *this; - } - - CompositeGenerator& then( T value ) { - ValuesGenerator<T>* valuesGen = new ValuesGenerator<T>(); - valuesGen->add( value ); - add( valuesGen ); - return *this; - } - -private: - - void move( CompositeGenerator& other ) { - std::copy( other.m_composed.begin(), other.m_composed.end(), std::back_inserter( m_composed ) ); - m_totalSize += other.m_totalSize; - other.m_composed.clear(); - } - - std::vector<const IGenerator<T>*> m_composed; - std::string m_fileInfo; - size_t m_totalSize; -}; - -namespace Generators -{ - template<typename T> - CompositeGenerator<T> between( T from, T to ) { - CompositeGenerator<T> generators; - generators.add( new BetweenGenerator<T>( from, to ) ); - return generators; - } +#define INTERNAL_CATCH_SECTION( ... ) \ + CATCH_INTERNAL_SUPPRESS_UNUSED_WARNINGS \ + if( Catch::Section const& INTERNAL_CATCH_UNIQUE_NAME( catch_internal_Section ) = Catch::SectionInfo( CATCH_INTERNAL_LINEINFO, __VA_ARGS__ ) ) \ + CATCH_INTERNAL_UNSUPPRESS_UNUSED_WARNINGS - template<typename T> - CompositeGenerator<T> values( T val1, T val2 ) { - CompositeGenerator<T> generators; - ValuesGenerator<T>* valuesGen = new ValuesGenerator<T>(); - valuesGen->add( val1 ); - valuesGen->add( val2 ); - generators.add( valuesGen ); - return generators; - } +#define INTERNAL_CATCH_DYNAMIC_SECTION( ... ) \ + CATCH_INTERNAL_SUPPRESS_UNUSED_WARNINGS \ + if( Catch::Section const& INTERNAL_CATCH_UNIQUE_NAME( catch_internal_Section ) = Catch::SectionInfo( CATCH_INTERNAL_LINEINFO, (Catch::ReusableStringStream() << __VA_ARGS__).str() ) ) \ + CATCH_INTERNAL_UNSUPPRESS_UNUSED_WARNINGS - template<typename T> - CompositeGenerator<T> values( T val1, T val2, T val3 ){ - CompositeGenerator<T> generators; - ValuesGenerator<T>* valuesGen = new ValuesGenerator<T>(); - valuesGen->add( val1 ); - valuesGen->add( val2 ); - valuesGen->add( val3 ); - generators.add( valuesGen ); - return generators; - } - - template<typename T> - CompositeGenerator<T> values( T val1, T val2, T val3, T val4 ) { - CompositeGenerator<T> generators; - ValuesGenerator<T>* valuesGen = new ValuesGenerator<T>(); - valuesGen->add( val1 ); - valuesGen->add( val2 ); - valuesGen->add( val3 ); - valuesGen->add( val4 ); - generators.add( valuesGen ); - return generators; - } +// end catch_section.h +// start catch_interfaces_exception.h -} // end namespace Generators - -using namespace Generators; - -} // end namespace Catch - -#define INTERNAL_CATCH_LINESTR2( line ) #line -#define INTERNAL_CATCH_LINESTR( line ) INTERNAL_CATCH_LINESTR2( line ) - -#define INTERNAL_CATCH_GENERATE( expr ) expr.setFileInfo( __FILE__ "(" INTERNAL_CATCH_LINESTR( __LINE__ ) ")" ) - -// #included from: internal/catch_interfaces_exception.h -#define TWOBLUECUBES_CATCH_INTERFACES_EXCEPTION_H_INCLUDED - -#include <string> -#include <vector> - -// #included from: catch_interfaces_registry_hub.h -#define TWOBLUECUBES_CATCH_INTERFACES_REGISTRY_HUB_H_INCLUDED +// start catch_interfaces_registry_hub.h #include <string> +#include <memory> namespace Catch { @@ -2724,36 +2880,57 @@ namespace Catch { struct IExceptionTranslator; struct IReporterRegistry; struct IReporterFactory; + struct ITagAliasRegistry; + struct IMutableEnumValuesRegistry; + + class StartupExceptionRegistry; + + using IReporterFactoryPtr = std::shared_ptr<IReporterFactory>; struct IRegistryHub { virtual ~IRegistryHub(); virtual IReporterRegistry const& getReporterRegistry() const = 0; virtual ITestCaseRegistry const& getTestCaseRegistry() const = 0; - virtual IExceptionTranslatorRegistry& getExceptionTranslatorRegistry() = 0; + virtual ITagAliasRegistry const& getTagAliasRegistry() const = 0; + virtual IExceptionTranslatorRegistry const& getExceptionTranslatorRegistry() const = 0; + + virtual StartupExceptionRegistry const& getStartupExceptionRegistry() const = 0; }; struct IMutableRegistryHub { virtual ~IMutableRegistryHub(); - virtual void registerReporter( std::string const& name, Ptr<IReporterFactory> const& factory ) = 0; - virtual void registerListener( Ptr<IReporterFactory> const& factory ) = 0; + virtual void registerReporter( std::string const& name, IReporterFactoryPtr const& factory ) = 0; + virtual void registerListener( IReporterFactoryPtr const& factory ) = 0; virtual void registerTest( TestCase const& testInfo ) = 0; virtual void registerTranslator( const IExceptionTranslator* translator ) = 0; + virtual void registerTagAlias( std::string const& alias, std::string const& tag, SourceLineInfo const& lineInfo ) = 0; + virtual void registerStartupException() noexcept = 0; + virtual IMutableEnumValuesRegistry& getMutableEnumValuesRegistry() = 0; }; - IRegistryHub& getRegistryHub(); + IRegistryHub const& getRegistryHub(); IMutableRegistryHub& getMutableRegistryHub(); void cleanUp(); std::string translateActiveException(); } -namespace Catch { +// end catch_interfaces_registry_hub.h +#if defined(CATCH_CONFIG_DISABLE) + #define INTERNAL_CATCH_TRANSLATE_EXCEPTION_NO_REG( translatorName, signature) \ + static std::string translatorName( signature ) +#endif - typedef std::string(*exceptionTranslateFunction)(); +#include <exception> +#include <string> +#include <vector> + +namespace Catch { + using exceptionTranslateFunction = std::string(*)(); struct IExceptionTranslator; - typedef std::vector<const IExceptionTranslator*> ExceptionTranslators; + using ExceptionTranslators = std::vector<std::unique_ptr<IExceptionTranslator const>>; struct IExceptionTranslator { virtual ~IExceptionTranslator(); @@ -2775,10 +2952,10 @@ namespace Catch { : m_translateFunction( translateFunction ) {} - virtual std::string translate( ExceptionTranslators::const_iterator it, ExceptionTranslators::const_iterator itEnd ) const CATCH_OVERRIDE { + std::string translate( ExceptionTranslators::const_iterator it, ExceptionTranslators::const_iterator itEnd ) const override { try { if( it == itEnd ) - throw; + std::rethrow_exception(std::current_exception()); else return (*it)->translate( it+1, itEnd ); } @@ -2803,55 +2980,55 @@ namespace Catch { /////////////////////////////////////////////////////////////////////////////// #define INTERNAL_CATCH_TRANSLATE_EXCEPTION2( translatorName, signature ) \ static std::string translatorName( signature ); \ - namespace{ Catch::ExceptionTranslatorRegistrar INTERNAL_CATCH_UNIQUE_NAME( catch_internal_ExceptionRegistrar )( &translatorName ); }\ + CATCH_INTERNAL_SUPPRESS_GLOBALS_WARNINGS \ + namespace{ Catch::ExceptionTranslatorRegistrar INTERNAL_CATCH_UNIQUE_NAME( catch_internal_ExceptionRegistrar )( &translatorName ); } \ + CATCH_INTERNAL_UNSUPPRESS_GLOBALS_WARNINGS \ static std::string translatorName( signature ) #define INTERNAL_CATCH_TRANSLATE_EXCEPTION( signature ) INTERNAL_CATCH_TRANSLATE_EXCEPTION2( INTERNAL_CATCH_UNIQUE_NAME( catch_internal_ExceptionTranslator ), signature ) -// #included from: internal/catch_approx.hpp -#define TWOBLUECUBES_CATCH_APPROX_HPP_INCLUDED - -#include <cmath> -#include <limits> +// end catch_interfaces_exception.h +// start catch_approx.h -#if defined(CATCH_CONFIG_CPP11_TYPE_TRAITS) #include <type_traits> -#endif namespace Catch { namespace Detail { class Approx { + private: + bool equalityComparisonImpl(double other) const; + // Validates the new margin (margin >= 0) + // out-of-line to avoid including stdexcept in the header + void setMargin(double margin); + // Validates the new epsilon (0 < epsilon < 1) + // out-of-line to avoid including stdexcept in the header + void setEpsilon(double epsilon); + public: - explicit Approx ( double value ) - : m_epsilon( std::numeric_limits<float>::epsilon()*100 ), - m_scale( 1.0 ), - m_value( value ) - {} + explicit Approx ( double value ); - Approx( Approx const& other ) - : m_epsilon( other.m_epsilon ), - m_scale( other.m_scale ), - m_value( other.m_value ) - {} + static Approx custom(); - static Approx custom() { - return Approx( 0 ); - } + Approx operator-() const; - Approx operator()( double value ) { - Approx approx( value ); - approx.epsilon( m_epsilon ); - approx.scale( m_scale ); + template <typename T, typename = typename std::enable_if<std::is_constructible<double, T>::value>::type> + Approx operator()( T const& value ) { + Approx approx( static_cast<double>(value) ); + approx.m_epsilon = m_epsilon; + approx.m_margin = m_margin; + approx.m_scale = m_scale; return approx; } -#if defined(CATCH_CONFIG_CPP11_TYPE_TRAITS) + template <typename T, typename = typename std::enable_if<std::is_constructible<double, T>::value>::type> + explicit Approx( T const& value ): Approx(static_cast<double>(value)) + {} + template <typename T, typename = typename std::enable_if<std::is_constructible<double, T>::value>::type> friend bool operator == ( const T& lhs, Approx const& rhs ) { - // Thanks to Richard Harris for his help refining this formula - auto lhs_v = double(lhs); - return std::fabs( lhs_v - rhs.m_value ) < rhs.m_epsilon * (rhs.m_scale + (std::max)( std::fabs(lhs_v), std::fabs(rhs.m_value) ) ); + auto lhs_v = static_cast<double>(lhs); + return rhs.equalityComparisonImpl(lhs_v); } template <typename T, typename = typename std::enable_if<std::is_constructible<double, T>::value>::type> @@ -2860,133 +3037,1222 @@ namespace Detail { } template <typename T, typename = typename std::enable_if<std::is_constructible<double, T>::value>::type> - friend bool operator != ( T lhs, Approx const& rhs ) { + friend bool operator != ( T const& lhs, Approx const& rhs ) { return !operator==( lhs, rhs ); } template <typename T, typename = typename std::enable_if<std::is_constructible<double, T>::value>::type> - friend bool operator != ( Approx const& lhs, T rhs ) { + friend bool operator != ( Approx const& lhs, T const& rhs ) { return !operator==( rhs, lhs ); } template <typename T, typename = typename std::enable_if<std::is_constructible<double, T>::value>::type> - friend bool operator <= ( T lhs, Approx const& rhs ) - { - return double(lhs) < rhs.m_value || lhs == rhs; + friend bool operator <= ( T const& lhs, Approx const& rhs ) { + return static_cast<double>(lhs) < rhs.m_value || lhs == rhs; } template <typename T, typename = typename std::enable_if<std::is_constructible<double, T>::value>::type> - friend bool operator <= ( Approx const& lhs, T rhs ) - { - return lhs.m_value < double(rhs) || lhs == rhs; + friend bool operator <= ( Approx const& lhs, T const& rhs ) { + return lhs.m_value < static_cast<double>(rhs) || lhs == rhs; } template <typename T, typename = typename std::enable_if<std::is_constructible<double, T>::value>::type> - friend bool operator >= ( T lhs, Approx const& rhs ) - { - return double(lhs) > rhs.m_value || lhs == rhs; + friend bool operator >= ( T const& lhs, Approx const& rhs ) { + return static_cast<double>(lhs) > rhs.m_value || lhs == rhs; } template <typename T, typename = typename std::enable_if<std::is_constructible<double, T>::value>::type> - friend bool operator >= ( Approx const& lhs, T rhs ) - { - return lhs.m_value > double(rhs) || lhs == rhs; + friend bool operator >= ( Approx const& lhs, T const& rhs ) { + return lhs.m_value > static_cast<double>(rhs) || lhs == rhs; } -#else - friend bool operator == ( double lhs, Approx const& rhs ) { - // Thanks to Richard Harris for his help refining this formula - return std::fabs( lhs - rhs.m_value ) < rhs.m_epsilon * (rhs.m_scale + (std::max)( std::fabs(lhs), std::fabs(rhs.m_value) ) ); + + template <typename T, typename = typename std::enable_if<std::is_constructible<double, T>::value>::type> + Approx& epsilon( T const& newEpsilon ) { + double epsilonAsDouble = static_cast<double>(newEpsilon); + setEpsilon(epsilonAsDouble); + return *this; } - friend bool operator == ( Approx const& lhs, double rhs ) { - return operator==( rhs, lhs ); + template <typename T, typename = typename std::enable_if<std::is_constructible<double, T>::value>::type> + Approx& margin( T const& newMargin ) { + double marginAsDouble = static_cast<double>(newMargin); + setMargin(marginAsDouble); + return *this; } - friend bool operator != ( double lhs, Approx const& rhs ) { - return !operator==( lhs, rhs ); + template <typename T, typename = typename std::enable_if<std::is_constructible<double, T>::value>::type> + Approx& scale( T const& newScale ) { + m_scale = static_cast<double>(newScale); + return *this; } - friend bool operator != ( Approx const& lhs, double rhs ) { - return !operator==( rhs, lhs ); + std::string toString() const; + + private: + double m_epsilon; + double m_margin; + double m_scale; + double m_value; + }; +} // end namespace Detail + +namespace literals { + Detail::Approx operator "" _a(long double val); + Detail::Approx operator "" _a(unsigned long long val); +} // end namespace literals + +template<> +struct StringMaker<Catch::Detail::Approx> { + static std::string convert(Catch::Detail::Approx const& value); +}; + +} // end namespace Catch + +// end catch_approx.h +// start catch_string_manip.h + +#include <string> +#include <iosfwd> +#include <vector> + +namespace Catch { + + bool startsWith( std::string const& s, std::string const& prefix ); + bool startsWith( std::string const& s, char prefix ); + bool endsWith( std::string const& s, std::string const& suffix ); + bool endsWith( std::string const& s, char suffix ); + bool contains( std::string const& s, std::string const& infix ); + void toLowerInPlace( std::string& s ); + std::string toLower( std::string const& s ); + std::string trim( std::string const& str ); + + // !!! 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 ); + + struct pluralise { + pluralise( std::size_t count, std::string const& label ); + + friend std::ostream& operator << ( std::ostream& os, pluralise const& pluraliser ); + + std::size_t m_count; + std::string m_label; + }; +} + +// end catch_string_manip.h +#ifndef CATCH_CONFIG_DISABLE_MATCHERS +// start catch_capture_matchers.h + +// start catch_matchers.h + +#include <string> +#include <vector> + +namespace Catch { +namespace Matchers { + namespace Impl { + + template<typename ArgT> struct MatchAllOf; + template<typename ArgT> struct MatchAnyOf; + template<typename ArgT> struct MatchNotOf; + + class MatcherUntypedBase { + public: + MatcherUntypedBase() = default; + MatcherUntypedBase ( MatcherUntypedBase const& ) = default; + MatcherUntypedBase& operator = ( MatcherUntypedBase const& ) = delete; + std::string toString() const; + + protected: + virtual ~MatcherUntypedBase(); + virtual std::string describe() const = 0; + mutable std::string m_cachedToString; + }; + +#ifdef __clang__ +# pragma clang diagnostic push +# pragma clang diagnostic ignored "-Wnon-virtual-dtor" +#endif + + template<typename ObjectT> + struct MatcherMethod { + virtual bool match( ObjectT const& arg ) const = 0; + }; + +#ifdef __clang__ +# pragma clang diagnostic pop +#endif + + template<typename T> + struct MatcherBase : MatcherUntypedBase, MatcherMethod<T> { + + MatchAllOf<T> operator && ( MatcherBase const& other ) const; + MatchAnyOf<T> operator || ( MatcherBase const& other ) const; + MatchNotOf<T> operator ! () const; + }; + + template<typename ArgT> + struct MatchAllOf : MatcherBase<ArgT> { + bool match( ArgT const& arg ) const override { + for( auto matcher : m_matchers ) { + if (!matcher->match(arg)) + return false; + } + return true; + } + std::string describe() const override { + std::string description; + description.reserve( 4 + m_matchers.size()*32 ); + description += "( "; + bool first = true; + for( auto matcher : m_matchers ) { + if( first ) + first = false; + else + description += " and "; + description += matcher->toString(); + } + description += " )"; + return description; + } + + MatchAllOf<ArgT>& operator && ( MatcherBase<ArgT> const& other ) { + m_matchers.push_back( &other ); + return *this; + } + + std::vector<MatcherBase<ArgT> const*> m_matchers; + }; + template<typename ArgT> + struct MatchAnyOf : MatcherBase<ArgT> { + + bool match( ArgT const& arg ) const override { + for( auto matcher : m_matchers ) { + if (matcher->match(arg)) + return true; + } + return false; + } + std::string describe() const override { + std::string description; + description.reserve( 4 + m_matchers.size()*32 ); + description += "( "; + bool first = true; + for( auto matcher : m_matchers ) { + if( first ) + first = false; + else + description += " or "; + description += matcher->toString(); + } + description += " )"; + return description; + } + + MatchAnyOf<ArgT>& operator || ( MatcherBase<ArgT> const& other ) { + m_matchers.push_back( &other ); + return *this; + } + + std::vector<MatcherBase<ArgT> const*> m_matchers; + }; + + template<typename ArgT> + struct MatchNotOf : MatcherBase<ArgT> { + + 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(); + } + MatcherBase<ArgT> const& m_underlyingMatcher; + }; + + template<typename T> + MatchAllOf<T> MatcherBase<T>::operator && ( MatcherBase const& other ) const { + return MatchAllOf<T>() && *this && other; + } + template<typename T> + MatchAnyOf<T> MatcherBase<T>::operator || ( MatcherBase const& other ) const { + return MatchAnyOf<T>() || *this || other; + } + template<typename T> + MatchNotOf<T> MatcherBase<T>::operator ! () const { + return MatchNotOf<T>( *this ); } - friend bool operator <= ( double lhs, Approx const& rhs ) + } // namespace Impl + +} // namespace Matchers + +using namespace Matchers; +using Matchers::Impl::MatcherBase; + +} // namespace Catch + +// end catch_matchers.h +// start catch_matchers_floating.h + +#include <type_traits> +#include <cmath> + +namespace Catch { +namespace Matchers { + + namespace Floating { + + enum class FloatingPointKind : uint8_t; + + struct WithinAbsMatcher : MatcherBase<double> { + WithinAbsMatcher(double target, double margin); + bool match(double const& matchee) const override; + std::string describe() const override; + private: + double m_target; + double m_margin; + }; + + struct WithinUlpsMatcher : MatcherBase<double> { + WithinUlpsMatcher(double target, int ulps, FloatingPointKind baseType); + bool match(double const& matchee) const override; + std::string describe() const override; + private: + double m_target; + int m_ulps; + FloatingPointKind m_type; + }; + + } // namespace Floating + + // The following functions create the actual matcher objects. + // This allows the types to be inferred + Floating::WithinUlpsMatcher WithinULP(double target, int maxUlpDiff); + Floating::WithinUlpsMatcher WithinULP(float target, int maxUlpDiff); + Floating::WithinAbsMatcher WithinAbs(double target, double margin); + +} // namespace Matchers +} // namespace Catch + +// end catch_matchers_floating.h +// start catch_matchers_generic.hpp + +#include <functional> +#include <string> + +namespace Catch { +namespace Matchers { +namespace Generic { + +namespace Detail { + std::string finalizeDescription(const std::string& desc); +} + +template <typename T> +class PredicateMatcher : public MatcherBase<T> { + std::function<bool(T const&)> m_predicate; + std::string m_description; +public: + + PredicateMatcher(std::function<bool(T const&)> const& elem, std::string const& descr) + :m_predicate(std::move(elem)), + m_description(Detail::finalizeDescription(descr)) + {} + + bool match( T const& item ) const override { + return m_predicate(item); + } + + std::string describe() const override { + return m_description; + } +}; + +} // namespace Generic + + // The following functions create the actual matcher objects. + // The user has to explicitly specify type to the function, because + // inferring std::function<bool(T const&)> is hard (but possible) and + // requires a lot of TMP. + template<typename T> + Generic::PredicateMatcher<T> Predicate(std::function<bool(T const&)> const& predicate, std::string const& description = "") { + return Generic::PredicateMatcher<T>(predicate, description); + } + +} // namespace Matchers +} // namespace Catch + +// end catch_matchers_generic.hpp +// start catch_matchers_string.h + +#include <string> + +namespace Catch { +namespace Matchers { + + namespace StdString { + + struct CasedString { - return lhs < rhs.m_value || lhs == rhs; + CasedString( std::string const& str, CaseSensitive::Choice caseSensitivity ); + std::string adjustString( std::string const& str ) const; + std::string caseSensitivitySuffix() const; + + CaseSensitive::Choice m_caseSensitivity; + std::string m_str; + }; + + struct StringMatcherBase : MatcherBase<std::string> { + StringMatcherBase( std::string const& operation, CasedString const& comparator ); + std::string describe() const override; + + CasedString m_comparator; + std::string m_operation; + }; + + struct EqualsMatcher : StringMatcherBase { + EqualsMatcher( CasedString const& comparator ); + bool match( std::string const& source ) const override; + }; + struct ContainsMatcher : StringMatcherBase { + ContainsMatcher( CasedString const& comparator ); + bool match( std::string const& source ) const override; + }; + struct StartsWithMatcher : StringMatcherBase { + StartsWithMatcher( CasedString const& comparator ); + bool match( std::string const& source ) const override; + }; + struct EndsWithMatcher : StringMatcherBase { + EndsWithMatcher( CasedString const& comparator ); + bool match( std::string const& source ) const override; + }; + + struct RegexMatcher : MatcherBase<std::string> { + RegexMatcher( std::string regex, CaseSensitive::Choice caseSensitivity ); + bool match( std::string const& matchee ) const override; + std::string describe() const override; + + private: + std::string m_regex; + CaseSensitive::Choice m_caseSensitivity; + }; + + } // namespace StdString + + // The following functions create the actual matcher objects. + // This allows the types to be inferred + + StdString::EqualsMatcher Equals( std::string const& str, CaseSensitive::Choice caseSensitivity = CaseSensitive::Yes ); + StdString::ContainsMatcher Contains( std::string const& str, CaseSensitive::Choice caseSensitivity = CaseSensitive::Yes ); + StdString::EndsWithMatcher EndsWith( std::string const& str, CaseSensitive::Choice caseSensitivity = CaseSensitive::Yes ); + StdString::StartsWithMatcher StartsWith( std::string const& str, CaseSensitive::Choice caseSensitivity = CaseSensitive::Yes ); + StdString::RegexMatcher Matches( std::string const& regex, CaseSensitive::Choice caseSensitivity = CaseSensitive::Yes ); + +} // namespace Matchers +} // namespace Catch + +// end catch_matchers_string.h +// start catch_matchers_vector.h + +#include <algorithm> + +namespace Catch { +namespace Matchers { + + namespace Vector { + template<typename T> + struct ContainsElementMatcher : MatcherBase<std::vector<T>> { + + ContainsElementMatcher(T const &comparator) : m_comparator( comparator) {} + + bool match(std::vector<T> const &v) const override { + for (auto const& el : v) { + if (el == m_comparator) { + return true; + } + } + return false; + } + + std::string describe() const override { + return "Contains: " + ::Catch::Detail::stringify( m_comparator ); + } + + T const& m_comparator; + }; + + template<typename T> + struct ContainsMatcher : MatcherBase<std::vector<T>> { + + ContainsMatcher(std::vector<T> const &comparator) : m_comparator( comparator ) {} + + bool match(std::vector<T> const &v) const override { + // !TBD: see note in EqualsMatcher + if (m_comparator.size() > v.size()) + return false; + for (auto const& comparator : m_comparator) { + auto present = false; + for (const auto& el : v) { + if (el == comparator) { + present = true; + break; + } + } + if (!present) { + return false; + } + } + return true; + } + std::string describe() const override { + return "Contains: " + ::Catch::Detail::stringify( m_comparator ); + } + + std::vector<T> const& m_comparator; + }; + + template<typename T> + struct EqualsMatcher : MatcherBase<std::vector<T>> { + + EqualsMatcher(std::vector<T> const &comparator) : m_comparator( comparator ) {} + + bool match(std::vector<T> 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 ); + } + std::vector<T> const& m_comparator; + }; + + template<typename T> + struct ApproxMatcher : MatcherBase<std::vector<T>> { + + ApproxMatcher(std::vector<T> const& comparator) : m_comparator( comparator ) {} + + bool match(std::vector<T> 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 = typename std::enable_if<std::is_constructible<double, T>::value>::type> + ApproxMatcher& epsilon( T const& newEpsilon ) { + approx.epsilon(newEpsilon); + return *this; + } + template <typename = typename std::enable_if<std::is_constructible<double, T>::value>::type> + ApproxMatcher& margin( T const& newMargin ) { + approx.margin(newMargin); + return *this; + } + template <typename = typename std::enable_if<std::is_constructible<double, T>::value>::type> + ApproxMatcher& scale( T const& newScale ) { + approx.scale(newScale); + return *this; + } + + std::vector<T> const& m_comparator; + mutable Catch::Detail::Approx approx = Catch::Detail::Approx::custom(); + }; + + template<typename T> + struct UnorderedEqualsMatcher : MatcherBase<std::vector<T>> { + UnorderedEqualsMatcher(std::vector<T> const& target) : m_target(target) {} + bool match(std::vector<T> const& vec) const override { + // Note: This is a reimplementation of std::is_permutation, + // because I don't want to include <algorithm> inside the common path + if (m_target.size() != vec.size()) { + return false; + } + return std::is_permutation(m_target.begin(), m_target.end(), vec.begin()); + } + + std::string describe() const override { + return "UnorderedEquals: " + ::Catch::Detail::stringify(m_target); + } + private: + std::vector<T> const& m_target; + }; + + } // namespace Vector + + // The following functions create the actual matcher objects. + // This allows the types to be inferred + + template<typename T> + Vector::ContainsMatcher<T> Contains( std::vector<T> const& comparator ) { + return Vector::ContainsMatcher<T>( comparator ); + } + + template<typename T> + Vector::ContainsElementMatcher<T> VectorContains( T const& comparator ) { + return Vector::ContainsElementMatcher<T>( comparator ); + } + + template<typename T> + Vector::EqualsMatcher<T> Equals( std::vector<T> const& comparator ) { + return Vector::EqualsMatcher<T>( comparator ); + } + + template<typename T> + Vector::ApproxMatcher<T> Approx( std::vector<T> const& comparator ) { + return Vector::ApproxMatcher<T>( comparator ); + } + + template<typename T> + Vector::UnorderedEqualsMatcher<T> UnorderedEquals(std::vector<T> const& target) { + return Vector::UnorderedEqualsMatcher<T>(target); + } + +} // namespace Matchers +} // namespace Catch + +// end catch_matchers_vector.h +namespace Catch { + + template<typename ArgT, typename MatcherT> + class MatchExpr : public ITransientExpression { + ArgT const& m_arg; + MatcherT m_matcher; + StringRef m_matcherString; + public: + MatchExpr( ArgT const& arg, MatcherT const& matcher, StringRef const& matcherString ) + : ITransientExpression{ true, matcher.match( arg ) }, + m_arg( arg ), + m_matcher( matcher ), + m_matcherString( matcherString ) + {} + + void streamReconstructedExpression( std::ostream &os ) const override { + auto matcherAsString = m_matcher.toString(); + os << Catch::Detail::stringify( m_arg ) << ' '; + if( matcherAsString == Detail::unprintableString ) + os << m_matcherString; + else + os << matcherAsString; + } + }; + + using StringMatcher = Matchers::Impl::MatcherBase<std::string>; + + void handleExceptionMatchExpr( AssertionHandler& handler, StringMatcher const& matcher, StringRef const& matcherString ); + + template<typename ArgT, typename MatcherT> + auto makeMatchExpr( ArgT const& arg, MatcherT const& matcher, StringRef const& matcherString ) -> MatchExpr<ArgT, MatcherT> { + return MatchExpr<ArgT, MatcherT>( arg, matcher, matcherString ); + } + +} // namespace Catch + +/////////////////////////////////////////////////////////////////////////////// +#define INTERNAL_CHECK_THAT( macroName, matcher, resultDisposition, arg ) \ + do { \ + Catch::AssertionHandler catchAssertionHandler( macroName##_catch_sr, CATCH_INTERNAL_LINEINFO, CATCH_INTERNAL_STRINGIFY(arg) ", " CATCH_INTERNAL_STRINGIFY(matcher), resultDisposition ); \ + INTERNAL_CATCH_TRY { \ + catchAssertionHandler.handleExpr( Catch::makeMatchExpr( arg, matcher, #matcher##_catch_sr ) ); \ + } INTERNAL_CATCH_CATCH( catchAssertionHandler ) \ + INTERNAL_CATCH_REACT( catchAssertionHandler ) \ + } while( false ) + +/////////////////////////////////////////////////////////////////////////////// +#define INTERNAL_CATCH_THROWS_MATCHES( macroName, exceptionType, resultDisposition, matcher, ... ) \ + do { \ + Catch::AssertionHandler catchAssertionHandler( macroName##_catch_sr, CATCH_INTERNAL_LINEINFO, CATCH_INTERNAL_STRINGIFY(__VA_ARGS__) ", " CATCH_INTERNAL_STRINGIFY(exceptionType) ", " CATCH_INTERNAL_STRINGIFY(matcher), resultDisposition ); \ + if( catchAssertionHandler.allowThrows() ) \ + try { \ + static_cast<void>(__VA_ARGS__ ); \ + catchAssertionHandler.handleUnexpectedExceptionNotThrown(); \ + } \ + catch( exceptionType const& ex ) { \ + catchAssertionHandler.handleExpr( Catch::makeMatchExpr( ex, matcher, #matcher##_catch_sr ) ); \ + } \ + catch( ... ) { \ + catchAssertionHandler.handleUnexpectedInflightException(); \ + } \ + else \ + catchAssertionHandler.handleThrowingCallSkipped(); \ + INTERNAL_CATCH_REACT( catchAssertionHandler ) \ + } while( false ) + +// end catch_capture_matchers.h +#endif +// start catch_generators.hpp + +// start catch_interfaces_generatortracker.h + + +#include <memory> + +namespace Catch { + + namespace Generators { + class GeneratorUntypedBase { + public: + GeneratorUntypedBase() = default; + virtual ~GeneratorUntypedBase(); + // Attempts to move the generator to the next element + // + // Returns true iff the move succeeded (and a valid element + // can be retrieved). + virtual bool next() = 0; + }; + using GeneratorBasePtr = std::unique_ptr<GeneratorUntypedBase>; + + } // namespace Generators + + struct IGeneratorTracker { + virtual ~IGeneratorTracker(); + virtual auto hasGenerator() const -> bool = 0; + virtual auto getGenerator() const -> Generators::GeneratorBasePtr const& = 0; + virtual void setGenerator( Generators::GeneratorBasePtr&& generator ) = 0; + }; + +} // namespace Catch + +// end catch_interfaces_generatortracker.h +// start catch_enforce.h + +#include <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) + +// end catch_enforce.h +#include <memory> +#include <vector> +#include <cassert> + +#include <utility> +#include <exception> + +namespace Catch { + +class GeneratorException : public std::exception { + const char* const m_msg = ""; + +public: + GeneratorException(const char* msg): + m_msg(msg) + {} + + const char* what() const noexcept override final; +}; + +namespace Generators { + + // !TBD move this into its own location? + namespace pf{ + template<typename T, typename... Args> + std::unique_ptr<T> make_unique( Args&&... args ) { + return std::unique_ptr<T>(new T(std::forward<Args>(args)...)); + } + } + + template<typename T> + struct IGenerator : GeneratorUntypedBase { + virtual ~IGenerator() = default; + + // Returns the current element of the generator + // + // \Precondition The generator is either freshly constructed, + // or the last call to `next()` returned true + virtual T const& get() const = 0; + using type = T; + }; + + template<typename T> + class SingleValueGenerator final : public IGenerator<T> { + T m_value; + public: + SingleValueGenerator(T const& value) : m_value( value ) {} + SingleValueGenerator(T&& value) : m_value(std::move(value)) {} + + T const& get() const override { + return m_value; + } + bool next() override { + return false; + } + }; + + template<typename T> + class FixedValuesGenerator final : public IGenerator<T> { + 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> + class GeneratorWrapper final { + std::unique_ptr<IGenerator<T>> m_generator; + public: + GeneratorWrapper(std::unique_ptr<IGenerator<T>> generator): + m_generator(std::move(generator)) + {} + T const& get() const { + return m_generator->get(); + } + bool next() { + return m_generator->next(); } + }; - friend bool operator <= ( Approx const& lhs, double rhs ) + template <typename T> + GeneratorWrapper<T> value(T&& value) { + return GeneratorWrapper<T>(pf::make_unique<SingleValueGenerator<T>>(std::forward<T>(value))); + } + template <typename T> + GeneratorWrapper<T> values(std::initializer_list<T> values) { + return GeneratorWrapper<T>(pf::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 populate(GeneratorWrapper<T>&& generator) { + m_generators.emplace_back(std::move(generator)); + } + void populate(T&& val) { + m_generators.emplace_back(value(std::move(val))); + } + template<typename U> + void populate(U&& val) { + populate(T(std::move(val))); + } + template<typename U, typename... Gs> + void populate(U&& valueOrGenerator, Gs... moreGenerators) { + populate(std::forward<U>(valueOrGenerator)); + populate(std::forward<Gs>(moreGenerators)...); + } + + public: + template <typename... Gs> + Generators(Gs... moreGenerators) { + m_generators.reserve(sizeof...(Gs)); + populate(std::forward<Gs>(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<Ts...>> table( std::initializer_list<std::tuple<typename std::decay<Ts>::type...>> 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>(std::move(generator), std::forward<Gs>(moreGenerators)...); + } + template<typename T> + auto makeGenerators( GeneratorWrapper<T>&& generator ) -> Generators<T> { + return Generators<T>(std::move(generator)); + } + template<typename T, typename... Gs> + auto makeGenerators( T&& val, Gs... moreGenerators ) -> Generators<T> { + return makeGenerators( value( std::forward<T>( val ) ), std::forward<Gs>( moreGenerators )... ); + } + template<typename T, typename U, typename... Gs> + auto makeGenerators( as<T>, U&& val, Gs... moreGenerators ) -> Generators<T> { + return makeGenerators( value( T( std::forward<U>( val ) ) ), std::forward<Gs>( moreGenerators )... ); + } + + auto acquireGeneratorTracker( SourceLineInfo const& lineInfo ) -> IGeneratorTracker&; + + template<typename L> + // Note: The type after -> is weird, because VS2015 cannot parse + // the expression used in the typedef inside, when it is in + // return type. Yeah. + auto generate( SourceLineInfo const& lineInfo, L const& generatorExpression ) -> decltype(std::declval<decltype(generatorExpression())>().get()) { + using UnderlyingType = typename decltype(generatorExpression())::type; + + IGeneratorTracker& tracker = acquireGeneratorTracker( lineInfo ); + if (!tracker.hasGenerator()) { + tracker.setGenerator(pf::make_unique<Generators<UnderlyingType>>(generatorExpression())); + } + + auto const& generator = static_cast<IGenerator<UnderlyingType> const&>( *tracker.getGenerator() ); + return generator.get(); + } + +} // namespace Generators +} // namespace Catch + +#define GENERATE( ... ) \ + Catch::Generators::generate( CATCH_INTERNAL_LINEINFO, [ ]{ using namespace Catch::Generators; return makeGenerators( __VA_ARGS__ ); } ) +#define GENERATE_COPY( ... ) \ + Catch::Generators::generate( CATCH_INTERNAL_LINEINFO, [=]{ using namespace Catch::Generators; return makeGenerators( __VA_ARGS__ ); } ) +#define GENERATE_REF( ... ) \ + Catch::Generators::generate( CATCH_INTERNAL_LINEINFO, [&]{ using namespace Catch::Generators; return makeGenerators( __VA_ARGS__ ); } ) + +// end catch_generators.hpp +// start catch_generators_generic.hpp + +namespace Catch { +namespace Generators { + + template <typename T> + class TakeGenerator : 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(std::move(generator)), + m_target(target) { - return lhs.m_value < rhs || lhs == rhs; + 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>(pf::make_unique<TakeGenerator<T>>(target, std::move(generator))); + } - friend bool operator >= ( double lhs, Approx const& rhs ) + template <typename T, typename Predicate> + class FilterGenerator : public IGenerator<T> { + GeneratorWrapper<T> m_generator; + Predicate m_predicate; + public: + template <typename P = Predicate> + FilterGenerator(P&& pred, GeneratorWrapper<T>&& generator): + m_generator(std::move(generator)), + m_predicate(std::forward<P>(pred)) { - return lhs > rhs.m_value || lhs == rhs; + 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) { + Catch::throw_exception(GeneratorException("No valid value found in filtered generator")); + } + } } - friend bool operator >= ( Approx const& lhs, double rhs ) + 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>(std::unique_ptr<IGenerator<T>>(pf::make_unique<FilterGenerator<T, Predicate>>(std::forward<Predicate>(pred), std::move(generator)))); + } + + template <typename T> + class RepeatGenerator : public IGenerator<T> { + 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(std::move(generator)), + m_target_repeats(repeats) { - return lhs.m_value > rhs || lhs == rhs; + assert(m_target_repeats > 0 && "Repeat generator must repeat at least once"); } -#endif - Approx& epsilon( double newEpsilon ) { - m_epsilon = newEpsilon; - return *this; + 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]; } - Approx& scale( double newScale ) { - m_scale = newScale; - return *this; + 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; } + }; - std::string toString() const { - std::ostringstream oss; - oss << "Approx( " << Catch::toString( m_value ) << " )"; - return oss.str(); + template <typename T> + GeneratorWrapper<T> repeat(size_t repeats, GeneratorWrapper<T>&& generator) { + return GeneratorWrapper<T>(pf::make_unique<RepeatGenerator<T>>(repeats, std::move(generator))); + } + + template <typename T, typename U, typename Func> + class MapGenerator : 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(std::move(generator)), + m_function(std::forward<F2>(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; + } + }; - private: - double m_epsilon; - double m_scale; - double m_value; +#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. Also *_t format is preferred over + // typename *::type format. + template <typename Func, typename U> + using MapFunctionReturnType = std::remove_reference_t<std::remove_cv_t<std::invoke_result_t<Func, U>>>; +#else + template <typename Func, typename U> + using MapFunctionReturnType = typename std::remove_reference<typename std::remove_cv<typename std::result_of<Func(U)>::type>::type>::type; +#endif + + template <typename Func, typename U, typename T = MapFunctionReturnType<Func, U>> + GeneratorWrapper<T> map(Func&& function, GeneratorWrapper<U>&& generator) { + return GeneratorWrapper<T>( + pf::make_unique<MapGenerator<T, U, Func>>(std::forward<Func>(function), std::move(generator)) + ); + } + + template <typename T, typename U, typename Func> + GeneratorWrapper<T> map(Func&& function, GeneratorWrapper<U>&& generator) { + return GeneratorWrapper<T>( + pf::make_unique<MapGenerator<T, U, Func>>(std::forward<Func>(function), std::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(std::move(generator)) + { + m_chunk.reserve(m_chunk_size); + m_chunk.push_back(m_generator.get()); + for (size_t i = 1; i < m_chunk_size; ++i) { + if (!m_generator.next()) { + Catch::throw_exception(GeneratorException("Not enough values to initialize the first chunk")); + } + m_chunk.push_back(m_generator.get()); + } + } + std::vector<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<> -inline std::string toString<Detail::Approx>( Detail::Approx const& value ) { - return value.toString(); -} + template <typename T> + GeneratorWrapper<std::vector<T>> chunk(size_t size, GeneratorWrapper<T>&& generator) { + return GeneratorWrapper<std::vector<T>>( + pf::make_unique<ChunkGenerator<T>>(size, std::move(generator)) + ); + } -} // end namespace Catch +} // namespace Generators +} // namespace Catch -// #included from: internal/catch_interfaces_tag_alias_registry.h -#define TWOBLUECUBES_CATCH_INTERFACES_TAG_ALIAS_REGISTRY_H_INCLUDED +// end catch_generators_generic.hpp +// start catch_generators_specific.hpp -// #included from: catch_tag_alias.h -#define TWOBLUECUBES_CATCH_TAG_ALIAS_H_INCLUDED +// start catch_context.h -#include <string> +#include <memory> namespace Catch { - struct TagAlias { - TagAlias( std::string _tag, SourceLineInfo _lineInfo ) : tag( _tag ), lineInfo( _lineInfo ) {} + struct IResultCapture; + struct IRunner; + struct IConfig; + struct IMutableContext; - std::string tag; - SourceLineInfo lineInfo; + using IConfigPtr = std::shared_ptr<IConfig const>; + + struct IContext + { + virtual ~IContext(); + + virtual IResultCapture* getResultCapture() = 0; + virtual IRunner* getRunner() = 0; + virtual IConfigPtr const& getConfig() const = 0; }; - struct RegistrarForTagAliases { - RegistrarForTagAliases( char const* alias, char const* tag, SourceLineInfo const& lineInfo ); + struct IMutableContext : IContext + { + virtual ~IMutableContext(); + virtual void setResultCapture( IResultCapture* resultCapture ) = 0; + virtual void setRunner( IRunner* runner ) = 0; + virtual void setConfig( IConfigPtr const& config ) = 0; + + private: + static IMutableContext *currentContext; + friend IMutableContext& getCurrentMutableContext(); + friend void cleanUpContext(); + static void createContext(); }; -} // end namespace Catch + inline IMutableContext& getCurrentMutableContext() + { + if( !IMutableContext::currentContext ) + IMutableContext::createContext(); + return *IMutableContext::currentContext; + } + + inline IContext& getCurrentContext() + { + return getCurrentMutableContext(); + } + + void cleanUpContext(); +} -#define CATCH_REGISTER_TAG_ALIAS( alias, spec ) namespace{ Catch::RegistrarForTagAliases INTERNAL_CATCH_UNIQUE_NAME( AutoRegisterTagAlias )( alias, spec, CATCH_INTERNAL_LINEINFO ); } -// #included from: catch_option.hpp -#define TWOBLUECUBES_CATCH_OPTION_HPP_INCLUDED +// end catch_context.h +// start catch_interfaces_config.h + +// start catch_option.hpp namespace Catch { @@ -2994,12 +4260,12 @@ namespace Catch { template<typename T> class Option { public: - Option() : nullableValue( CATCH_NULL ) {} + Option() : nullableValue( nullptr ) {} Option( T const& _value ) : nullableValue( new( storage ) T( _value ) ) {} Option( Option const& _other ) - : nullableValue( _other ? new( storage ) T( *_other ) : CATCH_NULL ) + : nullableValue( _other ? new( storage ) T( *_other ) : nullptr ) {} ~Option() { @@ -3023,7 +4289,7 @@ namespace Catch { void reset() { if( nullableValue ) nullableValue->~T(); - nullableValue = CATCH_NULL; + nullableValue = nullptr; } T& operator*() { return *nullableValue; } @@ -3035,40 +4301,225 @@ namespace Catch { return nullableValue ? *nullableValue : defaultValue; } - bool some() const { return nullableValue != CATCH_NULL; } - bool none() const { return nullableValue == CATCH_NULL; } + bool some() const { return nullableValue != nullptr; } + bool none() const { return nullableValue == nullptr; } - bool operator !() const { return nullableValue == CATCH_NULL; } - operator SafeBool::type() const { - return SafeBool::makeSafe( some() ); + bool operator !() const { return nullableValue == nullptr; } + explicit operator bool() const { + return some(); } private: - T* nullableValue; - char storage[sizeof(T)]; + T *nullableValue; + alignas(alignof(T)) char storage[sizeof(T)]; }; } // end namespace Catch +// end catch_option.hpp +#include <iosfwd> +#include <string> +#include <vector> +#include <memory> + namespace Catch { - struct ITagAliasRegistry { - virtual ~ITagAliasRegistry(); - virtual Option<TagAlias> find( std::string const& alias ) const = 0; - virtual std::string expandAliases( std::string const& unexpandedTestSpec ) const = 0; + enum class Verbosity { + Quiet = 0, + Normal, + High + }; - static ITagAliasRegistry const& get(); + struct WarnAbout { enum What { + Nothing = 0x00, + NoAssertions = 0x01, + NoTests = 0x02 + }; }; + + struct ShowDurations { enum OrNot { + DefaultForReporter, + Always, + Never + }; }; + struct RunTests { enum InWhatOrder { + InDeclarationOrder, + InLexicographicalOrder, + InRandomOrder + }; }; + struct UseColour { enum YesOrNo { + Auto, + Yes, + No + }; }; + struct WaitForKeypress { enum When { + Never, + BeforeStart = 1, + BeforeExit = 2, + BeforeStartAndExit = BeforeStart | BeforeExit + }; }; + + class TestSpec; + + struct IConfig : NonCopyable { + + virtual ~IConfig(); + + virtual bool allowThrows() const = 0; + virtual std::ostream& stream() const = 0; + virtual std::string name() const = 0; + virtual bool includeSuccessfulResults() const = 0; + virtual bool shouldDebugBreak() const = 0; + virtual bool warnAboutMissingAssertions() const = 0; + virtual bool warnAboutNoTests() const = 0; + virtual int abortAfter() const = 0; + virtual bool showInvisibles() const = 0; + virtual ShowDurations::OrNot showDurations() const = 0; + virtual TestSpec const& testSpec() const = 0; + virtual bool hasTestFilters() const = 0; + virtual std::vector<std::string> const& getTestsOrTags() const = 0; + virtual RunTests::InWhatOrder runOrder() const = 0; + virtual unsigned int rngSeed() const = 0; + virtual UseColour::YesOrNo useColour() const = 0; + virtual std::vector<std::string> const& getSectionsToRun() const = 0; + virtual Verbosity verbosity() const = 0; + + virtual bool benchmarkNoAnalysis() const = 0; + virtual int benchmarkSamples() const = 0; + virtual double benchmarkConfidenceInterval() const = 0; + virtual unsigned int benchmarkResamples() const = 0; }; -} // end namespace Catch + using IConfigPtr = std::shared_ptr<IConfig const>; +} + +// end catch_interfaces_config.h +#include <random> + +namespace Catch { +namespace Generators { + +template <typename Float> +class RandomFloatingGenerator final : public IGenerator<Float> { + // FIXME: What is the right seed? + std::minstd_rand m_rand; + std::uniform_real_distribution<Float> m_dist; + Float m_current_number; +public: + + RandomFloatingGenerator(Float a, Float b): + m_rand(getCurrentContext().getConfig()->rngSeed()), + 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_rand); + return true; + } +}; + +template <typename Integer> +class RandomIntegerGenerator final : public IGenerator<Integer> { + std::minstd_rand m_rand; + std::uniform_int_distribution<Integer> m_dist; + Integer m_current_number; +public: + + RandomIntegerGenerator(Integer a, Integer b): + m_rand(getCurrentContext().getConfig()->rngSeed()), + 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_rand); + return true; + } +}; + +// TODO: Ideally this would be also constrained against the various char types, +// but I don't expect users to run into that in practice. +template <typename T> +typename std::enable_if<std::is_integral<T>::value && !std::is_same<T, bool>::value, +GeneratorWrapper<T>>::type +random(T a, T b) { + return GeneratorWrapper<T>( + pf::make_unique<RandomIntegerGenerator<T>>(a, b) + ); +} + +template <typename T> +typename std::enable_if<std::is_floating_point<T>::value, +GeneratorWrapper<T>>::type +random(T a, T b) { + return GeneratorWrapper<T>( + pf::make_unique<RandomFloatingGenerator<T>>(a, b) + ); +} + +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_integral<T>::value && !std::is_same<T, bool>::value, "Type must be an integer"); + return GeneratorWrapper<T>(pf::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>(pf::make_unique<RangeGenerator<T>>(start, end)); +} + +} // namespace Generators +} // namespace Catch + +// end catch_generators_specific.hpp // These files are included here so the single_include script doesn't put them // in the conditionally compiled sections -// #included from: internal/catch_test_case_info.h -#define TWOBLUECUBES_CATCH_TEST_CASE_INFO_H_INCLUDED +// start catch_test_case_info.h #include <string> -#include <set> +#include <vector> +#include <memory> #ifdef __clang__ #pragma clang diagnostic push @@ -3077,7 +4528,7 @@ namespace Catch { namespace Catch { - struct ITestCase; + struct ITestInvoker; struct TestCaseInfo { enum SpecialProperties{ @@ -3086,30 +4537,30 @@ namespace Catch { ShouldFail = 1 << 2, MayFail = 1 << 3, Throws = 1 << 4, - NonPortable = 1 << 5 + NonPortable = 1 << 5, + Benchmark = 1 << 6 }; TestCaseInfo( std::string const& _name, std::string const& _className, std::string const& _description, - std::set<std::string> const& _tags, + std::vector<std::string> const& _tags, SourceLineInfo const& _lineInfo ); - TestCaseInfo( TestCaseInfo const& other ); - - friend void setTags( TestCaseInfo& testCaseInfo, std::set<std::string> const& tags ); + friend void setTags( TestCaseInfo& testCaseInfo, std::vector<std::string> tags ); bool isHidden() const; bool throws() const; bool okToFail() const; bool expectedToFail() const; + std::string tagsAsString() const; + std::string name; std::string className; std::string description; - std::set<std::string> tags; - std::set<std::string> lcaseTags; - std::string tagsAsString; + std::vector<std::string> tags; + std::vector<std::string> lcaseTags; SourceLineInfo lineInfo; SpecialProperties properties; }; @@ -3117,8 +4568,7 @@ namespace Catch { class TestCase : public TestCaseInfo { public: - TestCase( ITestCase* testCase, TestCaseInfo const& info ); - TestCase( TestCase const& other ); + TestCase( ITestInvoker* testCase, TestCaseInfo&& info ); TestCase withName( std::string const& _newName ) const; @@ -3126,19 +4576,16 @@ namespace Catch { TestCaseInfo const& getTestCaseInfo() const; - void swap( TestCase& other ); bool operator == ( TestCase const& other ) const; bool operator < ( TestCase const& other ) const; - TestCase& operator = ( TestCase const& other ); private: - Ptr<ITestCase> test; + std::shared_ptr<ITestInvoker> test; }; - TestCase makeTestCase( ITestCase* testCase, + TestCase makeTestCase( ITestInvoker* testCase, std::string const& className, - std::string const& name, - std::string const& description, + NameAndTags const& nameAndTags, SourceLineInfo const& lineInfo ); } @@ -3146,10 +4593,21 @@ namespace Catch { #pragma clang diagnostic pop #endif +// end catch_test_case_info.h +// start catch_interfaces_runner.h + +namespace Catch { + + struct IRunner { + virtual ~IRunner(); + virtual bool aborting() const = 0; + }; +} + +// end catch_interfaces_runner.h #ifdef __OBJC__ -// #included from: internal/catch_objc.hpp -#define TWOBLUECUBES_CATCH_OBJC_HPP_INCLUDED +// start catch_objc.hpp #import <objc/runtime.h> @@ -3173,7 +4631,7 @@ namespace Catch { namespace Catch { - class OcMethod : public SharedImpl<ITestCase> { + class OcMethod : public ITestInvoker { public: OcMethod( Class cls, SEL sel ) : m_cls( cls ), m_sel( sel ) {} @@ -3209,9 +4667,9 @@ namespace Catch { } } - inline size_t registerTestMethods() { - size_t noTestMethods = 0; - int noClasses = objc_getClassList( CATCH_NULL, 0 ); + inline std::size_t registerTestMethods() { + std::size_t noTestMethods = 0; + int noClasses = objc_getClassList( nullptr, 0 ); Class* classes = (CATCH_UNSAFE_UNRETAINED Class *)malloc( sizeof(Class) * noClasses); objc_getClassList( classes, noClasses ); @@ -3230,7 +4688,7 @@ namespace Catch { std::string desc = Detail::getAnnotation( cls, "Description", testCaseName ); const char* className = class_getName( cls ); - getMutableRegistryHub().registerTest( makeTestCase( new OcMethod( cls, selector ), className, name.c_str(), desc.c_str(), SourceLineInfo() ) ); + getMutableRegistryHub().registerTest( makeTestCase( new OcMethod( cls, selector ), className, NameAndTags( name.c_str(), desc.c_str() ), SourceLineInfo("",0) ) ); noTestMethods++; } } @@ -3240,69 +4698,74 @@ namespace Catch { return noTestMethods; } +#if !defined(CATCH_CONFIG_DISABLE_MATCHERS) + namespace Matchers { namespace Impl { namespace NSStringMatchers { - template<typename MatcherT> - struct StringHolder : MatcherImpl<MatcherT, NSString*>{ + struct StringHolder : MatcherBase<NSString*>{ StringHolder( NSString* substr ) : m_substr( [substr copy] ){} StringHolder( StringHolder const& other ) : m_substr( [other.m_substr copy] ){} StringHolder() { arcSafeRelease( m_substr ); } - NSString* m_substr; + bool match( NSString* const& str ) const override { + return false; + } + + NSString* CATCH_ARC_STRONG m_substr; }; - struct Equals : StringHolder<Equals> { + struct Equals : StringHolder { Equals( NSString* substr ) : StringHolder( substr ){} - virtual bool match( ExpressionType const& str ) const { + bool match( NSString* const& str ) const override { return (str != nil || m_substr == nil ) && [str isEqualToString:m_substr]; } - virtual std::string toString() const { - return "equals string: " + Catch::toString( m_substr ); + std::string describe() const override { + return "equals string: " + Catch::Detail::stringify( m_substr ); } }; - struct Contains : StringHolder<Contains> { + struct Contains : StringHolder { Contains( NSString* substr ) : StringHolder( substr ){} - virtual bool match( ExpressionType const& str ) const { + bool match( NSString* const& str ) const override { return (str != nil || m_substr == nil ) && [str rangeOfString:m_substr].location != NSNotFound; } - virtual std::string toString() const { - return "contains string: " + Catch::toString( m_substr ); + std::string describe() const override { + return "contains string: " + Catch::Detail::stringify( m_substr ); } }; - struct StartsWith : StringHolder<StartsWith> { + struct StartsWith : StringHolder { StartsWith( NSString* substr ) : StringHolder( substr ){} - virtual bool match( ExpressionType const& str ) const { + bool match( NSString* const& str ) const override { return (str != nil || m_substr == nil ) && [str rangeOfString:m_substr].location == 0; } - virtual std::string toString() const { - return "starts with: " + Catch::toString( m_substr ); + std::string describe() const override { + return "starts with: " + Catch::Detail::stringify( m_substr ); } }; - struct EndsWith : StringHolder<EndsWith> { + struct EndsWith : StringHolder { EndsWith( NSString* substr ) : StringHolder( substr ){} - virtual bool match( ExpressionType const& str ) const { + bool match( NSString* const& str ) const override { return (str != nil || m_substr == nil ) && [str rangeOfString:m_substr].location == [str length] - [m_substr length]; } - virtual std::string toString() const { - return "ends with: " + Catch::toString( m_substr ); + std::string describe() const override { + return "ends with: " + Catch::Detail::stringify( m_substr ); } }; @@ -3325,63 +4788,52 @@ namespace Catch { using namespace Matchers; +#endif // CATCH_CONFIG_DISABLE_MATCHERS + } // namespace Catch /////////////////////////////////////////////////////////////////////////////// -#define OC_TEST_CASE( name, desc )\ -+(NSString*) INTERNAL_CATCH_UNIQUE_NAME( Catch_Name_test ) \ -{\ +#define OC_MAKE_UNIQUE_NAME( root, uniqueSuffix ) root##uniqueSuffix +#define OC_TEST_CASE2( name, desc, uniqueSuffix ) \ ++(NSString*) OC_MAKE_UNIQUE_NAME( Catch_Name_test_, uniqueSuffix ) \ +{ \ return @ name; \ -}\ -+(NSString*) INTERNAL_CATCH_UNIQUE_NAME( Catch_Description_test ) \ +} \ ++(NSString*) OC_MAKE_UNIQUE_NAME( Catch_Description_test_, uniqueSuffix ) \ { \ return @ desc; \ } \ --(void) INTERNAL_CATCH_UNIQUE_NAME( Catch_TestCase_test ) - -#endif - -#ifdef CATCH_IMPL -// #included from: internal/catch_impl.hpp -#define TWOBLUECUBES_CATCH_IMPL_HPP_INCLUDED +-(void) OC_MAKE_UNIQUE_NAME( Catch_TestCase_test_, uniqueSuffix ) -// Collect all the implementation files together here -// These are the equivalent of what would usually be cpp files +#define OC_TEST_CASE( name, desc ) OC_TEST_CASE2( name, desc, __LINE__ ) -#ifdef __clang__ -#pragma clang diagnostic push -#pragma clang diagnostic ignored "-Wweak-vtables" +// end catch_objc.hpp #endif -// #included from: ../catch_session.hpp -#define TWOBLUECUBES_CATCH_RUNNER_HPP_INCLUDED +#ifdef CATCH_CONFIG_EXTERNAL_INTERFACES +// start catch_external_interfaces.h + +// start catch_reporter_bases.hpp -// #included from: internal/catch_commandline.hpp -#define TWOBLUECUBES_CATCH_COMMANDLINE_HPP_INCLUDED +// start catch_interfaces_reporter.h -// #included from: catch_config.hpp -#define TWOBLUECUBES_CATCH_CONFIG_HPP_INCLUDED +// start catch_config.hpp -// #included from: catch_test_spec_parser.hpp -#define TWOBLUECUBES_CATCH_TEST_SPEC_PARSER_HPP_INCLUDED +// start catch_test_spec_parser.h #ifdef __clang__ #pragma clang diagnostic push #pragma clang diagnostic ignored "-Wpadded" #endif -// #included from: catch_test_spec.hpp -#define TWOBLUECUBES_CATCH_TEST_SPEC_HPP_INCLUDED +// start catch_test_spec.h #ifdef __clang__ #pragma clang diagnostic push #pragma clang diagnostic ignored "-Wpadded" #endif -// #included from: catch_wildcard_pattern.hpp -#define TWOBLUECUBES_CATCH_WILDCARD_PATTERN_HPP_INCLUDED - -#include <stdexcept> +// start catch_wildcard_pattern.h namespace Catch { @@ -3395,119 +4847,68 @@ namespace Catch public: - WildcardPattern( std::string const& pattern, CaseSensitive::Choice caseSensitivity ) - : m_caseSensitivity( caseSensitivity ), - m_wildcard( NoWildcard ), - m_pattern( adjustCase( pattern ) ) - { - if( startsWith( m_pattern, '*' ) ) { - m_pattern = m_pattern.substr( 1 ); - m_wildcard = WildcardAtStart; - } - if( endsWith( m_pattern, '*' ) ) { - m_pattern = m_pattern.substr( 0, m_pattern.size()-1 ); - m_wildcard = static_cast<WildcardPosition>( m_wildcard | WildcardAtEnd ); - } - } - virtual ~WildcardPattern(); - virtual bool matches( std::string const& str ) const { - switch( m_wildcard ) { - case NoWildcard: - return m_pattern == adjustCase( str ); - case WildcardAtStart: - return endsWith( adjustCase( str ), m_pattern ); - case WildcardAtEnd: - return startsWith( adjustCase( str ), m_pattern ); - case WildcardAtBothEnds: - return contains( adjustCase( str ), m_pattern ); - } + WildcardPattern( std::string const& pattern, CaseSensitive::Choice caseSensitivity ); + virtual ~WildcardPattern() = default; + virtual bool matches( std::string const& str ) const; -#ifdef __clang__ -#pragma clang diagnostic push -#pragma clang diagnostic ignored "-Wunreachable-code" -#endif - throw std::logic_error( "Unknown enum" ); -#ifdef __clang__ -#pragma clang diagnostic pop -#endif - } private: - std::string adjustCase( std::string const& str ) const { - return m_caseSensitivity == CaseSensitive::No ? toLower( str ) : str; - } + std::string adjustCase( std::string const& str ) const; CaseSensitive::Choice m_caseSensitivity; - WildcardPosition m_wildcard; + WildcardPosition m_wildcard = NoWildcard; std::string m_pattern; }; } +// end catch_wildcard_pattern.h #include <string> #include <vector> +#include <memory> namespace Catch { class TestSpec { - struct Pattern : SharedImpl<> { + struct Pattern { virtual ~Pattern(); virtual bool matches( TestCaseInfo const& testCase ) const = 0; }; + using PatternPtr = std::shared_ptr<Pattern>; + class NamePattern : public Pattern { public: - NamePattern( std::string const& name ) - : m_wildcardPattern( toLower( name ), CaseSensitive::No ) - {} + NamePattern( std::string const& name ); virtual ~NamePattern(); - virtual bool matches( TestCaseInfo const& testCase ) const { - return m_wildcardPattern.matches( toLower( testCase.name ) ); - } + bool matches( TestCaseInfo const& testCase ) const override; private: WildcardPattern m_wildcardPattern; }; class TagPattern : public Pattern { public: - TagPattern( std::string const& tag ) : m_tag( toLower( tag ) ) {} + TagPattern( std::string const& tag ); virtual ~TagPattern(); - virtual bool matches( TestCaseInfo const& testCase ) const { - return testCase.lcaseTags.find( m_tag ) != testCase.lcaseTags.end(); - } + bool matches( TestCaseInfo const& testCase ) const override; private: std::string m_tag; }; class ExcludedPattern : public Pattern { public: - ExcludedPattern( Ptr<Pattern> const& underlyingPattern ) : m_underlyingPattern( underlyingPattern ) {} + ExcludedPattern( PatternPtr const& underlyingPattern ); virtual ~ExcludedPattern(); - virtual bool matches( TestCaseInfo const& testCase ) const { return !m_underlyingPattern->matches( testCase ); } + bool matches( TestCaseInfo const& testCase ) const override; private: - Ptr<Pattern> m_underlyingPattern; + PatternPtr m_underlyingPattern; }; struct Filter { - std::vector<Ptr<Pattern> > m_patterns; + std::vector<PatternPtr> m_patterns; - bool matches( TestCaseInfo const& testCase ) const { - // All patterns in a filter must match for the filter to be a match - for( std::vector<Ptr<Pattern> >::const_iterator it = m_patterns.begin(), itEnd = m_patterns.end(); it != itEnd; ++it ) { - if( !(*it)->matches( testCase ) ) - return false; - } - return true; - } + bool matches( TestCaseInfo const& testCase ) const; }; public: - bool hasFilters() const { - return !m_filters.empty(); - } - bool matches( TestCaseInfo const& testCase ) const { - // A TestSpec matches if any filter matches - for( std::vector<Filter>::const_iterator it = m_filters.begin(), itEnd = m_filters.end(); it != itEnd; ++it ) - if( it->matches( testCase ) ) - return true; - return false; - } + bool hasFilters() const; + bool matches( TestCaseInfo const& testCase ) const; private: std::vector<Filter> m_filters; @@ -3520,112 +4921,75 @@ namespace Catch { #pragma clang diagnostic pop #endif +// end catch_test_spec.h +// start catch_interfaces_tag_alias_registry.h + +#include <string> + +namespace Catch { + + struct TagAlias; + + struct ITagAliasRegistry { + virtual ~ITagAliasRegistry(); + // Nullptr if not present + virtual TagAlias const* find( std::string const& alias ) const = 0; + virtual std::string expandAliases( std::string const& unexpandedTestSpec ) const = 0; + + static ITagAliasRegistry const& get(); + }; + +} // end namespace Catch + +// end catch_interfaces_tag_alias_registry.h namespace Catch { class TestSpecParser { enum Mode{ None, Name, QuotedName, Tag, EscapedName }; - Mode m_mode; - bool m_exclusion; - std::size_t m_start, m_pos; + Mode m_mode = None; + bool m_exclusion = false; + std::size_t m_start = std::string::npos, m_pos = 0; std::string m_arg; std::vector<std::size_t> m_escapeChars; TestSpec::Filter m_currentFilter; TestSpec m_testSpec; - ITagAliasRegistry const* m_tagAliases; + ITagAliasRegistry const* m_tagAliases = nullptr; public: - TestSpecParser( ITagAliasRegistry const& tagAliases ) : m_tagAliases( &tagAliases ) {} + TestSpecParser( ITagAliasRegistry const& tagAliases ); + + TestSpecParser& parse( std::string const& arg ); + TestSpec testSpec(); - TestSpecParser& parse( std::string const& arg ) { - m_mode = None; - m_exclusion = false; - m_start = std::string::npos; - m_arg = m_tagAliases->expandAliases( arg ); - m_escapeChars.clear(); - for( m_pos = 0; m_pos < m_arg.size(); ++m_pos ) - visitChar( m_arg[m_pos] ); - if( m_mode == Name ) - addPattern<TestSpec::NamePattern>(); - return *this; - } - TestSpec testSpec() { - addFilter(); - return m_testSpec; - } private: - void visitChar( char c ) { - if( m_mode == None ) { - switch( c ) { - case ' ': return; - case '~': m_exclusion = true; return; - case '[': return startNewMode( Tag, ++m_pos ); - case '"': return startNewMode( QuotedName, ++m_pos ); - case '\\': return escape(); - default: startNewMode( Name, m_pos ); break; - } - } - if( m_mode == Name ) { - if( c == ',' ) { - addPattern<TestSpec::NamePattern>(); - addFilter(); - } - else if( c == '[' ) { - if( subString() == "exclude:" ) - m_exclusion = true; - else - addPattern<TestSpec::NamePattern>(); - startNewMode( Tag, ++m_pos ); - } - else if( c == '\\' ) - escape(); - } - else if( m_mode == EscapedName ) - m_mode = Name; - else if( m_mode == QuotedName && c == '"' ) - addPattern<TestSpec::NamePattern>(); - else if( m_mode == Tag && c == ']' ) - addPattern<TestSpec::TagPattern>(); - } - void startNewMode( Mode mode, std::size_t start ) { - m_mode = mode; - m_start = start; - } - void escape() { - if( m_mode == None ) - m_start = m_pos; - m_mode = EscapedName; - m_escapeChars.push_back( m_pos ); - } - std::string subString() const { return m_arg.substr( m_start, m_pos - m_start ); } + void visitChar( char c ); + void startNewMode( Mode mode, std::size_t start ); + void escape(); + std::string subString() const; + template<typename T> void addPattern() { std::string token = subString(); - for( size_t i = 0; i < m_escapeChars.size(); ++i ) - token = token.substr( 0, m_escapeChars[i]-i ) + token.substr( m_escapeChars[i]+1-i ); + for( std::size_t i = 0; i < m_escapeChars.size(); ++i ) + token = token.substr( 0, m_escapeChars[i]-m_start-i ) + token.substr( m_escapeChars[i]-m_start-i+1 ); m_escapeChars.clear(); if( startsWith( token, "exclude:" ) ) { m_exclusion = true; token = token.substr( 8 ); } if( !token.empty() ) { - Ptr<TestSpec::Pattern> pattern = new T( token ); + TestSpec::PatternPtr pattern = std::make_shared<T>( token ); if( m_exclusion ) - pattern = new TestSpec::ExcludedPattern( pattern ); + pattern = std::make_shared<TestSpec::ExcludedPattern>( pattern ); m_currentFilter.m_patterns.push_back( pattern ); } m_exclusion = false; m_mode = None; } - void addFilter() { - if( !m_currentFilter.m_patterns.empty() ) { - m_testSpec.m_filters.push_back( m_currentFilter ); - m_currentFilter = TestSpec::Filter(); - } - } + + void addFilter(); }; - inline TestSpec parseTestSpec( std::string const& arg ) { - return TestSpecParser( ITagAliasRegistry::get() ).parse( arg ).testSpec(); - } + TestSpec parseTestSpec( std::string const& arg ); } // namespace Catch @@ -3633,2167 +4997,2114 @@ namespace Catch { #pragma clang diagnostic pop #endif -// #included from: catch_interfaces_config.h -#define TWOBLUECUBES_CATCH_INTERFACES_CONFIG_H_INCLUDED +// end catch_test_spec_parser.h +// Libstdc++ doesn't like incomplete classes for unique_ptr -#include <iosfwd> -#include <string> +#include <memory> #include <vector> +#include <string> + +#ifndef CATCH_CONFIG_CONSOLE_WIDTH +#define CATCH_CONFIG_CONSOLE_WIDTH 80 +#endif namespace Catch { - struct Verbosity { enum Level { - NoOutput = 0, - Quiet, - Normal - }; }; + struct IStream; - struct WarnAbout { enum What { - Nothing = 0x00, - NoAssertions = 0x01 - }; }; + struct ConfigData { + bool listTests = false; + bool listTags = false; + bool listReporters = false; + bool listTestNamesOnly = false; + + bool showSuccessfulTests = false; + bool shouldDebugBreak = false; + bool noThrow = false; + bool showHelp = false; + bool showInvisibles = false; + bool filenamesAsTags = false; + bool libIdentify = false; + + int abortAfter = -1; + unsigned int rngSeed = 0; + + bool benchmarkNoAnalysis = false; + unsigned int benchmarkSamples = 100; + double benchmarkConfidenceInterval = 0.95; + unsigned int benchmarkResamples = 100000; + + Verbosity verbosity = Verbosity::Normal; + WarnAbout::What warnings = WarnAbout::Nothing; + ShowDurations::OrNot showDurations = ShowDurations::DefaultForReporter; + RunTests::InWhatOrder runOrder = RunTests::InDeclarationOrder; + UseColour::YesOrNo useColour = UseColour::Auto; + WaitForKeypress::When waitForKeypress = WaitForKeypress::Never; - struct ShowDurations { enum OrNot { - DefaultForReporter, - Always, - Never - }; }; - struct RunTests { enum InWhatOrder { - InDeclarationOrder, - InLexicographicalOrder, - InRandomOrder - }; }; - struct UseColour { enum YesOrNo { - Auto, - Yes, - No - }; }; + std::string outputFilename; + std::string name; + std::string processName; +#ifndef CATCH_CONFIG_DEFAULT_REPORTER +#define CATCH_CONFIG_DEFAULT_REPORTER "console" +#endif + std::string reporterName = CATCH_CONFIG_DEFAULT_REPORTER; +#undef CATCH_CONFIG_DEFAULT_REPORTER - class TestSpec; + std::vector<std::string> testsOrTags; + std::vector<std::string> sectionsToRun; + }; - struct IConfig : IShared { + class Config : public IConfig { + public: - virtual ~IConfig(); + Config() = default; + Config( ConfigData const& data ); + virtual ~Config() = default; - virtual bool allowThrows() const = 0; - virtual std::ostream& stream() const = 0; - virtual std::string name() const = 0; - virtual bool includeSuccessfulResults() const = 0; - virtual bool shouldDebugBreak() const = 0; - virtual bool warnAboutMissingAssertions() const = 0; - virtual int abortAfter() const = 0; - virtual bool showInvisibles() const = 0; - virtual ShowDurations::OrNot showDurations() const = 0; - virtual TestSpec const& testSpec() const = 0; - virtual RunTests::InWhatOrder runOrder() const = 0; - virtual unsigned int rngSeed() const = 0; - virtual UseColour::YesOrNo useColour() const = 0; - virtual std::vector<std::string> const& getSectionsToRun() const = 0; + std::string const& getFilename() const; + + bool listTests() const; + bool listTestNamesOnly() const; + bool listTags() const; + bool listReporters() const; + + std::string getProcessName() const; + std::string const& getReporterName() const; + + std::vector<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; + std::ostream& stream() const override; + std::string name() const override; + bool includeSuccessfulResults() const override; + bool warnAboutMissingAssertions() const override; + bool warnAboutNoTests() const override; + ShowDurations::OrNot showDurations() const override; + RunTests::InWhatOrder runOrder() const override; + unsigned int rngSeed() const override; + UseColour::YesOrNo useColour() const override; + bool shouldDebugBreak() const override; + int abortAfter() const override; + bool showInvisibles() const override; + Verbosity verbosity() const override; + bool benchmarkNoAnalysis() const override; + int benchmarkSamples() const override; + double benchmarkConfidenceInterval() const override; + unsigned int benchmarkResamples() const override; + + private: + + IStream const* openStream(); + ConfigData m_data; + + std::unique_ptr<IStream const> m_stream; + TestSpec m_testSpec; + bool m_hasTestFilters = false; }; -} -// #included from: catch_stream.h -#define TWOBLUECUBES_CATCH_STREAM_H_INCLUDED +} // end namespace Catch -// #included from: catch_streambuf.h -#define TWOBLUECUBES_CATCH_STREAMBUF_H_INCLUDED +// end catch_config.hpp +// start catch_assertionresult.h -#include <streambuf> +#include <string> namespace Catch { - class StreamBufBase : public std::streambuf { + 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: - virtual ~StreamBufBase() CATCH_NOEXCEPT; + AssertionResult() = delete; + AssertionResult( AssertionInfo const& info, AssertionResultData const& data ); + + bool isOk() const; + bool succeeded() const; + ResultWas::OfType getResultType() const; + bool hasExpression() const; + bool hasMessage() const; + std::string getExpression() const; + std::string getExpressionInMacro() const; + bool hasExpandedExpression() const; + std::string getExpandedExpression() const; + std::string getMessage() const; + SourceLineInfo getSourceInfo() const; + StringRef getTestMacroName() const; + + //protected: + AssertionInfo m_info; + AssertionResultData m_resultData; }; -} -#include <streambuf> -#include <ostream> -#include <fstream> +} // end namespace Catch + +// end catch_assertionresult.h +#if defined(CATCH_CONFIG_ENABLE_BENCHMARKING) +// start catch_estimate.hpp + + // Statistics estimates + + +namespace Catch { + namespace Benchmark { + template <typename Duration> + struct Estimate { + Duration point; + Duration lower_bound; + Duration upper_bound; + double confidence_interval; + + template <typename Duration2> + operator Estimate<Duration2>() const { + return { point, lower_bound, upper_bound, confidence_interval }; + } + }; + } // namespace Benchmark +} // namespace Catch + +// end catch_estimate.hpp +// start catch_outlier_classification.hpp + +// Outlier information + +namespace Catch { + namespace Benchmark { + struct OutlierClassification { + int samples_seen = 0; + int low_severe = 0; // more than 3 times IQR below Q1 + int low_mild = 0; // 1.5 to 3 times IQR below Q1 + int high_mild = 0; // 1.5 to 3 times IQR above Q3 + int high_severe = 0; // more than 3 times IQR above Q3 + + int total() const { + return low_severe + low_mild + high_mild + high_severe; + } + }; + } // namespace Benchmark +} // namespace Catch + +// end catch_outlier_classification.hpp +#endif // CATCH_CONFIG_ENABLE_BENCHMARKING + +#include <string> +#include <iosfwd> +#include <map> +#include <set> #include <memory> +#include <algorithm> namespace Catch { - std::ostream& cout(); - std::ostream& cerr(); + struct ReporterConfig { + explicit ReporterConfig( IConfigPtr const& _fullConfig ); - struct IStream { - virtual ~IStream() CATCH_NOEXCEPT; - virtual std::ostream& stream() const = 0; + ReporterConfig( IConfigPtr const& _fullConfig, std::ostream& _stream ); + + std::ostream& stream() const; + IConfigPtr fullConfig() const; + + private: + std::ostream* m_stream; + IConfigPtr m_fullConfig; }; - class FileStream : public IStream { - mutable std::ofstream m_ofs; - public: - FileStream( std::string const& filename ); - virtual ~FileStream() CATCH_NOEXCEPT; - public: // IStream - virtual std::ostream& stream() const CATCH_OVERRIDE; + struct ReporterPreferences { + bool shouldRedirectStdOut = false; + bool shouldReportAllAssertions = false; }; - class CoutStream : public IStream { - mutable std::ostream m_os; - public: - CoutStream(); - virtual ~CoutStream() CATCH_NOEXCEPT; + template<typename T> + struct LazyStat : Option<T> { + LazyStat& operator=( T const& _value ) { + Option<T>::operator=( _value ); + used = false; + return *this; + } + void reset() { + Option<T>::reset(); + used = false; + } + bool used = false; + }; - public: // IStream - virtual std::ostream& stream() const CATCH_OVERRIDE; + struct TestRunInfo { + TestRunInfo( std::string const& _name ); + std::string name; }; + struct GroupInfo { + GroupInfo( std::string const& _name, + std::size_t _groupIndex, + std::size_t _groupsCount ); - class DebugOutStream : public IStream { - CATCH_AUTO_PTR( StreamBufBase ) m_streamBuf; - mutable std::ostream m_os; - public: - DebugOutStream(); - virtual ~DebugOutStream() CATCH_NOEXCEPT; + std::string name; + std::size_t groupIndex; + std::size_t groupsCounts; + }; - public: // IStream - virtual std::ostream& stream() const CATCH_OVERRIDE; + 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; + virtual ~AssertionStats(); + + AssertionResult assertionResult; + std::vector<MessageInfo> infoMessages; + Totals totals; }; -} -#include <memory> -#include <vector> -#include <string> -#include <stdexcept> + struct SectionStats { + SectionStats( SectionInfo const& _sectionInfo, + Counts const& _assertions, + double _durationInSeconds, + bool _missingAssertions ); + SectionStats( SectionStats const& ) = default; + SectionStats( SectionStats && ) = default; + SectionStats& operator = ( SectionStats const& ) = default; + SectionStats& operator = ( SectionStats && ) = default; + virtual ~SectionStats(); -#ifndef CATCH_CONFIG_CONSOLE_WIDTH -#define CATCH_CONFIG_CONSOLE_WIDTH 80 -#endif + SectionInfo sectionInfo; + Counts assertions; + double durationInSeconds; + bool missingAssertions; + }; -namespace Catch { + struct TestCaseStats { + TestCaseStats( TestCaseInfo const& _testInfo, + Totals const& _totals, + std::string const& _stdOut, + std::string const& _stdErr, + bool _aborting ); - struct ConfigData { + TestCaseStats( TestCaseStats const& ) = default; + TestCaseStats( TestCaseStats && ) = default; + TestCaseStats& operator = ( TestCaseStats const& ) = default; + TestCaseStats& operator = ( TestCaseStats && ) = default; + virtual ~TestCaseStats(); - ConfigData() - : listTests( false ), - listTags( false ), - listReporters( false ), - listTestNamesOnly( false ), - showSuccessfulTests( false ), - shouldDebugBreak( false ), - noThrow( false ), - showHelp( false ), - showInvisibles( false ), - filenamesAsTags( false ), - abortAfter( -1 ), - rngSeed( 0 ), - verbosity( Verbosity::Normal ), - warnings( WarnAbout::Nothing ), - showDurations( ShowDurations::DefaultForReporter ), - runOrder( RunTests::InDeclarationOrder ), - useColour( UseColour::Auto ) - {} + TestCaseInfo testInfo; + Totals totals; + std::string stdOut; + std::string stdErr; + bool aborting; + }; + + struct TestGroupStats { + TestGroupStats( GroupInfo const& _groupInfo, + Totals const& _totals, + bool _aborting ); + TestGroupStats( GroupInfo const& _groupInfo ); + + TestGroupStats( TestGroupStats const& ) = default; + TestGroupStats( TestGroupStats && ) = default; + TestGroupStats& operator = ( TestGroupStats const& ) = default; + TestGroupStats& operator = ( TestGroupStats && ) = default; + virtual ~TestGroupStats(); - bool listTests; - bool listTags; - bool listReporters; - bool listTestNamesOnly; + GroupInfo groupInfo; + Totals totals; + bool aborting; + }; - bool showSuccessfulTests; - bool shouldDebugBreak; - bool noThrow; - bool showHelp; - bool showInvisibles; - bool filenamesAsTags; + struct TestRunStats { + TestRunStats( TestRunInfo const& _runInfo, + Totals const& _totals, + bool _aborting ); - int abortAfter; - unsigned int rngSeed; + TestRunStats( TestRunStats const& ) = default; + TestRunStats( TestRunStats && ) = default; + TestRunStats& operator = ( TestRunStats const& ) = default; + TestRunStats& operator = ( TestRunStats && ) = default; + virtual ~TestRunStats(); - Verbosity::Level verbosity; - WarnAbout::What warnings; - ShowDurations::OrNot showDurations; - RunTests::InWhatOrder runOrder; - UseColour::YesOrNo useColour; + TestRunInfo runInfo; + Totals totals; + bool aborting; + }; - std::string outputFilename; +#if defined(CATCH_CONFIG_ENABLE_BENCHMARKING) + struct BenchmarkInfo { std::string name; - std::string processName; + double estimatedDuration; + int iterations; + int samples; + unsigned int resamples; + double clockResolution; + double clockCost; + }; - std::vector<std::string> reporterNames; - std::vector<std::string> testsOrTags; - std::vector<std::string> sectionsToRun; + template <class Duration> + struct BenchmarkStats { + BenchmarkInfo info; + + std::vector<Duration> samples; + Benchmark::Estimate<Duration> mean; + Benchmark::Estimate<Duration> standardDeviation; + Benchmark::OutlierClassification outliers; + double outlierVariance; + + template <typename Duration2> + operator BenchmarkStats<Duration2>() const { + std::vector<Duration2> samples2; + samples2.reserve(samples.size()); + std::transform(samples.begin(), samples.end(), std::back_inserter(samples2), [](Duration d) { return Duration2(d); }); + return { + info, + std::move(samples2), + mean, + standardDeviation, + outliers, + outlierVariance, + }; + } }; +#endif // CATCH_CONFIG_ENABLE_BENCHMARKING - class Config : public SharedImpl<IConfig> { - private: - Config( Config const& other ); - Config& operator = ( Config const& other ); - virtual void dummy(); - public: + struct IStreamingReporter { + virtual ~IStreamingReporter() = default; - Config() - {} + // Implementing class must also provide the following static methods: + // static std::string getDescription(); + // static std::set<Verbosity> getSupportedVerbosities() - Config( ConfigData const& data ) - : m_data( data ), - m_stream( openStream() ) - { - if( !data.testsOrTags.empty() ) { - TestSpecParser parser( ITagAliasRegistry::get() ); - for( std::size_t i = 0; i < data.testsOrTags.size(); ++i ) - parser.parse( data.testsOrTags[i] ); - m_testSpec = parser.testSpec(); - } - } + virtual ReporterPreferences getPreferences() const = 0; - virtual ~Config() {} + virtual void noMatchingTestCases( std::string const& spec ) = 0; - std::string const& getFilename() const { - return m_data.outputFilename ; - } + virtual void testRunStarting( TestRunInfo const& testRunInfo ) = 0; + virtual void testGroupStarting( GroupInfo const& groupInfo ) = 0; - bool listTests() const { return m_data.listTests; } - bool listTestNamesOnly() const { return m_data.listTestNamesOnly; } - bool listTags() const { return m_data.listTags; } - bool listReporters() const { return m_data.listReporters; } + virtual void testCaseStarting( TestCaseInfo const& testInfo ) = 0; + virtual void sectionStarting( SectionInfo const& sectionInfo ) = 0; - std::string getProcessName() const { return m_data.processName; } +#if defined(CATCH_CONFIG_ENABLE_BENCHMARKING) + virtual void benchmarkPreparing( std::string const& ) {} + virtual void benchmarkStarting( BenchmarkInfo const& ) {} + virtual void benchmarkEnded( BenchmarkStats<> const& ) {} + virtual void benchmarkFailed( std::string const& ) {} +#endif // CATCH_CONFIG_ENABLE_BENCHMARKING - std::vector<std::string> const& getReporterNames() const { return m_data.reporterNames; } - std::vector<std::string> const& getSectionsToRun() const CATCH_OVERRIDE { return m_data.sectionsToRun; } + virtual void assertionStarting( AssertionInfo const& assertionInfo ) = 0; - virtual TestSpec const& testSpec() const CATCH_OVERRIDE { return m_testSpec; } + // The return value indicates if the messages buffer should be cleared: + virtual bool assertionEnded( AssertionStats const& assertionStats ) = 0; - bool showHelp() const { return m_data.showHelp; } + virtual void sectionEnded( SectionStats const& sectionStats ) = 0; + virtual void testCaseEnded( TestCaseStats const& testCaseStats ) = 0; + virtual void testGroupEnded( TestGroupStats const& testGroupStats ) = 0; + virtual void testRunEnded( TestRunStats const& testRunStats ) = 0; - // IConfig interface - virtual bool allowThrows() const CATCH_OVERRIDE { return !m_data.noThrow; } - virtual std::ostream& stream() const CATCH_OVERRIDE { return m_stream->stream(); } - virtual std::string name() const CATCH_OVERRIDE { return m_data.name.empty() ? m_data.processName : m_data.name; } - virtual bool includeSuccessfulResults() const CATCH_OVERRIDE { return m_data.showSuccessfulTests; } - virtual bool warnAboutMissingAssertions() const CATCH_OVERRIDE { return m_data.warnings & WarnAbout::NoAssertions; } - virtual ShowDurations::OrNot showDurations() const CATCH_OVERRIDE { return m_data.showDurations; } - virtual RunTests::InWhatOrder runOrder() const CATCH_OVERRIDE { return m_data.runOrder; } - virtual unsigned int rngSeed() const CATCH_OVERRIDE { return m_data.rngSeed; } - virtual UseColour::YesOrNo useColour() const CATCH_OVERRIDE { return m_data.useColour; } - virtual bool shouldDebugBreak() const CATCH_OVERRIDE { return m_data.shouldDebugBreak; } - virtual int abortAfter() const CATCH_OVERRIDE { return m_data.abortAfter; } - virtual bool showInvisibles() const CATCH_OVERRIDE { return m_data.showInvisibles; } + virtual void skipTest( TestCaseInfo const& testInfo ) = 0; - private: + // Default empty implementation provided + virtual void fatalErrorEncountered( StringRef name ); - IStream const* openStream() { - if( m_data.outputFilename.empty() ) - return new CoutStream(); - else if( m_data.outputFilename[0] == '%' ) { - if( m_data.outputFilename == "%debug" ) - return new DebugOutStream(); - else - throw std::domain_error( "Unrecognised stream: " + m_data.outputFilename ); - } - else - return new FileStream( m_data.outputFilename ); - } - ConfigData m_data; + virtual bool isMulti() const; + }; + using IStreamingReporterPtr = std::unique_ptr<IStreamingReporter>; - CATCH_AUTO_PTR( IStream const ) m_stream; - TestSpec m_testSpec; + struct IReporterFactory { + virtual ~IReporterFactory(); + virtual IStreamingReporterPtr create( ReporterConfig const& config ) const = 0; + virtual std::string getDescription() const = 0; + }; + using IReporterFactoryPtr = std::shared_ptr<IReporterFactory>; + + struct IReporterRegistry { + using FactoryMap = std::map<std::string, IReporterFactoryPtr>; + using Listeners = std::vector<IReporterFactoryPtr>; + + virtual ~IReporterRegistry(); + virtual IStreamingReporterPtr create( std::string const& name, IConfigPtr const& config ) const = 0; + virtual FactoryMap const& getFactories() const = 0; + virtual Listeners const& getListeners() const = 0; }; } // end namespace Catch -// #included from: catch_clara.h -#define TWOBLUECUBES_CATCH_CLARA_H_INCLUDED +// end catch_interfaces_reporter.h +#include <algorithm> +#include <cstring> +#include <cfloat> +#include <cstdio> +#include <cassert> +#include <memory> +#include <ostream> -// Use Catch's value for console width (store Clara's off to the side, if present) -#ifdef CLARA_CONFIG_CONSOLE_WIDTH -#define CATCH_TEMP_CLARA_CONFIG_CONSOLE_WIDTH CLARA_CONFIG_CONSOLE_WIDTH -#undef CLARA_CONFIG_CONSOLE_WIDTH -#endif -#define CLARA_CONFIG_CONSOLE_WIDTH CATCH_CONFIG_CONSOLE_WIDTH +namespace Catch { + void prepareExpandedExpression(AssertionResult& result); -// Declare Clara inside the Catch namespace -#define STITCH_CLARA_OPEN_NAMESPACE namespace Catch { -// #included from: ../external/clara.h + // Returns double formatted as %.3f (format expected on output) + std::string getFormattedDuration( double duration ); -// Version 0.0.2.4 + std::string serializeFilters( std::vector<std::string> const& container ); -// Only use header guard if we are not using an outer namespace -#if !defined(TWOBLUECUBES_CLARA_H_INCLUDED) || defined(STITCH_CLARA_OPEN_NAMESPACE) + template<typename DerivedT> + struct StreamingReporterBase : IStreamingReporter { -#ifndef STITCH_CLARA_OPEN_NAMESPACE -#define TWOBLUECUBES_CLARA_H_INCLUDED -#define STITCH_CLARA_OPEN_NAMESPACE -#define STITCH_CLARA_CLOSE_NAMESPACE -#else -#define STITCH_CLARA_CLOSE_NAMESPACE } -#endif + StreamingReporterBase( ReporterConfig const& _config ) + : m_config( _config.fullConfig() ), + stream( _config.stream() ) + { + m_reporterPrefs.shouldRedirectStdOut = false; + if( !DerivedT::getSupportedVerbosities().count( m_config->verbosity() ) ) + CATCH_ERROR( "Verbosity level not supported by this reporter" ); + } -#define STITCH_TBC_TEXT_FORMAT_OPEN_NAMESPACE STITCH_CLARA_OPEN_NAMESPACE + ReporterPreferences getPreferences() const override { + return m_reporterPrefs; + } -// ----------- #included from tbc_text_format.h ----------- + static std::set<Verbosity> getSupportedVerbosities() { + return { Verbosity::Normal }; + } -// Only use header guard if we are not using an outer namespace -#if !defined(TBC_TEXT_FORMAT_H_INCLUDED) || defined(STITCH_TBC_TEXT_FORMAT_OUTER_NAMESPACE) -#ifndef STITCH_TBC_TEXT_FORMAT_OUTER_NAMESPACE -#define TBC_TEXT_FORMAT_H_INCLUDED -#endif + ~StreamingReporterBase() override = default; -#include <string> -#include <vector> -#include <sstream> -#include <algorithm> + void noMatchingTestCases(std::string const&) override {} -// Use optional outer namespace -#ifdef STITCH_TBC_TEXT_FORMAT_OUTER_NAMESPACE -namespace STITCH_TBC_TEXT_FORMAT_OUTER_NAMESPACE { -#endif + void testRunStarting(TestRunInfo const& _testRunInfo) override { + currentTestRunInfo = _testRunInfo; + } -namespace Tbc { + void testGroupStarting(GroupInfo const& _groupInfo) override { + currentGroupInfo = _groupInfo; + } -#ifdef TBC_TEXT_FORMAT_CONSOLE_WIDTH - const unsigned int consoleWidth = TBC_TEXT_FORMAT_CONSOLE_WIDTH; -#else - const unsigned int consoleWidth = 80; -#endif + void testCaseStarting(TestCaseInfo const& _testInfo) override { + currentTestCaseInfo = _testInfo; + } + void sectionStarting(SectionInfo const& _sectionInfo) override { + m_sectionStack.push_back(_sectionInfo); + } - struct TextAttributes { - TextAttributes() - : initialIndent( std::string::npos ), - indent( 0 ), - width( consoleWidth-1 ), - tabChar( '\t' ) - {} + void sectionEnded(SectionStats const& /* _sectionStats */) override { + m_sectionStack.pop_back(); + } + void testCaseEnded(TestCaseStats const& /* _testCaseStats */) override { + currentTestCaseInfo.reset(); + } + void testGroupEnded(TestGroupStats const& /* _testGroupStats */) override { + currentGroupInfo.reset(); + } + void testRunEnded(TestRunStats const& /* _testRunStats */) override { + currentTestCaseInfo.reset(); + currentGroupInfo.reset(); + currentTestRunInfo.reset(); + } + + void skipTest(TestCaseInfo const&) override { + // Don't do anything with this by default. + // It can optionally be overridden in the derived class. + } - TextAttributes& setInitialIndent( std::size_t _value ) { initialIndent = _value; return *this; } - TextAttributes& setIndent( std::size_t _value ) { indent = _value; return *this; } - TextAttributes& setWidth( std::size_t _value ) { width = _value; return *this; } - TextAttributes& setTabChar( char _value ) { tabChar = _value; return *this; } + IConfigPtr m_config; + std::ostream& stream; - std::size_t initialIndent; // indent of first line, or npos - std::size_t indent; // indent of subsequent lines, or all if initialIndent is npos - std::size_t width; // maximum width of text, including indent. Longer text will wrap - char tabChar; // If this char is seen the indent is changed to current pos + LazyStat<TestRunInfo> currentTestRunInfo; + LazyStat<GroupInfo> currentGroupInfo; + LazyStat<TestCaseInfo> currentTestCaseInfo; + + std::vector<SectionInfo> m_sectionStack; + ReporterPreferences m_reporterPrefs; }; - class Text { - public: - Text( std::string const& _str, TextAttributes const& _attr = TextAttributes() ) - : attr( _attr ) - { - std::string wrappableChars = " [({.,/|\\-"; - std::size_t indent = _attr.initialIndent != std::string::npos - ? _attr.initialIndent - : _attr.indent; - std::string remainder = _str; - - while( !remainder.empty() ) { - if( lines.size() >= 1000 ) { - lines.push_back( "... message truncated due to excessive size" ); - return; - } - std::size_t tabPos = std::string::npos; - std::size_t width = (std::min)( remainder.size(), _attr.width - indent ); - std::size_t pos = remainder.find_first_of( '\n' ); - if( pos <= width ) { - width = pos; - } - pos = remainder.find_last_of( _attr.tabChar, width ); - if( pos != std::string::npos ) { - tabPos = pos; - if( remainder[width] == '\n' ) - width--; - remainder = remainder.substr( 0, tabPos ) + remainder.substr( tabPos+1 ); - } + template<typename DerivedT> + struct CumulativeReporterBase : IStreamingReporter { + template<typename T, typename ChildNodeT> + struct Node { + explicit Node( T const& _value ) : value( _value ) {} + virtual ~Node() {} - if( width == remainder.size() ) { - spliceLine( indent, remainder, width ); - } - else if( remainder[width] == '\n' ) { - spliceLine( indent, remainder, width ); - if( width <= 1 || remainder.size() != 1 ) - remainder = remainder.substr( 1 ); - indent = _attr.indent; - } - else { - pos = remainder.find_last_of( wrappableChars, width ); - if( pos != std::string::npos && pos > 0 ) { - spliceLine( indent, remainder, pos ); - if( remainder[0] == ' ' ) - remainder = remainder.substr( 1 ); - } - else { - spliceLine( indent, remainder, width-1 ); - lines.back() += "-"; - } - if( lines.size() == 1 ) - indent = _attr.indent; - if( tabPos != std::string::npos ) - indent += tabPos; - } + using ChildNodes = std::vector<std::shared_ptr<ChildNodeT>>; + T value; + ChildNodes children; + }; + struct SectionNode { + explicit SectionNode(SectionStats const& _stats) : stats(_stats) {} + virtual ~SectionNode() = default; + + bool operator == (SectionNode const& other) const { + return stats.sectionInfo.lineInfo == other.stats.sectionInfo.lineInfo; } - } + bool operator == (std::shared_ptr<SectionNode> const& other) const { + return operator==(*other); + } + + SectionStats stats; + using ChildSections = std::vector<std::shared_ptr<SectionNode>>; + using Assertions = std::vector<AssertionStats>; + ChildSections childSections; + Assertions assertions; + std::string stdOut; + std::string stdErr; + }; + + struct BySectionInfo { + BySectionInfo( SectionInfo const& other ) : m_other( other ) {} + BySectionInfo( BySectionInfo const& other ) : m_other( other.m_other ) {} + bool operator() (std::shared_ptr<SectionNode> const& node) const { + return ((node->stats.sectionInfo.name == m_other.name) && + (node->stats.sectionInfo.lineInfo == m_other.lineInfo)); + } + void operator=(BySectionInfo const&) = delete; + + private: + SectionInfo const& m_other; + }; + + using TestCaseNode = Node<TestCaseStats, SectionNode>; + using TestGroupNode = Node<TestGroupStats, TestCaseNode>; + using TestRunNode = Node<TestRunStats, TestGroupNode>; - void spliceLine( std::size_t _indent, std::string& _remainder, std::size_t _pos ) { - lines.push_back( std::string( _indent, ' ' ) + _remainder.substr( 0, _pos ) ); - _remainder = _remainder.substr( _pos ); + CumulativeReporterBase( ReporterConfig const& _config ) + : m_config( _config.fullConfig() ), + stream( _config.stream() ) + { + m_reporterPrefs.shouldRedirectStdOut = false; + if( !DerivedT::getSupportedVerbosities().count( m_config->verbosity() ) ) + CATCH_ERROR( "Verbosity level not supported by this reporter" ); } + ~CumulativeReporterBase() override = default; - typedef std::vector<std::string>::const_iterator const_iterator; + ReporterPreferences getPreferences() const override { + return m_reporterPrefs; + } - const_iterator begin() const { return lines.begin(); } - const_iterator end() const { return lines.end(); } - std::string const& last() const { return lines.back(); } - std::size_t size() const { return lines.size(); } - std::string const& operator[]( std::size_t _index ) const { return lines[_index]; } - std::string toString() const { - std::ostringstream oss; - oss << *this; - return oss.str(); + static std::set<Verbosity> getSupportedVerbosities() { + return { Verbosity::Normal }; } - inline friend std::ostream& operator << ( std::ostream& _stream, Text const& _text ) { - for( Text::const_iterator it = _text.begin(), itEnd = _text.end(); - it != itEnd; ++it ) { - if( it != _text.begin() ) - _stream << "\n"; - _stream << *it; + void testRunStarting( TestRunInfo const& ) override {} + void testGroupStarting( GroupInfo const& ) override {} + + void testCaseStarting( TestCaseInfo const& ) override {} + + void sectionStarting( SectionInfo const& sectionInfo ) override { + SectionStats incompleteStats( sectionInfo, Counts(), 0, false ); + std::shared_ptr<SectionNode> node; + if( m_sectionStack.empty() ) { + if( !m_rootSection ) + m_rootSection = std::make_shared<SectionNode>( incompleteStats ); + node = m_rootSection; + } + else { + SectionNode& parentNode = *m_sectionStack.back(); + auto it = + std::find_if( parentNode.childSections.begin(), + parentNode.childSections.end(), + BySectionInfo( sectionInfo ) ); + if( it == parentNode.childSections.end() ) { + node = std::make_shared<SectionNode>( incompleteStats ); + parentNode.childSections.push_back( node ); + } + else + node = *it; } - return _stream; + m_sectionStack.push_back( node ); + m_deepestSection = std::move(node); } - private: - std::string str; - TextAttributes attr; - std::vector<std::string> lines; + void assertionStarting(AssertionInfo const&) override {} + + bool assertionEnded(AssertionStats const& assertionStats) override { + assert(!m_sectionStack.empty()); + // AssertionResult holds a pointer to a temporary DecomposedExpression, + // which getExpandedExpression() calls to build the expression string. + // Our section stack copy of the assertionResult will likely outlive the + // temporary, so it must be expanded or discarded now to avoid calling + // a destroyed object later. + prepareExpandedExpression(const_cast<AssertionResult&>( assertionStats.assertionResult ) ); + SectionNode& sectionNode = *m_sectionStack.back(); + sectionNode.assertions.push_back(assertionStats); + return true; + } + void sectionEnded(SectionStats const& sectionStats) override { + assert(!m_sectionStack.empty()); + SectionNode& node = *m_sectionStack.back(); + node.stats = sectionStats; + m_sectionStack.pop_back(); + } + void testCaseEnded(TestCaseStats const& testCaseStats) override { + auto node = std::make_shared<TestCaseNode>(testCaseStats); + assert(m_sectionStack.size() == 0); + node->children.push_back(m_rootSection); + m_testCases.push_back(node); + m_rootSection.reset(); + + assert(m_deepestSection); + m_deepestSection->stdOut = testCaseStats.stdOut; + m_deepestSection->stdErr = testCaseStats.stdErr; + } + void testGroupEnded(TestGroupStats const& testGroupStats) override { + auto node = std::make_shared<TestGroupNode>(testGroupStats); + node->children.swap(m_testCases); + m_testGroups.push_back(node); + } + void testRunEnded(TestRunStats const& testRunStats) override { + auto node = std::make_shared<TestRunNode>(testRunStats); + node->children.swap(m_testGroups); + m_testRuns.push_back(node); + testRunEndedCumulative(); + } + virtual void testRunEndedCumulative() = 0; + + void skipTest(TestCaseInfo const&) override {} + + IConfigPtr m_config; + std::ostream& stream; + std::vector<AssertionStats> m_assertions; + std::vector<std::vector<std::shared_ptr<SectionNode>>> m_sections; + std::vector<std::shared_ptr<TestCaseNode>> m_testCases; + std::vector<std::shared_ptr<TestGroupNode>> m_testGroups; + + std::vector<std::shared_ptr<TestRunNode>> m_testRuns; + + std::shared_ptr<SectionNode> m_rootSection; + std::shared_ptr<SectionNode> m_deepestSection; + std::vector<std::shared_ptr<SectionNode>> m_sectionStack; + ReporterPreferences m_reporterPrefs; }; -} // end namespace Tbc + template<char C> + char const* getLineOfChars() { + static char line[CATCH_CONFIG_CONSOLE_WIDTH] = {0}; + if( !*line ) { + std::memset( line, C, CATCH_CONFIG_CONSOLE_WIDTH-1 ); + line[CATCH_CONFIG_CONSOLE_WIDTH-1] = 0; + } + return line; + } -#ifdef STITCH_TBC_TEXT_FORMAT_OUTER_NAMESPACE -} // end outer namespace -#endif + struct TestEventListenerBase : StreamingReporterBase<TestEventListenerBase> { + TestEventListenerBase( ReporterConfig const& _config ); -#endif // TBC_TEXT_FORMAT_H_INCLUDED + static std::set<Verbosity> getSupportedVerbosities(); -// ----------- end of #include from tbc_text_format.h ----------- -// ........... back in clara.h + void assertionStarting(AssertionInfo const&) override; + bool assertionEnded(AssertionStats const&) override; + }; -#undef STITCH_TBC_TEXT_FORMAT_OPEN_NAMESPACE +} // end namespace Catch -// ----------- #included from clara_compilers.h ----------- +// end catch_reporter_bases.hpp +// start catch_console_colour.h -#ifndef TWOBLUECUBES_CLARA_COMPILERS_H_INCLUDED -#define TWOBLUECUBES_CLARA_COMPILERS_H_INCLUDED +namespace Catch { -// Detect a number of compiler features - mostly C++11/14 conformance - by compiler -// The following features are defined: -// -// CLARA_CONFIG_CPP11_NULLPTR : is nullptr supported? -// CLARA_CONFIG_CPP11_NOEXCEPT : is noexcept supported? -// CLARA_CONFIG_CPP11_GENERATED_METHODS : The delete and default keywords for compiler generated methods -// CLARA_CONFIG_CPP11_OVERRIDE : is override supported? -// CLARA_CONFIG_CPP11_UNIQUE_PTR : is unique_ptr supported (otherwise use auto_ptr) + struct Colour { + enum Code { + None = 0, -// CLARA_CONFIG_CPP11_OR_GREATER : Is C++11 supported? + White, + Red, + Green, + Blue, + Cyan, + Yellow, + Grey, -// CLARA_CONFIG_VARIADIC_MACROS : are variadic macros supported? + Bright = 0x10, -// In general each macro has a _NO_<feature name> form -// (e.g. CLARA_CONFIG_CPP11_NO_NULLPTR) 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. + BrightRed = Bright | Red, + BrightGreen = Bright | Green, + LightGrey = Bright | Grey, + BrightWhite = Bright | White, + BrightYellow = Bright | Yellow, -// All the C++11 features can be disabled with CLARA_CONFIG_NO_CPP11 + // By intention + FileName = LightGrey, + Warning = BrightYellow, + ResultError = BrightRed, + ResultSuccess = BrightGreen, + ResultExpectedFailure = Warning, -#ifdef __clang__ + Error = BrightRed, + Success = Green, -#if __has_feature(cxx_nullptr) -#define CLARA_INTERNAL_CONFIG_CPP11_NULLPTR -#endif + OriginalExpression = Cyan, + ReconstructedExpression = BrightYellow, -#if __has_feature(cxx_noexcept) -#define CLARA_INTERNAL_CONFIG_CPP11_NOEXCEPT -#endif + SecondaryText = LightGrey, + Headers = White + }; -#endif // __clang__ + // Use constructed object for RAII guard + Colour( Code _colourCode ); + Colour( Colour&& other ) noexcept; + Colour& operator=( Colour&& other ) noexcept; + ~Colour(); -//////////////////////////////////////////////////////////////////////////////// -// GCC -#ifdef __GNUC__ + // Use static method for one-shot changes + static void use( Code _colourCode ); -#if __GNUC__ == 4 && __GNUC_MINOR__ >= 6 && defined(__GXX_EXPERIMENTAL_CXX0X__) -#define CLARA_INTERNAL_CONFIG_CPP11_NULLPTR -#endif + private: + bool m_moved = false; + }; -// - otherwise more recent versions define __cplusplus >= 201103L -// and will get picked up below + std::ostream& operator << ( std::ostream& os, Colour const& ); -#endif // __GNUC__ +} // end namespace Catch -//////////////////////////////////////////////////////////////////////////////// -// Visual C++ -#ifdef _MSC_VER +// end catch_console_colour.h +// start catch_reporter_registrars.hpp -#if (_MSC_VER >= 1600) -#define CLARA_INTERNAL_CONFIG_CPP11_NULLPTR -#define CLARA_INTERNAL_CONFIG_CPP11_UNIQUE_PTR -#endif -#if (_MSC_VER >= 1900 ) // (VC++ 13 (VS2015)) -#define CLARA_INTERNAL_CONFIG_CPP11_NOEXCEPT -#define CLARA_INTERNAL_CONFIG_CPP11_GENERATED_METHODS -#endif +namespace Catch { -#endif // _MSC_VER + template<typename T> + class ReporterRegistrar { -//////////////////////////////////////////////////////////////////////////////// -// C++ language feature support + class ReporterFactory : public IReporterFactory { -// catch all support for C++11 -#if defined(__cplusplus) && __cplusplus >= 201103L + IStreamingReporterPtr create( ReporterConfig const& config ) const override { + return std::unique_ptr<T>( new T( config ) ); + } -#define CLARA_CPP11_OR_GREATER + std::string getDescription() const override { + return T::getDescription(); + } + }; -#if !defined(CLARA_INTERNAL_CONFIG_CPP11_NULLPTR) -#define CLARA_INTERNAL_CONFIG_CPP11_NULLPTR -#endif + public: -#ifndef CLARA_INTERNAL_CONFIG_CPP11_NOEXCEPT -#define CLARA_INTERNAL_CONFIG_CPP11_NOEXCEPT -#endif + explicit ReporterRegistrar( std::string const& name ) { + getMutableRegistryHub().registerReporter( name, std::make_shared<ReporterFactory>() ); + } + }; -#ifndef CLARA_INTERNAL_CONFIG_CPP11_GENERATED_METHODS -#define CLARA_INTERNAL_CONFIG_CPP11_GENERATED_METHODS -#endif + template<typename T> + class ListenerRegistrar { -#if !defined(CLARA_INTERNAL_CONFIG_CPP11_OVERRIDE) -#define CLARA_INTERNAL_CONFIG_CPP11_OVERRIDE -#endif -#if !defined(CLARA_INTERNAL_CONFIG_CPP11_UNIQUE_PTR) -#define CLARA_INTERNAL_CONFIG_CPP11_UNIQUE_PTR -#endif + class ListenerFactory : public IReporterFactory { -#endif // __cplusplus >= 201103L + IStreamingReporterPtr create( ReporterConfig const& config ) const override { + return std::unique_ptr<T>( new T( config ) ); + } + std::string getDescription() const override { + return std::string(); + } + }; -// Now set the actual defines based on the above + anything the user has configured -#if defined(CLARA_INTERNAL_CONFIG_CPP11_NULLPTR) && !defined(CLARA_CONFIG_CPP11_NO_NULLPTR) && !defined(CLARA_CONFIG_CPP11_NULLPTR) && !defined(CLARA_CONFIG_NO_CPP11) -#define CLARA_CONFIG_CPP11_NULLPTR -#endif -#if defined(CLARA_INTERNAL_CONFIG_CPP11_NOEXCEPT) && !defined(CLARA_CONFIG_CPP11_NO_NOEXCEPT) && !defined(CLARA_CONFIG_CPP11_NOEXCEPT) && !defined(CLARA_CONFIG_NO_CPP11) -#define CLARA_CONFIG_CPP11_NOEXCEPT -#endif -#if defined(CLARA_INTERNAL_CONFIG_CPP11_GENERATED_METHODS) && !defined(CLARA_CONFIG_CPP11_NO_GENERATED_METHODS) && !defined(CLARA_CONFIG_CPP11_GENERATED_METHODS) && !defined(CLARA_CONFIG_NO_CPP11) -#define CLARA_CONFIG_CPP11_GENERATED_METHODS -#endif -#if defined(CLARA_INTERNAL_CONFIG_CPP11_OVERRIDE) && !defined(CLARA_CONFIG_NO_OVERRIDE) && !defined(CLARA_CONFIG_CPP11_OVERRIDE) && !defined(CLARA_CONFIG_NO_CPP11) -#define CLARA_CONFIG_CPP11_OVERRIDE -#endif -#if defined(CLARA_INTERNAL_CONFIG_CPP11_UNIQUE_PTR) && !defined(CLARA_CONFIG_NO_UNIQUE_PTR) && !defined(CLARA_CONFIG_CPP11_UNIQUE_PTR) && !defined(CLARA_CONFIG_NO_CPP11) -#define CLARA_CONFIG_CPP11_UNIQUE_PTR -#endif + public: -// noexcept support: -#if defined(CLARA_CONFIG_CPP11_NOEXCEPT) && !defined(CLARA_NOEXCEPT) -#define CLARA_NOEXCEPT noexcept -# define CLARA_NOEXCEPT_IS(x) noexcept(x) -#else -#define CLARA_NOEXCEPT throw() -# define CLARA_NOEXCEPT_IS(x) -#endif + ListenerRegistrar() { + getMutableRegistryHub().registerListener( std::make_shared<ListenerFactory>() ); + } + }; +} -// nullptr support -#ifdef CLARA_CONFIG_CPP11_NULLPTR -#define CLARA_NULL nullptr -#else -#define CLARA_NULL NULL -#endif +#if !defined(CATCH_CONFIG_DISABLE) -// override support -#ifdef CLARA_CONFIG_CPP11_OVERRIDE -#define CLARA_OVERRIDE override -#else -#define CLARA_OVERRIDE -#endif +#define CATCH_REGISTER_REPORTER( name, reporterType ) \ + CATCH_INTERNAL_SUPPRESS_GLOBALS_WARNINGS \ + namespace{ Catch::ReporterRegistrar<reporterType> catch_internal_RegistrarFor##reporterType( name ); } \ + CATCH_INTERNAL_UNSUPPRESS_GLOBALS_WARNINGS -// unique_ptr support -#ifdef CLARA_CONFIG_CPP11_UNIQUE_PTR -# define CLARA_AUTO_PTR( T ) std::unique_ptr<T> -#else -# define CLARA_AUTO_PTR( T ) std::auto_ptr<T> -#endif +#define CATCH_REGISTER_LISTENER( listenerType ) \ + CATCH_INTERNAL_SUPPRESS_GLOBALS_WARNINGS \ + namespace{ Catch::ListenerRegistrar<listenerType> catch_internal_RegistrarFor##listenerType; } \ + CATCH_INTERNAL_SUPPRESS_GLOBALS_WARNINGS +#else // CATCH_CONFIG_DISABLE -#endif // TWOBLUECUBES_CLARA_COMPILERS_H_INCLUDED +#define CATCH_REGISTER_REPORTER(name, reporterType) +#define CATCH_REGISTER_LISTENER(listenerType) -// ----------- end of #include from clara_compilers.h ----------- -// ........... back in clara.h +#endif // CATCH_CONFIG_DISABLE -#include <map> -#include <stdexcept> -#include <memory> +// end catch_reporter_registrars.hpp +// Allow users to base their work off existing reporters +// start catch_reporter_compact.h -#if defined(WIN32) || defined(__WIN32__) || defined(_WIN32) || defined(_MSC_VER) -#define CLARA_PLATFORM_WINDOWS -#endif +namespace Catch { -// Use optional outer namespace -#ifdef STITCH_CLARA_OPEN_NAMESPACE -STITCH_CLARA_OPEN_NAMESPACE -#endif + struct CompactReporter : StreamingReporterBase<CompactReporter> { -namespace Clara { + using StreamingReporterBase::StreamingReporterBase; - struct UnpositionalTag {}; + ~CompactReporter() override; - extern UnpositionalTag _; + static std::string getDescription(); -#ifdef CLARA_CONFIG_MAIN - UnpositionalTag _; -#endif + ReporterPreferences getPreferences() const override; - namespace Detail { + void noMatchingTestCases(std::string const& spec) override; -#ifdef CLARA_CONSOLE_WIDTH - const unsigned int consoleWidth = CLARA_CONFIG_CONSOLE_WIDTH; -#else - const unsigned int consoleWidth = 80; -#endif + void assertionStarting(AssertionInfo const&) override; - using namespace Tbc; + bool assertionEnded(AssertionStats const& _assertionStats) override; - inline bool startsWith( std::string const& str, std::string const& prefix ) { - return str.size() >= prefix.size() && str.substr( 0, prefix.size() ) == prefix; - } + void sectionEnded(SectionStats const& _sectionStats) override; - template<typename T> struct RemoveConstRef{ typedef T type; }; - template<typename T> struct RemoveConstRef<T&>{ typedef T type; }; - template<typename T> struct RemoveConstRef<T const&>{ typedef T type; }; - template<typename T> struct RemoveConstRef<T const>{ typedef T type; }; + void testRunEnded(TestRunStats const& _testRunStats) override; - template<typename T> struct IsBool { static const bool value = false; }; - template<> struct IsBool<bool> { static const bool value = true; }; + }; - template<typename T> - void convertInto( std::string const& _source, T& _dest ) { - std::stringstream ss; - ss << _source; - ss >> _dest; - if( ss.fail() ) - throw std::runtime_error( "Unable to convert " + _source + " to destination type" ); - } - inline void convertInto( std::string const& _source, std::string& _dest ) { - _dest = _source; - } - char toLowerCh(char c) { - return static_cast<char>( ::tolower( c ) ); - } - inline void convertInto( std::string const& _source, bool& _dest ) { - std::string sourceLC = _source; - std::transform( sourceLC.begin(), sourceLC.end(), sourceLC.begin(), toLowerCh ); - if( sourceLC == "y" || sourceLC == "1" || sourceLC == "true" || sourceLC == "yes" || sourceLC == "on" ) - _dest = true; - else if( sourceLC == "n" || sourceLC == "0" || sourceLC == "false" || sourceLC == "no" || sourceLC == "off" ) - _dest = false; - else - throw std::runtime_error( "Expected a boolean value but did not recognise:\n '" + _source + "'" ); - } +} // end namespace Catch + +// end catch_reporter_compact.h +// start catch_reporter_console.h - template<typename ConfigT> - struct IArgFunction { - virtual ~IArgFunction() {} -#ifdef CLARA_CONFIG_CPP11_GENERATED_METHODS - IArgFunction() = default; - IArgFunction( IArgFunction const& ) = default; +#if defined(_MSC_VER) +#pragma warning(push) +#pragma warning(disable:4061) // Not all labels are EXPLICITLY handled in switch + // Note that 4062 (not all labels are handled + // and default is missing) is enabled #endif - virtual void set( ConfigT& config, std::string const& value ) const = 0; - virtual bool takesArg() const = 0; - virtual IArgFunction* clone() const = 0; - }; - template<typename ConfigT> - class BoundArgFunction { - public: - BoundArgFunction() : functionObj( CLARA_NULL ) {} - BoundArgFunction( IArgFunction<ConfigT>* _functionObj ) : functionObj( _functionObj ) {} - BoundArgFunction( BoundArgFunction const& other ) : functionObj( other.functionObj ? other.functionObj->clone() : CLARA_NULL ) {} - BoundArgFunction& operator = ( BoundArgFunction const& other ) { - IArgFunction<ConfigT>* newFunctionObj = other.functionObj ? other.functionObj->clone() : CLARA_NULL; - delete functionObj; - functionObj = newFunctionObj; - return *this; - } - ~BoundArgFunction() { delete functionObj; } +namespace Catch { + // Fwd decls + struct SummaryColumn; + class TablePrinter; - void set( ConfigT& config, std::string const& value ) const { - functionObj->set( config, value ); - } - bool takesArg() const { return functionObj->takesArg(); } + struct ConsoleReporter : StreamingReporterBase<ConsoleReporter> { + std::unique_ptr<TablePrinter> m_tablePrinter; - bool isSet() const { - return functionObj != CLARA_NULL; - } - private: - IArgFunction<ConfigT>* functionObj; - }; + ConsoleReporter(ReporterConfig const& config); + ~ConsoleReporter() override; + static std::string getDescription(); - template<typename C> - struct NullBinder : IArgFunction<C>{ - virtual void set( C&, std::string const& ) const {} - virtual bool takesArg() const { return true; } - virtual IArgFunction<C>* clone() const { return new NullBinder( *this ); } - }; + void noMatchingTestCases(std::string const& spec) override; - template<typename C, typename M> - struct BoundDataMember : IArgFunction<C>{ - BoundDataMember( M C::* _member ) : member( _member ) {} - virtual void set( C& p, std::string const& stringValue ) const { - convertInto( stringValue, p.*member ); - } - virtual bool takesArg() const { return !IsBool<M>::value; } - virtual IArgFunction<C>* clone() const { return new BoundDataMember( *this ); } - M C::* member; - }; - template<typename C, typename M> - struct BoundUnaryMethod : IArgFunction<C>{ - BoundUnaryMethod( void (C::*_member)( M ) ) : member( _member ) {} - virtual void set( C& p, std::string const& stringValue ) const { - typename RemoveConstRef<M>::type value; - convertInto( stringValue, value ); - (p.*member)( value ); - } - virtual bool takesArg() const { return !IsBool<M>::value; } - virtual IArgFunction<C>* clone() const { return new BoundUnaryMethod( *this ); } - void (C::*member)( M ); - }; - template<typename C> - struct BoundNullaryMethod : IArgFunction<C>{ - BoundNullaryMethod( void (C::*_member)() ) : member( _member ) {} - virtual void set( C& p, std::string const& stringValue ) const { - bool value; - convertInto( stringValue, value ); - if( value ) - (p.*member)(); - } - virtual bool takesArg() const { return false; } - virtual IArgFunction<C>* clone() const { return new BoundNullaryMethod( *this ); } - void (C::*member)(); - }; + void assertionStarting(AssertionInfo const&) override; - template<typename C> - struct BoundUnaryFunction : IArgFunction<C>{ - BoundUnaryFunction( void (*_function)( C& ) ) : function( _function ) {} - virtual void set( C& obj, std::string const& stringValue ) const { - bool value; - convertInto( stringValue, value ); - if( value ) - function( obj ); - } - virtual bool takesArg() const { return false; } - virtual IArgFunction<C>* clone() const { return new BoundUnaryFunction( *this ); } - void (*function)( C& ); - }; + bool assertionEnded(AssertionStats const& _assertionStats) override; - template<typename C, typename T> - struct BoundBinaryFunction : IArgFunction<C>{ - BoundBinaryFunction( void (*_function)( C&, T ) ) : function( _function ) {} - virtual void set( C& obj, std::string const& stringValue ) const { - typename RemoveConstRef<T>::type value; - convertInto( stringValue, value ); - function( obj, value ); - } - virtual bool takesArg() const { return !IsBool<T>::value; } - virtual IArgFunction<C>* clone() const { return new BoundBinaryFunction( *this ); } - void (*function)( C&, T ); - }; + void sectionStarting(SectionInfo const& _sectionInfo) override; + void sectionEnded(SectionStats const& _sectionStats) override; - } // namespace Detail +#if defined(CATCH_CONFIG_ENABLE_BENCHMARKING) + void benchmarkPreparing(std::string const& name) override; + void benchmarkStarting(BenchmarkInfo const& info) override; + void benchmarkEnded(BenchmarkStats<> const& stats) override; + void benchmarkFailed(std::string const& error) override; +#endif // CATCH_CONFIG_ENABLE_BENCHMARKING - inline std::vector<std::string> argsToVector( int argc, char const* const* const argv ) { - std::vector<std::string> args( static_cast<std::size_t>( argc ) ); - for( std::size_t i = 0; i < static_cast<std::size_t>( argc ); ++i ) - args[i] = argv[i]; + void testCaseEnded(TestCaseStats const& _testCaseStats) override; + void testGroupEnded(TestGroupStats const& _testGroupStats) override; + void testRunEnded(TestRunStats const& _testRunStats) override; + void testRunStarting(TestRunInfo const& _testRunInfo) override; + private: - return args; - } + void lazyPrint(); - class Parser { - enum Mode { None, MaybeShortOpt, SlashOpt, ShortOpt, LongOpt, Positional }; - Mode mode; - std::size_t from; - bool inQuotes; - public: + void lazyPrintWithoutClosingBenchmarkTable(); + void lazyPrintRunInfo(); + void lazyPrintGroupInfo(); + void printTestCaseAndSectionHeader(); - struct Token { - enum Type { Positional, ShortOpt, LongOpt }; - Token( Type _type, std::string const& _data ) : type( _type ), data( _data ) {} - Type type; - std::string data; - }; + void printClosedHeader(std::string const& _name); + void printOpenHeader(std::string const& _name); - Parser() : mode( None ), from( 0 ), inQuotes( false ){} + // 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 parseIntoTokens( std::vector<std::string> const& args, std::vector<Token>& tokens ) { - const std::string doubleDash = "--"; - for( std::size_t i = 1; i < args.size() && args[i] != doubleDash; ++i ) - parseIntoTokens( args[i], tokens); - } + void printTotals(Totals const& totals); + void printSummaryRow(std::string const& label, std::vector<SummaryColumn> const& cols, std::size_t row); - void parseIntoTokens( std::string const& arg, std::vector<Token>& tokens ) { - for( std::size_t i = 0; i <= arg.size(); ++i ) { - char c = arg[i]; - if( c == '"' ) - inQuotes = !inQuotes; - mode = handleMode( i, c, arg, tokens ); - } - } - Mode handleMode( std::size_t i, char c, std::string const& arg, std::vector<Token>& tokens ) { - switch( mode ) { - case None: return handleNone( i, c ); - case MaybeShortOpt: return handleMaybeShortOpt( i, c ); - case ShortOpt: - case LongOpt: - case SlashOpt: return handleOpt( i, c, arg, tokens ); - case Positional: return handlePositional( i, c, arg, tokens ); - default: throw std::logic_error( "Unknown mode" ); - } - } + void printTotalsDivider(Totals const& totals); + void printSummaryDivider(); + void printTestFilters(); - Mode handleNone( std::size_t i, char c ) { - if( inQuotes ) { - from = i; - return Positional; - } - switch( c ) { - case '-': return MaybeShortOpt; -#ifdef CLARA_PLATFORM_WINDOWS - case '/': from = i+1; return SlashOpt; + private: + bool m_headerPrinted = false; + }; + +} // end namespace Catch + +#if defined(_MSC_VER) +#pragma warning(pop) #endif - default: from = i; return Positional; - } - } - Mode handleMaybeShortOpt( std::size_t i, char c ) { - switch( c ) { - case '-': from = i+1; return LongOpt; - default: from = i; return ShortOpt; - } - } - Mode handleOpt( std::size_t i, char c, std::string const& arg, std::vector<Token>& tokens ) { - if( std::string( ":=\0", 3 ).find( c ) == std::string::npos ) - return mode; - std::string optName = arg.substr( from, i-from ); - if( mode == ShortOpt ) - for( std::size_t j = 0; j < optName.size(); ++j ) - tokens.push_back( Token( Token::ShortOpt, optName.substr( j, 1 ) ) ); - else if( mode == SlashOpt && optName.size() == 1 ) - tokens.push_back( Token( Token::ShortOpt, optName ) ); - else - tokens.push_back( Token( Token::LongOpt, optName ) ); - return None; - } - Mode handlePositional( std::size_t i, char c, std::string const& arg, std::vector<Token>& tokens ) { - if( inQuotes || std::string( "\0", 1 ).find( c ) == std::string::npos ) - return mode; +// end catch_reporter_console.h +// start catch_reporter_junit.h - std::string data = arg.substr( from, i-from ); - tokens.push_back( Token( Token::Positional, data ) ); - return None; - } - }; +// start catch_xmlwriter.h - template<typename ConfigT> - struct CommonArgProperties { - CommonArgProperties() {} - CommonArgProperties( Detail::BoundArgFunction<ConfigT> const& _boundField ) : boundField( _boundField ) {} +#include <vector> - Detail::BoundArgFunction<ConfigT> boundField; - std::string description; - std::string detail; - std::string placeholder; // Only value if boundField takes an arg +namespace Catch { - bool takesArg() const { - return !placeholder.empty(); - } - void validate() const { - if( !boundField.isSet() ) - throw std::logic_error( "option not bound" ); - } - }; - struct OptionArgProperties { - std::vector<std::string> shortNames; - std::string longName; + class XmlEncode { + public: + enum ForWhat { ForTextNodes, ForAttributes }; - bool hasShortName( std::string const& shortName ) const { - return std::find( shortNames.begin(), shortNames.end(), shortName ) != shortNames.end(); - } - bool hasLongName( std::string const& _longName ) const { - return _longName == longName; - } - }; - struct PositionalArgProperties { - PositionalArgProperties() : position( -1 ) {} - int position; // -1 means non-positional (floating) + XmlEncode( std::string const& str, ForWhat forWhat = ForTextNodes ); - bool isFixedPositional() const { - return position != -1; - } + void encodeTo( std::ostream& os ) const; + + friend std::ostream& operator << ( std::ostream& os, XmlEncode const& xmlEncode ); + + private: + std::string m_str; + ForWhat m_forWhat; }; - template<typename ConfigT> - class CommandLine { + class XmlWriter { + public: - struct Arg : CommonArgProperties<ConfigT>, OptionArgProperties, PositionalArgProperties { - Arg() {} - Arg( Detail::BoundArgFunction<ConfigT> const& _boundField ) : CommonArgProperties<ConfigT>( _boundField ) {} + class ScopedElement { + public: + ScopedElement( XmlWriter* writer ); - using CommonArgProperties<ConfigT>::placeholder; // !TBD + ScopedElement( ScopedElement&& other ) noexcept; + ScopedElement& operator=( ScopedElement&& other ) noexcept; - std::string dbgName() const { - if( !longName.empty() ) - return "--" + longName; - if( !shortNames.empty() ) - return "-" + shortNames[0]; - return "positional args"; - } - std::string commands() const { - std::ostringstream oss; - bool first = true; - std::vector<std::string>::const_iterator it = shortNames.begin(), itEnd = shortNames.end(); - for(; it != itEnd; ++it ) { - if( first ) - first = false; - else - oss << ", "; - oss << "-" << *it; - } - if( !longName.empty() ) { - if( !first ) - oss << ", "; - oss << "--" << longName; - } - if( !placeholder.empty() ) - oss << " <" << placeholder << ">"; - return oss.str(); + ~ScopedElement(); + + ScopedElement& writeText( std::string const& text, bool indent = true ); + + template<typename T> + ScopedElement& writeAttribute( std::string const& name, T const& attribute ) { + m_writer->writeAttribute( name, attribute ); + return *this; } + + private: + mutable XmlWriter* m_writer = nullptr; }; - typedef CLARA_AUTO_PTR( Arg ) ArgAutoPtr; + XmlWriter( std::ostream& os = Catch::cout() ); + ~XmlWriter(); - friend void addOptName( Arg& arg, std::string const& optName ) - { - if( optName.empty() ) - return; - if( Detail::startsWith( optName, "--" ) ) { - if( !arg.longName.empty() ) - throw std::logic_error( "Only one long opt may be specified. '" - + arg.longName - + "' already specified, now attempting to add '" - + optName + "'" ); - arg.longName = optName.substr( 2 ); - } - else if( Detail::startsWith( optName, "-" ) ) - arg.shortNames.push_back( optName.substr( 1 ) ); - else - throw std::logic_error( "option must begin with - or --. Option was: '" + optName + "'" ); - } - friend void setPositionalArg( Arg& arg, int position ) - { - arg.position = position; + XmlWriter( XmlWriter const& ) = delete; + XmlWriter& operator=( XmlWriter const& ) = delete; + + XmlWriter& startElement( std::string const& name ); + + ScopedElement scopedElement( std::string const& name ); + + XmlWriter& endElement(); + + XmlWriter& writeAttribute( std::string const& name, std::string const& attribute ); + + XmlWriter& writeAttribute( std::string const& name, bool attribute ); + + template<typename T> + XmlWriter& writeAttribute( std::string const& name, T const& attribute ) { + ReusableStringStream rss; + rss << attribute; + return writeAttribute( name, rss.str() ); } - class ArgBuilder { - public: - ArgBuilder( Arg* arg ) : m_arg( arg ) {} + XmlWriter& writeText( std::string const& text, bool indent = true ); - // Bind a non-boolean data member (requires placeholder string) - template<typename C, typename M> - void bind( M C::* field, std::string const& placeholder ) { - m_arg->boundField = new Detail::BoundDataMember<C,M>( field ); - m_arg->placeholder = placeholder; - } - // Bind a boolean data member (no placeholder required) - template<typename C> - void bind( bool C::* field ) { - m_arg->boundField = new Detail::BoundDataMember<C,bool>( field ); - } + XmlWriter& writeComment( std::string const& text ); - // Bind a method taking a single, non-boolean argument (requires a placeholder string) - template<typename C, typename M> - void bind( void (C::* unaryMethod)( M ), std::string const& placeholder ) { - m_arg->boundField = new Detail::BoundUnaryMethod<C,M>( unaryMethod ); - m_arg->placeholder = placeholder; - } + void writeStylesheetRef( std::string const& url ); - // Bind a method taking a single, boolean argument (no placeholder string required) - template<typename C> - void bind( void (C::* unaryMethod)( bool ) ) { - m_arg->boundField = new Detail::BoundUnaryMethod<C,bool>( unaryMethod ); - } + XmlWriter& writeBlankLine(); - // Bind a method that takes no arguments (will be called if opt is present) - template<typename C> - void bind( void (C::* nullaryMethod)() ) { - m_arg->boundField = new Detail::BoundNullaryMethod<C>( nullaryMethod ); - } + void ensureTagClosed(); - // Bind a free function taking a single argument - the object to operate on (no placeholder string required) - template<typename C> - void bind( void (* unaryFunction)( C& ) ) { - m_arg->boundField = new Detail::BoundUnaryFunction<C>( unaryFunction ); - } + private: - // Bind a free function taking a single argument - the object to operate on (requires a placeholder string) - template<typename C, typename T> - void bind( void (* binaryFunction)( C&, T ), std::string const& placeholder ) { - m_arg->boundField = new Detail::BoundBinaryFunction<C, T>( binaryFunction ); - m_arg->placeholder = placeholder; - } + void writeDeclaration(); - ArgBuilder& describe( std::string const& description ) { - m_arg->description = description; - return *this; - } - ArgBuilder& detail( std::string const& detail ) { - m_arg->detail = detail; - return *this; - } + void newlineIfNecessary(); - protected: - Arg* m_arg; - }; + bool m_tagIsOpen = false; + bool m_needsNewline = false; + std::vector<std::string> m_tags; + std::string m_indent; + std::ostream& m_os; + }; - class OptBuilder : public ArgBuilder { - public: - OptBuilder( Arg* arg ) : ArgBuilder( arg ) {} - OptBuilder( OptBuilder& other ) : ArgBuilder( other ) {} +} - OptBuilder& operator[]( std::string const& optName ) { - addOptName( *ArgBuilder::m_arg, optName ); - return *this; - } - }; +// end catch_xmlwriter.h +namespace Catch { + class JunitReporter : public CumulativeReporterBase<JunitReporter> { public: + JunitReporter(ReporterConfig const& _config); - CommandLine() - : m_boundProcessName( new Detail::NullBinder<ConfigT>() ), - m_highestSpecifiedArgPosition( 0 ), - m_throwOnUnrecognisedTokens( false ) - {} - CommandLine( CommandLine const& other ) - : m_boundProcessName( other.m_boundProcessName ), - m_options ( other.m_options ), - m_positionalArgs( other.m_positionalArgs ), - m_highestSpecifiedArgPosition( other.m_highestSpecifiedArgPosition ), - m_throwOnUnrecognisedTokens( other.m_throwOnUnrecognisedTokens ) - { - if( other.m_floatingArg.get() ) - m_floatingArg.reset( new Arg( *other.m_floatingArg ) ); - } + ~JunitReporter() override; - CommandLine& setThrowOnUnrecognisedTokens( bool shouldThrow = true ) { - m_throwOnUnrecognisedTokens = shouldThrow; - return *this; - } + static std::string getDescription(); - OptBuilder operator[]( std::string const& optName ) { - m_options.push_back( Arg() ); - addOptName( m_options.back(), optName ); - OptBuilder builder( &m_options.back() ); - return builder; - } + void noMatchingTestCases(std::string const& /*spec*/) override; - ArgBuilder operator[]( int position ) { - m_positionalArgs.insert( std::make_pair( position, Arg() ) ); - if( position > m_highestSpecifiedArgPosition ) - m_highestSpecifiedArgPosition = position; - setPositionalArg( m_positionalArgs[position], position ); - ArgBuilder builder( &m_positionalArgs[position] ); - return builder; - } + void testRunStarting(TestRunInfo const& runInfo) override; - // Invoke this with the _ instance - ArgBuilder operator[]( UnpositionalTag ) { - if( m_floatingArg.get() ) - throw std::logic_error( "Only one unpositional argument can be added" ); - m_floatingArg.reset( new Arg() ); - ArgBuilder builder( m_floatingArg.get() ); - return builder; - } + void testGroupStarting(GroupInfo const& groupInfo) override; - template<typename C, typename M> - void bindProcessName( M C::* field ) { - m_boundProcessName = new Detail::BoundDataMember<C,M>( field ); - } - template<typename C, typename M> - void bindProcessName( void (C::*_unaryMethod)( M ) ) { - m_boundProcessName = new Detail::BoundUnaryMethod<C,M>( _unaryMethod ); - } + void testCaseStarting(TestCaseInfo const& testCaseInfo) override; + bool assertionEnded(AssertionStats const& assertionStats) override; - void optUsage( std::ostream& os, std::size_t indent = 0, std::size_t width = Detail::consoleWidth ) const { - typename std::vector<Arg>::const_iterator itBegin = m_options.begin(), itEnd = m_options.end(), it; - std::size_t maxWidth = 0; - for( it = itBegin; it != itEnd; ++it ) - maxWidth = (std::max)( maxWidth, it->commands().size() ); + void testCaseEnded(TestCaseStats const& testCaseStats) override; - for( it = itBegin; it != itEnd; ++it ) { - Detail::Text usage( it->commands(), Detail::TextAttributes() - .setWidth( maxWidth+indent ) - .setIndent( indent ) ); - Detail::Text desc( it->description, Detail::TextAttributes() - .setWidth( width - maxWidth - 3 ) ); + void testGroupEnded(TestGroupStats const& testGroupStats) override; - for( std::size_t i = 0; i < (std::max)( usage.size(), desc.size() ); ++i ) { - std::string usageCol = i < usage.size() ? usage[i] : ""; - os << usageCol; + void testRunEndedCumulative() override; - if( i < desc.size() && !desc[i].empty() ) - os << std::string( indent + 2 + maxWidth - usageCol.size(), ' ' ) - << desc[i]; - os << "\n"; - } - } - } - std::string optUsage() const { - std::ostringstream oss; - optUsage( oss ); - return oss.str(); - } - - void argSynopsis( std::ostream& os ) const { - for( int i = 1; i <= m_highestSpecifiedArgPosition; ++i ) { - if( i > 1 ) - os << " "; - typename std::map<int, Arg>::const_iterator it = m_positionalArgs.find( i ); - if( it != m_positionalArgs.end() ) - os << "<" << it->second.placeholder << ">"; - else if( m_floatingArg.get() ) - os << "<" << m_floatingArg->placeholder << ">"; - else - throw std::logic_error( "non consecutive positional arguments with no floating args" ); - } - // !TBD No indication of mandatory args - if( m_floatingArg.get() ) { - if( m_highestSpecifiedArgPosition > 1 ) - os << " "; - os << "[<" << m_floatingArg->placeholder << "> ...]"; - } - } - std::string argSynopsis() const { - std::ostringstream oss; - argSynopsis( oss ); - return oss.str(); - } + void writeGroup(TestGroupNode const& groupNode, double suiteTime); - void usage( std::ostream& os, std::string const& procName ) const { - validate(); - os << "usage:\n " << procName << " "; - argSynopsis( os ); - if( !m_options.empty() ) { - os << " [options]\n\nwhere options are: \n"; - optUsage( os, 2 ); - } - os << "\n"; - } - std::string usage( std::string const& procName ) const { - std::ostringstream oss; - usage( oss, procName ); - return oss.str(); - } - - ConfigT parse( std::vector<std::string> const& args ) const { - ConfigT config; - parseInto( args, config ); - return config; - } - - std::vector<Parser::Token> parseInto( std::vector<std::string> const& args, ConfigT& config ) const { - std::string processName = args[0]; - std::size_t lastSlash = processName.find_last_of( "/\\" ); - if( lastSlash != std::string::npos ) - processName = processName.substr( lastSlash+1 ); - m_boundProcessName.set( config, processName ); - std::vector<Parser::Token> tokens; - Parser parser; - parser.parseIntoTokens( args, tokens ); - return populate( tokens, config ); - } - - std::vector<Parser::Token> populate( std::vector<Parser::Token> const& tokens, ConfigT& config ) const { - validate(); - std::vector<Parser::Token> unusedTokens = populateOptions( tokens, config ); - unusedTokens = populateFixedArgs( unusedTokens, config ); - unusedTokens = populateFloatingArgs( unusedTokens, config ); - return unusedTokens; - } - - std::vector<Parser::Token> populateOptions( std::vector<Parser::Token> const& tokens, ConfigT& config ) const { - std::vector<Parser::Token> unusedTokens; - std::vector<std::string> errors; - for( std::size_t i = 0; i < tokens.size(); ++i ) { - Parser::Token const& token = tokens[i]; - typename std::vector<Arg>::const_iterator it = m_options.begin(), itEnd = m_options.end(); - for(; it != itEnd; ++it ) { - Arg const& arg = *it; - - try { - if( ( token.type == Parser::Token::ShortOpt && arg.hasShortName( token.data ) ) || - ( token.type == Parser::Token::LongOpt && arg.hasLongName( token.data ) ) ) { - if( arg.takesArg() ) { - if( i == tokens.size()-1 || tokens[i+1].type != Parser::Token::Positional ) - errors.push_back( "Expected argument to option: " + token.data ); - else - arg.boundField.set( config, tokens[++i].data ); - } - else { - arg.boundField.set( config, "true" ); - } - break; - } - } - catch( std::exception& ex ) { - errors.push_back( std::string( ex.what() ) + "\n- while parsing: (" + arg.commands() + ")" ); - } - } - if( it == itEnd ) { - if( token.type == Parser::Token::Positional || !m_throwOnUnrecognisedTokens ) - unusedTokens.push_back( token ); - else if( errors.empty() && m_throwOnUnrecognisedTokens ) - errors.push_back( "unrecognised option: " + token.data ); - } - } - if( !errors.empty() ) { - std::ostringstream oss; - for( std::vector<std::string>::const_iterator it = errors.begin(), itEnd = errors.end(); - it != itEnd; - ++it ) { - if( it != errors.begin() ) - oss << "\n"; - oss << *it; - } - throw std::runtime_error( oss.str() ); - } - return unusedTokens; - } - std::vector<Parser::Token> populateFixedArgs( std::vector<Parser::Token> const& tokens, ConfigT& config ) const { - std::vector<Parser::Token> unusedTokens; - int position = 1; - for( std::size_t i = 0; i < tokens.size(); ++i ) { - Parser::Token const& token = tokens[i]; - typename std::map<int, Arg>::const_iterator it = m_positionalArgs.find( position ); - if( it != m_positionalArgs.end() ) - it->second.boundField.set( config, token.data ); - else - unusedTokens.push_back( token ); - if( token.type == Parser::Token::Positional ) - position++; - } - return unusedTokens; - } - std::vector<Parser::Token> populateFloatingArgs( std::vector<Parser::Token> const& tokens, ConfigT& config ) const { - if( !m_floatingArg.get() ) - return tokens; - std::vector<Parser::Token> unusedTokens; - for( std::size_t i = 0; i < tokens.size(); ++i ) { - Parser::Token const& token = tokens[i]; - if( token.type == Parser::Token::Positional ) - m_floatingArg->boundField.set( config, token.data ); - else - unusedTokens.push_back( token ); - } - return unusedTokens; - } + void writeTestCase(TestCaseNode const& testCaseNode); - void validate() const - { - if( m_options.empty() && m_positionalArgs.empty() && !m_floatingArg.get() ) - throw std::logic_error( "No options or arguments specified" ); + void writeSection(std::string const& className, + std::string const& rootName, + SectionNode const& sectionNode); - for( typename std::vector<Arg>::const_iterator it = m_options.begin(), - itEnd = m_options.end(); - it != itEnd; ++it ) - it->validate(); - } + void writeAssertions(SectionNode const& sectionNode); + void writeAssertion(AssertionStats const& stats); - private: - Detail::BoundArgFunction<ConfigT> m_boundProcessName; - std::vector<Arg> m_options; - std::map<int, Arg> m_positionalArgs; - ArgAutoPtr m_floatingArg; - int m_highestSpecifiedArgPosition; - bool m_throwOnUnrecognisedTokens; + XmlWriter xml; + Timer suiteTimer; + std::string stdOutForSuite; + std::string stdErrForSuite; + unsigned int unexpectedExceptions = 0; + bool m_okToFail = false; }; -} // end namespace Clara +} // end namespace Catch -STITCH_CLARA_CLOSE_NAMESPACE -#undef STITCH_CLARA_OPEN_NAMESPACE -#undef STITCH_CLARA_CLOSE_NAMESPACE +// end catch_reporter_junit.h +// start catch_reporter_xml.h -#endif // TWOBLUECUBES_CLARA_H_INCLUDED -#undef STITCH_CLARA_OPEN_NAMESPACE +namespace Catch { + class XmlReporter : public StreamingReporterBase<XmlReporter> { + public: + XmlReporter(ReporterConfig const& _config); -// Restore Clara's value for console width, if present -#ifdef CATCH_TEMP_CLARA_CONFIG_CONSOLE_WIDTH -#define CLARA_CONFIG_CONSOLE_WIDTH CATCH_TEMP_CLARA_CONFIG_CONSOLE_WIDTH -#undef CATCH_TEMP_CLARA_CONFIG_CONSOLE_WIDTH -#endif + ~XmlReporter() override; -#include <fstream> -#include <ctime> + static std::string getDescription(); -namespace Catch { + virtual std::string getStylesheetRef() const; - inline void abortAfterFirst( ConfigData& config ) { config.abortAfter = 1; } - inline void abortAfterX( ConfigData& config, int x ) { - if( x < 1 ) - throw std::runtime_error( "Value after -x or --abortAfter must be greater than zero" ); - config.abortAfter = x; - } - inline void addTestOrTags( ConfigData& config, std::string const& _testSpec ) { config.testsOrTags.push_back( _testSpec ); } - inline void addSectionToRun( ConfigData& config, std::string const& sectionName ) { config.sectionsToRun.push_back( sectionName ); } - inline void addReporterName( ConfigData& config, std::string const& _reporterName ) { config.reporterNames.push_back( _reporterName ); } + void writeSourceInfo(SourceLineInfo const& sourceInfo); - inline void addWarning( ConfigData& config, std::string const& _warning ) { - if( _warning == "NoAssertions" ) - config.warnings = static_cast<WarnAbout::What>( config.warnings | WarnAbout::NoAssertions ); - else - throw std::runtime_error( "Unrecognised warning: '" + _warning + '\'' ); - } - inline void setOrder( ConfigData& config, std::string const& order ) { - if( startsWith( "declared", order ) ) - config.runOrder = RunTests::InDeclarationOrder; - else if( startsWith( "lexical", order ) ) - config.runOrder = RunTests::InLexicographicalOrder; - else if( startsWith( "random", order ) ) - config.runOrder = RunTests::InRandomOrder; - else - throw std::runtime_error( "Unrecognised ordering: '" + order + '\'' ); - } - inline void setRngSeed( ConfigData& config, std::string const& seed ) { - if( seed == "time" ) { - config.rngSeed = static_cast<unsigned int>( std::time(0) ); - } - else { - std::stringstream ss; - ss << seed; - ss >> config.rngSeed; - if( ss.fail() ) - throw std::runtime_error( "Argument to --rng-seed should be the word 'time' or a number" ); - } - } - inline void setVerbosity( ConfigData& config, int level ) { - // !TBD: accept strings? - config.verbosity = static_cast<Verbosity::Level>( level ); - } - inline void setShowDurations( ConfigData& config, bool _showDurations ) { - config.showDurations = _showDurations - ? ShowDurations::Always - : ShowDurations::Never; - } - inline void setUseColour( ConfigData& config, std::string const& value ) { - std::string mode = toLower( value ); - - if( mode == "yes" ) - config.useColour = UseColour::Yes; - else if( mode == "no" ) - config.useColour = UseColour::No; - else if( mode == "auto" ) - config.useColour = UseColour::Auto; - else - throw std::runtime_error( "colour mode must be one of: auto, yes or no" ); - } - inline void forceColour( ConfigData& config ) { - config.useColour = UseColour::Yes; - } - inline void loadTestNamesFromFile( ConfigData& config, std::string const& _filename ) { - std::ifstream f( _filename.c_str() ); - if( !f.is_open() ) - throw std::domain_error( "Unable to load input file: " + _filename ); + public: // StreamingReporterBase - std::string line; - while( std::getline( f, line ) ) { - line = trim(line); - if( !line.empty() && !startsWith( line, '#' ) ) { - if( !startsWith( line, '"' ) ) - line = '"' + line + '"'; - addTestOrTags( config, line + ',' ); - } - } - } + void noMatchingTestCases(std::string const& s) override; - inline Clara::CommandLine<ConfigData> makeCommandLineParser() { + void testRunStarting(TestRunInfo const& testInfo) override; - using namespace Clara; - CommandLine<ConfigData> cli; + void testGroupStarting(GroupInfo const& groupInfo) override; - cli.bindProcessName( &ConfigData::processName ); + void testCaseStarting(TestCaseInfo const& testInfo) override; - cli["-?"]["-h"]["--help"] - .describe( "display usage information" ) - .bind( &ConfigData::showHelp ); + void sectionStarting(SectionInfo const& sectionInfo) override; - cli["-l"]["--list-tests"] - .describe( "list all/matching test cases" ) - .bind( &ConfigData::listTests ); + void assertionStarting(AssertionInfo const&) override; - cli["-t"]["--list-tags"] - .describe( "list all/matching tags" ) - .bind( &ConfigData::listTags ); + bool assertionEnded(AssertionStats const& assertionStats) override; - cli["-s"]["--success"] - .describe( "include successful tests in output" ) - .bind( &ConfigData::showSuccessfulTests ); + void sectionEnded(SectionStats const& sectionStats) override; - cli["-b"]["--break"] - .describe( "break into debugger on failure" ) - .bind( &ConfigData::shouldDebugBreak ); + void testCaseEnded(TestCaseStats const& testCaseStats) override; - cli["-e"]["--nothrow"] - .describe( "skip exception tests" ) - .bind( &ConfigData::noThrow ); + void testGroupEnded(TestGroupStats const& testGroupStats) override; - cli["-i"]["--invisibles"] - .describe( "show invisibles (tabs, newlines)" ) - .bind( &ConfigData::showInvisibles ); + void testRunEnded(TestRunStats const& testRunStats) override; - cli["-o"]["--out"] - .describe( "output filename" ) - .bind( &ConfigData::outputFilename, "filename" ); +#if defined(CATCH_CONFIG_ENABLE_BENCHMARKING) + void benchmarkStarting(BenchmarkInfo const&) override; + void benchmarkEnded(BenchmarkStats<> const&) override; + void benchmarkFailed(std::string const&) override; +#endif // CATCH_CONFIG_ENABLE_BENCHMARKING - cli["-r"]["--reporter"] -// .placeholder( "name[:filename]" ) - .describe( "reporter to use (defaults to console)" ) - .bind( &addReporterName, "name" ); + private: + Timer m_testCaseTimer; + XmlWriter m_xml; + int m_sectionDepth = 0; + }; - cli["-n"]["--name"] - .describe( "suite name" ) - .bind( &ConfigData::name, "name" ); +} // end namespace Catch - cli["-a"]["--abort"] - .describe( "abort at first failure" ) - .bind( &abortAfterFirst ); +// end catch_reporter_xml.h - cli["-x"]["--abortx"] - .describe( "abort after x failures" ) - .bind( &abortAfterX, "no. failures" ); +// end catch_external_interfaces.h +#endif - cli["-w"]["--warn"] - .describe( "enable warnings" ) - .bind( &addWarning, "warning name" ); +#if defined(CATCH_CONFIG_ENABLE_BENCHMARKING) +// start catch_benchmark.hpp -// - needs updating if reinstated -// cli.into( &setVerbosity ) -// .describe( "level of verbosity (0=no output)" ) -// .shortOpt( "v") -// .longOpt( "verbosity" ) -// .placeholder( "level" ); + // Benchmark - cli[_] - .describe( "which test or tests to use" ) - .bind( &addTestOrTags, "test name, pattern or tags" ); +// start catch_chronometer.hpp - cli["-d"]["--durations"] - .describe( "show test durations" ) - .bind( &setShowDurations, "yes|no" ); +// User-facing chronometer - cli["-f"]["--input-file"] - .describe( "load test names to run from a file" ) - .bind( &loadTestNamesFromFile, "filename" ); - cli["-#"]["--filenames-as-tags"] - .describe( "adds a tag for the filename" ) - .bind( &ConfigData::filenamesAsTags ); +// start catch_clock.hpp - cli["-c"]["--section"] - .describe( "specify section to run" ) - .bind( &addSectionToRun, "section name" ); +// Clocks - // Less common commands which don't have a short form - cli["--list-test-names-only"] - .describe( "list all/matching test cases names only" ) - .bind( &ConfigData::listTestNamesOnly ); - cli["--list-reporters"] - .describe( "list all reporters" ) - .bind( &ConfigData::listReporters ); +#include <chrono> +#include <ratio> - cli["--order"] - .describe( "test case order (defaults to decl)" ) - .bind( &setOrder, "decl|lex|rand" ); +namespace Catch { + namespace Benchmark { + template <typename Clock> + using ClockDuration = typename Clock::duration; + template <typename Clock> + using FloatDuration = std::chrono::duration<double, typename Clock::period>; - cli["--rng-seed"] - .describe( "set a specific seed for random numbers" ) - .bind( &setRngSeed, "'time'|number" ); + template <typename Clock> + using TimePoint = typename Clock::time_point; - cli["--force-colour"] - .describe( "force colourised output (deprecated)" ) - .bind( &forceColour ); - - cli["--use-colour"] - .describe( "should output be colourised" ) - .bind( &setUseColour, "yes|no" ); + using default_clock = std::chrono::steady_clock; - return cli; - } + template <typename Clock> + struct now { + TimePoint<Clock> operator()() const { + return Clock::now(); + } + }; -} // end namespace Catch + using fp_seconds = std::chrono::duration<double, std::ratio<1>>; + } // namespace Benchmark +} // namespace Catch -// #included from: internal/catch_list.hpp -#define TWOBLUECUBES_CATCH_LIST_HPP_INCLUDED +// end catch_clock.hpp +// start catch_optimizer.hpp -// #included from: catch_text.h -#define TWOBLUECUBES_CATCH_TEXT_H_INCLUDED + // Hinting the optimizer -#define TBC_TEXT_FORMAT_CONSOLE_WIDTH CATCH_CONFIG_CONSOLE_WIDTH -#define CLICHE_TBC_TEXT_FORMAT_OUTER_NAMESPACE Catch -// #included from: ../external/tbc_text_format.h -// Only use header guard if we are not using an outer namespace -#ifndef CLICHE_TBC_TEXT_FORMAT_OUTER_NAMESPACE -# ifdef TWOBLUECUBES_TEXT_FORMAT_H_INCLUDED -# ifndef TWOBLUECUBES_TEXT_FORMAT_H_ALREADY_INCLUDED -# define TWOBLUECUBES_TEXT_FORMAT_H_ALREADY_INCLUDED -# endif -# else -# define TWOBLUECUBES_TEXT_FORMAT_H_INCLUDED -# endif +#if defined(_MSC_VER) +# include <atomic> // atomic_thread_fence #endif -#ifndef TWOBLUECUBES_TEXT_FORMAT_H_ALREADY_INCLUDED -#include <string> -#include <vector> -#include <sstream> -// Use optional outer namespace -#ifdef CLICHE_TBC_TEXT_FORMAT_OUTER_NAMESPACE -namespace CLICHE_TBC_TEXT_FORMAT_OUTER_NAMESPACE { -#endif +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 Tbc { + namespace Detail { + inline void optimizer_barrier() { keep_memory(); } + } // namespace Detail +#elif defined(_MSC_VER) + +#pragma optimize("", off) + 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() +#pragma optimize("", on) + + namespace Detail { + inline void optimizer_barrier() { + std::atomic_thread_fence(std::memory_order_seq_cst); + } + } // namespace Detail -#ifdef TBC_TEXT_FORMAT_CONSOLE_WIDTH - const unsigned int consoleWidth = TBC_TEXT_FORMAT_CONSOLE_WIDTH; -#else - const unsigned int consoleWidth = 80; #endif - struct TextAttributes { - TextAttributes() - : initialIndent( std::string::npos ), - indent( 0 ), - width( consoleWidth-1 ) - {} + template <typename T> + inline void deoptimize_value(T&& x) { + keep_memory(&x); + } - TextAttributes& setInitialIndent( std::size_t _value ) { initialIndent = _value; return *this; } - TextAttributes& setIndent( std::size_t _value ) { indent = _value; return *this; } - TextAttributes& setWidth( std::size_t _value ) { width = _value; return *this; } + template <typename Fn, typename... Args> + inline auto invoke_deoptimized(Fn&& fn, Args&&... args) -> typename std::enable_if<!std::is_same<void, decltype(fn(args...))>::value>::type { + deoptimize_value(std::forward<Fn>(fn) (std::forward<Args...>(args...))); + } - std::size_t initialIndent; // indent of first line, or npos - std::size_t indent; // indent of subsequent lines, or all if initialIndent is npos - std::size_t width; // maximum width of text, including indent. Longer text will wrap - }; + template <typename Fn, typename... Args> + inline auto invoke_deoptimized(Fn&& fn, Args&&... args) -> typename std::enable_if<std::is_same<void, decltype(fn(args...))>::value>::type { + std::forward<Fn>(fn) (std::forward<Args...>(args...)); + } + } // namespace Benchmark +} // namespace Catch - class Text { - public: - Text( std::string const& _str, TextAttributes const& _attr = TextAttributes() ) - : attr( _attr ) - { - const std::string wrappableBeforeChars = "[({<\t"; - const std::string wrappableAfterChars = "])}>-,./|\\"; - const std::string wrappableInsteadOfChars = " \n\r"; - std::string indent = _attr.initialIndent != std::string::npos - ? std::string( _attr.initialIndent, ' ' ) - : std::string( _attr.indent, ' ' ); - - typedef std::string::const_iterator iterator; - iterator it = _str.begin(); - const iterator strEnd = _str.end(); - - while( it != strEnd ) { - - if( lines.size() >= 1000 ) { - lines.push_back( "... message truncated due to excessive size" ); - return; - } +// end catch_optimizer.hpp +// start catch_complete_invoke.hpp - std::string suffix; - std::size_t width = (std::min)( static_cast<size_t>( strEnd-it ), _attr.width-static_cast<size_t>( indent.size() ) ); - iterator itEnd = it+width; - iterator itNext = _str.end(); - - iterator itNewLine = std::find( it, itEnd, '\n' ); - if( itNewLine != itEnd ) - itEnd = itNewLine; - - if( itEnd != strEnd ) { - bool foundWrapPoint = false; - iterator findIt = itEnd; - do { - if( wrappableAfterChars.find( *findIt ) != std::string::npos && findIt != itEnd ) { - itEnd = findIt+1; - itNext = findIt+1; - foundWrapPoint = true; - } - else if( findIt > it && wrappableBeforeChars.find( *findIt ) != std::string::npos ) { - itEnd = findIt; - itNext = findIt; - foundWrapPoint = true; - } - else if( wrappableInsteadOfChars.find( *findIt ) != std::string::npos ) { - itNext = findIt+1; - itEnd = findIt; - foundWrapPoint = true; - } - if( findIt == it ) - break; - else - --findIt; - } - while( !foundWrapPoint ); +// Invoke with a special case for void - if( !foundWrapPoint ) { - // No good wrap char, so we'll break mid word and add a hyphen - --itEnd; - itNext = itEnd; - suffix = "-"; - } - else { - while( itEnd > it && wrappableInsteadOfChars.find( *(itEnd-1) ) != std::string::npos ) - --itEnd; - } + +#include <type_traits> +#include <utility> + +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 std::forward<Fun>(fun)(std::forward<Args>(args)...); } - lines.push_back( indent + std::string( it, itEnd ) + suffix ); + }; + template <> + struct CompleteInvoker<void> { + template <typename Fun, typename... Args> + static CompleteType_t<void> invoke(Fun&& fun, Args&&... args) { + std::forward<Fun>(fun)(std::forward<Args>(args)...); + return {}; + } + }; + template <typename Sig> + using ResultOf_t = typename std::result_of<Sig>::type; - if( indent.size() != _attr.indent ) - indent = std::string( _attr.indent, ' ' ); - it = itNext; + // invoke and not return void :( + template <typename Fun, typename... Args> + CompleteType_t<ResultOf_t<Fun(Args...)>> complete_invoke(Fun&& fun, Args&&... args) { + return CompleteInvoker<ResultOf_t<Fun(Args...)>>::invoke(std::forward<Fun>(fun), std::forward<Args>(args)...); } - } - typedef std::vector<std::string>::const_iterator const_iterator; + const std::string benchmarkErrorMsg = "a benchmark failed to run successfully"; + } // namespace Detail - const_iterator begin() const { return lines.begin(); } - const_iterator end() const { return lines.end(); } - std::string const& last() const { return lines.back(); } - std::size_t size() const { return lines.size(); } - std::string const& operator[]( std::size_t _index ) const { return lines[_index]; } - std::string toString() const { - std::ostringstream oss; - oss << *this; - return oss.str(); + template <typename Fun> + Detail::CompleteType_t<Detail::ResultOf_t<Fun()>> user_code(Fun&& fun) { + CATCH_TRY{ + return Detail::complete_invoke(std::forward<Fun>(fun)); + } CATCH_CATCH_ALL{ + getResultCapture().benchmarkFailed(translateActiveException()); + CATCH_RUNTIME_ERROR(Detail::benchmarkErrorMsg); + } } + } // namespace Benchmark +} // namespace Catch - inline friend std::ostream& operator << ( std::ostream& _stream, Text const& _text ) { - for( Text::const_iterator it = _text.begin(), itEnd = _text.end(); - it != itEnd; ++it ) { - if( it != _text.begin() ) - _stream << "\n"; - _stream << *it; +// end catch_complete_invoke.hpp +namespace Catch { + namespace Benchmark { + namespace Detail { + struct ChronometerConcept { + virtual void start() = 0; + virtual void finish() = 0; + virtual ~ChronometerConcept() = default; + }; + template <typename Clock> + struct ChronometerModel final : public ChronometerConcept { + void start() override { started = Clock::now(); } + void finish() override { finished = Clock::now(); } + + ClockDuration<Clock> elapsed() const { return finished - started; } + + TimePoint<Clock> started; + TimePoint<Clock> finished; + }; + } // namespace Detail + + struct Chronometer { + public: + template <typename Fun> + void measure(Fun&& fun) { measure(std::forward<Fun>(fun), is_callable<Fun(int)>()); } + + int runs() const { return k; } + + Chronometer(Detail::ChronometerConcept& meter, int k) + : impl(&meter) + , k(k) {} + + private: + template <typename Fun> + void measure(Fun&& fun, std::false_type) { + measure([&fun](int) { return fun(); }, std::true_type()); } - return _stream; - } - private: - std::string str; - TextAttributes attr; - std::vector<std::string> lines; - }; + template <typename Fun> + void measure(Fun&& fun, std::true_type) { + Detail::optimizer_barrier(); + impl->start(); + for (int i = 0; i < k; ++i) invoke_deoptimized(fun, i); + impl->finish(); + Detail::optimizer_barrier(); + } -} // end namespace Tbc + Detail::ChronometerConcept* impl; + int k; + }; + } // namespace Benchmark +} // namespace Catch -#ifdef CLICHE_TBC_TEXT_FORMAT_OUTER_NAMESPACE -} // end outer namespace -#endif +// end catch_chronometer.hpp +// start catch_environment.hpp + +// Environment information -#endif // TWOBLUECUBES_TEXT_FORMAT_H_ALREADY_INCLUDED -#undef CLICHE_TBC_TEXT_FORMAT_OUTER_NAMESPACE namespace Catch { - using Tbc::Text; - using Tbc::TextAttributes; -} + namespace Benchmark { + template <typename Duration> + struct EnvironmentEstimate { + Duration mean; + OutlierClassification outliers; + + template <typename Duration2> + operator EnvironmentEstimate<Duration2>() const { + return { mean, outliers }; + } + }; + template <typename Clock> + struct Environment { + using clock_type = Clock; + EnvironmentEstimate<FloatDuration<Clock>> clock_resolution; + EnvironmentEstimate<FloatDuration<Clock>> clock_cost; + }; + } // namespace Benchmark +} // namespace Catch + +// end catch_environment.hpp +// start catch_execution_plan.hpp + + // Execution plan + -// #included from: catch_console_colour.hpp -#define TWOBLUECUBES_CATCH_CONSOLE_COLOUR_HPP_INCLUDED +// start catch_benchmark_function.hpp + + // Dumb std::function implementation for consistent call overhead + + +#include <cassert> +#include <type_traits> +#include <utility> +#include <memory> namespace Catch { + namespace Benchmark { + namespace Detail { + template <typename T> + using Decay = typename std::decay<T>::type; + template <typename T, typename U> + struct is_related + : std::is_same<Decay<T>, Decay<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 callable* clone() const = 0; + virtual ~callable() = default; + }; + template <typename Fun> + struct model : public callable { + model(Fun&& fun) : fun(std::move(fun)) {} + model(Fun const& fun) : fun(fun) {} + + model<Fun>* clone() const override { return new 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); + } - struct Colour { - enum Code { - None = 0, + Fun fun; + }; - White, - Red, - Green, - Blue, - Cyan, - Yellow, - Grey, + struct do_nothing { void operator()() const {} }; - Bright = 0x10, + template <typename T> + BenchmarkFunction(model<T>* c) : f(c) {} - BrightRed = Bright | Red, - BrightGreen = Bright | Green, - LightGrey = Bright | Grey, - BrightWhite = Bright | White, + public: + BenchmarkFunction() + : f(new model<do_nothing>{ {} }) {} - // By intention - FileName = LightGrey, - Warning = Yellow, - ResultError = BrightRed, - ResultSuccess = BrightGreen, - ResultExpectedFailure = Warning, + template <typename Fun, + typename std::enable_if<!is_related<Fun, BenchmarkFunction>::value, int>::type = 0> + BenchmarkFunction(Fun&& fun) + : f(new model<typename std::decay<Fun>::type>(std::forward<Fun>(fun))) {} - Error = BrightRed, - Success = Green, + BenchmarkFunction(BenchmarkFunction&& that) + : f(std::move(that.f)) {} - OriginalExpression = Cyan, - ReconstructedExpression = Yellow, + BenchmarkFunction(BenchmarkFunction const& that) + : f(that.f->clone()) {} - SecondaryText = LightGrey, - Headers = White - }; + BenchmarkFunction& operator=(BenchmarkFunction&& that) { + f = std::move(that.f); + return *this; + } - // Use constructed object for RAII guard - Colour( Code _colourCode ); - Colour( Colour const& other ); - ~Colour(); + BenchmarkFunction& operator=(BenchmarkFunction const& that) { + f.reset(that.f->clone()); + return *this; + } - // Use static method for one-shot changes - static void use( Code _colourCode ); + void operator()(Chronometer meter) const { f->call(meter); } - private: - bool m_moved; - }; + private: + std::unique_ptr<callable> f; + }; + } // namespace Detail + } // namespace Benchmark +} // namespace Catch - inline std::ostream& operator << ( std::ostream& os, Colour const& ) { return os; } +// end catch_benchmark_function.hpp +// start catch_repeat.hpp -} // end namespace Catch +// repeat algorithm -// #included from: catch_interfaces_reporter.h -#define TWOBLUECUBES_CATCH_INTERFACES_REPORTER_H_INCLUDED -#include <string> -#include <ostream> -#include <map> +#include <type_traits> +#include <utility> -namespace Catch -{ - struct ReporterConfig { - explicit ReporterConfig( Ptr<IConfig const> const& _fullConfig ) - : m_stream( &_fullConfig->stream() ), m_fullConfig( _fullConfig ) {} +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<typename std::decay<Fun>::type> repeat(Fun&& fun) { + return { std::forward<Fun>(fun) }; + } + } // namespace Detail + } // namespace Benchmark +} // namespace Catch - ReporterConfig( Ptr<IConfig const> const& _fullConfig, std::ostream& _stream ) - : m_stream( &_stream ), m_fullConfig( _fullConfig ) {} +// end catch_repeat.hpp +// start catch_run_for_at_least.hpp - std::ostream& stream() const { return *m_stream; } - Ptr<IConfig const> fullConfig() const { return m_fullConfig; } +// Run a function for a minimum amount of time - private: - std::ostream* m_stream; - Ptr<IConfig const> m_fullConfig; - }; - struct ReporterPreferences { - ReporterPreferences() - : shouldRedirectStdOut( false ) - {} +// start catch_measure.hpp - bool shouldRedirectStdOut; - }; +// Measure - template<typename T> - struct LazyStat : Option<T> { - LazyStat() : used( false ) {} - LazyStat& operator=( T const& _value ) { - Option<T>::operator=( _value ); - used = false; - return *this; - } - void reset() { - Option<T>::reset(); - used = false; - } - bool used; - }; - struct TestRunInfo { - TestRunInfo( std::string const& _name ) : name( _name ) {} - std::string name; - }; - struct GroupInfo { - GroupInfo( std::string const& _name, - std::size_t _groupIndex, - std::size_t _groupsCount ) - : name( _name ), - groupIndex( _groupIndex ), - groupsCounts( _groupsCount ) - {} +// start catch_timing.hpp - std::string name; - std::size_t groupIndex; - std::size_t groupsCounts; - }; +// Timing - struct AssertionStats { - AssertionStats( AssertionResult const& _assertionResult, - std::vector<MessageInfo> const& _infoMessages, - Totals const& _totals ) - : assertionResult( _assertionResult ), - infoMessages( _infoMessages ), - totals( _totals ) - { - if( assertionResult.hasMessage() ) { - // Copy message into messages list. - // !TBD This should have been done earlier, somewhere - MessageBuilder builder( assertionResult.getTestMacroName(), assertionResult.getSourceInfo(), assertionResult.getResultType() ); - builder << assertionResult.getMessage(); - builder.m_info.message = builder.m_stream.str(); - infoMessages.push_back( builder.m_info ); - } - } - virtual ~AssertionStats(); +#include <tuple> +#include <type_traits> -# ifdef CATCH_CONFIG_CPP11_GENERATED_METHODS - AssertionStats( AssertionStats const& ) = default; - AssertionStats( AssertionStats && ) = default; - AssertionStats& operator = ( AssertionStats const& ) = default; - AssertionStats& operator = ( AssertionStats && ) = default; -# endif +namespace Catch { + namespace Benchmark { + template <typename Duration, typename Result> + struct Timing { + Duration elapsed; + Result result; + int iterations; + }; + template <typename Clock, typename Sig> + using TimingOf = Timing<ClockDuration<Clock>, Detail::CompleteType_t<Detail::ResultOf_t<Sig>>>; + } // namespace Benchmark +} // namespace Catch - AssertionResult assertionResult; - std::vector<MessageInfo> infoMessages; - Totals totals; - }; +// end catch_timing.hpp +#include <utility> - struct SectionStats { - SectionStats( SectionInfo const& _sectionInfo, - Counts const& _assertions, - double _durationInSeconds, - bool _missingAssertions ) - : sectionInfo( _sectionInfo ), - assertions( _assertions ), - durationInSeconds( _durationInSeconds ), - missingAssertions( _missingAssertions ) - {} - virtual ~SectionStats(); -# ifdef CATCH_CONFIG_CPP11_GENERATED_METHODS - SectionStats( SectionStats const& ) = default; - SectionStats( SectionStats && ) = default; - SectionStats& operator = ( SectionStats const& ) = default; - SectionStats& operator = ( SectionStats && ) = default; -# endif +namespace Catch { + namespace Benchmark { + namespace Detail { + template <typename Clock, typename Fun, typename... Args> + TimingOf<Clock, Fun(Args...)> measure(Fun&& fun, Args&&... args) { + auto start = Clock::now(); + auto&& r = Detail::complete_invoke(fun, std::forward<Args>(args)...); + auto end = Clock::now(); + auto delta = end - start; + return { delta, std::forward<decltype(r)>(r), 1 }; + } + } // namespace Detail + } // namespace Benchmark +} // namespace Catch - SectionInfo sectionInfo; - Counts assertions; - double durationInSeconds; - bool missingAssertions; - }; +// end catch_measure.hpp +#include <utility> +#include <type_traits> - struct TestCaseStats { - TestCaseStats( TestCaseInfo const& _testInfo, - Totals const& _totals, - std::string const& _stdOut, - std::string const& _stdErr, - bool _aborting ) - : testInfo( _testInfo ), - totals( _totals ), - stdOut( _stdOut ), - stdErr( _stdErr ), - aborting( _aborting ) - {} - virtual ~TestCaseStats(); +namespace Catch { + namespace Benchmark { + namespace Detail { + template <typename Clock, typename Fun> + TimingOf<Clock, Fun(int)> measure_one(Fun&& fun, int iters, std::false_type) { + return Detail::measure<Clock>(fun, iters); + } + template <typename Clock, typename Fun> + TimingOf<Clock, Fun(Chronometer)> measure_one(Fun&& fun, int iters, std::true_type) { + Detail::ChronometerModel<Clock> meter; + auto&& result = Detail::complete_invoke(fun, Chronometer(meter, iters)); -# ifdef CATCH_CONFIG_CPP11_GENERATED_METHODS - TestCaseStats( TestCaseStats const& ) = default; - TestCaseStats( TestCaseStats && ) = default; - TestCaseStats& operator = ( TestCaseStats const& ) = default; - TestCaseStats& operator = ( TestCaseStats && ) = default; -# endif + return { meter.elapsed(), std::move(result), iters }; + } - TestCaseInfo testInfo; - Totals totals; - std::string stdOut; - std::string stdErr; - bool aborting; - }; + template <typename Clock, typename Fun> + using run_for_at_least_argument_t = typename std::conditional<is_callable<Fun(Chronometer)>::value, Chronometer, int>::type; - struct TestGroupStats { - TestGroupStats( GroupInfo const& _groupInfo, - Totals const& _totals, - bool _aborting ) - : groupInfo( _groupInfo ), - totals( _totals ), - aborting( _aborting ) - {} - TestGroupStats( GroupInfo const& _groupInfo ) - : groupInfo( _groupInfo ), - aborting( false ) - {} - virtual ~TestGroupStats(); + struct optimized_away_error : std::exception { + const char* what() const noexcept override { + return "could not measure benchmark, maybe it was optimized away"; + } + }; -# ifdef CATCH_CONFIG_CPP11_GENERATED_METHODS - TestGroupStats( TestGroupStats const& ) = default; - TestGroupStats( TestGroupStats && ) = default; - TestGroupStats& operator = ( TestGroupStats const& ) = default; - TestGroupStats& operator = ( TestGroupStats && ) = default; -# endif + template <typename Clock, typename Fun> + TimingOf<Clock, Fun(run_for_at_least_argument_t<Clock, Fun>)> run_for_at_least(ClockDuration<Clock> how_long, int seed, Fun&& fun) { + auto iters = seed; + while (iters < (1 << 30)) { + auto&& Timing = measure_one<Clock>(fun, iters, is_callable<Fun(Chronometer)>()); - GroupInfo groupInfo; - Totals totals; - bool aborting; - }; + if (Timing.elapsed >= how_long) { + return { Timing.elapsed, std::move(Timing.result), iters }; + } + iters *= 2; + } + throw optimized_away_error{}; + } + } // namespace Detail + } // namespace Benchmark +} // namespace Catch - struct TestRunStats { - TestRunStats( TestRunInfo const& _runInfo, - Totals const& _totals, - bool _aborting ) - : runInfo( _runInfo ), - totals( _totals ), - aborting( _aborting ) - {} - virtual ~TestRunStats(); +// end catch_run_for_at_least.hpp +#include <algorithm> -# ifndef CATCH_CONFIG_CPP11_GENERATED_METHODS - TestRunStats( TestRunStats const& _other ) - : runInfo( _other.runInfo ), - totals( _other.totals ), - aborting( _other.aborting ) - {} -# else - TestRunStats( TestRunStats const& ) = default; - TestRunStats( TestRunStats && ) = default; - TestRunStats& operator = ( TestRunStats const& ) = default; - TestRunStats& operator = ( TestRunStats && ) = default; -# endif +namespace Catch { + namespace Benchmark { + template <typename Duration> + struct ExecutionPlan { + int iterations_per_sample; + Duration estimated_duration; + Detail::BenchmarkFunction benchmark; + Duration warmup_time; + int warmup_iterations; + + template <typename Duration2> + operator ExecutionPlan<Duration2>() const { + return { iterations_per_sample, estimated_duration, benchmark, warmup_time, warmup_iterations }; + } - TestRunInfo runInfo; - Totals totals; - bool aborting; - }; + template <typename Clock> + std::vector<FloatDuration<Clock>> run(const IConfig &cfg, Environment<FloatDuration<Clock>> env) const { + // warmup a bit + Detail::run_for_at_least<Clock>(std::chrono::duration_cast<ClockDuration<Clock>>(warmup_time), warmup_iterations, Detail::repeat(now<Clock>{})); + + std::vector<FloatDuration<Clock>> times; + times.reserve(cfg.benchmarkSamples()); + std::generate_n(std::back_inserter(times), cfg.benchmarkSamples(), [this, env] { + Detail::ChronometerModel<Clock> model; + this->benchmark(Chronometer(model, iterations_per_sample)); + auto sample_time = model.elapsed() - env.clock_cost.mean; + if (sample_time < FloatDuration<Clock>::zero()) sample_time = FloatDuration<Clock>::zero(); + return sample_time / iterations_per_sample; + }); + return times; + } + }; + } // namespace Benchmark +} // namespace Catch - class MultipleReporters; +// end catch_execution_plan.hpp +// start catch_estimate_clock.hpp - struct IStreamingReporter : IShared { - virtual ~IStreamingReporter(); + // Environment measurement - // Implementing class must also provide the following static method: - // static std::string getDescription(); - virtual ReporterPreferences getPreferences() const = 0; +// start catch_stats.hpp - virtual void noMatchingTestCases( std::string const& spec ) = 0; +// Statistical analysis tools - virtual void testRunStarting( TestRunInfo const& testRunInfo ) = 0; - virtual void testGroupStarting( GroupInfo const& groupInfo ) = 0; - virtual void testCaseStarting( TestCaseInfo const& testInfo ) = 0; - virtual void sectionStarting( SectionInfo const& sectionInfo ) = 0; +#include <algorithm> +#include <functional> +#include <vector> +#include <numeric> +#include <tuple> +#include <cmath> +#include <utility> +#include <cstddef> - virtual void assertionStarting( AssertionInfo const& assertionInfo ) = 0; +namespace Catch { + namespace Benchmark { + namespace Detail { + using sample = std::vector<double>; + + double weighted_average_quantile(int k, int q, std::vector<double>::iterator first, std::vector<double>::iterator last); + + template <typename Iterator> + OutlierClassification classify_outliers(Iterator first, Iterator last) { + std::vector<double> copy(first, last); + + auto q1 = weighted_average_quantile(1, 4, copy.begin(), copy.end()); + auto q3 = weighted_average_quantile(3, 4, copy.begin(), copy.end()); + auto iqr = q3 - q1; + auto los = q1 - (iqr * 3.); + auto lom = q1 - (iqr * 1.5); + auto him = q3 + (iqr * 1.5); + auto his = q3 + (iqr * 3.); + + OutlierClassification o; + for (; first != last; ++first) { + auto&& t = *first; + if (t < los) ++o.low_severe; + else if (t < lom) ++o.low_mild; + else if (t > his) ++o.high_severe; + else if (t > him) ++o.high_mild; + ++o.samples_seen; + } + return o; + } - // The return value indicates if the messages buffer should be cleared: - virtual bool assertionEnded( AssertionStats const& assertionStats ) = 0; + template <typename Iterator> + double mean(Iterator first, Iterator last) { + auto count = last - first; + double sum = std::accumulate(first, last, 0.); + return sum / count; + } - virtual void sectionEnded( SectionStats const& sectionStats ) = 0; - virtual void testCaseEnded( TestCaseStats const& testCaseStats ) = 0; - virtual void testGroupEnded( TestGroupStats const& testGroupStats ) = 0; - virtual void testRunEnded( TestRunStats const& testRunStats ) = 0; + template <typename URng, typename Iterator, typename Estimator> + sample resample(URng& rng, int resamples, Iterator first, Iterator last, Estimator& estimator) { + auto n = last - first; + std::uniform_int_distribution<decltype(n)> dist(0, n - 1); + + sample out; + out.reserve(resamples); + std::generate_n(std::back_inserter(out), resamples, [n, first, &estimator, &dist, &rng] { + std::vector<double> resampled; + resampled.reserve(n); + std::generate_n(std::back_inserter(resampled), n, [first, &dist, &rng] { return first[dist(rng)]; }); + return estimator(resampled.begin(), resampled.end()); + }); + std::sort(out.begin(), out.end()); + return out; + } - virtual void skipTest( TestCaseInfo const& testInfo ) = 0; + template <typename Estimator, typename Iterator> + sample jackknife(Estimator&& estimator, Iterator first, Iterator last) { + auto n = last - first; + auto second = std::next(first); + sample results; + results.reserve(n); - virtual MultipleReporters* tryAsMulti() { return CATCH_NULL; } - }; + for (auto it = first; it != last; ++it) { + std::iter_swap(it, first); + results.push_back(estimator(second, last)); + } - struct IReporterFactory : IShared { - virtual ~IReporterFactory(); - virtual IStreamingReporter* create( ReporterConfig const& config ) const = 0; - virtual std::string getDescription() const = 0; - }; + return results; + } - struct IReporterRegistry { - typedef std::map<std::string, Ptr<IReporterFactory> > FactoryMap; - typedef std::vector<Ptr<IReporterFactory> > Listeners; + inline double normal_cdf(double x) { + return std::erfc(-x / std::sqrt(2.0)) / 2.0; + } - virtual ~IReporterRegistry(); - virtual IStreamingReporter* create( std::string const& name, Ptr<IConfig const> const& config ) const = 0; - virtual FactoryMap const& getFactories() const = 0; - virtual Listeners const& getListeners() const = 0; - }; + double erfc_inv(double x); + + double normal_quantile(double p); + + template <typename Iterator, typename Estimator> + Estimate<double> bootstrap(double confidence_level, Iterator first, Iterator last, sample const& resample, Estimator&& estimator) { + auto n_samples = last - first; + + double point = estimator(first, last); + // Degenerate case with a single sample + if (n_samples == 1) return { point, point, point, confidence_level }; + + sample jack = jackknife(estimator, first, last); + double jack_mean = mean(jack.begin(), jack.end()); + double sum_squares, sum_cubes; + std::tie(sum_squares, sum_cubes) = std::accumulate(jack.begin(), jack.end(), std::make_pair(0., 0.), [jack_mean](std::pair<double, double> sqcb, double x) -> std::pair<double, double> { + auto d = jack_mean - x; + auto d2 = d * d; + auto d3 = d2 * d; + return { sqcb.first + d2, sqcb.second + d3 }; + }); + + double accel = sum_cubes / (6 * std::pow(sum_squares, 1.5)); + int n = static_cast<int>(resample.size()); + double prob_n = std::count_if(resample.begin(), resample.end(), [point](double x) { return x < point; }) / (double)n; + // degenerate case with uniform samples + if (prob_n == 0) return { point, point, point, confidence_level }; + + double bias = normal_quantile(prob_n); + double z1 = normal_quantile((1. - confidence_level) / 2.); + + auto cumn = [n](double x) -> int { + return std::lround(normal_cdf(x) * n); }; + auto a = [bias, accel](double b) { return bias + b / (1. - accel * b); }; + double b1 = bias + z1; + double b2 = bias - z1; + double a1 = a(b1); + double a2 = a(b2); + auto lo = std::max(cumn(a1), 0); + auto hi = std::min(cumn(a2), n - 1); + + return { point, resample[lo], resample[hi], confidence_level }; + } - Ptr<IStreamingReporter> addReporter( Ptr<IStreamingReporter> const& existingReporter, Ptr<IStreamingReporter> const& additionalReporter ); + double outlier_variance(Estimate<double> mean, Estimate<double> stddev, int n); -} + struct bootstrap_analysis { + Estimate<double> mean; + Estimate<double> standard_deviation; + double outlier_variance; + }; -#include <limits> + bootstrap_analysis analyse_samples(double confidence_level, int n_resamples, std::vector<double>::iterator first, std::vector<double>::iterator last); + } // namespace Detail + } // namespace Benchmark +} // namespace Catch + +// end catch_stats.hpp #include <algorithm> +#include <iterator> +#include <tuple> +#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(k + 1); + std::generate_n(std::back_inserter(times), k + 1, now<Clock>{}); + + std::vector<double> deltas; + deltas.reserve(k); + std::transform(std::next(times.begin()), times.end(), times.begin(), + std::back_inserter(deltas), + [](TimePoint<Clock> a, TimePoint<Clock> b) { return static_cast<double>((a - b).count()); }); + + return deltas; + } - inline std::size_t listTests( Config const& config ) { + const auto warmup_iterations = 10000; + const auto warmup_time = std::chrono::milliseconds(100); + const auto minimum_ticks = 1000; + const auto warmup_seed = 10000; + const auto clock_resolution_estimation_time = std::chrono::milliseconds(500); + const auto clock_cost_estimation_time_limit = std::chrono::seconds(1); + const auto clock_cost_estimation_tick_limit = 100000; + const auto clock_cost_estimation_time = std::chrono::milliseconds(10); + const auto clock_cost_estimation_iterations = 10000; + + template <typename Clock> + int warmup() { + return run_for_at_least<Clock>(std::chrono::duration_cast<ClockDuration<Clock>>(warmup_time), warmup_seed, &resolution<Clock>) + .iterations; + } + template <typename Clock> + EnvironmentEstimate<FloatDuration<Clock>> estimate_clock_resolution(int iterations) { + auto r = run_for_at_least<Clock>(std::chrono::duration_cast<ClockDuration<Clock>>(clock_resolution_estimation_time), iterations, &resolution<Clock>) + .result; + return { + FloatDuration<Clock>(mean(r.begin(), r.end())), + classify_outliers(r.begin(), r.end()), + }; + } + template <typename Clock> + EnvironmentEstimate<FloatDuration<Clock>> estimate_clock_cost(FloatDuration<Clock> resolution) { + auto time_limit = std::min(resolution * clock_cost_estimation_tick_limit, FloatDuration<Clock>(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<ClockDuration<Clock>>(clock_cost_estimation_time), iters, time_clock); + std::vector<double> times; + int nsamples = static_cast<int>(std::ceil(time_limit / r.elapsed)); + times.reserve(nsamples); + std::generate_n(std::back_inserter(times), nsamples, [time_clock, &r] { + return static_cast<double>((time_clock(r.iterations) / r.iterations).count()); + }); + return { + FloatDuration<Clock>(mean(times.begin(), times.end())), + classify_outliers(times.begin(), times.end()), + }; + } - TestSpec testSpec = config.testSpec(); - if( config.testSpec().hasFilters() ) - Catch::cout() << "Matching test cases:\n"; - else { - Catch::cout() << "All available test cases:\n"; - testSpec = TestSpecParser( ITagAliasRegistry::get() ).parse( "*" ).testSpec(); - } + template <typename Clock> + Environment<FloatDuration<Clock>> measure_environment() { + static Environment<FloatDuration<Clock>>* env = nullptr; + if (env) { + return *env; + } - std::size_t matchedTests = 0; - TextAttributes nameAttr, tagsAttr; - nameAttr.setInitialIndent( 2 ).setIndent( 4 ); - tagsAttr.setIndent( 6 ); + auto iters = Detail::warmup<Clock>(); + auto resolution = Detail::estimate_clock_resolution<Clock>(iters); + auto cost = Detail::estimate_clock_cost<Clock>(resolution.mean); - std::vector<TestCase> matchedTestCases = filterTests( getAllTestCasesSorted( config ), testSpec, config ); - for( std::vector<TestCase>::const_iterator it = matchedTestCases.begin(), itEnd = matchedTestCases.end(); - it != itEnd; - ++it ) { - matchedTests++; - TestCaseInfo const& testCaseInfo = it->getTestCaseInfo(); - Colour::Code colour = testCaseInfo.isHidden() - ? Colour::SecondaryText - : Colour::None; - Colour colourGuard( colour ); + env = new Environment<FloatDuration<Clock>>{ resolution, cost }; + return *env; + } + } // namespace Detail + } // namespace Benchmark +} // namespace Catch - Catch::cout() << Text( testCaseInfo.name, nameAttr ) << std::endl; - if( !testCaseInfo.tags.empty() ) - Catch::cout() << Text( testCaseInfo.tagsAsString, tagsAttr ) << std::endl; - } +// end catch_estimate_clock.hpp +// start catch_analyse.hpp - if( !config.testSpec().hasFilters() ) - Catch::cout() << pluralise( matchedTests, "test case" ) << '\n' << std::endl; - else - Catch::cout() << pluralise( matchedTests, "matching test case" ) << '\n' << std::endl; - return matchedTests; - } + // Run and analyse one benchmark - inline std::size_t listTestsNamesOnly( Config const& config ) { - TestSpec testSpec = config.testSpec(); - if( !config.testSpec().hasFilters() ) - testSpec = TestSpecParser( ITagAliasRegistry::get() ).parse( "*" ).testSpec(); - std::size_t matchedTests = 0; - std::vector<TestCase> matchedTestCases = filterTests( getAllTestCasesSorted( config ), testSpec, config ); - for( std::vector<TestCase>::const_iterator it = matchedTestCases.begin(), itEnd = matchedTestCases.end(); - it != itEnd; - ++it ) { - matchedTests++; - TestCaseInfo const& testCaseInfo = it->getTestCaseInfo(); - if( startsWith( testCaseInfo.name, '#' ) ) - Catch::cout() << '"' << testCaseInfo.name << '"' << std::endl; - else - Catch::cout() << testCaseInfo.name << std::endl; - } - return matchedTests; - } - struct TagInfo { - TagInfo() : count ( 0 ) {} - void add( std::string const& spelling ) { - ++count; - spellings.insert( spelling ); - } - std::string all() const { - std::string out; - for( std::set<std::string>::const_iterator it = spellings.begin(), itEnd = spellings.end(); - it != itEnd; - ++it ) - out += "[" + *it + "]"; - return out; - } - std::set<std::string> spellings; - std::size_t count; - }; +// start catch_sample_analysis.hpp - inline std::size_t listTags( Config const& config ) { - TestSpec testSpec = config.testSpec(); - if( config.testSpec().hasFilters() ) - Catch::cout() << "Tags for matching test cases:\n"; - else { - Catch::cout() << "All available tags:\n"; - testSpec = TestSpecParser( ITagAliasRegistry::get() ).parse( "*" ).testSpec(); - } +// Benchmark results - std::map<std::string, TagInfo> tagCounts; - std::vector<TestCase> matchedTestCases = filterTests( getAllTestCasesSorted( config ), testSpec, config ); - for( std::vector<TestCase>::const_iterator it = matchedTestCases.begin(), itEnd = matchedTestCases.end(); - it != itEnd; - ++it ) { - for( std::set<std::string>::const_iterator tagIt = it->getTestCaseInfo().tags.begin(), - tagItEnd = it->getTestCaseInfo().tags.end(); - tagIt != tagItEnd; - ++tagIt ) { - std::string tagName = *tagIt; - std::string lcaseTagName = toLower( tagName ); - std::map<std::string, TagInfo>::iterator countIt = tagCounts.find( lcaseTagName ); - if( countIt == tagCounts.end() ) - countIt = tagCounts.insert( std::make_pair( lcaseTagName, TagInfo() ) ).first; - countIt->second.add( tagName ); +#include <algorithm> +#include <vector> +#include <string> +#include <iterator> + +namespace Catch { + namespace Benchmark { + template <typename Duration> + struct SampleAnalysis { + std::vector<Duration> samples; + Estimate<Duration> mean; + Estimate<Duration> standard_deviation; + OutlierClassification outliers; + double outlier_variance; + + template <typename Duration2> + operator SampleAnalysis<Duration2>() const { + std::vector<Duration2> samples2; + samples2.reserve(samples.size()); + std::transform(samples.begin(), samples.end(), std::back_inserter(samples2), [](Duration d) { return Duration2(d); }); + return { + std::move(samples2), + mean, + standard_deviation, + outliers, + outlier_variance, + }; } - } + }; + } // namespace Benchmark +} // namespace Catch - for( std::map<std::string, TagInfo>::const_iterator countIt = tagCounts.begin(), - countItEnd = tagCounts.end(); - countIt != countItEnd; - ++countIt ) { - std::ostringstream oss; - oss << " " << std::setw(2) << countIt->second.count << " "; - Text wrapper( countIt->second.all(), TextAttributes() - .setInitialIndent( 0 ) - .setIndent( oss.str().size() ) - .setWidth( CATCH_CONFIG_CONSOLE_WIDTH-10 ) ); - Catch::cout() << oss.str() << wrapper << '\n'; - } - Catch::cout() << pluralise( tagCounts.size(), "tag" ) << '\n' << std::endl; - return tagCounts.size(); - } +// end catch_sample_analysis.hpp +#include <algorithm> +#include <iterator> +#include <vector> - inline std::size_t listReporters( Config const& /*config*/ ) { - Catch::cout() << "Available reporters:\n"; - IReporterRegistry::FactoryMap const& factories = getRegistryHub().getReporterRegistry().getFactories(); - IReporterRegistry::FactoryMap::const_iterator itBegin = factories.begin(), itEnd = factories.end(), it; - std::size_t maxNameLen = 0; - for(it = itBegin; it != itEnd; ++it ) - maxNameLen = (std::max)( maxNameLen, it->first.size() ); - - for(it = itBegin; it != itEnd; ++it ) { - Text wrapper( it->second->getDescription(), TextAttributes() - .setInitialIndent( 0 ) - .setIndent( 7+maxNameLen ) - .setWidth( CATCH_CONFIG_CONSOLE_WIDTH - maxNameLen-8 ) ); - Catch::cout() << " " - << it->first - << ':' - << std::string( maxNameLen - it->first.size() + 2, ' ' ) - << wrapper << '\n'; - } - Catch::cout() << std::endl; - return factories.size(); - } +namespace Catch { + namespace Benchmark { + namespace Detail { + template <typename Duration, typename Iterator> + SampleAnalysis<Duration> analyse(const IConfig &cfg, Environment<Duration>, Iterator first, Iterator last) { + if (!cfg.benchmarkNoAnalysis()) { + std::vector<double> samples; + samples.reserve(last - first); + std::transform(first, last, std::back_inserter(samples), [](Duration d) { return d.count(); }); + + auto analysis = Catch::Benchmark::Detail::analyse_samples(cfg.benchmarkConfidenceInterval(), cfg.benchmarkResamples(), samples.begin(), samples.end()); + auto outliers = Catch::Benchmark::Detail::classify_outliers(samples.begin(), samples.end()); + + auto wrap_estimate = [](Estimate<double> e) { + return Estimate<Duration> { + Duration(e.point), + Duration(e.lower_bound), + Duration(e.upper_bound), + e.confidence_interval, + }; + }; + std::vector<Duration> samples2; + samples2.reserve(samples.size()); + std::transform(samples.begin(), samples.end(), std::back_inserter(samples2), [](double d) { return Duration(d); }); + return { + std::move(samples2), + wrap_estimate(analysis.mean), + wrap_estimate(analysis.standard_deviation), + outliers, + analysis.outlier_variance, + }; + } else { + std::vector<Duration> samples; + samples.reserve(last - first); + + Duration mean = Duration(0); + int i = 0; + for (auto it = first; it < last; ++it, ++i) { + samples.push_back(Duration(*it)); + mean += Duration(*it); + } + mean /= i; + + return { + std::move(samples), + Estimate<Duration>{mean, mean, mean, 0.0}, + Estimate<Duration>{Duration(0), Duration(0), Duration(0), 0.0}, + OutlierClassification{}, + 0.0 + }; + } + } + } // namespace Detail + } // namespace Benchmark +} // namespace Catch - inline Option<std::size_t> list( Config const& config ) { - Option<std::size_t> listedCount; - if( config.listTests() ) - listedCount = listedCount.valueOr(0) + listTests( config ); - if( config.listTestNamesOnly() ) - listedCount = listedCount.valueOr(0) + listTestsNamesOnly( config ); - if( config.listTags() ) - listedCount = listedCount.valueOr(0) + listTags( config ); - if( config.listReporters() ) - listedCount = listedCount.valueOr(0) + listReporters( config ); - return listedCount; +// end catch_analyse.hpp +#include <algorithm> +#include <functional> +#include <string> +#include <vector> +#include <cmath> + +namespace Catch { + namespace Benchmark { + struct Benchmark { + Benchmark(std::string &&name) + : name(std::move(name)) {} + + template <class FUN> + Benchmark(std::string &&name, FUN &&func) + : fun(std::move(func)), name(std::move(name)) {} + + template <typename Clock> + ExecutionPlan<FloatDuration<Clock>> prepare(const IConfig &cfg, Environment<FloatDuration<Clock>> 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)>(Detail::warmup_time)); + auto&& test = Detail::run_for_at_least<Clock>(std::chrono::duration_cast<ClockDuration<Clock>>(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<FloatDuration<Clock>>(Detail::warmup_time), Detail::warmup_iterations }; + } + + template <typename Clock = default_clock> + void run() { + IConfigPtr 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 { + 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, env, samples.begin(), samples.end()); + BenchmarkStats<std::chrono::duration<double, std::nano>> stats{ info, analysis.samples, analysis.mean, analysis.standard_deviation, analysis.outliers, analysis.outlier_variance }; + getResultCapture().benchmarkEnded(stats); + + } CATCH_CATCH_ALL{ + if (translateActiveException() != Detail::benchmarkErrorMsg) // benchmark errors have been reported, otherwise rethrow. + std::rethrow_exception(std::current_exception()); + } + } + + // sets lambda to be used in fun *and* executes benchmark! + template <typename Fun, + typename std::enable_if<!Detail::is_related<Fun, Benchmark>::value, int>::type = 0> + Benchmark & operator=(Fun func) { + fun = Detail::BenchmarkFunction(func); + run(); + return *this; + } + + explicit operator bool() { + return true; + } + + private: + Detail::BenchmarkFunction fun; + std::string name; + }; } +} // namespace Catch -} // end namespace Catch +#define INTERNAL_CATCH_GET_1_ARG(arg1, arg2, ...) arg1 +#define INTERNAL_CATCH_GET_2_ARG(arg1, arg2, ...) arg2 -// #included from: internal/catch_run_context.hpp -#define TWOBLUECUBES_CATCH_RUNNER_IMPL_HPP_INCLUDED +#define INTERNAL_CATCH_BENCHMARK(BenchmarkName, name, benchmarkIndex)\ + if( Catch::Benchmark::Benchmark BenchmarkName{name} ) \ + BenchmarkName = [&](int benchmarkIndex) -// #included from: catch_test_case_tracker.hpp -#define TWOBLUECUBES_CATCH_TEST_CASE_TRACKER_HPP_INCLUDED +#define INTERNAL_CATCH_BENCHMARK_ADVANCED(BenchmarkName, name)\ + if( Catch::Benchmark::Benchmark BenchmarkName{name} ) \ + BenchmarkName = [&] + +// end catch_benchmark.hpp +#endif + +#endif // ! CATCH_CONFIG_IMPL_ONLY + +#ifdef CATCH_IMPL +// start catch_impl.hpp + +#ifdef __clang__ +#pragma clang diagnostic push +#pragma clang diagnostic ignored "-Wweak-vtables" +#endif + +// Keep these here for external reporters +// start catch_test_case_tracker.h -#include <map> #include <string> -#include <assert.h> #include <vector> -#include <iterator> -#include <stdexcept> +#include <memory> namespace Catch { namespace TestCaseTracking { @@ -5802,13 +7113,14 @@ namespace TestCaseTracking { std::string name; SourceLineInfo location; - NameAndLocation( std::string const& _name, SourceLineInfo const& _location ) - : name( _name ), - location( _location ) - {} + NameAndLocation( std::string const& _name, SourceLineInfo const& _location ); }; - struct ITracker : SharedImpl<> { + struct ITracker; + + using ITrackerPtr = std::shared_ptr<ITracker>; + + struct ITracker { virtual ~ITracker(); // static queries @@ -5827,16 +7139,16 @@ namespace TestCaseTracking { virtual void fail() = 0; virtual void markAsNeedingAnotherRun() = 0; - virtual void addChild( Ptr<ITracker> const& child ) = 0; - virtual ITracker* findChild( NameAndLocation const& nameAndLocation ) = 0; + virtual void addChild( ITrackerPtr const& child ) = 0; + virtual ITrackerPtr findChild( NameAndLocation const& nameAndLocation ) = 0; virtual void openChild() = 0; // Debug/ checking virtual bool isSectionTracker() const = 0; - virtual bool isIndexTracker() const = 0; + virtual bool isGeneratorTracker() const = 0; }; - class TrackerContext { + class TrackerContext { enum RunState { NotStarted, @@ -5844,47 +7156,21 @@ namespace TestCaseTracking { CompletedCycle }; - Ptr<ITracker> m_rootTracker; - ITracker* m_currentTracker; - RunState m_runState; + ITrackerPtr m_rootTracker; + ITracker* m_currentTracker = nullptr; + RunState m_runState = NotStarted; public: - static TrackerContext& instance() { - static TrackerContext s_instance; - return s_instance; - } - - TrackerContext() - : m_currentTracker( CATCH_NULL ), - m_runState( NotStarted ) - {} - ITracker& startRun(); + void endRun(); - void endRun() { - m_rootTracker.reset(); - m_currentTracker = CATCH_NULL; - m_runState = NotStarted; - } - - void startCycle() { - m_currentTracker = m_rootTracker.get(); - m_runState = Executing; - } - void completeCycle() { - m_runState = CompletedCycle; - } + void startCycle(); + void completeCycle(); - bool completedCycle() const { - return m_runState == CompletedCycle; - } - ITracker& currentTracker() { - return *m_currentTracker; - } - void setCurrentTracker( ITracker* tracker ) { - m_currentTracker = tracker; - } + bool completedCycle() const; + ITracker& currentTracker(); + void setCurrentTracker( ITracker* tracker ); }; class TrackerBase : public ITracker { @@ -5897,272 +7183,423 @@ namespace TestCaseTracking { CompletedSuccessfully, Failed }; - class TrackerHasName { - NameAndLocation m_nameAndLocation; - public: - TrackerHasName( NameAndLocation const& nameAndLocation ) : m_nameAndLocation( nameAndLocation ) {} - bool operator ()( Ptr<ITracker> const& tracker ) { - return - tracker->nameAndLocation().name == m_nameAndLocation.name && - tracker->nameAndLocation().location == m_nameAndLocation.location; - } - }; - typedef std::vector<Ptr<ITracker> > Children; + + using Children = std::vector<ITrackerPtr>; NameAndLocation m_nameAndLocation; TrackerContext& m_ctx; ITracker* m_parent; Children m_children; - CycleState m_runState; - public: - TrackerBase( NameAndLocation const& nameAndLocation, TrackerContext& ctx, ITracker* parent ) - : m_nameAndLocation( nameAndLocation ), - m_ctx( ctx ), - m_parent( parent ), - m_runState( NotStarted ) - {} - virtual ~TrackerBase(); + CycleState m_runState = NotStarted; - virtual NameAndLocation const& nameAndLocation() const CATCH_OVERRIDE { - return m_nameAndLocation; - } - virtual bool isComplete() const CATCH_OVERRIDE { - return m_runState == CompletedSuccessfully || m_runState == Failed; - } - virtual bool isSuccessfullyCompleted() const CATCH_OVERRIDE { - return m_runState == CompletedSuccessfully; - } - virtual bool isOpen() const CATCH_OVERRIDE { - return m_runState != NotStarted && !isComplete(); - } - virtual bool hasChildren() const CATCH_OVERRIDE { - return !m_children.empty(); - } + public: + TrackerBase( NameAndLocation const& nameAndLocation, TrackerContext& ctx, ITracker* parent ); - virtual void addChild( Ptr<ITracker> const& child ) CATCH_OVERRIDE { - m_children.push_back( child ); - } + NameAndLocation const& nameAndLocation() const override; + bool isComplete() const override; + bool isSuccessfullyCompleted() const override; + bool isOpen() const override; + bool hasChildren() const override; - virtual ITracker* findChild( NameAndLocation const& nameAndLocation ) CATCH_OVERRIDE { - Children::const_iterator it = std::find_if( m_children.begin(), m_children.end(), TrackerHasName( nameAndLocation ) ); - return( it != m_children.end() ) - ? it->get() - : CATCH_NULL; - } - virtual ITracker& parent() CATCH_OVERRIDE { - assert( m_parent ); // Should always be non-null except for root - return *m_parent; - } + void addChild( ITrackerPtr const& child ) override; - virtual void openChild() CATCH_OVERRIDE { - if( m_runState != ExecutingChildren ) { - m_runState = ExecutingChildren; - if( m_parent ) - m_parent->openChild(); - } - } + ITrackerPtr findChild( NameAndLocation const& nameAndLocation ) override; + ITracker& parent() override; - virtual bool isSectionTracker() const CATCH_OVERRIDE { return false; } - virtual bool isIndexTracker() const CATCH_OVERRIDE { return false; } + void openChild() override; - void open() { - m_runState = Executing; - moveToThis(); - if( m_parent ) - m_parent->openChild(); - } + bool isSectionTracker() const override; + bool isGeneratorTracker() const override; - virtual void close() CATCH_OVERRIDE { + void open(); - // Close any still open children (e.g. generators) - while( &m_ctx.currentTracker() != this ) - m_ctx.currentTracker().close(); + void close() override; + void fail() override; + void markAsNeedingAnotherRun() override; - switch( m_runState ) { - case NotStarted: - case CompletedSuccessfully: - case Failed: - throw std::logic_error( "Illogical state" ); - - case NeedsAnotherRun: - break;; - - case Executing: - m_runState = CompletedSuccessfully; - break; - case ExecutingChildren: - if( m_children.empty() || m_children.back()->isComplete() ) - m_runState = CompletedSuccessfully; - break; - - default: - throw std::logic_error( "Unexpected state" ); - } - moveToParent(); - m_ctx.completeCycle(); - } - virtual void fail() CATCH_OVERRIDE { - m_runState = Failed; - if( m_parent ) - m_parent->markAsNeedingAnotherRun(); - moveToParent(); - m_ctx.completeCycle(); - } - virtual void markAsNeedingAnotherRun() CATCH_OVERRIDE { - m_runState = NeedsAnotherRun; - } private: - void moveToParent() { - assert( m_parent ); - m_ctx.setCurrentTracker( m_parent ); - } - void moveToThis() { - m_ctx.setCurrentTracker( this ); - } + void moveToParent(); + void moveToThis(); }; class SectionTracker : public TrackerBase { std::vector<std::string> m_filters; public: - SectionTracker( NameAndLocation const& nameAndLocation, TrackerContext& ctx, ITracker* parent ) - : TrackerBase( nameAndLocation, ctx, parent ) - { - if( parent ) { - while( !parent->isSectionTracker() ) - parent = &parent->parent(); + SectionTracker( NameAndLocation const& nameAndLocation, TrackerContext& ctx, ITracker* parent ); - SectionTracker& parentSection = static_cast<SectionTracker&>( *parent ); - addNextFilters( parentSection.m_filters ); - } - } - virtual ~SectionTracker(); + bool isSectionTracker() const override; - virtual bool isSectionTracker() const CATCH_OVERRIDE { return true; } + bool isComplete() const override; - static SectionTracker& acquire( TrackerContext& ctx, NameAndLocation const& nameAndLocation ) { - SectionTracker* section = CATCH_NULL; + static SectionTracker& acquire( TrackerContext& ctx, NameAndLocation const& nameAndLocation ); - ITracker& currentTracker = ctx.currentTracker(); - if( ITracker* childTracker = currentTracker.findChild( nameAndLocation ) ) { - assert( childTracker ); - assert( childTracker->isSectionTracker() ); - section = static_cast<SectionTracker*>( childTracker ); - } - else { - section = new SectionTracker( nameAndLocation, ctx, ¤tTracker ); - currentTracker.addChild( section ); - } - if( !ctx.completedCycle() ) - section->tryOpen(); - return *section; - } + void tryOpen(); - void tryOpen() { - if( !isComplete() && (m_filters.empty() || m_filters[0].empty() || m_filters[0] == m_nameAndLocation.name ) ) - open(); - } + void addInitialFilters( std::vector<std::string> const& filters ); + void addNextFilters( std::vector<std::string> const& filters ); + }; - void addInitialFilters( std::vector<std::string> const& filters ) { - if( !filters.empty() ) { - m_filters.push_back(""); // Root - should never be consulted - m_filters.push_back(""); // Test Case - not a section filter - std::copy( filters.begin(), filters.end(), std::back_inserter( m_filters ) ); - } - } - void addNextFilters( std::vector<std::string> const& filters ) { - if( filters.size() > 1 ) - std::copy( filters.begin()+1, filters.end(), std::back_inserter( m_filters ) ); - } +} // namespace TestCaseTracking + +using TestCaseTracking::ITracker; +using TestCaseTracking::TrackerContext; +using TestCaseTracking::SectionTracker; + +} // namespace Catch + +// end catch_test_case_tracker.h + +// start catch_leak_detector.h + +namespace Catch { + + struct LeakDetector { + LeakDetector(); + ~LeakDetector(); }; - class IndexTracker : public TrackerBase { - int m_size; - int m_index; - public: - IndexTracker( NameAndLocation const& nameAndLocation, TrackerContext& ctx, ITracker* parent, int size ) - : TrackerBase( nameAndLocation, ctx, parent ), - m_size( size ), - m_index( -1 ) - {} - virtual ~IndexTracker(); +} +// end catch_leak_detector.h +// Cpp files will be included in the single-header file here +// start catch_stats.cpp + +// Statistical analysis tools - virtual bool isIndexTracker() const CATCH_OVERRIDE { return true; } +#if defined(CATCH_CONFIG_ENABLE_BENCHMARKING) - static IndexTracker& acquire( TrackerContext& ctx, NameAndLocation const& nameAndLocation, int size ) { - IndexTracker* tracker = CATCH_NULL; +#include <cassert> +#include <random> - ITracker& currentTracker = ctx.currentTracker(); - if( ITracker* childTracker = currentTracker.findChild( nameAndLocation ) ) { - assert( childTracker ); - assert( childTracker->isIndexTracker() ); - tracker = static_cast<IndexTracker*>( childTracker ); +#if defined(CATCH_CONFIG_USE_ASYNC) +#include <future> +#endif + +namespace { + double erf_inv(double x) { + // Code accompanying the article "Approximating the erfinv function" in GPU Computing Gems, Volume 2 + double w, p; + + w = -log((1.0 - x) * (1.0 + x)); + + if (w < 6.250000) { + w = w - 3.125000; + p = -3.6444120640178196996e-21; + p = -1.685059138182016589e-19 + p * w; + p = 1.2858480715256400167e-18 + p * w; + p = 1.115787767802518096e-17 + p * w; + p = -1.333171662854620906e-16 + p * w; + p = 2.0972767875968561637e-17 + p * w; + p = 6.6376381343583238325e-15 + p * w; + p = -4.0545662729752068639e-14 + p * w; + p = -8.1519341976054721522e-14 + p * w; + p = 2.6335093153082322977e-12 + p * w; + p = -1.2975133253453532498e-11 + p * w; + p = -5.4154120542946279317e-11 + p * w; + p = 1.051212273321532285e-09 + p * w; + p = -4.1126339803469836976e-09 + p * w; + p = -2.9070369957882005086e-08 + p * w; + p = 4.2347877827932403518e-07 + p * w; + p = -1.3654692000834678645e-06 + p * w; + p = -1.3882523362786468719e-05 + p * w; + p = 0.0001867342080340571352 + p * w; + p = -0.00074070253416626697512 + p * w; + p = -0.0060336708714301490533 + p * w; + p = 0.24015818242558961693 + p * w; + p = 1.6536545626831027356 + p * w; + } else if (w < 16.000000) { + w = sqrt(w) - 3.250000; + p = 2.2137376921775787049e-09; + p = 9.0756561938885390979e-08 + p * w; + p = -2.7517406297064545428e-07 + p * w; + p = 1.8239629214389227755e-08 + p * w; + p = 1.5027403968909827627e-06 + p * w; + p = -4.013867526981545969e-06 + p * w; + p = 2.9234449089955446044e-06 + p * w; + p = 1.2475304481671778723e-05 + p * w; + p = -4.7318229009055733981e-05 + p * w; + p = 6.8284851459573175448e-05 + p * w; + p = 2.4031110387097893999e-05 + p * w; + p = -0.0003550375203628474796 + p * w; + p = 0.00095328937973738049703 + p * w; + p = -0.0016882755560235047313 + p * w; + p = 0.0024914420961078508066 + p * w; + p = -0.0037512085075692412107 + p * w; + p = 0.005370914553590063617 + p * w; + p = 1.0052589676941592334 + p * w; + p = 3.0838856104922207635 + p * w; + } else { + w = sqrt(w) - 5.000000; + p = -2.7109920616438573243e-11; + p = -2.5556418169965252055e-10 + p * w; + p = 1.5076572693500548083e-09 + p * w; + p = -3.7894654401267369937e-09 + p * w; + p = 7.6157012080783393804e-09 + p * w; + p = -1.4960026627149240478e-08 + p * w; + p = 2.9147953450901080826e-08 + p * w; + p = -6.7711997758452339498e-08 + p * w; + p = 2.2900482228026654717e-07 + p * w; + p = -9.9298272942317002539e-07 + p * w; + p = 4.5260625972231537039e-06 + p * w; + p = -1.9681778105531670567e-05 + p * w; + p = 7.5995277030017761139e-05 + p * w; + p = -0.00021503011930044477347 + p * w; + p = -0.00013871931833623122026 + p * w; + p = 1.0103004648645343977 + p * w; + p = 4.8499064014085844221 + p * w; + } + return p * x; + } + + double standard_deviation(std::vector<double>::iterator first, std::vector<double>::iterator last) { + auto m = Catch::Benchmark::Detail::mean(first, last); + double variance = std::accumulate(first, last, 0., [m](double a, double b) { + double diff = b - m; + return a + diff * diff; + }) / (last - first); + return std::sqrt(variance); + } + +} + +namespace Catch { + namespace Benchmark { + namespace Detail { + + double weighted_average_quantile(int k, int q, std::vector<double>::iterator first, std::vector<double>::iterator last) { + auto count = last - first; + double idx = (count - 1) * k / static_cast<double>(q); + int j = static_cast<int>(idx); + double g = idx - j; + std::nth_element(first, first + j, last); + auto xj = first[j]; + if (g == 0) return xj; + + auto xj1 = *std::min_element(first + (j + 1), last); + return xj + g * (xj1 - xj); } - else { - tracker = new IndexTracker( nameAndLocation, ctx, ¤tTracker, size ); - currentTracker.addChild( tracker ); + + double erfc_inv(double x) { + return erf_inv(1.0 - x); } - if( !ctx.completedCycle() && !tracker->isComplete() ) { - if( tracker->m_runState != ExecutingChildren && tracker->m_runState != NeedsAnotherRun ) - tracker->moveNext(); - tracker->open(); + double normal_quantile(double p) { + static const double ROOT_TWO = std::sqrt(2.0); + + double result = 0.0; + assert(p >= 0 && p <= 1); + if (p < 0 || p > 1) { + return result; + } + + result = -erfc_inv(2.0 * p); + // result *= normal distribution standard deviation (1.0) * sqrt(2) + result *= /*sd * */ ROOT_TWO; + // result += normal disttribution mean (0) + return result; } - return *tracker; - } + double outlier_variance(Estimate<double> mean, Estimate<double> stddev, int n) { + double sb = stddev.point; + double mn = mean.point / n; + double mg_min = mn / 2.; + double sg = std::min(mg_min / 4., sb / std::sqrt(n)); + double sg2 = sg * sg; + double sb2 = sb * sb; + + auto c_max = [n, mn, sb2, sg2](double x) -> double { + double k = mn - x; + double d = k * k; + double nd = n * d; + double k0 = -n * nd; + double k1 = sb2 - n * sg2 + nd; + double det = k1 * k1 - 4 * sg2 * k0; + return (int)(-2. * k0 / (k1 + std::sqrt(det))); + }; + + auto var_out = [n, sb2, sg2](double c) { + double nc = n - c; + return (nc / n) * (sb2 - nc * sg2); + }; + + return std::min(var_out(1), var_out(std::min(c_max(0.), c_max(mg_min)))) / sb2; + } - int index() const { return m_index; } + bootstrap_analysis analyse_samples(double confidence_level, int n_resamples, std::vector<double>::iterator first, std::vector<double>::iterator last) { + CATCH_INTERNAL_SUPPRESS_GLOBALS_WARNINGS + static std::random_device entropy; + CATCH_INTERNAL_UNSUPPRESS_GLOBALS_WARNINGS - void moveNext() { - m_index++; - m_children.clear(); - } + auto n = static_cast<int>(last - first); // seriously, one can't use integral types without hell in C++ - virtual void close() CATCH_OVERRIDE { - TrackerBase::close(); - if( m_runState == CompletedSuccessfully && m_index < m_size-1 ) - m_runState = Executing; - } - }; + auto mean = &Detail::mean<std::vector<double>::iterator>; + auto stddev = &standard_deviation; - inline ITracker& TrackerContext::startRun() { - m_rootTracker = new SectionTracker( NameAndLocation( "{root}", CATCH_INTERNAL_LINEINFO ), *this, CATCH_NULL ); - m_currentTracker = CATCH_NULL; - m_runState = Executing; - return *m_rootTracker; - } +#if defined(CATCH_CONFIG_USE_ASYNC) + auto Estimate = [=](double(*f)(std::vector<double>::iterator, std::vector<double>::iterator)) { + auto seed = entropy(); + return std::async(std::launch::async, [=] { + std::mt19937 rng(seed); + auto resampled = resample(rng, n_resamples, first, last, f); + return bootstrap(confidence_level, first, last, resampled, f); + }); + }; -} // namespace TestCaseTracking + auto mean_future = Estimate(mean); + auto stddev_future = Estimate(stddev); -using TestCaseTracking::ITracker; -using TestCaseTracking::TrackerContext; -using TestCaseTracking::SectionTracker; -using TestCaseTracking::IndexTracker; + auto mean_estimate = mean_future.get(); + auto stddev_estimate = stddev_future.get(); +#else + auto Estimate = [=](double(*f)(std::vector<double>::iterator, std::vector<double>::iterator)) { + auto seed = entropy(); + std::mt19937 rng(seed); + auto resampled = resample(rng, n_resamples, first, last, f); + return bootstrap(confidence_level, first, last, resampled, f); + }; + + auto mean_estimate = Estimate(mean); + auto stddev_estimate = Estimate(stddev); +#endif // CATCH_USE_ASYNC + + double outlier_variance = Detail::outlier_variance(mean_estimate, stddev_estimate, n); + return { mean_estimate, stddev_estimate, outlier_variance }; + } + } // namespace Detail + } // namespace Benchmark } // namespace Catch -// #included from: catch_fatal_condition.hpp -#define TWOBLUECUBES_CATCH_FATAL_CONDITION_H_INCLUDED +#endif // CATCH_CONFIG_ENABLE_BENCHMARKING +// end catch_stats.cpp +// start catch_approx.cpp + +#include <cmath> +#include <limits> + +namespace { + +// Performs equivalent check of std::fabs(lhs - rhs) <= margin +// But without the subtraction to allow for INFINITY in comparison +bool marginComparison(double lhs, double rhs, double margin) { + return (lhs + margin >= rhs) && (rhs + margin >= lhs); +} + +} namespace Catch { +namespace Detail { - // Report the error condition - inline void reportFatal( std::string const& message ) { - IContext& context = Catch::getCurrentContext(); - IResultCapture* resultCapture = context.getResultCapture(); - resultCapture->handleFatalErrorCondition( message ); + Approx::Approx ( double value ) + : m_epsilon( std::numeric_limits<float>::epsilon()*100 ), + m_margin( 0.0 ), + m_scale( 0.0 ), + m_value( value ) + {} + + Approx Approx::custom() { + return Approx( 0 ); } -} // namespace Catch + Approx Approx::operator-() const { + auto temp(*this); + temp.m_value = -temp.m_value; + return temp; + } + + std::string Approx::toString() const { + ReusableStringStream rss; + rss << "Approx( " << ::Catch::Detail::stringify( m_value ) << " )"; + return rss.str(); + } + + bool Approx::equalityComparisonImpl(const double other) const { + // First try with fixed margin, then compute margin based on epsilon, scale and Approx's value + // Thanks to Richard Harris for his help refining the scaled margin value + return marginComparison(m_value, other, m_margin) || marginComparison(m_value, other, m_epsilon * (m_scale + std::fabs(m_value))); + } + + void Approx::setMargin(double newMargin) { + CATCH_ENFORCE(newMargin >= 0, + "Invalid Approx::margin: " << newMargin << '.' + << " Approx::Margin has to be non-negative."); + m_margin = newMargin; + } + + void Approx::setEpsilon(double newEpsilon) { + CATCH_ENFORCE(newEpsilon >= 0 && newEpsilon <= 1.0, + "Invalid Approx::epsilon: " << newEpsilon << '.' + << " Approx::epsilon has to be in [0, 1]"); + m_epsilon = newEpsilon; + } + +} // end namespace Detail + +namespace literals { + Detail::Approx operator "" _a(long double val) { + return Detail::Approx(val); + } + Detail::Approx operator "" _a(unsigned long long val) { + return Detail::Approx(val); + } +} // end namespace literals + +std::string StringMaker<Catch::Detail::Approx>::convert(Catch::Detail::Approx const& value) { + return value.toString(); +} + +} // end namespace Catch +// end catch_approx.cpp +// start catch_assertionhandler.cpp + +// start catch_debugger.h + +namespace Catch { + bool isDebuggerActive(); +} + +#ifdef CATCH_PLATFORM_MAC -#if defined ( CATCH_PLATFORM_WINDOWS ) ///////////////////////////////////////// -// #included from: catch_windows_h_proxy.h + #define CATCH_TRAP() __asm__("int $3\n" : : ) /* NOLINT */ -#define TWOBLUECUBES_CATCH_WINDOWS_H_PROXY_H_INCLUDED +#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 + +#ifdef CATCH_TRAP + #define CATCH_BREAK_INTO_DEBUGGER() []{ if( Catch::isDebuggerActive() ) { CATCH_TRAP(); } }() +#else + #define CATCH_BREAK_INTO_DEBUGGER() []{}() +#endif + +// end catch_debugger.h +// start catch_run_context.h -#ifdef CATCH_DEFINES_NOMINMAX +// start catch_fatal_condition.h + +// start catch_windows_h_proxy.h + + +#if defined(CATCH_PLATFORM_WINDOWS) + +#if !defined(NOMINMAX) && !defined(CATCH_CONFIG_NO_NOMINMAX) +# define CATCH_DEFINED_NOMINMAX # define NOMINMAX #endif -#ifdef CATCH_DEFINES_WIN32_LEAN_AND_MEAN +#if !defined(WIN32_LEAN_AND_MEAN) && !defined(CATCH_CONFIG_NO_WIN32_LEAN_AND_MEAN) +# define CATCH_DEFINED_WIN32_LEAN_AND_MEAN # define WIN32_LEAN_AND_MEAN #endif @@ -6172,1325 +7609,2038 @@ namespace Catch { #include <windows.h> #endif -#ifdef CATCH_DEFINES_NOMINMAX +#ifdef CATCH_DEFINED_NOMINMAX # undef NOMINMAX #endif -#ifdef CATCH_DEFINES_WIN32_LEAN_AND_MEAN +#ifdef CATCH_DEFINED_WIN32_LEAN_AND_MEAN # undef WIN32_LEAN_AND_MEAN #endif +#endif // defined(CATCH_PLATFORM_WINDOWS) -# if !defined ( CATCH_CONFIG_WINDOWS_SEH ) - -namespace Catch { - struct FatalConditionHandler { - void reset() {} - }; -} - -# else // CATCH_CONFIG_WINDOWS_SEH is defined +// end catch_windows_h_proxy.h +#if defined( CATCH_CONFIG_WINDOWS_SEH ) namespace Catch { - struct SignalDefs { DWORD id; const char* name; }; - extern SignalDefs signalDefs[]; - // There is no 1-1 mapping between signals and windows exceptions. - // Windows can easily distinguish between SO and SigSegV, - // but SigInt, SigTerm, etc are handled differently. - SignalDefs signalDefs[] = { - { EXCEPTION_ILLEGAL_INSTRUCTION, "SIGILL - Illegal instruction signal" }, - { EXCEPTION_STACK_OVERFLOW, "SIGSEGV - Stack overflow" }, - { EXCEPTION_ACCESS_VIOLATION, "SIGSEGV - Segmentation violation signal" }, - { EXCEPTION_INT_DIVIDE_BY_ZERO, "Divide by zero error" }, - }; - struct FatalConditionHandler { - static LONG CALLBACK handleVectoredException(PEXCEPTION_POINTERS ExceptionInfo) { - for (int i = 0; i < sizeof(signalDefs) / sizeof(SignalDefs); ++i) { - if (ExceptionInfo->ExceptionRecord->ExceptionCode == signalDefs[i].id) { - reset(); - reportFatal(signalDefs[i].name); - } - } - // If its not an exception we care about, pass it along. - // This stops us from eating debugger breaks etc. - return EXCEPTION_CONTINUE_SEARCH; - } - - FatalConditionHandler() { - isSet = true; - // 32k seems enough for Catch to handle stack overflow, - // but the value was found experimentally, so there is no strong guarantee - guaranteeSize = 32 * 1024; - exceptionHandlerHandle = CATCH_NULL; - // Register as first handler in current chain - exceptionHandlerHandle = AddVectoredExceptionHandler(1, handleVectoredException); - // Pass in guarantee size to be filled - SetThreadStackGuarantee(&guaranteeSize); - } - - static void reset() { - if (isSet) { - // Unregister handler and restore the old guarantee - RemoveVectoredExceptionHandler(exceptionHandlerHandle); - SetThreadStackGuarantee(&guaranteeSize); - exceptionHandlerHandle = CATCH_NULL; - isSet = false; - } - } + static LONG CALLBACK handleVectoredException(PEXCEPTION_POINTERS ExceptionInfo); + FatalConditionHandler(); + static void reset(); + ~FatalConditionHandler(); - ~FatalConditionHandler() { - reset(); - } private: static bool isSet; static ULONG guaranteeSize; static PVOID exceptionHandlerHandle; }; - bool FatalConditionHandler::isSet = false; - ULONG FatalConditionHandler::guaranteeSize = 0; - PVOID FatalConditionHandler::exceptionHandlerHandle = CATCH_NULL; - } // namespace Catch -# endif // CATCH_CONFIG_WINDOWS_SEH - -#else // Not Windows - assumed to be POSIX compatible ////////////////////////// +#elif defined ( CATCH_CONFIG_POSIX_SIGNALS ) #include <signal.h> namespace Catch { - struct SignalDefs { - int id; - const char* name; - }; - extern SignalDefs signalDefs[]; - SignalDefs signalDefs[] = { - { SIGINT, "SIGINT - Terminal interrupt signal" }, - { SIGILL, "SIGILL - Illegal instruction signal" }, - { SIGFPE, "SIGFPE - Floating point error signal" }, - { SIGSEGV, "SIGSEGV - Segmentation violation signal" }, - { SIGTERM, "SIGTERM - Termination request signal" }, - { SIGABRT, "SIGABRT - Abort (abnormal termination) signal" } - }; - struct FatalConditionHandler { static bool isSet; - static struct sigaction oldSigActions [sizeof(signalDefs)/sizeof(SignalDefs)]; + static struct sigaction oldSigActions[]; static stack_t oldSigStack; - static char altStackMem[SIGSTKSZ]; - - static void handleSignal( int sig ) { - std::string name = "<unknown signal>"; - for (std::size_t i = 0; i < sizeof(signalDefs) / sizeof(SignalDefs); ++i) { - SignalDefs &def = signalDefs[i]; - if (sig == def.id) { - name = def.name; - break; - } - } - reset(); - reportFatal(name); - raise( sig ); - } - - FatalConditionHandler() { - isSet = true; - stack_t sigStack; - sigStack.ss_sp = altStackMem; - sigStack.ss_size = SIGSTKSZ; - sigStack.ss_flags = 0; - sigaltstack(&sigStack, &oldSigStack); - struct sigaction sa = { 0 }; + static char altStackMem[]; - sa.sa_handler = handleSignal; - sa.sa_flags = SA_ONSTACK; - for (std::size_t i = 0; i < sizeof(signalDefs)/sizeof(SignalDefs); ++i) { - sigaction(signalDefs[i].id, &sa, &oldSigActions[i]); - } - } + static void handleSignal( int sig ); - ~FatalConditionHandler() { - reset(); - } - static void reset() { - if( isSet ) { - // Set signals back to previous values -- hopefully nobody overwrote them in the meantime - for( std::size_t i = 0; i < sizeof(signalDefs)/sizeof(SignalDefs); ++i ) { - sigaction(signalDefs[i].id, &oldSigActions[i], CATCH_NULL); - } - // Return the old stack - sigaltstack(&oldSigStack, CATCH_NULL); - isSet = false; - } - } + FatalConditionHandler(); + ~FatalConditionHandler(); + static void reset(); }; - bool FatalConditionHandler::isSet = false; - struct sigaction FatalConditionHandler::oldSigActions[sizeof(signalDefs)/sizeof(SignalDefs)] = {}; - stack_t FatalConditionHandler::oldSigStack = {}; - char FatalConditionHandler::altStackMem[SIGSTKSZ] = {}; - } // namespace Catch -#endif // not Windows +#else -#include <set> +namespace Catch { + struct FatalConditionHandler { + void reset(); + }; +} + +#endif + +// end catch_fatal_condition.h #include <string> namespace Catch { - class StreamRedirect { + struct IMutableContext; + + /////////////////////////////////////////////////////////////////////////// + + class RunContext : public IResultCapture, public IRunner { public: - StreamRedirect( std::ostream& stream, std::string& targetString ) - : m_stream( stream ), - m_prevBuf( stream.rdbuf() ), - m_targetString( targetString ) - { - stream.rdbuf( m_oss.rdbuf() ); - } + RunContext( RunContext const& ) = delete; + RunContext& operator =( RunContext const& ) = delete; - ~StreamRedirect() { - m_targetString += m_oss.str(); - m_stream.rdbuf( m_prevBuf ); - } + explicit RunContext( IConfigPtr const& _config, IStreamingReporterPtr&& reporter ); - private: - std::ostream& m_stream; - std::streambuf* m_prevBuf; - std::ostringstream m_oss; - std::string& m_targetString; - }; + ~RunContext() override; - /////////////////////////////////////////////////////////////////////////// + void testGroupStarting( std::string const& testSpec, std::size_t groupIndex, std::size_t groupsCount ); + void testGroupEnded( std::string const& testSpec, Totals const& totals, std::size_t groupIndex, std::size_t groupsCount ); - class RunContext : public IResultCapture, public IRunner { + Totals runTest(TestCase const& testCase); - RunContext( RunContext const& ); - void operator =( RunContext const& ); + IConfigPtr config() const; + IStreamingReporter& reporter() const; - public: + public: // IResultCapture - explicit RunContext( Ptr<IConfig const> const& _config, Ptr<IStreamingReporter> const& reporter ) - : m_runInfo( _config->name() ), - m_context( getCurrentMutableContext() ), - m_activeTestCase( CATCH_NULL ), - m_config( _config ), - m_reporter( reporter ) - { - m_context.setRunner( this ); - m_context.setConfig( m_config ); - m_context.setResultCapture( this ); - m_reporter->testRunStarting( m_runInfo ); - } + // Assertion handlers + void handleExpr + ( AssertionInfo const& info, + ITransientExpression const& expr, + AssertionReaction& reaction ) override; + void handleMessage + ( AssertionInfo const& info, + ResultWas::OfType resultType, + StringRef const& message, + AssertionReaction& reaction ) override; + void handleUnexpectedExceptionNotThrown + ( AssertionInfo const& info, + AssertionReaction& reaction ) override; + void handleUnexpectedInflightException + ( AssertionInfo const& info, + std::string const& message, + AssertionReaction& reaction ) override; + void handleIncomplete + ( AssertionInfo const& info ) override; + void handleNonExpr + ( AssertionInfo const &info, + ResultWas::OfType resultType, + AssertionReaction &reaction ) override; - virtual ~RunContext() { - m_reporter->testRunEnded( TestRunStats( m_runInfo, m_totals, aborting() ) ); - } + bool sectionStarted( SectionInfo const& sectionInfo, Counts& assertions ) override; - void testGroupStarting( std::string const& testSpec, std::size_t groupIndex, std::size_t groupsCount ) { - m_reporter->testGroupStarting( GroupInfo( testSpec, groupIndex, groupsCount ) ); - } - void testGroupEnded( std::string const& testSpec, Totals const& totals, std::size_t groupIndex, std::size_t groupsCount ) { - m_reporter->testGroupEnded( TestGroupStats( GroupInfo( testSpec, groupIndex, groupsCount ), totals, aborting() ) ); - } + void sectionEnded( SectionEndInfo const& endInfo ) override; + void sectionEndedEarly( SectionEndInfo const& endInfo ) override; - Totals runTest( TestCase const& testCase ) { - Totals prevTotals = m_totals; + auto acquireGeneratorTracker( SourceLineInfo const& lineInfo ) -> IGeneratorTracker& override; - std::string redirectedCout; - std::string redirectedCerr; +#if defined(CATCH_CONFIG_ENABLE_BENCHMARKING) + void benchmarkPreparing( std::string const& name ) override; + void benchmarkStarting( BenchmarkInfo const& info ) override; + void benchmarkEnded( BenchmarkStats<> const& stats ) override; + void benchmarkFailed( std::string const& error ) override; +#endif // CATCH_CONFIG_ENABLE_BENCHMARKING - TestCaseInfo testInfo = testCase.getTestCaseInfo(); + void pushScopedMessage( MessageInfo const& message ) override; + void popScopedMessage( MessageInfo const& message ) override; - m_reporter->testCaseStarting( testInfo ); + void emplaceUnscopedMessage( MessageBuilder const& builder ) override; - m_activeTestCase = &testCase; + std::string getCurrentTestName() const override; - do { - ITracker& rootTracker = m_trackerContext.startRun(); - assert( rootTracker.isSectionTracker() ); - static_cast<SectionTracker&>( rootTracker ).addInitialFilters( m_config->getSectionsToRun() ); - do { - m_trackerContext.startCycle(); - m_testCaseTracker = &SectionTracker::acquire( m_trackerContext, TestCaseTracking::NameAndLocation( testInfo.name, testInfo.lineInfo ) ); - runCurrentTest( redirectedCout, redirectedCerr ); - } - while( !m_testCaseTracker->isSuccessfullyCompleted() && !aborting() ); - } - // !TBD: deprecated - this will be replaced by indexed trackers - while( getCurrentContext().advanceGeneratorsForCurrentTest() && !aborting() ); + const AssertionResult* getLastResult() const override; - Totals deltaTotals = m_totals.delta( prevTotals ); - if( testInfo.expectedToFail() && deltaTotals.testCases.passed > 0 ) { - deltaTotals.assertions.failed++; - deltaTotals.testCases.passed--; - deltaTotals.testCases.failed++; - } - m_totals.testCases += deltaTotals.testCases; - m_reporter->testCaseEnded( TestCaseStats( testInfo, - deltaTotals, - redirectedCout, - redirectedCerr, - aborting() ) ); + void exceptionEarlyReported() override; - m_activeTestCase = CATCH_NULL; - m_testCaseTracker = CATCH_NULL; + void handleFatalErrorCondition( StringRef message ) override; - return deltaTotals; - } + bool lastAssertionPassed() override; - Ptr<IConfig const> config() const { - return m_config; - } + void assertionPassed() override; - private: // IResultCapture + public: + // !TBD We need to do this another way! + bool aborting() const final; - virtual void assertionEnded( AssertionResult const& result ) { - if( result.getResultType() == ResultWas::Ok ) { - m_totals.assertions.passed++; - } - else if( !result.isOk() ) { - m_totals.assertions.failed++; - } + private: - if( m_reporter->assertionEnded( AssertionStats( result, m_messages, m_totals ) ) ) - m_messages.clear(); + void runCurrentTest( std::string& redirectedCout, std::string& redirectedCerr ); + void invokeActiveTestCase(); - // Reset working state - m_lastAssertionInfo = AssertionInfo( std::string(), m_lastAssertionInfo.lineInfo, "{Unknown expression after the reported line}" , m_lastAssertionInfo.resultDisposition ); - m_lastResult = result; - } + void resetAssertionInfo(); + bool testForMissingAssertions( Counts& assertions ); - virtual bool sectionStarted ( - SectionInfo const& sectionInfo, - Counts& assertions - ) - { - ITracker& sectionTracker = SectionTracker::acquire( m_trackerContext, TestCaseTracking::NameAndLocation( sectionInfo.name, sectionInfo.lineInfo ) ); - if( !sectionTracker.isOpen() ) - return false; - m_activeSections.push_back( §ionTracker ); + void assertionEnded( AssertionResult const& result ); + void reportExpr + ( AssertionInfo const &info, + ResultWas::OfType resultType, + ITransientExpression const *expr, + bool negated ); - m_lastAssertionInfo.lineInfo = sectionInfo.lineInfo; + void populateReaction( AssertionReaction& reaction ); - m_reporter->sectionStarting( sectionInfo ); + private: - assertions = m_totals.assertions; + void handleUnfinishedSections(); - return true; - } - bool testForMissingAssertions( Counts& assertions ) { - if( assertions.total() != 0 ) - return false; - if( !m_config->warnAboutMissingAssertions() ) - return false; - if( m_trackerContext.currentTracker().hasChildren() ) - return false; - m_totals.assertions.failed++; - assertions.failed++; - return true; - } + TestRunInfo m_runInfo; + IMutableContext& m_context; + TestCase const* m_activeTestCase = nullptr; + ITracker* m_testCaseTracker = nullptr; + Option<AssertionResult> m_lastResult; + + IConfigPtr m_config; + Totals m_totals; + IStreamingReporterPtr 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; + bool m_lastAssertionPassed = false; + bool m_shouldReportUnexpected = true; + bool m_includeSuccessfulResults; + }; - virtual void sectionEnded( SectionEndInfo const& endInfo ) { - Counts assertions = m_totals.assertions - endInfo.prevAssertions; - bool missingAssertions = testForMissingAssertions( assertions ); +} // end namespace Catch - if( !m_activeSections.empty() ) { - m_activeSections.back()->close(); - m_activeSections.pop_back(); - } +// end catch_run_context.h +namespace Catch { - m_reporter->sectionEnded( SectionStats( endInfo.sectionInfo, assertions, endInfo.durationInSeconds, missingAssertions ) ); - m_messages.clear(); + namespace { + auto operator <<( std::ostream& os, ITransientExpression const& expr ) -> std::ostream& { + expr.streamReconstructedExpression( os ); + return os; } + } - virtual void sectionEndedEarly( SectionEndInfo const& endInfo ) { - if( m_unfinishedSections.empty() ) - m_activeSections.back()->fail(); - else - m_activeSections.back()->close(); - m_activeSections.pop_back(); + LazyExpression::LazyExpression( bool isNegated ) + : m_isNegated( isNegated ) + {} - m_unfinishedSections.push_back( endInfo ); - } + LazyExpression::LazyExpression( LazyExpression const& other ) : m_isNegated( other.m_isNegated ) {} - virtual void pushScopedMessage( MessageInfo const& message ) { - m_messages.push_back( message ); - } + LazyExpression::operator bool() const { + return m_transientExpression != nullptr; + } - virtual void popScopedMessage( MessageInfo const& message ) { - m_messages.erase( std::remove( m_messages.begin(), m_messages.end(), message ), m_messages.end() ); - } + auto operator << ( std::ostream& os, LazyExpression const& lazyExpr ) -> std::ostream& { + if( lazyExpr.m_isNegated ) + os << "!"; - virtual std::string getCurrentTestName() const { - return m_activeTestCase - ? m_activeTestCase->getTestCaseInfo().name - : std::string(); + if( lazyExpr ) { + if( lazyExpr.m_isNegated && lazyExpr.m_transientExpression->isBinaryExpression() ) + os << "(" << *lazyExpr.m_transientExpression << ")"; + else + os << *lazyExpr.m_transientExpression; } - - virtual const AssertionResult* getLastResult() const { - return &m_lastResult; + else { + os << "{** error - unchecked empty expression requested **}"; } + return os; + } - virtual void handleFatalErrorCondition( std::string const& message ) { - ResultBuilder resultBuilder = makeUnexpectedResultBuilder(); - resultBuilder.setResultType( ResultWas::FatalErrorCondition ); - resultBuilder << message; - resultBuilder.captureExpression(); + AssertionHandler::AssertionHandler + ( StringRef const& macroName, + SourceLineInfo const& lineInfo, + StringRef capturedExpression, + ResultDisposition::Flags resultDisposition ) + : m_assertionInfo{ macroName, lineInfo, capturedExpression, resultDisposition }, + m_resultCapture( getResultCapture() ) + {} - handleUnfinishedSections(); + void AssertionHandler::handleExpr( ITransientExpression const& expr ) { + m_resultCapture.handleExpr( m_assertionInfo, expr, m_reaction ); + } + void AssertionHandler::handleMessage(ResultWas::OfType resultType, StringRef const& message) { + m_resultCapture.handleMessage( m_assertionInfo, resultType, message, m_reaction ); + } - // Recreate section for test case (as we will lose the one that was in scope) - TestCaseInfo const& testCaseInfo = m_activeTestCase->getTestCaseInfo(); - SectionInfo testCaseSection( testCaseInfo.lineInfo, testCaseInfo.name, testCaseInfo.description ); + auto AssertionHandler::allowThrows() const -> bool { + return getCurrentContext().getConfig()->allowThrows(); + } - Counts assertions; - assertions.failed = 1; - SectionStats testCaseSectionStats( testCaseSection, assertions, 0, false ); - m_reporter->sectionEnded( testCaseSectionStats ); + void AssertionHandler::complete() { + setCompleted(); + if( m_reaction.shouldDebugBreak ) { - TestCaseInfo testInfo = m_activeTestCase->getTestCaseInfo(); + // If you find your debugger stopping you here then go one level up on the + // call-stack for the code that caused it (typically a failed assertion) - Totals deltaTotals; - deltaTotals.testCases.failed = 1; - m_reporter->testCaseEnded( TestCaseStats( testInfo, - deltaTotals, - std::string(), - std::string(), - false ) ); - m_totals.testCases.failed++; - testGroupEnded( std::string(), m_totals, 1, 1 ); - m_reporter->testRunEnded( TestRunStats( m_runInfo, m_totals, false ) ); + // (To go back to the test and change execution, jump over the throw, next) + CATCH_BREAK_INTO_DEBUGGER(); } - - public: - // !TBD We need to do this another way! - bool aborting() const { - return m_totals.assertions.failed == static_cast<std::size_t>( m_config->abortAfter() ); + if (m_reaction.shouldThrow) { +#if !defined(CATCH_CONFIG_DISABLE_EXCEPTIONS) + throw Catch::TestFailureException(); +#else + CATCH_ERROR( "Test failure requires aborting test!" ); +#endif } + } + void AssertionHandler::setCompleted() { + m_completed = true; + } - private: + void AssertionHandler::handleUnexpectedInflightException() { + m_resultCapture.handleUnexpectedInflightException( m_assertionInfo, Catch::translateActiveException(), m_reaction ); + } - void runCurrentTest( std::string& redirectedCout, std::string& redirectedCerr ) { - TestCaseInfo const& testCaseInfo = m_activeTestCase->getTestCaseInfo(); - SectionInfo testCaseSection( testCaseInfo.lineInfo, testCaseInfo.name, testCaseInfo.description ); - m_reporter->sectionStarting( testCaseSection ); - Counts prevAssertions = m_totals.assertions; - double duration = 0; - try { - m_lastAssertionInfo = AssertionInfo( "TEST_CASE", testCaseInfo.lineInfo, std::string(), ResultDisposition::Normal ); + void AssertionHandler::handleExceptionThrownAsExpected() { + m_resultCapture.handleNonExpr(m_assertionInfo, ResultWas::Ok, m_reaction); + } + void AssertionHandler::handleExceptionNotThrownAsExpected() { + m_resultCapture.handleNonExpr(m_assertionInfo, ResultWas::Ok, m_reaction); + } - seedRng( *m_config ); + void AssertionHandler::handleUnexpectedExceptionNotThrown() { + m_resultCapture.handleUnexpectedExceptionNotThrown( m_assertionInfo, m_reaction ); + } - Timer timer; - timer.start(); - if( m_reporter->getPreferences().shouldRedirectStdOut ) { - StreamRedirect coutRedir( Catch::cout(), redirectedCout ); - StreamRedirect cerrRedir( Catch::cerr(), redirectedCerr ); - invokeActiveTestCase(); - } - else { - invokeActiveTestCase(); - } - duration = timer.getElapsedSeconds(); - } - catch( TestFailureException& ) { - // This just means the test was aborted due to failure - } - catch(...) { - makeUnexpectedResultBuilder().useActiveException(); - } - m_testCaseTracker->close(); - handleUnfinishedSections(); - m_messages.clear(); + void AssertionHandler::handleThrowingCallSkipped() { + m_resultCapture.handleNonExpr(m_assertionInfo, ResultWas::Ok, m_reaction); + } - Counts assertions = m_totals.assertions - prevAssertions; - bool missingAssertions = testForMissingAssertions( assertions ); + // This is the overload that takes a string and infers the Equals matcher from it + // The more general overload, that takes any string matcher, is in catch_capture_matchers.cpp + void handleExceptionMatchExpr( AssertionHandler& handler, std::string const& str, StringRef const& matcherString ) { + handleExceptionMatchExpr( handler, Matchers::Equals( str ), matcherString ); + } - if( testCaseInfo.okToFail() ) { - std::swap( assertions.failedButOk, assertions.failed ); - m_totals.assertions.failed -= assertions.failedButOk; - m_totals.assertions.failedButOk += assertions.failedButOk; - } +} // namespace Catch +// end catch_assertionhandler.cpp +// start catch_assertionresult.cpp - SectionStats testCaseSectionStats( testCaseSection, assertions, duration, missingAssertions ); - m_reporter->sectionEnded( testCaseSectionStats ); - } +namespace Catch { + AssertionResultData::AssertionResultData(ResultWas::OfType _resultType, LazyExpression const & _lazyExpression): + lazyExpression(_lazyExpression), + resultType(_resultType) {} - void invokeActiveTestCase() { - FatalConditionHandler fatalConditionHandler; // Handle signals - m_activeTestCase->invoke(); - fatalConditionHandler.reset(); + std::string AssertionResultData::reconstructExpression() const { + + if( reconstructedExpression.empty() ) { + if( lazyExpression ) { + ReusableStringStream rss; + rss << lazyExpression; + reconstructedExpression = rss.str(); + } } + return reconstructedExpression; + } - private: + AssertionResult::AssertionResult( AssertionInfo const& info, AssertionResultData const& data ) + : m_info( info ), + m_resultData( data ) + {} - ResultBuilder makeUnexpectedResultBuilder() const { - return ResultBuilder( m_lastAssertionInfo.macroName.c_str(), - m_lastAssertionInfo.lineInfo, - m_lastAssertionInfo.capturedExpression.c_str(), - m_lastAssertionInfo.resultDisposition ); - } + // Result was a success + bool AssertionResult::succeeded() const { + return Catch::isOk( m_resultData.resultType ); + } - void handleUnfinishedSections() { - // If sections ended prematurely due to an exception we stored their - // infos here so we can tear them down outside the unwind process. - for( std::vector<SectionEndInfo>::const_reverse_iterator it = m_unfinishedSections.rbegin(), - itEnd = m_unfinishedSections.rend(); - it != itEnd; - ++it ) - sectionEnded( *it ); - m_unfinishedSections.clear(); - } + // Result was a success, or failure is suppressed + bool AssertionResult::isOk() const { + return Catch::isOk( m_resultData.resultType ) || shouldSuppressFailure( m_info.resultDisposition ); + } - TestRunInfo m_runInfo; - IMutableContext& m_context; - TestCase const* m_activeTestCase; - ITracker* m_testCaseTracker; - ITracker* m_currentSectionTracker; - AssertionResult m_lastResult; + ResultWas::OfType AssertionResult::getResultType() const { + return m_resultData.resultType; + } - Ptr<IConfig const> m_config; - Totals m_totals; - Ptr<IStreamingReporter> m_reporter; - std::vector<MessageInfo> m_messages; - AssertionInfo m_lastAssertionInfo; - std::vector<SectionEndInfo> m_unfinishedSections; - std::vector<ITracker*> m_activeSections; - TrackerContext m_trackerContext; - }; + bool AssertionResult::hasExpression() const { + return m_info.capturedExpression[0] != 0; + } - IResultCapture& getResultCapture() { - if( IResultCapture* capture = getCurrentContext().getResultCapture() ) - return *capture; + bool AssertionResult::hasMessage() const { + return !m_resultData.message.empty(); + } + + std::string AssertionResult::getExpression() const { + if( isFalseTest( m_info.resultDisposition ) ) + return "!(" + m_info.capturedExpression + ")"; else - throw std::logic_error( "No result capture instance" ); + return m_info.capturedExpression; } -} // end namespace Catch + std::string AssertionResult::getExpressionInMacro() const { + std::string expr; + if( m_info.macroName[0] == 0 ) + expr = m_info.capturedExpression; + else { + expr.reserve( m_info.macroName.size() + m_info.capturedExpression.size() + 4 ); + expr += m_info.macroName; + expr += "( "; + expr += m_info.capturedExpression; + expr += " )"; + } + return expr; + } + + bool AssertionResult::hasExpandedExpression() const { + return hasExpression() && getExpandedExpression() != getExpression(); + } + + std::string AssertionResult::getExpandedExpression() const { + std::string expr = m_resultData.reconstructExpression(); + return expr.empty() + ? getExpression() + : expr; + } -// #included from: internal/catch_version.h -#define TWOBLUECUBES_CATCH_VERSION_H_INCLUDED + std::string AssertionResult::getMessage() const { + return m_resultData.message; + } + SourceLineInfo AssertionResult::getSourceInfo() const { + return m_info.lineInfo; + } + + StringRef AssertionResult::getTestMacroName() const { + return m_info.macroName; + } + +} // end namespace Catch +// end catch_assertionresult.cpp +// start catch_capture_matchers.cpp namespace Catch { - // Versioning information - struct Version { - Version( unsigned int _majorVersion, - unsigned int _minorVersion, - unsigned int _patchNumber, - std::string const& _branchName, - unsigned int _buildNumber ); + using StringMatcher = Matchers::Impl::MatcherBase<std::string>; - unsigned int const majorVersion; - unsigned int const minorVersion; - unsigned int const patchNumber; + // This is the general overload that takes a any string matcher + // There is another overload, in catch_assertionhandler.h/.cpp, that only takes a string and infers + // the Equals matcher (so the header does not mention matchers) + void handleExceptionMatchExpr( AssertionHandler& handler, StringMatcher const& matcher, StringRef const& matcherString ) { + std::string exceptionMessage = Catch::translateActiveException(); + MatchExpr<std::string, StringMatcher const&> expr( exceptionMessage, matcher, matcherString ); + handler.handleExpr( expr ); + } - // buildNumber is only used if branchName is not null - std::string const branchName; - unsigned int const buildNumber; +} // namespace Catch +// end catch_capture_matchers.cpp +// start catch_commandline.cpp - friend std::ostream& operator << ( std::ostream& os, Version const& version ); +// start catch_commandline.h - private: - void operator=( Version const& ); - }; +// start catch_clara.h - extern Version libraryVersion; -} +// Use Catch's value for console width (store Clara's off to the side, if present) +#ifdef CLARA_CONFIG_CONSOLE_WIDTH +#define CATCH_TEMP_CLARA_CONFIG_CONSOLE_WIDTH CATCH_CLARA_TEXTFLOW_CONFIG_CONSOLE_WIDTH +#undef CATCH_CLARA_TEXTFLOW_CONFIG_CONSOLE_WIDTH +#endif +#define CATCH_CLARA_TEXTFLOW_CONFIG_CONSOLE_WIDTH CATCH_CONFIG_CONSOLE_WIDTH-1 + +#ifdef __clang__ +#pragma clang diagnostic push +#pragma clang diagnostic ignored "-Wweak-vtables" +#pragma clang diagnostic ignored "-Wexit-time-destructors" +#pragma clang diagnostic ignored "-Wshadow" +#endif + +// start clara.hpp +// Copyright 2017 Two Blue Cubes Ltd. All rights reserved. +// +// Distributed under the Boost Software License, Version 1.0. (See accompanying +// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) +// +// See https://github.com/philsquared/Clara for more details + +// Clara v1.1.5 -#include <fstream> -#include <stdlib.h> -#include <limits> + +#ifndef CATCH_CLARA_CONFIG_CONSOLE_WIDTH +#define CATCH_CLARA_CONFIG_CONSOLE_WIDTH 80 +#endif + +#ifndef CATCH_CLARA_TEXTFLOW_CONFIG_CONSOLE_WIDTH +#define CATCH_CLARA_TEXTFLOW_CONFIG_CONSOLE_WIDTH CATCH_CLARA_CONFIG_CONSOLE_WIDTH +#endif + +#ifndef CLARA_CONFIG_OPTIONAL_TYPE +#ifdef __has_include +#if __has_include(<optional>) && __cplusplus >= 201703L +#include <optional> +#define CLARA_CONFIG_OPTIONAL_TYPE std::optional +#endif +#endif +#endif + +// ----------- #included from clara_textflow.hpp ----------- + +// TextFlowCpp +// +// A single-header library for wrapping and laying out basic text, by Phil Nash +// +// Distributed under the Boost Software License, Version 1.0. (See accompanying +// file LICENSE.txt or copy at http://www.boost.org/LICENSE_1_0.txt) +// +// This project is hosted at https://github.com/philsquared/textflowcpp + + +#include <cassert> +#include <ostream> +#include <sstream> +#include <vector> + +#ifndef CATCH_CLARA_TEXTFLOW_CONFIG_CONSOLE_WIDTH +#define CATCH_CLARA_TEXTFLOW_CONFIG_CONSOLE_WIDTH 80 +#endif namespace Catch { +namespace clara { +namespace TextFlow { - Ptr<IStreamingReporter> createReporter( std::string const& reporterName, Ptr<Config> const& config ) { - Ptr<IStreamingReporter> reporter = getRegistryHub().getReporterRegistry().create( reporterName, config.get() ); - if( !reporter ) { - std::ostringstream oss; - oss << "No reporter registered with name: '" << reporterName << "'"; - throw std::domain_error( oss.str() ); - } - return reporter; - } +inline auto isWhitespace(char c) -> bool { + static std::string chars = " \t\n\r"; + return chars.find(c) != std::string::npos; +} +inline auto isBreakableBefore(char c) -> bool { + static std::string chars = "[({<|"; + return chars.find(c) != std::string::npos; +} +inline auto isBreakableAfter(char c) -> bool { + static std::string chars = "])}>.,:;*+-=&/\\"; + return chars.find(c) != std::string::npos; +} - Ptr<IStreamingReporter> makeReporter( Ptr<Config> const& config ) { - std::vector<std::string> reporters = config->getReporterNames(); - if( reporters.empty() ) - reporters.push_back( "console" ); +class Columns; - Ptr<IStreamingReporter> reporter; - for( std::vector<std::string>::const_iterator it = reporters.begin(), itEnd = reporters.end(); - it != itEnd; - ++it ) - reporter = addReporter( reporter, createReporter( *it, config ) ); - return reporter; - } - Ptr<IStreamingReporter> addListeners( Ptr<IConfig const> const& config, Ptr<IStreamingReporter> reporters ) { - IReporterRegistry::Listeners listeners = getRegistryHub().getReporterRegistry().getListeners(); - for( IReporterRegistry::Listeners::const_iterator it = listeners.begin(), itEnd = listeners.end(); - it != itEnd; - ++it ) - reporters = addReporter(reporters, (*it)->create( ReporterConfig( config ) ) ); - return reporters; - } +class Column { + std::vector<std::string> m_strings; + size_t m_width = CATCH_CLARA_TEXTFLOW_CONFIG_CONSOLE_WIDTH; + size_t m_indent = 0; + size_t m_initialIndent = std::string::npos; - Totals runTests( Ptr<Config> const& config ) { +public: + class iterator { + friend Column; + + Column const& m_column; + size_t m_stringIndex = 0; + size_t m_pos = 0; + + size_t m_len = 0; + size_t m_end = 0; + bool m_suffix = false; + + iterator(Column const& column, size_t stringIndex) + : m_column(column), + m_stringIndex(stringIndex) {} + + auto line() const -> std::string const& { return m_column.m_strings[m_stringIndex]; } + + auto isBoundary(size_t at) const -> bool { + assert(at > 0); + assert(at <= line().size()); + + return at == line().size() || + (isWhitespace(line()[at]) && !isWhitespace(line()[at - 1])) || + isBreakableBefore(line()[at]) || + isBreakableAfter(line()[at - 1]); + } + + void calcLength() { + assert(m_stringIndex < m_column.m_strings.size()); + + m_suffix = false; + auto width = m_column.m_width - indent(); + m_end = m_pos; + if (line()[m_pos] == '\n') { + ++m_end; + } + while (m_end < line().size() && line()[m_end] != '\n') + ++m_end; + + if (m_end < m_pos + width) { + m_len = m_end - m_pos; + } else { + size_t len = width; + while (len > 0 && !isBoundary(m_pos + len)) + --len; + while (len > 0 && isWhitespace(line()[m_pos + len - 1])) + --len; + + if (len > 0) { + m_len = len; + } else { + m_suffix = true; + m_len = width - 1; + } + } + } + + auto indent() const -> size_t { + auto initial = m_pos == 0 && m_stringIndex == 0 ? m_column.m_initialIndent : std::string::npos; + return initial == std::string::npos ? m_column.m_indent : initial; + } + + auto addIndentAndSuffix(std::string const &plain) const -> std::string { + return std::string(indent(), ' ') + (m_suffix ? plain + "-" : plain); + } + + public: + using difference_type = std::ptrdiff_t; + using value_type = std::string; + using pointer = value_type * ; + using reference = value_type & ; + using iterator_category = std::forward_iterator_tag; + + explicit iterator(Column const& column) : m_column(column) { + assert(m_column.m_width > m_column.m_indent); + assert(m_column.m_initialIndent == std::string::npos || m_column.m_width > m_column.m_initialIndent); + calcLength(); + if (m_len == 0) + m_stringIndex++; // Empty string + } + + auto operator *() const -> std::string { + assert(m_stringIndex < m_column.m_strings.size()); + assert(m_pos <= m_end); + return addIndentAndSuffix(line().substr(m_pos, m_len)); + } + + auto operator ++() -> iterator& { + m_pos += m_len; + if (m_pos < line().size() && line()[m_pos] == '\n') + m_pos += 1; + else + while (m_pos < line().size() && isWhitespace(line()[m_pos])) + ++m_pos; + + if (m_pos == line().size()) { + m_pos = 0; + ++m_stringIndex; + } + if (m_stringIndex < m_column.m_strings.size()) + calcLength(); + return *this; + } + auto operator ++(int) -> iterator { + iterator prev(*this); + operator++(); + return prev; + } + + auto operator ==(iterator const& other) const -> bool { + return + m_pos == other.m_pos && + m_stringIndex == other.m_stringIndex && + &m_column == &other.m_column; + } + auto operator !=(iterator const& other) const -> bool { + return !operator==(other); + } + }; + using const_iterator = iterator; + + explicit Column(std::string const& text) { m_strings.push_back(text); } + + auto width(size_t newWidth) -> Column& { + assert(newWidth > 0); + m_width = newWidth; + return *this; + } + auto indent(size_t newIndent) -> Column& { + m_indent = newIndent; + return *this; + } + auto initialIndent(size_t newIndent) -> Column& { + m_initialIndent = newIndent; + return *this; + } + + auto width() const -> size_t { return m_width; } + auto begin() const -> iterator { return iterator(*this); } + auto end() const -> iterator { return { *this, m_strings.size() }; } + + inline friend std::ostream& operator << (std::ostream& os, Column const& col) { + bool first = true; + for (auto line : col) { + if (first) + first = false; + else + os << "\n"; + os << line; + } + return os; + } + + auto operator + (Column const& other)->Columns; + + auto toString() const -> std::string { + std::ostringstream oss; + oss << *this; + return oss.str(); + } +}; - Ptr<IConfig const> iconfig = config.get(); +class Spacer : public Column { - Ptr<IStreamingReporter> reporter = makeReporter( config ); - reporter = addListeners( iconfig, reporter ); +public: + explicit Spacer(size_t spaceWidth) : Column("") { + width(spaceWidth); + } +}; - RunContext context( iconfig, reporter ); +class Columns { + std::vector<Column> m_columns; - Totals totals; +public: - context.testGroupStarting( config->name(), 1, 1 ); + class iterator { + friend Columns; + struct EndTag {}; + + std::vector<Column> const& m_columns; + std::vector<Column::iterator> m_iterators; + size_t m_activeIterators; + + iterator(Columns const& columns, EndTag) + : m_columns(columns.m_columns), + m_activeIterators(0) { + m_iterators.reserve(m_columns.size()); + + for (auto const& col : m_columns) + m_iterators.push_back(col.end()); + } + + public: + using difference_type = std::ptrdiff_t; + using value_type = std::string; + using pointer = value_type * ; + using reference = value_type & ; + using iterator_category = std::forward_iterator_tag; + + explicit iterator(Columns const& columns) + : m_columns(columns.m_columns), + m_activeIterators(m_columns.size()) { + m_iterators.reserve(m_columns.size()); + + for (auto const& col : m_columns) + m_iterators.push_back(col.begin()); + } + + auto operator ==(iterator const& other) const -> bool { + return m_iterators == other.m_iterators; + } + auto operator !=(iterator const& other) const -> bool { + return m_iterators != other.m_iterators; + } + auto operator *() const -> std::string { + std::string row, padding; + + for (size_t i = 0; i < m_columns.size(); ++i) { + auto width = m_columns[i].width(); + if (m_iterators[i] != m_columns[i].end()) { + std::string col = *m_iterators[i]; + row += padding + col; + if (col.size() < width) + padding = std::string(width - col.size(), ' '); + else + padding = ""; + } else { + padding += std::string(width, ' '); + } + } + return row; + } + auto operator ++() -> iterator& { + for (size_t i = 0; i < m_columns.size(); ++i) { + if (m_iterators[i] != m_columns[i].end()) + ++m_iterators[i]; + } + return *this; + } + auto operator ++(int) -> iterator { + iterator prev(*this); + operator++(); + return prev; + } + }; + using const_iterator = iterator; + + auto begin() const -> iterator { return iterator(*this); } + auto end() const -> iterator { return { *this, iterator::EndTag() }; } + + auto operator += (Column const& col) -> Columns& { + m_columns.push_back(col); + return *this; + } + auto operator + (Column const& col) -> Columns { + Columns combined = *this; + combined += col; + return combined; + } + + inline friend std::ostream& operator << (std::ostream& os, Columns const& cols) { + + bool first = true; + for (auto line : cols) { + if (first) + first = false; + else + os << "\n"; + os << line; + } + return os; + } + + auto toString() const -> std::string { + std::ostringstream oss; + oss << *this; + return oss.str(); + } +}; - TestSpec testSpec = config->testSpec(); - if( !testSpec.hasFilters() ) - testSpec = TestSpecParser( ITagAliasRegistry::get() ).parse( "~[.]" ).testSpec(); // All not hidden tests +inline auto Column::operator + (Column const& other) -> Columns { + Columns cols; + cols += *this; + cols += other; + return cols; +} +} - std::vector<TestCase> const& allTestCases = getAllTestCasesSorted( *iconfig ); - for( std::vector<TestCase>::const_iterator it = allTestCases.begin(), itEnd = allTestCases.end(); - it != itEnd; - ++it ) { - if( !context.aborting() && matchTest( *it, testSpec, *iconfig ) ) - totals += context.runTest( *it ); - else - reporter->skipTest( *it ); - } +} +} - context.testGroupEnded( iconfig->name(), totals, 1, 1 ); - return totals; - } +// ----------- end of #include from clara_textflow.hpp ----------- +// ........... back in clara.hpp - void applyFilenamesAsTags( IConfig const& config ) { - std::vector<TestCase> const& tests = getAllTestCasesSorted( config ); - for(std::size_t i = 0; i < tests.size(); ++i ) { - TestCase& test = const_cast<TestCase&>( tests[i] ); - std::set<std::string> tags = test.tags; +#include <cctype> +#include <string> +#include <memory> +#include <set> +#include <algorithm> - std::string filename = test.lineInfo.file; - std::string::size_type lastSlash = filename.find_last_of( "\\/" ); - if( lastSlash != std::string::npos ) - filename = filename.substr( lastSlash+1 ); +#if !defined(CATCH_PLATFORM_WINDOWS) && ( defined(WIN32) || defined(__WIN32__) || defined(_WIN32) || defined(_MSC_VER) ) +#define CATCH_PLATFORM_WINDOWS +#endif - std::string::size_type lastDot = filename.find_last_of( "." ); - if( lastDot != std::string::npos ) - filename = filename.substr( 0, lastDot ); +namespace Catch { namespace clara { +namespace detail { - tags.insert( "#" + filename ); - setTags( test, tags ); - } - } + // Traits for extracting arg and return type of lambdas (for single argument lambdas) + template<typename L> + struct UnaryLambdaTraits : UnaryLambdaTraits<decltype( &L::operator() )> {}; - class Session : NonCopyable { - static bool alreadyInstantiated; + template<typename ClassT, typename ReturnT, typename... Args> + struct UnaryLambdaTraits<ReturnT( ClassT::* )( Args... ) const> { + static const bool isValid = false; + }; - public: + template<typename ClassT, typename ReturnT, typename ArgT> + struct UnaryLambdaTraits<ReturnT( ClassT::* )( ArgT ) const> { + static const bool isValid = true; + using ArgType = typename std::remove_const<typename std::remove_reference<ArgT>::type>::type; + using ReturnType = ReturnT; + }; - struct OnUnusedOptions { enum DoWhat { Ignore, Fail }; }; + class TokenStream; - Session() - : m_cli( makeCommandLineParser() ) { - if( alreadyInstantiated ) { - std::string msg = "Only one instance of Catch::Session can ever be used"; - Catch::cerr() << msg << std::endl; - throw std::logic_error( msg ); - } - alreadyInstantiated = true; - } - ~Session() { - Catch::cleanUp(); - } + // Transport for raw args (copied from main args, or supplied via init list for testing) + class Args { + friend TokenStream; + std::string m_exeName; + std::vector<std::string> m_args; + + public: + Args( int argc, char const* const* argv ) + : m_exeName(argv[0]), + m_args(argv + 1, argv + argc) {} - void showHelp( std::string const& processName ) { - Catch::cout() << "\nCatch v" << libraryVersion << "\n"; + Args( std::initializer_list<std::string> args ) + : m_exeName( *args.begin() ), + m_args( args.begin()+1, args.end() ) + {} - m_cli.usage( Catch::cout(), processName ); - Catch::cout() << "For more detail usage please see the project docs\n" << std::endl; + auto exeName() const -> std::string { + return m_exeName; } + }; - int applyCommandLine( int argc, char const* const* const argv, OnUnusedOptions::DoWhat unusedOptionBehaviour = OnUnusedOptions::Fail ) { - try { - m_cli.setThrowOnUnrecognisedTokens( unusedOptionBehaviour == OnUnusedOptions::Fail ); - m_unusedTokens = m_cli.parseInto( Clara::argsToVector( argc, argv ), m_configData ); - if( m_configData.showHelp ) - showHelp( m_configData.processName ); - m_config.reset(); - } - catch( std::exception& ex ) { - { - Colour colourGuard( Colour::Red ); - Catch::cerr() - << "\nError(s) in input:\n" - << Text( ex.what(), TextAttributes().setIndent(2) ) - << "\n\n"; + // Wraps a token coming from a token stream. These may not directly correspond to strings as a single string + // may encode an option + its argument if the : or = form is used + enum class TokenType { + Option, Argument + }; + struct Token { + TokenType type; + std::string token; + }; + + inline auto isOptPrefix( char c ) -> bool { + return c == '-' +#ifdef CATCH_PLATFORM_WINDOWS + || c == '/' +#endif + ; + } + + // Abstracts iterators into args as a stream of tokens, with option arguments uniformly handled + class TokenStream { + using Iterator = std::vector<std::string>::const_iterator; + Iterator it; + Iterator itEnd; + std::vector<Token> m_tokenBuffer; + + void loadBuffer() { + m_tokenBuffer.resize( 0 ); + + // Skip any empty strings + while( it != itEnd && it->empty() ) + ++it; + + if( it != itEnd ) { + auto const &next = *it; + if( isOptPrefix( next[0] ) ) { + auto delimiterPos = next.find_first_of( " :=" ); + if( delimiterPos != std::string::npos ) { + m_tokenBuffer.push_back( { TokenType::Option, next.substr( 0, delimiterPos ) } ); + m_tokenBuffer.push_back( { TokenType::Argument, next.substr( delimiterPos + 1 ) } ); + } else { + if( next[1] != '-' && next.size() > 2 ) { + std::string opt = "- "; + for( size_t i = 1; i < next.size(); ++i ) { + opt[1] = next[i]; + m_tokenBuffer.push_back( { TokenType::Option, opt } ); + } + } else { + m_tokenBuffer.push_back( { TokenType::Option, next } ); + } + } + } else { + m_tokenBuffer.push_back( { TokenType::Argument, next } ); } - m_cli.usage( Catch::cout(), m_configData.processName ); - return (std::numeric_limits<int>::max)(); } - return 0; } - void useConfigData( ConfigData const& _configData ) { - m_configData = _configData; - m_config.reset(); + public: + explicit TokenStream( Args const &args ) : TokenStream( args.m_args.begin(), args.m_args.end() ) {} + + TokenStream( Iterator it, Iterator itEnd ) : it( it ), itEnd( itEnd ) { + loadBuffer(); } - int run( int argc, char const* const* const argv ) { + explicit operator bool() const { + return !m_tokenBuffer.empty() || it != itEnd; + } - int returnCode = applyCommandLine( argc, argv ); - if( returnCode == 0 ) - returnCode = run(); - return returnCode; + auto count() const -> size_t { return m_tokenBuffer.size() + (itEnd - it); } + + auto operator*() const -> Token { + assert( !m_tokenBuffer.empty() ); + return m_tokenBuffer.front(); } - int run() { - if( m_configData.showHelp ) - return 0; + auto operator->() const -> Token const * { + assert( !m_tokenBuffer.empty() ); + return &m_tokenBuffer.front(); + } - try - { - config(); // Force config to be constructed + auto operator++() -> TokenStream & { + if( m_tokenBuffer.size() >= 2 ) { + m_tokenBuffer.erase( m_tokenBuffer.begin() ); + } else { + if( it != itEnd ) + ++it; + loadBuffer(); + } + return *this; + } + }; - seedRng( *m_config ); + class ResultBase { + public: + enum Type { + Ok, LogicError, RuntimeError + }; - if( m_configData.filenamesAsTags ) - applyFilenamesAsTags( *m_config ); + protected: + ResultBase( Type type ) : m_type( type ) {} + virtual ~ResultBase() = default; - // Handle list request - if( Option<std::size_t> listed = list( config() ) ) - return static_cast<int>( *listed ); + virtual void enforceOk() const = 0; - return static_cast<int>( runTests( m_config ).assertions.failed ); - } - catch( std::exception& ex ) { - Catch::cerr() << ex.what() << std::endl; - return (std::numeric_limits<int>::max)(); - } + Type m_type; + }; + + template<typename T> + class ResultValueBase : public ResultBase { + public: + auto value() const -> T const & { + enforceOk(); + return m_value; } - Clara::CommandLine<ConfigData> const& cli() const { - return m_cli; + protected: + ResultValueBase( Type type ) : ResultBase( type ) {} + + ResultValueBase( ResultValueBase const &other ) : ResultBase( other ) { + if( m_type == ResultBase::Ok ) + new( &m_value ) T( other.m_value ); } - std::vector<Clara::Parser::Token> const& unusedTokens() const { - return m_unusedTokens; + + ResultValueBase( Type, T const &value ) : ResultBase( Ok ) { + new( &m_value ) T( value ); } - ConfigData& configData() { - return m_configData; + + auto operator=( ResultValueBase const &other ) -> ResultValueBase & { + if( m_type == ResultBase::Ok ) + m_value.~T(); + ResultBase::operator=(other); + if( m_type == ResultBase::Ok ) + new( &m_value ) T( other.m_value ); + return *this; } - Config& config() { - if( !m_config ) - m_config = new Config( m_configData ); - return *m_config; + + ~ResultValueBase() override { + if( m_type == Ok ) + m_value.~T(); } - private: - Clara::CommandLine<ConfigData> m_cli; - std::vector<Clara::Parser::Token> m_unusedTokens; - ConfigData m_configData; - Ptr<Config> m_config; - }; - bool Session::alreadyInstantiated = false; + union { + T m_value; + }; + }; -} // end namespace Catch + template<> + class ResultValueBase<void> : public ResultBase { + protected: + using ResultBase::ResultBase; + }; -// #included from: catch_registry_hub.hpp -#define TWOBLUECUBES_CATCH_REGISTRY_HUB_HPP_INCLUDED + 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() != ResultBase::Ok ); + } -// #included from: catch_test_case_registry_impl.hpp -#define TWOBLUECUBES_CATCH_TEST_CASE_REGISTRY_IMPL_HPP_INCLUDED + template<typename U> + static auto ok( U const &value ) -> BasicResult { return { ResultBase::Ok, value }; } + static auto ok() -> BasicResult { return { ResultBase::Ok }; } + static auto logicError( std::string const &message ) -> BasicResult { return { ResultBase::LogicError, message }; } + static auto runtimeError( std::string const &message ) -> BasicResult { return { ResultBase::RuntimeError, message }; } -#include <vector> -#include <set> -#include <sstream> -#include <algorithm> + explicit operator bool() const { return m_type == ResultBase::Ok; } + auto type() const -> ResultBase::Type { return m_type; } + auto errorMessage() const -> std::string { return m_errorMessage; } -namespace Catch { + protected: + void enforceOk() const override { - struct RandomNumberGenerator { - typedef std::ptrdiff_t result_type; + // Errors shouldn't reach this point, but if they do + // the actual error message will be in m_errorMessage + assert( m_type != ResultBase::LogicError ); + assert( m_type != ResultBase::RuntimeError ); + if( m_type != ResultBase::Ok ) + std::abort(); + } - result_type operator()( result_type n ) const { return std::rand() % n; } + std::string m_errorMessage; // Only populated if resultType is an error -#ifdef CATCH_CONFIG_CPP11_SHUFFLE - static constexpr result_type min() { return 0; } - static constexpr result_type max() { return 1000000; } - result_type operator()() const { return std::rand() % max(); } -#endif - template<typename V> - static void shuffle( V& vector ) { - RandomNumberGenerator rng; -#ifdef CATCH_CONFIG_CPP11_SHUFFLE - std::shuffle( vector.begin(), vector.end(), rng ); -#else - std::random_shuffle( vector.begin(), vector.end(), rng ); -#endif + BasicResult( ResultBase::Type type, std::string const &message ) + : ResultValueBase<T>(type), + m_errorMessage(message) + { + assert( m_type != ResultBase::Ok ); } + + using ResultValueBase<T>::ResultValueBase; + using ResultBase::m_type; }; - inline std::vector<TestCase> sortTests( IConfig const& config, std::vector<TestCase> const& unsortedTestCases ) { + enum class ParseResultType { + Matched, NoMatch, ShortCircuitAll, ShortCircuitSame + }; - std::vector<TestCase> sorted = unsortedTestCases; + class ParseState { + public: - switch( config.runOrder() ) { - case RunTests::InLexicographicalOrder: - std::sort( sorted.begin(), sorted.end() ); - break; - case RunTests::InRandomOrder: - { - seedRng( config ); - RandomNumberGenerator::shuffle( sorted ); - } - break; - case RunTests::InDeclarationOrder: - // already in declaration order - break; - } - return sorted; - } - bool matchTest( TestCase const& testCase, TestSpec const& testSpec, IConfig const& config ) { - return testSpec.matches( testCase ) && ( config.allowThrows() || !testCase.throws() ); + ParseState( ParseResultType type, TokenStream const &remainingTokens ) + : m_type(type), + m_remainingTokens( remainingTokens ) + {} + + auto type() const -> ParseResultType { return m_type; } + auto remainingTokens() const -> TokenStream { return m_remainingTokens; } + + private: + ParseResultType m_type; + TokenStream m_remainingTokens; + }; + + using Result = BasicResult<void>; + using ParserResult = BasicResult<ParseResultType>; + using InternalParseResult = BasicResult<ParseState>; + + struct HelpColumns { + std::string left; + std::string right; + }; + + template<typename T> + inline auto convertInto( std::string const &source, T& target ) -> ParserResult { + std::stringstream ss; + ss << source; + ss >> target; + if( ss.fail() ) + return ParserResult::runtimeError( "Unable to convert '" + source + "' to destination type" ); + else + return ParserResult::ok( ParseResultType::Matched ); + } + inline auto convertInto( std::string const &source, std::string& target ) -> ParserResult { + target = source; + return ParserResult::ok( ParseResultType::Matched ); + } + inline auto convertInto( std::string const &source, bool &target ) -> ParserResult { + std::string srcLC = source; + std::transform( srcLC.begin(), srcLC.end(), srcLC.begin(), []( char c ) { return static_cast<char>( std::tolower(c) ); } ); + if (srcLC == "y" || srcLC == "1" || srcLC == "true" || srcLC == "yes" || srcLC == "on") + target = true; + else if (srcLC == "n" || srcLC == "0" || srcLC == "false" || srcLC == "no" || srcLC == "off") + target = false; + else + return ParserResult::runtimeError( "Expected a boolean value but did not recognise: '" + source + "'" ); + return ParserResult::ok( ParseResultType::Matched ); } +#ifdef CLARA_CONFIG_OPTIONAL_TYPE + template<typename T> + inline auto convertInto( std::string const &source, CLARA_CONFIG_OPTIONAL_TYPE<T>& target ) -> ParserResult { + T temp; + auto result = convertInto( source, temp ); + if( result ) + target = std::move(temp); + return result; + } +#endif // CLARA_CONFIG_OPTIONAL_TYPE + + struct NonCopyable { + NonCopyable() = default; + NonCopyable( NonCopyable const & ) = delete; + NonCopyable( NonCopyable && ) = delete; + NonCopyable &operator=( NonCopyable const & ) = delete; + NonCopyable &operator=( NonCopyable && ) = delete; + }; - void enforceNoDuplicateTestCases( std::vector<TestCase> const& functions ) { - std::set<TestCase> seenFunctions; - for( std::vector<TestCase>::const_iterator it = functions.begin(), itEnd = functions.end(); - it != itEnd; - ++it ) { - std::pair<std::set<TestCase>::const_iterator, bool> prev = seenFunctions.insert( *it ); - if( !prev.second ) { - std::ostringstream ss; + struct BoundRef : NonCopyable { + virtual ~BoundRef() = default; + virtual auto isContainer() const -> bool { return false; } + virtual auto isFlag() const -> bool { return false; } + }; + struct BoundValueRefBase : BoundRef { + virtual auto setValue( std::string const &arg ) -> ParserResult = 0; + }; + struct BoundFlagRefBase : BoundRef { + virtual auto setFlag( bool flag ) -> ParserResult = 0; + virtual auto isFlag() const -> bool { return true; } + }; + + template<typename T> + struct BoundValueRef : BoundValueRefBase { + T &m_ref; - ss << Colour( Colour::Red ) - << "error: TEST_CASE( \"" << it->name << "\" ) already defined.\n" - << "\tFirst seen at " << prev.first->getTestCaseInfo().lineInfo << '\n' - << "\tRedefined at " << it->getTestCaseInfo().lineInfo << std::endl; + explicit BoundValueRef( T &ref ) : m_ref( ref ) {} - throw std::runtime_error(ss.str()); - } + auto setValue( std::string const &arg ) -> ParserResult override { + return convertInto( arg, m_ref ); } - } + }; - std::vector<TestCase> filterTests( std::vector<TestCase> const& testCases, TestSpec const& testSpec, IConfig const& config ) { - std::vector<TestCase> filtered; - filtered.reserve( testCases.size() ); - for( std::vector<TestCase>::const_iterator it = testCases.begin(), itEnd = testCases.end(); - it != itEnd; - ++it ) - if( matchTest( *it, testSpec, config ) ) - filtered.push_back( *it ); - return filtered; - } - std::vector<TestCase> const& getAllTestCasesSorted( IConfig const& config ) { - return getRegistryHub().getTestCaseRegistry().getAllTestsSorted( config ); - } + template<typename T> + struct BoundValueRef<std::vector<T>> : BoundValueRefBase { + std::vector<T> &m_ref; - class TestRegistry : public ITestCaseRegistry { - public: - TestRegistry() - : m_currentSortOrder( RunTests::InDeclarationOrder ), - m_unnamedCount( 0 ) - {} - virtual ~TestRegistry(); + explicit BoundValueRef( std::vector<T> &ref ) : m_ref( ref ) {} - virtual void registerTest( TestCase const& testCase ) { - std::string name = testCase.getTestCaseInfo().name; - if( name.empty() ) { - std::ostringstream oss; - oss << "Anonymous test case " << ++m_unnamedCount; - return registerTest( testCase.withName( oss.str() ) ); - } - m_functions.push_back( testCase ); + 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 ) {} - virtual std::vector<TestCase> const& getAllTests() const { - return m_functions; + auto setFlag( bool flag ) -> ParserResult override { + m_ref = flag; + return ParserResult::ok( ParseResultType::Matched ); } - virtual std::vector<TestCase> const& getAllTestsSorted( IConfig const& config ) const { - if( m_sortedFunctions.empty() ) - enforceNoDuplicateTestCases( m_functions ); + }; - if( m_currentSortOrder != config.runOrder() || m_sortedFunctions.empty() ) { - m_sortedFunctions = sortTests( config, m_functions ); - m_currentSortOrder = config.runOrder(); - } - return m_sortedFunctions; + 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 ); } + }; - private: - std::vector<TestCase> m_functions; - mutable RunTests::InWhatOrder m_currentSortOrder; - mutable std::vector<TestCase> m_sortedFunctions; - size_t m_unnamedCount; - std::ios_base::Init m_ostreamInit; // Forces cout/ cerr to be initialised + 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> + inline 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 ); + } - class FreeFunctionTestCase : public SharedImpl<ITestCase> { - public: + template<typename L> + struct BoundLambda : BoundValueRefBase { + L m_lambda; - FreeFunctionTestCase( TestFunction fun ) : m_fun( fun ) {} + static_assert( UnaryLambdaTraits<L>::isValid, "Supplied lambda must take exactly one argument" ); + explicit BoundLambda( L const &lambda ) : m_lambda( lambda ) {} - virtual void invoke() const { - m_fun(); + auto setValue( std::string const &arg ) -> ParserResult override { + return invokeLambda<typename UnaryLambdaTraits<L>::ArgType>( m_lambda, arg ); } + }; - private: - virtual ~FreeFunctionTestCase(); + template<typename L> + struct BoundFlagLambda : BoundFlagRefBase { + L m_lambda; - TestFunction m_fun; - }; + 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" ); - inline std::string extractClassName( std::string const& classOrQualifiedMethodName ) { - std::string className = classOrQualifiedMethodName; - if( startsWith( className, '&' ) ) - { - std::size_t lastColons = className.rfind( "::" ); - std::size_t penultimateColons = className.rfind( "::", lastColons-1 ); - if( penultimateColons == std::string::npos ) - penultimateColons = 1; - className = className.substr( penultimateColons, lastColons-penultimateColons ); + explicit BoundFlagLambda( L const &lambda ) : m_lambda( lambda ) {} + + auto setFlag( bool flag ) -> ParserResult override { + return LambdaInvoker<typename UnaryLambdaTraits<L>::ReturnType>::invoke( m_lambda, flag ); } - return className; - } + }; - void registerTestCase - ( ITestCase* testCase, - char const* classOrQualifiedMethodName, - NameAndDesc const& nameAndDesc, - SourceLineInfo const& lineInfo ) { + enum class Optionality { Optional, Required }; - getMutableRegistryHub().registerTest - ( makeTestCase - ( testCase, - extractClassName( classOrQualifiedMethodName ), - nameAndDesc.name, - nameAndDesc.description, - lineInfo ) ); - } - void registerTestCaseFunction - ( TestFunction function, - SourceLineInfo const& lineInfo, - NameAndDesc const& nameAndDesc ) { - registerTestCase( new FreeFunctionTestCase( function ), "", nameAndDesc, lineInfo ); - } + struct Parser; - /////////////////////////////////////////////////////////////////////////// + class ParserBase { + public: + virtual ~ParserBase() = default; + virtual auto validate() const -> Result { return Result::ok(); } + virtual auto parse( std::string const& exeName, TokenStream const &tokens) const -> InternalParseResult = 0; + virtual auto cardinality() const -> size_t { return 1; } - AutoReg::AutoReg - ( TestFunction function, - SourceLineInfo const& lineInfo, - NameAndDesc const& nameAndDesc ) { - registerTestCaseFunction( function, lineInfo, nameAndDesc ); - } + auto parse( Args const &args ) const -> InternalParseResult { + return parse( args.exeName(), TokenStream( args ) ); + } + }; - AutoReg::~AutoReg() {} + template<typename DerivedT> + class ComposableParserImpl : public ParserBase { + public: + template<typename T> + auto operator|( T const &other ) const -> Parser; -} // end namespace Catch + template<typename T> + auto operator+( T const &other ) const -> Parser; + }; -// #included from: catch_reporter_registry.hpp -#define TWOBLUECUBES_CATCH_REPORTER_REGISTRY_HPP_INCLUDED + // 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; -#include <map> + explicit ParserRefImpl( std::shared_ptr<BoundRef> const &ref ) : m_ref( ref ) {} -namespace Catch { + public: + template<typename T> + ParserRefImpl( T &ref, std::string const &hint ) + : m_ref( std::make_shared<BoundValueRef<T>>( ref ) ), + m_hint( hint ) + {} - class ReporterRegistry : public IReporterRegistry { + template<typename LambdaT> + ParserRefImpl( LambdaT const &ref, std::string const &hint ) + : m_ref( std::make_shared<BoundLambda<LambdaT>>( ref ) ), + m_hint(hint) + {} - public: + 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 ); + }; - virtual ~ReporterRegistry() CATCH_OVERRIDE {} + auto isOptional() const -> bool { + return m_optionality == Optionality::Optional; + } - virtual IStreamingReporter* create( std::string const& name, Ptr<IConfig const> const& config ) const CATCH_OVERRIDE { - FactoryMap::const_iterator it = m_factories.find( name ); - if( it == m_factories.end() ) - return CATCH_NULL; - return it->second->create( ReporterConfig( config ) ); + auto cardinality() const -> size_t override { + if( m_ref->isContainer() ) + return 0; + else + return 1; } - void registerReporter( std::string const& name, Ptr<IReporterFactory> const& factory ) { - m_factories.insert( std::make_pair( name, factory ) ); + auto hint() const -> std::string { return m_hint; } + }; + + class ExeName : public ComposableParserImpl<ExeName> { + std::shared_ptr<std::string> m_name; + std::shared_ptr<BoundValueRefBase> m_ref; + + template<typename LambdaT> + static auto makeRef(LambdaT const &lambda) -> std::shared_ptr<BoundValueRefBase> { + return std::make_shared<BoundLambda<LambdaT>>( lambda) ; } - void registerListener( Ptr<IReporterFactory> const& factory ) { - m_listeners.push_back( factory ); + + public: + ExeName() : m_name( std::make_shared<std::string>( "<executable>" ) ) {} + + explicit ExeName( std::string &ref ) : ExeName() { + m_ref = std::make_shared<BoundValueRef<std::string>>( ref ); } - virtual FactoryMap const& getFactories() const CATCH_OVERRIDE { - return m_factories; + template<typename LambdaT> + explicit ExeName( LambdaT const& lambda ) : ExeName() { + m_ref = std::make_shared<BoundLambda<LambdaT>>( lambda ); } - virtual Listeners const& getListeners() const CATCH_OVERRIDE { - return m_listeners; + + // The exe name is not parsed out of the normal tokens, but is handled specially + auto parse( std::string const&, TokenStream const &tokens ) const -> InternalParseResult override { + return InternalParseResult::ok( ParseState( ParseResultType::NoMatch, tokens ) ); } - private: - FactoryMap m_factories; - Listeners m_listeners; + auto name() const -> std::string { return *m_name; } + auto set( std::string const& newName ) -> ParserResult { + + auto lastSlash = newName.find_last_of( "\\/" ); + auto filename = ( lastSlash == std::string::npos ) + ? newName + : newName.substr( lastSlash+1 ); + + *m_name = filename; + if( m_ref ) + return m_ref->setValue( filename ); + else + return ParserResult::ok( ParseResultType::Matched ); + } }; -} -// #included from: catch_exception_translator_registry.hpp -#define TWOBLUECUBES_CATCH_EXCEPTION_TRANSLATOR_REGISTRY_HPP_INCLUDED + class Arg : public ParserRefImpl<Arg> { + public: + using ParserRefImpl::ParserRefImpl; -#ifdef __OBJC__ -#import "Foundation/Foundation.h" + auto parse( std::string const &, TokenStream const &tokens ) const -> InternalParseResult override { + auto validationResult = validate(); + if( !validationResult ) + return InternalParseResult( validationResult ); + + auto remainingTokens = tokens; + auto const &token = *remainingTokens; + if( token.type != TokenType::Argument ) + return InternalParseResult::ok( ParseState( ParseResultType::NoMatch, remainingTokens ) ); + + assert( !m_ref->isFlag() ); + auto valueRef = static_cast<detail::BoundValueRefBase*>( m_ref.get() ); + + auto result = valueRef->setValue( remainingTokens->token ); + if( !result ) + return InternalParseResult( result ); + else + return InternalParseResult::ok( ParseState( ParseResultType::Matched, ++remainingTokens ) ); + } + }; + + inline auto normaliseOpt( std::string const &optName ) -> std::string { +#ifdef CATCH_PLATFORM_WINDOWS + if( optName[0] == '/' ) + return "-" + optName.substr( 1 ); + else #endif + return optName; + } -namespace Catch { + class Opt : public ParserRefImpl<Opt> { + protected: + std::vector<std::string> m_optNames; - class ExceptionTranslatorRegistry : public IExceptionTranslatorRegistry { public: - ~ExceptionTranslatorRegistry() { - deleteAll( m_translators ); + template<typename LambdaT> + explicit Opt( LambdaT const &ref ) : ParserRefImpl( std::make_shared<BoundFlagLambda<LambdaT>>( ref ) ) {} + + explicit Opt( bool &ref ) : ParserRefImpl( std::make_shared<BoundFlagRef>( ref ) ) {} + + template<typename LambdaT> + Opt( LambdaT const &ref, std::string const &hint ) : ParserRefImpl( ref, hint ) {} + + template<typename T> + Opt( T &ref, std::string const &hint ) : ParserRefImpl( ref, hint ) {} + + auto operator[]( std::string const &optName ) -> Opt & { + m_optNames.push_back( optName ); + return *this; } - virtual void registerTranslator( const IExceptionTranslator* translator ) { - m_translators.push_back( translator ); + auto getHelpColumns() const -> std::vector<HelpColumns> { + std::ostringstream oss; + bool first = true; + for( auto const &opt : m_optNames ) { + if (first) + first = false; + else + oss << ", "; + oss << opt; + } + if( !m_hint.empty() ) + oss << " <" << m_hint << ">"; + return { { oss.str(), m_description } }; } - virtual std::string translateActiveException() const { - try { -#ifdef __OBJC__ - // In Objective-C try objective-c exceptions first - @try { - return tryTranslators(); - } - @catch (NSException *exception) { - return Catch::toString( [exception description] ); + auto isMatch( std::string const &optToken ) const -> bool { + auto normalisedToken = normaliseOpt( optToken ); + for( auto const &name : m_optNames ) { + if( normaliseOpt( name ) == normalisedToken ) + return true; + } + return false; + } + + using ParserBase::parse; + + auto parse( std::string const&, TokenStream const &tokens ) const -> InternalParseResult override { + auto validationResult = validate(); + if( !validationResult ) + return InternalParseResult( validationResult ); + + auto remainingTokens = tokens; + if( remainingTokens && remainingTokens->type == TokenType::Option ) { + auto const &token = *remainingTokens; + if( isMatch(token.token ) ) { + if( m_ref->isFlag() ) { + auto flagRef = static_cast<detail::BoundFlagRefBase*>( m_ref.get() ); + auto result = flagRef->setFlag( true ); + if( !result ) + return InternalParseResult( result ); + if( result.value() == ParseResultType::ShortCircuitAll ) + return InternalParseResult::ok( ParseState( result.value(), remainingTokens ) ); + } else { + auto valueRef = static_cast<detail::BoundValueRefBase*>( m_ref.get() ); + ++remainingTokens; + if( !remainingTokens ) + return InternalParseResult::runtimeError( "Expected argument following " + token.token ); + auto const &argToken = *remainingTokens; + if( argToken.type != TokenType::Argument ) + return InternalParseResult::runtimeError( "Expected argument following " + token.token ); + auto result = valueRef->setValue( argToken.token ); + if( !result ) + return InternalParseResult( result ); + if( result.value() == ParseResultType::ShortCircuitAll ) + return InternalParseResult::ok( ParseState( result.value(), remainingTokens ) ); + } + return InternalParseResult::ok( ParseState( ParseResultType::Matched, ++remainingTokens ) ); } + } + return InternalParseResult::ok( ParseState( ParseResultType::NoMatch, remainingTokens ) ); + } + + auto validate() const -> Result override { + if( m_optNames.empty() ) + return Result::logicError( "No options supplied to Opt" ); + for( auto const &name : m_optNames ) { + if( name.empty() ) + return Result::logicError( "Option name cannot be empty" ); +#ifdef CATCH_PLATFORM_WINDOWS + if( name[0] != '-' && name[0] != '/' ) + return Result::logicError( "Option name must begin with '-' or '/'" ); #else - return tryTranslators(); + if( name[0] != '-' ) + return Result::logicError( "Option name must begin with '-'" ); #endif } - catch( TestFailureException& ) { - throw; - } - catch( std::exception& ex ) { - return ex.what(); - } - catch( std::string& msg ) { - return msg; - } - catch( const char* msg ) { - return msg; - } - catch(...) { - return "Unknown exception"; - } + return ParserRefImpl::validate(); } + }; - std::string tryTranslators() const { - if( m_translators.empty() ) - throw; - else - return m_translators[0]->translate( m_translators.begin()+1, m_translators.end() ); + struct Help : Opt { + Help( bool &showHelpFlag ) + : Opt([&]( bool flag ) { + showHelpFlag = flag; + return ParserResult::ok( ParseResultType::ShortCircuitAll ); + }) + { + static_cast<Opt &>( *this ) + ("display usage information") + ["-?"]["-h"]["--help"] + .optional(); } - - private: - std::vector<const IExceptionTranslator*> m_translators; }; -} -namespace Catch { + struct Parser : ParserBase { - namespace { + mutable ExeName m_exeName; + std::vector<Opt> m_options; + std::vector<Arg> m_args; + + auto operator|=( ExeName const &exeName ) -> Parser & { + m_exeName = exeName; + return *this; + } - class RegistryHub : public IRegistryHub, public IMutableRegistryHub { + auto operator|=( Arg const &arg ) -> Parser & { + m_args.push_back(arg); + return *this; + } - RegistryHub( RegistryHub const& ); - void operator=( RegistryHub const& ); + auto operator|=( Opt const &opt ) -> Parser & { + m_options.push_back(opt); + return *this; + } - public: // IRegistryHub - RegistryHub() { - } - virtual IReporterRegistry const& getReporterRegistry() const CATCH_OVERRIDE { - return m_reporterRegistry; - } - virtual ITestCaseRegistry const& getTestCaseRegistry() const CATCH_OVERRIDE { - return m_testCaseRegistry; + auto operator|=( Parser const &other ) -> Parser & { + m_options.insert(m_options.end(), other.m_options.begin(), other.m_options.end()); + m_args.insert(m_args.end(), other.m_args.begin(), other.m_args.end()); + return *this; + } + + template<typename T> + auto operator|( T const &other ) const -> Parser { + return Parser( *this ) |= other; + } + + // Forward deprecated interface with '+' instead of '|' + template<typename T> + auto operator+=( T const &other ) -> Parser & { return operator|=( other ); } + template<typename T> + auto operator+( T const &other ) const -> Parser { return operator|( other ); } + + auto getHelpColumns() const -> std::vector<HelpColumns> { + std::vector<HelpColumns> cols; + for (auto const &o : m_options) { + auto childCols = o.getHelpColumns(); + cols.insert( cols.end(), childCols.begin(), childCols.end() ); } - virtual IExceptionTranslatorRegistry& getExceptionTranslatorRegistry() CATCH_OVERRIDE { - return m_exceptionTranslatorRegistry; + return cols; + } + + void writeToStream( std::ostream &os ) const { + if (!m_exeName.name().empty()) { + os << "usage:\n" << " " << m_exeName.name() << " "; + bool required = true, first = true; + for( auto const &arg : m_args ) { + if (first) + first = false; + else + os << " "; + if( arg.isOptional() && required ) { + os << "["; + required = false; + } + os << "<" << arg.hint() << ">"; + if( arg.cardinality() == 0 ) + os << " ... "; + } + if( !required ) + os << "]"; + if( !m_options.empty() ) + os << " options"; + os << "\n\nwhere options are:" << std::endl; } - public: // IMutableRegistryHub - virtual void registerReporter( std::string const& name, Ptr<IReporterFactory> const& factory ) CATCH_OVERRIDE { - m_reporterRegistry.registerReporter( name, factory ); + auto rows = getHelpColumns(); + size_t consoleWidth = CATCH_CLARA_CONFIG_CONSOLE_WIDTH; + size_t optWidth = 0; + for( auto const &cols : rows ) + optWidth = (std::max)(optWidth, cols.left.size() + 2); + + optWidth = (std::min)(optWidth, consoleWidth/2); + + for( auto const &cols : rows ) { + auto row = + TextFlow::Column( cols.left ).width( optWidth ).indent( 2 ) + + TextFlow::Spacer(4) + + TextFlow::Column( cols.right ).width( consoleWidth - 7 - optWidth ); + os << row << std::endl; } - virtual void registerListener( Ptr<IReporterFactory> const& factory ) CATCH_OVERRIDE { - m_reporterRegistry.registerListener( factory ); + } + + friend auto operator<<( std::ostream &os, Parser const &parser ) -> std::ostream& { + parser.writeToStream( os ); + return os; + } + + auto validate() const -> Result override { + for( auto const &opt : m_options ) { + auto result = opt.validate(); + if( !result ) + return result; } - virtual void registerTest( TestCase const& testInfo ) CATCH_OVERRIDE { - m_testCaseRegistry.registerTest( testInfo ); + for( auto const &arg : m_args ) { + auto result = arg.validate(); + if( !result ) + return result; } - virtual void registerTranslator( const IExceptionTranslator* translator ) CATCH_OVERRIDE { - m_exceptionTranslatorRegistry.registerTranslator( translator ); + return Result::ok(); + } + + using ParserBase::parse; + + auto parse( std::string const& exeName, TokenStream const &tokens ) const -> InternalParseResult override { + + struct ParserInfo { + ParserBase const* parser = nullptr; + size_t count = 0; + }; + const size_t totalParsers = m_options.size() + m_args.size(); + assert( totalParsers < 512 ); + // ParserInfo parseInfos[totalParsers]; // <-- this is what we really want to do + ParserInfo parseInfos[512]; + + { + size_t i = 0; + for (auto const &opt : m_options) parseInfos[i++].parser = &opt; + for (auto const &arg : m_args) parseInfos[i++].parser = &arg; } - private: - TestRegistry m_testCaseRegistry; - ReporterRegistry m_reporterRegistry; - ExceptionTranslatorRegistry m_exceptionTranslatorRegistry; - }; + m_exeName.set( exeName ); + + auto result = InternalParseResult::ok( ParseState( ParseResultType::NoMatch, tokens ) ); + while( result.value().remainingTokens() ) { + bool tokenParsed = false; + + for( size_t i = 0; i < totalParsers; ++i ) { + auto& parseInfo = parseInfos[i]; + if( parseInfo.parser->cardinality() == 0 || parseInfo.count < parseInfo.parser->cardinality() ) { + result = parseInfo.parser->parse(exeName, result.value().remainingTokens()); + if (!result) + return result; + if (result.value().type() != ParseResultType::NoMatch) { + tokenParsed = true; + ++parseInfo.count; + break; + } + } + } - // Single, global, instance - inline RegistryHub*& getTheRegistryHub() { - static RegistryHub* theRegistryHub = CATCH_NULL; - if( !theRegistryHub ) - theRegistryHub = new RegistryHub(); - return theRegistryHub; + if( result.value().type() == ParseResultType::ShortCircuitAll ) + return result; + if( !tokenParsed ) + return InternalParseResult::runtimeError( "Unrecognised token: " + result.value().remainingTokens()->token ); + } + // !TBD Check missing required options + return result; } - } + }; - IRegistryHub& getRegistryHub() { - return *getTheRegistryHub(); - } - IMutableRegistryHub& getMutableRegistryHub() { - return *getTheRegistryHub(); - } - void cleanUp() { - delete getTheRegistryHub(); - getTheRegistryHub() = CATCH_NULL; - cleanUpContext(); - } - std::string translateActiveException() { - return getRegistryHub().getExceptionTranslatorRegistry().translateActiveException(); + template<typename DerivedT> + template<typename T> + auto ComposableParserImpl<DerivedT>::operator|( T const &other ) const -> Parser { + return Parser() | static_cast<DerivedT const &>( *this ) | other; } +} // namespace detail -} // end namespace Catch +// A Combined parser +using detail::Parser; -// #included from: catch_notimplemented_exception.hpp -#define TWOBLUECUBES_CATCH_NOTIMPLEMENTED_EXCEPTION_HPP_INCLUDED +// A parser for options +using detail::Opt; -#include <sstream> +// A parser for arguments +using detail::Arg; -namespace Catch { +// Wrapper for argc, argv from main() +using detail::Args; - NotImplementedException::NotImplementedException( SourceLineInfo const& lineInfo ) - : m_lineInfo( lineInfo ) { - std::ostringstream oss; - oss << lineInfo << ": function "; - oss << "not implemented"; - m_what = oss.str(); - } +// Specifies the name of the executable +using detail::ExeName; - const char* NotImplementedException::what() const CATCH_NOEXCEPT { - return m_what.c_str(); - } +// Convenience wrapper for option parser that specifies the help option +using detail::Help; -} // end namespace Catch +// enum of result types from a parse +using detail::ParseResultType; -// #included from: catch_context_impl.hpp -#define TWOBLUECUBES_CATCH_CONTEXT_IMPL_HPP_INCLUDED +// Result type for parser operation +using detail::ParserResult; -// #included from: catch_stream.hpp -#define TWOBLUECUBES_CATCH_STREAM_HPP_INCLUDED +}} // namespace Catch::clara -#include <stdexcept> -#include <cstdio> -#include <iostream> +// end clara.hpp +#ifdef __clang__ +#pragma clang diagnostic pop +#endif + +// Restore Clara's value for console width, if present +#ifdef CATCH_TEMP_CLARA_CONFIG_CONSOLE_WIDTH +#define CATCH_CLARA_TEXTFLOW_CONFIG_CONSOLE_WIDTH CATCH_TEMP_CLARA_CONFIG_CONSOLE_WIDTH +#undef CATCH_TEMP_CLARA_CONFIG_CONSOLE_WIDTH +#endif +// end catch_clara.h namespace Catch { - template<typename WriterF, size_t bufferSize=256> - class StreamBufImpl : public StreamBufBase { - char data[bufferSize]; - WriterF m_writer; + clara::Parser makeCommandLineParser( ConfigData& config ); - public: - StreamBufImpl() { - setp( data, data + sizeof(data) ); - } +} // end namespace Catch - ~StreamBufImpl() CATCH_NOEXCEPT { - sync(); - } +// end catch_commandline.h +#include <fstream> +#include <ctime> - private: - int overflow( int c ) { - sync(); +namespace Catch { - if( c != EOF ) { - if( pbase() == epptr() ) - m_writer( std::string( 1, static_cast<char>( c ) ) ); - else - sputc( static_cast<char>( c ) ); - } - return 0; - } + clara::Parser makeCommandLineParser( ConfigData& config ) { - int sync() { - if( pbase() != pptr() ) { - m_writer( std::string( pbase(), static_cast<std::string::size_type>( pptr() - pbase() ) ) ); - setp( pbase(), epptr() ); - } - return 0; - } - }; + using namespace clara; - /////////////////////////////////////////////////////////////////////////// + auto const setWarning = [&]( std::string const& warning ) { + auto warningSet = [&]() { + if( warning == "NoAssertions" ) + return WarnAbout::NoAssertions; - FileStream::FileStream( std::string const& filename ) { - m_ofs.open( filename.c_str() ); - if( m_ofs.fail() ) { - std::ostringstream oss; - oss << "Unable to open file: '" << filename << '\''; - throw std::domain_error( oss.str() ); - } - } + if ( warning == "NoTests" ) + return WarnAbout::NoTests; - std::ostream& FileStream::stream() const { - return m_ofs; - } + return WarnAbout::Nothing; + }(); + + if (warningSet == WarnAbout::Nothing) + return ParserResult::runtimeError( "Unrecognised warning: '" + warning + "'" ); + config.warnings = static_cast<WarnAbout::What>( config.warnings | warningSet ); + return ParserResult::ok( ParseResultType::Matched ); + }; + auto const loadTestNamesFromFile = [&]( std::string const& filename ) { + std::ifstream f( filename.c_str() ); + if( !f.is_open() ) + return ParserResult::runtimeError( "Unable to load input file: '" + filename + "'" ); + + std::string line; + while( std::getline( f, line ) ) { + line = trim(line); + if( !line.empty() && !startsWith( line, '#' ) ) { + if( !startsWith( line, '"' ) ) + line = '"' + line + '"'; + config.testsOrTags.push_back( line + ',' ); + } + } + return ParserResult::ok( ParseResultType::Matched ); + }; + auto const setTestOrder = [&]( std::string const& order ) { + if( startsWith( "declared", order ) ) + config.runOrder = RunTests::InDeclarationOrder; + else if( startsWith( "lexical", order ) ) + config.runOrder = RunTests::InLexicographicalOrder; + else if( startsWith( "random", order ) ) + config.runOrder = RunTests::InRandomOrder; + else + return clara::ParserResult::runtimeError( "Unrecognised ordering: '" + order + "'" ); + return ParserResult::ok( ParseResultType::Matched ); + }; + auto const setRngSeed = [&]( std::string const& seed ) { + if( seed != "time" ) + return clara::detail::convertInto( seed, config.rngSeed ); + config.rngSeed = static_cast<unsigned int>( std::time(nullptr) ); + return ParserResult::ok( ParseResultType::Matched ); + }; + auto const setColourUsage = [&]( std::string const& useColour ) { + auto mode = toLower( useColour ); + + if( mode == "yes" ) + config.useColour = UseColour::Yes; + else if( mode == "no" ) + config.useColour = UseColour::No; + else if( mode == "auto" ) + config.useColour = UseColour::Auto; + else + return ParserResult::runtimeError( "colour mode must be one of: auto, yes or no. '" + useColour + "' not recognised" ); + return ParserResult::ok( ParseResultType::Matched ); + }; + auto const setWaitForKeypress = [&]( std::string const& keypress ) { + auto keypressLc = toLower( keypress ); + if( keypressLc == "start" ) + config.waitForKeypress = WaitForKeypress::BeforeStart; + else if( keypressLc == "exit" ) + config.waitForKeypress = WaitForKeypress::BeforeExit; + else if( keypressLc == "both" ) + config.waitForKeypress = WaitForKeypress::BeforeStartAndExit; + else + return ParserResult::runtimeError( "keypress argument must be one of: start, exit or both. '" + keypress + "' not recognised" ); + return ParserResult::ok( ParseResultType::Matched ); + }; + auto const setVerbosity = [&]( std::string const& verbosity ) { + auto lcVerbosity = toLower( verbosity ); + if( lcVerbosity == "quiet" ) + config.verbosity = Verbosity::Quiet; + else if( lcVerbosity == "normal" ) + config.verbosity = Verbosity::Normal; + else if( lcVerbosity == "high" ) + config.verbosity = Verbosity::High; + else + return ParserResult::runtimeError( "Unrecognised verbosity, '" + verbosity + "'" ); + return ParserResult::ok( ParseResultType::Matched ); + }; + auto const setReporter = [&]( std::string const& reporter ) { + IReporterRegistry::FactoryMap const& factories = getRegistryHub().getReporterRegistry().getFactories(); - struct OutputDebugWriter { + auto lcReporter = toLower( reporter ); + auto result = factories.find( lcReporter ); - void operator()( std::string const&str ) { - writeToDebugConsole( str ); - } - }; + if( factories.end() != result ) + config.reporterName = lcReporter; + else + return ParserResult::runtimeError( "Unrecognized reporter, '" + reporter + "'. Check available with --list-reporters" ); + return ParserResult::ok( ParseResultType::Matched ); + }; - DebugOutStream::DebugOutStream() - : m_streamBuf( new StreamBufImpl<OutputDebugWriter>() ), - m_os( m_streamBuf.get() ) - {} + auto cli + = ExeName( config.processName ) + | Help( config.showHelp ) + | Opt( config.listTests ) + ["-l"]["--list-tests"] + ( "list all/matching test cases" ) + | Opt( config.listTags ) + ["-t"]["--list-tags"] + ( "list all/matching tags" ) + | Opt( config.showSuccessfulTests ) + ["-s"]["--success"] + ( "include successful tests in output" ) + | Opt( config.shouldDebugBreak ) + ["-b"]["--break"] + ( "break into debugger on failure" ) + | Opt( config.noThrow ) + ["-e"]["--nothrow"] + ( "skip exception tests" ) + | Opt( config.showInvisibles ) + ["-i"]["--invisibles"] + ( "show invisibles (tabs, newlines)" ) + | Opt( config.outputFilename, "filename" ) + ["-o"]["--out"] + ( "output filename" ) + | Opt( setReporter, "name" ) + ["-r"]["--reporter"] + ( "reporter to use (defaults to console)" ) + | Opt( config.name, "name" ) + ["-n"]["--name"] + ( "suite name" ) + | Opt( [&]( bool ){ config.abortAfter = 1; } ) + ["-a"]["--abort"] + ( "abort at first failure" ) + | Opt( [&]( int x ){ config.abortAfter = x; }, "no. failures" ) + ["-x"]["--abortx"] + ( "abort after x failures" ) + | Opt( setWarning, "warning name" ) + ["-w"]["--warn"] + ( "enable warnings" ) + | Opt( [&]( bool flag ) { config.showDurations = flag ? ShowDurations::Always : ShowDurations::Never; }, "yes|no" ) + ["-d"]["--durations"] + ( "show test durations" ) + | Opt( loadTestNamesFromFile, "filename" ) + ["-f"]["--input-file"] + ( "load test names to run from a file" ) + | Opt( config.filenamesAsTags ) + ["-#"]["--filenames-as-tags"] + ( "adds a tag for the filename" ) + | Opt( config.sectionsToRun, "section name" ) + ["-c"]["--section"] + ( "specify section to run" ) + | Opt( setVerbosity, "quiet|normal|high" ) + ["-v"]["--verbosity"] + ( "set output verbosity" ) + | Opt( config.listTestNamesOnly ) + ["--list-test-names-only"] + ( "list all/matching test cases names only" ) + | Opt( config.listReporters ) + ["--list-reporters"] + ( "list all reporters" ) + | Opt( setTestOrder, "decl|lex|rand" ) + ["--order"] + ( "test case order (defaults to decl)" ) + | Opt( setRngSeed, "'time'|number" ) + ["--rng-seed"] + ( "set a specific seed for random numbers" ) + | Opt( setColourUsage, "yes|no" ) + ["--use-colour"] + ( "should output be colourised" ) + | Opt( config.libIdentify ) + ["--libidentify"] + ( "report name and version according to libidentify standard" ) + | Opt( setWaitForKeypress, "start|exit|both" ) + ["--wait-for-keypress"] + ( "waits for a keypress before exiting" ) + | Opt( config.benchmarkSamples, "samples" ) + ["--benchmark-samples"] + ( "number of samples to collect (default: 100)" ) + | Opt( config.benchmarkResamples, "resamples" ) + ["--benchmark-resamples"] + ( "number of resamples for the bootstrap (default: 100000)" ) + | Opt( config.benchmarkConfidenceInterval, "confidence interval" ) + ["--benchmark-confidence-interval"] + ( "confidence interval for the bootstrap (between 0 and 1, default: 0.95)" ) + | Opt( config.benchmarkNoAnalysis ) + ["--benchmark-no-analysis"] + ( "perform only measurements; do not perform any analysis" ) + | Arg( config.testsOrTags, "test name|pattern|tags" ) + ( "which test or tests to use" ); - std::ostream& DebugOutStream::stream() const { - return m_os; + return cli; } - // Store the streambuf from cout up-front because - // cout may get redirected when running tests - CoutStream::CoutStream() - : m_os( Catch::cout().rdbuf() ) - {} +} // end namespace Catch +// end catch_commandline.cpp +// start catch_common.cpp - std::ostream& CoutStream::stream() const { - return m_os; - } +#include <cstring> +#include <ostream> -#ifndef CATCH_CONFIG_NOSTDOUT // If you #define this you must implement these functions - std::ostream& cout() { - return std::cout; +namespace Catch { + + bool SourceLineInfo::empty() const noexcept { + return file[0] == '\0'; + } + bool SourceLineInfo::operator == ( SourceLineInfo const& other ) const noexcept { + return line == other.line && (file == other.file || std::strcmp(file, other.file) == 0); } - std::ostream& cerr() { - return std::cerr; + bool SourceLineInfo::operator < ( SourceLineInfo const& other ) const noexcept { + // We can assume that the same file will usually have the same pointer. + // Thus, if the pointers are the same, there is no point in calling the strcmp + return line < other.line || ( line == other.line && file != other.file && (std::strcmp(file, other.file) < 0)); } + + std::ostream& operator << ( std::ostream& os, SourceLineInfo const& info ) { +#ifndef __GNUG__ + os << info.file << '(' << info.line << ')'; +#else + os << info.file << ':' << info.line; #endif + return os; + } + + std::string StreamEndStop::operator+() const { + return std::string(); + } + + NonCopyable::NonCopyable() = default; + NonCopyable::~NonCopyable() = default; + } +// end catch_common.cpp +// start catch_config.cpp namespace Catch { - class Context : public IMutableContext { + Config::Config( ConfigData const& data ) + : m_data( data ), + m_stream( openStream() ) + { + TestSpecParser parser(ITagAliasRegistry::get()); + if (!data.testsOrTags.empty()) { + m_hasTestFilters = true; + for( auto const& testOrTags : data.testsOrTags ) + parser.parse( testOrTags ); + } + m_testSpec = parser.testSpec(); + } - Context() : m_config( CATCH_NULL ), m_runner( CATCH_NULL ), m_resultCapture( CATCH_NULL ) {} - Context( Context const& ); - void operator=( Context const& ); + std::string const& Config::getFilename() const { + return m_data.outputFilename ; + } - public: - virtual ~Context() { - deleteAllValues( m_generatorsByTestName ); - } + bool Config::listTests() const { return m_data.listTests; } + bool Config::listTestNamesOnly() const { return m_data.listTestNamesOnly; } + bool Config::listTags() const { return m_data.listTags; } + bool Config::listReporters() const { return m_data.listReporters; } - public: // IContext - virtual IResultCapture* getResultCapture() { - return m_resultCapture; - } - virtual IRunner* getRunner() { - return m_runner; - } - virtual size_t getGeneratorIndex( std::string const& fileInfo, size_t totalSize ) { - return getGeneratorsForCurrentTest() - .getGeneratorInfo( fileInfo, totalSize ) - .getCurrentIndex(); - } - virtual bool advanceGeneratorsForCurrentTest() { - IGeneratorsForTest* generators = findGeneratorsForCurrentTest(); - return generators && generators->moveNext(); - } + std::string Config::getProcessName() const { return m_data.processName; } + std::string const& Config::getReporterName() const { return m_data.reporterName; } - virtual Ptr<IConfig const> getConfig() const { - return m_config; - } + std::vector<std::string> const& Config::getTestsOrTags() const { return m_data.testsOrTags; } + std::vector<std::string> const& Config::getSectionsToRun() const { return m_data.sectionsToRun; } - public: // IMutableContext - virtual void setResultCapture( IResultCapture* resultCapture ) { - m_resultCapture = resultCapture; - } - virtual void setRunner( IRunner* runner ) { - m_runner = runner; - } - virtual void setConfig( Ptr<IConfig const> const& config ) { - m_config = config; - } + TestSpec const& Config::testSpec() const { return m_testSpec; } + bool Config::hasTestFilters() const { return m_hasTestFilters; } - friend IMutableContext& getCurrentMutableContext(); + bool Config::showHelp() const { return m_data.showHelp; } - private: - IGeneratorsForTest* findGeneratorsForCurrentTest() { - std::string testName = getResultCapture()->getCurrentTestName(); + // IConfig interface + bool Config::allowThrows() const { return !m_data.noThrow; } + std::ostream& Config::stream() const { return m_stream->stream(); } + std::string Config::name() const { return m_data.name.empty() ? m_data.processName : m_data.name; } + bool Config::includeSuccessfulResults() const { return m_data.showSuccessfulTests; } + bool Config::warnAboutMissingAssertions() const { return !!(m_data.warnings & WarnAbout::NoAssertions); } + bool Config::warnAboutNoTests() const { return !!(m_data.warnings & WarnAbout::NoTests); } + ShowDurations::OrNot Config::showDurations() const { return m_data.showDurations; } + RunTests::InWhatOrder Config::runOrder() const { return m_data.runOrder; } + unsigned int Config::rngSeed() const { return m_data.rngSeed; } + UseColour::YesOrNo Config::useColour() const { return m_data.useColour; } + bool Config::shouldDebugBreak() const { return m_data.shouldDebugBreak; } + int Config::abortAfter() const { return m_data.abortAfter; } + bool Config::showInvisibles() const { return m_data.showInvisibles; } + Verbosity Config::verbosity() const { return m_data.verbosity; } - std::map<std::string, IGeneratorsForTest*>::const_iterator it = - m_generatorsByTestName.find( testName ); - return it != m_generatorsByTestName.end() - ? it->second - : CATCH_NULL; - } + bool Config::benchmarkNoAnalysis() const { return m_data.benchmarkNoAnalysis; } + int Config::benchmarkSamples() const { return m_data.benchmarkSamples; } + double Config::benchmarkConfidenceInterval() const { return m_data.benchmarkConfidenceInterval; } + unsigned int Config::benchmarkResamples() const { return m_data.benchmarkResamples; } - IGeneratorsForTest& getGeneratorsForCurrentTest() { - IGeneratorsForTest* generators = findGeneratorsForCurrentTest(); - if( !generators ) { - std::string testName = getResultCapture()->getCurrentTestName(); - generators = createGeneratorsForTest(); - m_generatorsByTestName.insert( std::make_pair( testName, generators ) ); - } - return *generators; - } + IStream const* Config::openStream() { + return Catch::makeStream(m_data.outputFilename); + } + +} // end namespace Catch +// end catch_config.cpp +// start catch_console_colour.cpp + +#if defined(__clang__) +# pragma clang diagnostic push +# pragma clang diagnostic ignored "-Wexit-time-destructors" +#endif + +// start catch_errno_guard.h +namespace Catch { + + class ErrnoGuard { + public: + ErrnoGuard(); + ~ErrnoGuard(); private: - Ptr<IConfig const> m_config; - IRunner* m_runner; - IResultCapture* m_resultCapture; - std::map<std::string, IGeneratorsForTest*> m_generatorsByTestName; + int m_oldErrno; }; - namespace { - Context* currentContext = CATCH_NULL; - } - IMutableContext& getCurrentMutableContext() { - if( !currentContext ) - currentContext = new Context(); - return *currentContext; - } - IContext& getCurrentContext() { - return getCurrentMutableContext(); - } - - void cleanUpContext() { - delete currentContext; - currentContext = CATCH_NULL; - } } -// #included from: catch_console_colour_impl.hpp -#define TWOBLUECUBES_CATCH_CONSOLE_COLOUR_IMPL_HPP_INCLUDED +// end catch_errno_guard.h +#include <sstream> namespace Catch { namespace { struct IColourImpl { - virtual ~IColourImpl() {} + virtual ~IColourImpl() = default; virtual void use( Colour::Code _colourCode ) = 0; }; @@ -7529,7 +9679,7 @@ namespace { originalBackgroundAttributes = csbiInfo.wAttributes & ~( FOREGROUND_GREEN | FOREGROUND_RED | FOREGROUND_BLUE | FOREGROUND_INTENSITY ); } - virtual void use( Colour::Code _colourCode ) { + void use( Colour::Code _colourCode ) override { switch( _colourCode ) { case Colour::None: return setTextAttribute( originalForegroundAttributes ); case Colour::White: return setTextAttribute( FOREGROUND_GREEN | FOREGROUND_RED | FOREGROUND_BLUE ); @@ -7544,8 +9694,12 @@ namespace { case Colour::BrightRed: return setTextAttribute( FOREGROUND_INTENSITY | FOREGROUND_RED ); case Colour::BrightGreen: return setTextAttribute( FOREGROUND_INTENSITY | FOREGROUND_GREEN ); case Colour::BrightWhite: return setTextAttribute( FOREGROUND_INTENSITY | FOREGROUND_GREEN | FOREGROUND_RED | FOREGROUND_BLUE ); + case Colour::BrightYellow: return setTextAttribute( FOREGROUND_INTENSITY | FOREGROUND_RED | FOREGROUND_GREEN ); + + case Colour::Bright: CATCH_INTERNAL_ERROR( "not a colour" ); - case Colour::Bright: throw std::logic_error( "not a colour" ); + default: + CATCH_ERROR( "Unknown colour requested" ); } } @@ -7561,14 +9715,12 @@ namespace { IColourImpl* platformColourInstance() { static Win32ColourImpl s_instance; - Ptr<IConfig const> config = getCurrentContext().getConfig(); + IConfigPtr config = getCurrentContext().getConfig(); UseColour::YesOrNo colourMode = config ? config->useColour() : UseColour::Auto; if( colourMode == UseColour::Auto ) - colourMode = !isDebuggerActive() - ? UseColour::Yes - : UseColour::No; + colourMode = UseColour::Yes; return colourMode == UseColour::Yes ? &s_instance : NoColourImpl::instance(); @@ -7590,7 +9742,7 @@ namespace { // https://github.com/philsquared/Catch/pull/131 class PosixColourImpl : public IColourImpl { public: - virtual void use( Colour::Code _colourCode ) { + void use( Colour::Code _colourCode ) override { switch( _colourCode ) { case Colour::None: case Colour::White: return setColour( "[0m" ); @@ -7605,8 +9757,10 @@ namespace { case Colour::BrightRed: return setColour( "[1;31m" ); case Colour::BrightGreen: return setColour( "[1;32m" ); case Colour::BrightWhite: return setColour( "[1;37m" ); + case Colour::BrightYellow: return setColour( "[1;33m" ); - case Colour::Bright: throw std::logic_error( "not a colour" ); + case Colour::Bright: CATCH_INTERNAL_ERROR( "not a colour" ); + default: CATCH_INTERNAL_ERROR( "Unknown colour requested" ); } } static IColourImpl* instance() { @@ -7616,17 +9770,31 @@ namespace { private: void setColour( const char* _escapeCode ) { - Catch::cout() << '\033' << _escapeCode; + getCurrentContext().getConfig()->stream() + << '\033' << _escapeCode; } }; + bool useColourOnPlatform() { + return +#ifdef CATCH_PLATFORM_MAC + !isDebuggerActive() && +#endif +#if !(defined(__DJGPP__) && defined(__STRICT_ANSI__)) + isatty(STDOUT_FILENO) +#else + false +#endif + ; + } IColourImpl* platformColourInstance() { - Ptr<IConfig const> config = getCurrentContext().getConfig(); + ErrnoGuard guard; + IConfigPtr config = getCurrentContext().getConfig(); UseColour::YesOrNo colourMode = config ? config->useColour() : UseColour::Auto; if( colourMode == UseColour::Auto ) - colourMode = (!isDebuggerActive() && isatty(STDOUT_FILENO) ) + colourMode = useColourOnPlatform() ? UseColour::Yes : UseColour::No; return colourMode == UseColour::Yes @@ -7649,410 +9817,1421 @@ namespace Catch { namespace Catch { - Colour::Colour( Code _colourCode ) : m_moved( false ) { use( _colourCode ); } - Colour::Colour( Colour const& _other ) : m_moved( false ) { const_cast<Colour&>( _other ).m_moved = true; } + Colour::Colour( Code _colourCode ) { use( _colourCode ); } + Colour::Colour( Colour&& rhs ) noexcept { + m_moved = rhs.m_moved; + rhs.m_moved = true; + } + Colour& Colour::operator=( Colour&& rhs ) noexcept { + m_moved = rhs.m_moved; + rhs.m_moved = true; + return *this; + } + Colour::~Colour(){ if( !m_moved ) use( None ); } void Colour::use( Code _colourCode ) { static IColourImpl* impl = platformColourInstance(); - impl->use( _colourCode ); + // Strictly speaking, this cannot possibly happen. + // However, under some conditions it does happen (see #1626), + // and this change is small enough that we can let practicality + // triumph over purity in this case. + if (impl != NULL) { + impl->use( _colourCode ); + } + } + + std::ostream& operator << ( std::ostream& os, Colour const& ) { + return os; } } // end namespace Catch -// #included from: catch_generators_impl.hpp -#define TWOBLUECUBES_CATCH_GENERATORS_IMPL_HPP_INCLUDED +#if defined(__clang__) +# pragma clang diagnostic pop +#endif -#include <vector> -#include <string> -#include <map> +// end catch_console_colour.cpp +// start catch_context.cpp namespace Catch { - struct GeneratorInfo : IGeneratorInfo { + class Context : public IMutableContext, NonCopyable { - GeneratorInfo( std::size_t size ) - : m_size( size ), - m_currentIndex( 0 ) - {} + public: // IContext + IResultCapture* getResultCapture() override { + return m_resultCapture; + } + IRunner* getRunner() override { + return m_runner; + } - bool moveNext() { - if( ++m_currentIndex == m_size ) { - m_currentIndex = 0; - return false; - } - return true; + IConfigPtr const& getConfig() const override { + return m_config; } - std::size_t getCurrentIndex() const { - return m_currentIndex; + ~Context() override; + + public: // IMutableContext + void setResultCapture( IResultCapture* resultCapture ) override { + m_resultCapture = resultCapture; + } + void setRunner( IRunner* runner ) override { + m_runner = runner; + } + void setConfig( IConfigPtr const& config ) override { + m_config = config; } - std::size_t m_size; - std::size_t m_currentIndex; + friend IMutableContext& getCurrentMutableContext(); + + private: + IConfigPtr m_config; + IRunner* m_runner = nullptr; + IResultCapture* m_resultCapture = nullptr; }; - /////////////////////////////////////////////////////////////////////////// + IMutableContext *IMutableContext::currentContext = nullptr; + + void IMutableContext::createContext() + { + currentContext = new Context(); + } - class GeneratorsForTest : public IGeneratorsForTest { + void cleanUpContext() { + delete IMutableContext::currentContext; + IMutableContext::currentContext = nullptr; + } + IContext::~IContext() = default; + IMutableContext::~IMutableContext() = default; + Context::~Context() = default; +} +// end catch_context.cpp +// start catch_debug_console.cpp - public: - ~GeneratorsForTest() { - deleteAll( m_generatorsInOrder ); +// start catch_debug_console.h + +#include <string> + +namespace Catch { + void writeToDebugConsole( std::string const& text ); +} + +// end catch_debug_console.h +#ifdef CATCH_PLATFORM_WINDOWS + + namespace Catch { + void writeToDebugConsole( std::string const& text ) { + ::OutputDebugStringA( text.c_str() ); } + } - IGeneratorInfo& getGeneratorInfo( std::string const& fileInfo, std::size_t size ) { - std::map<std::string, IGeneratorInfo*>::const_iterator it = m_generatorsByName.find( fileInfo ); - if( it == m_generatorsByName.end() ) { - IGeneratorInfo* info = new GeneratorInfo( size ); - m_generatorsByName.insert( std::make_pair( fileInfo, info ) ); - m_generatorsInOrder.push_back( info ); - return *info; - } - return *it->second; +#else + + namespace Catch { + void writeToDebugConsole( std::string const& text ) { + // !TBD: Need a version for Mac/ XCode and other IDEs + Catch::cout() << text; } + } - bool moveNext() { - std::vector<IGeneratorInfo*>::const_iterator it = m_generatorsInOrder.begin(); - std::vector<IGeneratorInfo*>::const_iterator itEnd = m_generatorsInOrder.end(); - for(; it != itEnd; ++it ) { - if( (*it)->moveNext() ) - return true; +#endif // Platform +// end catch_debug_console.cpp +// start catch_debugger.cpp + +#ifdef CATCH_PLATFORM_MAC + +# include <assert.h> +# include <stdbool.h> +# include <sys/types.h> +# include <unistd.h> +# include <cstddef> +# include <ostream> + +#ifdef __apple_build_version__ + // These headers will only compile with AppleClang (XCode) + // For other compilers (Clang, GCC, ... ) we need to exclude them +# include <sys/sysctl.h> +#endif + + namespace Catch { + #ifdef __apple_build_version__ + // The following function is taken directly from the following technical note: + // https://developer.apple.com/library/archive/qa/qa1361/_index.html + + // Returns true if the current process is being debugged (either + // running under the debugger or has a debugger attached post facto). + bool isDebuggerActive(){ + int mib[4]; + struct kinfo_proc info; + std::size_t size; + + // Initialize the flags so that, if sysctl fails for some bizarre + // reason, we get a predictable result. + + info.kp_proc.p_flag = 0; + + // Initialize mib, which tells sysctl the info we want, in this case + // we're looking for information about a specific process ID. + + mib[0] = CTL_KERN; + mib[1] = KERN_PROC; + mib[2] = KERN_PROC_PID; + mib[3] = getpid(); + + // Call sysctl. + + size = sizeof(info); + if( sysctl(mib, sizeof(mib) / sizeof(*mib), &info, &size, nullptr, 0) != 0 ) { + Catch::cerr() << "\n** Call to sysctl failed - unable to determine if debugger is active **\n" << std::endl; + return false; } + + // We're being debugged if the P_TRACED flag is set. + + return ( (info.kp_proc.p_flag & P_TRACED) != 0 ); + } + #else + bool isDebuggerActive() { + // We need to find another way to determine this for non-appleclang compilers on macOS return false; } + #endif + } // namespace Catch - private: - std::map<std::string, IGeneratorInfo*> m_generatorsByName; - std::vector<IGeneratorInfo*> m_generatorsInOrder; - }; - - IGeneratorsForTest* createGeneratorsForTest() - { - return new GeneratorsForTest(); - } +#elif defined(CATCH_PLATFORM_LINUX) + #include <fstream> + #include <string> -} // end namespace Catch + namespace Catch{ + // The standard POSIX way of detecting a debugger is to attempt to + // ptrace() the process, but this needs to be done from a child and not + // this process itself to still allow attaching to this process later + // if wanted, so is rather heavy. Under Linux we have the PID of the + // "debugger" (which doesn't need to be gdb, of course, it could also + // be strace, for example) in /proc/$PID/status, so just get it from + // there instead. + bool isDebuggerActive(){ + // Libstdc++ has a bug, where std::ifstream sets errno to 0 + // This way our users can properly assert over errno values + ErrnoGuard guard; + std::ifstream in("/proc/self/status"); + for( std::string line; std::getline(in, line); ) { + static const int PREFIX_LEN = 11; + if( line.compare(0, PREFIX_LEN, "TracerPid:\t") == 0 ) { + // We're traced if the PID is not 0 and no other PID starts + // with 0 digit, so it's enough to check for just a single + // character. + return line.length() > PREFIX_LEN && line[PREFIX_LEN] != '0'; + } + } -// #included from: catch_assertionresult.hpp -#define TWOBLUECUBES_CATCH_ASSERTIONRESULT_HPP_INCLUDED + return false; + } + } // namespace Catch +#elif defined(_MSC_VER) + extern "C" __declspec(dllimport) int __stdcall IsDebuggerPresent(); + namespace Catch { + bool isDebuggerActive() { + return IsDebuggerPresent() != 0; + } + } +#elif defined(__MINGW32__) + extern "C" __declspec(dllimport) int __stdcall IsDebuggerPresent(); + namespace Catch { + bool isDebuggerActive() { + return IsDebuggerPresent() != 0; + } + } +#else + namespace Catch { + bool isDebuggerActive() { return false; } + } +#endif // Platform +// end catch_debugger.cpp +// start catch_decomposer.cpp namespace Catch { - AssertionInfo::AssertionInfo( std::string const& _macroName, - SourceLineInfo const& _lineInfo, - std::string const& _capturedExpression, - ResultDisposition::Flags _resultDisposition ) - : macroName( _macroName ), - lineInfo( _lineInfo ), - capturedExpression( _capturedExpression ), - resultDisposition( _resultDisposition ) - {} + ITransientExpression::~ITransientExpression() = default; - AssertionResult::AssertionResult() {} + void formatReconstructedExpression( std::ostream &os, std::string const& lhs, StringRef op, std::string const& rhs ) { + if( lhs.size() + rhs.size() < 40 && + lhs.find('\n') == std::string::npos && + rhs.find('\n') == std::string::npos ) + os << lhs << " " << op << " " << rhs; + else + os << lhs << "\n" << op << "\n" << rhs; + } +} +// end catch_decomposer.cpp +// start catch_enforce.cpp - AssertionResult::AssertionResult( AssertionInfo const& info, AssertionResultData const& data ) - : m_info( info ), - m_resultData( data ) - {} +#include <stdexcept> - AssertionResult::~AssertionResult() {} +namespace Catch { +#if defined(CATCH_CONFIG_DISABLE_EXCEPTIONS) && !defined(CATCH_CONFIG_DISABLE_EXCEPTIONS_CUSTOM_HANDLER) + [[noreturn]] + void throw_exception(std::exception const& e) { + Catch::cerr() << "Catch will terminate because it needed to throw an exception.\n" + << "The message was: " << e.what() << '\n'; + std::terminate(); + } +#endif - // Result was a success - bool AssertionResult::succeeded() const { - return Catch::isOk( m_resultData.resultType ); + [[noreturn]] + void throw_logic_error(std::string const& msg) { + throw_exception(std::logic_error(msg)); } - // Result was a success, or failure is suppressed - bool AssertionResult::isOk() const { - return Catch::isOk( m_resultData.resultType ) || shouldSuppressFailure( m_info.resultDisposition ); + [[noreturn]] + void throw_domain_error(std::string const& msg) { + throw_exception(std::domain_error(msg)); } - ResultWas::OfType AssertionResult::getResultType() const { - return m_resultData.resultType; + [[noreturn]] + void throw_runtime_error(std::string const& msg) { + throw_exception(std::runtime_error(msg)); } - bool AssertionResult::hasExpression() const { - return !m_info.capturedExpression.empty(); +} // namespace Catch; +// end catch_enforce.cpp +// start catch_enum_values_registry.cpp +// start catch_enum_values_registry.h + +#include <vector> +#include <memory> + +namespace Catch { + + namespace Detail { + + std::unique_ptr<EnumInfo> makeEnumInfo( StringRef enumName, StringRef allValueNames, std::vector<int> const& values ); + + class EnumValuesRegistry : public IMutableEnumValuesRegistry { + + std::vector<std::unique_ptr<EnumInfo>> m_enumInfos; + + EnumInfo const& registerEnum( StringRef enumName, StringRef allEnums, std::vector<int> const& values) override; + }; + + std::vector<std::string> parseEnums( StringRef enums ); + + } // Detail + +} // Catch + +// end catch_enum_values_registry.h + +#include <map> +#include <cassert> + +namespace Catch { + + IMutableEnumValuesRegistry::~IMutableEnumValuesRegistry() {} + + namespace Detail { + + std::vector<std::string> parseEnums( StringRef enums ) { + auto enumValues = splitStringRef( enums, ',' ); + std::vector<std::string> parsed; + parsed.reserve( enumValues.size() ); + for( auto const& enumValue : enumValues ) { + auto identifiers = splitStringRef( enumValue, ':' ); + parsed.push_back( Catch::trim( identifiers.back() ) ); + } + return parsed; + } + + EnumInfo::~EnumInfo() {} + + StringRef EnumInfo::lookup( int value ) const { + for( auto const& valueToName : m_values ) { + if( valueToName.first == value ) + return valueToName.second; + } + return "{** unexpected enum value **}"; + } + + std::unique_ptr<EnumInfo> makeEnumInfo( StringRef enumName, StringRef allValueNames, std::vector<int> const& values ) { + std::unique_ptr<EnumInfo> enumInfo( new EnumInfo ); + enumInfo->m_name = enumName; + enumInfo->m_values.reserve( values.size() ); + + const auto valueNames = Catch::Detail::parseEnums( allValueNames ); + assert( valueNames.size() == values.size() ); + std::size_t i = 0; + for( auto value : values ) + enumInfo->m_values.push_back({ value, valueNames[i++] }); + + return enumInfo; + } + + EnumInfo const& EnumValuesRegistry::registerEnum( StringRef enumName, StringRef allValueNames, std::vector<int> const& values ) { + auto enumInfo = makeEnumInfo( enumName, allValueNames, values ); + EnumInfo* raw = enumInfo.get(); + m_enumInfos.push_back( std::move( enumInfo ) ); + return *raw; + } + + } // Detail +} // Catch + +// end catch_enum_values_registry.cpp +// start catch_errno_guard.cpp + +#include <cerrno> + +namespace Catch { + ErrnoGuard::ErrnoGuard():m_oldErrno(errno){} + ErrnoGuard::~ErrnoGuard() { errno = m_oldErrno; } +} +// end catch_errno_guard.cpp +// start catch_exception_translator_registry.cpp + +// start catch_exception_translator_registry.h + +#include <vector> +#include <string> +#include <memory> + +namespace Catch { + + class ExceptionTranslatorRegistry : public IExceptionTranslatorRegistry { + public: + ~ExceptionTranslatorRegistry(); + virtual void registerTranslator( const IExceptionTranslator* translator ); + std::string translateActiveException() const override; + std::string tryTranslators() const; + + private: + std::vector<std::unique_ptr<IExceptionTranslator const>> m_translators; + }; +} + +// end catch_exception_translator_registry.h +#ifdef __OBJC__ +#import "Foundation/Foundation.h" +#endif + +namespace Catch { + + ExceptionTranslatorRegistry::~ExceptionTranslatorRegistry() { } - bool AssertionResult::hasMessage() const { - return !m_resultData.message.empty(); + void ExceptionTranslatorRegistry::registerTranslator( const IExceptionTranslator* translator ) { + m_translators.push_back( std::unique_ptr<const IExceptionTranslator>( translator ) ); } - std::string AssertionResult::getExpression() const { - if( isFalseTest( m_info.resultDisposition ) ) - return '!' + m_info.capturedExpression; - else - return m_info.capturedExpression; +#if !defined(CATCH_CONFIG_DISABLE_EXCEPTIONS) + std::string ExceptionTranslatorRegistry::translateActiveException() const { + try { +#ifdef __OBJC__ + // In Objective-C try objective-c exceptions first + @try { + return tryTranslators(); + } + @catch (NSException *exception) { + return Catch::Detail::stringify( [exception description] ); + } +#else + // Compiling a mixed mode project with MSVC means that CLR + // exceptions will be caught in (...) as well. However, these + // do not fill-in std::current_exception and thus lead to crash + // when attempting rethrow. + // /EHa switch also causes structured exceptions to be caught + // here, but they fill-in current_exception properly, so + // at worst the output should be a little weird, instead of + // causing a crash. + if (std::current_exception() == nullptr) { + return "Non C++ exception. Possibly a CLR exception."; + } + return tryTranslators(); +#endif + } + catch( TestFailureException& ) { + std::rethrow_exception(std::current_exception()); + } + catch( std::exception& ex ) { + return ex.what(); + } + catch( std::string& msg ) { + return msg; + } + catch( const char* msg ) { + return msg; + } + catch(...) { + return "Unknown exception"; + } } - std::string AssertionResult::getExpressionInMacro() const { - if( m_info.macroName.empty() ) - return m_info.capturedExpression; - else - return m_info.macroName + "( " + m_info.capturedExpression + " )"; + + std::string ExceptionTranslatorRegistry::tryTranslators() const { + if (m_translators.empty()) { + std::rethrow_exception(std::current_exception()); + } else { + return m_translators[0]->translate(m_translators.begin() + 1, m_translators.end()); + } } - bool AssertionResult::hasExpandedExpression() const { - return hasExpression() && getExpandedExpression() != getExpression(); +#else // ^^ Exceptions are enabled // Exceptions are disabled vv + std::string ExceptionTranslatorRegistry::translateActiveException() const { + CATCH_INTERNAL_ERROR("Attempted to translate active exception under CATCH_CONFIG_DISABLE_EXCEPTIONS!"); } - std::string AssertionResult::getExpandedExpression() const { - return m_resultData.reconstructExpression(); + std::string ExceptionTranslatorRegistry::tryTranslators() const { + CATCH_INTERNAL_ERROR("Attempted to use exception translators under CATCH_CONFIG_DISABLE_EXCEPTIONS!"); } +#endif - std::string AssertionResult::getMessage() const { - return m_resultData.message; +} +// end catch_exception_translator_registry.cpp +// start catch_fatal_condition.cpp + +#if defined(__GNUC__) +# pragma GCC diagnostic push +# pragma GCC diagnostic ignored "-Wmissing-field-initializers" +#endif + +#if defined( CATCH_CONFIG_WINDOWS_SEH ) || defined( CATCH_CONFIG_POSIX_SIGNALS ) + +namespace { + // Report the error condition + void reportFatal( char const * const message ) { + Catch::getCurrentContext().getResultCapture()->handleFatalErrorCondition( message ); } - SourceLineInfo AssertionResult::getSourceInfo() const { - return m_info.lineInfo; +} + +#endif // signals/SEH handling + +#if defined( CATCH_CONFIG_WINDOWS_SEH ) + +namespace Catch { + struct SignalDefs { DWORD id; const char* name; }; + + // There is no 1-1 mapping between signals and windows exceptions. + // Windows can easily distinguish between SO and SigSegV, + // but SigInt, SigTerm, etc are handled differently. + static SignalDefs signalDefs[] = { + { static_cast<DWORD>(EXCEPTION_ILLEGAL_INSTRUCTION), "SIGILL - Illegal instruction signal" }, + { static_cast<DWORD>(EXCEPTION_STACK_OVERFLOW), "SIGSEGV - Stack overflow" }, + { static_cast<DWORD>(EXCEPTION_ACCESS_VIOLATION), "SIGSEGV - Segmentation violation signal" }, + { static_cast<DWORD>(EXCEPTION_INT_DIVIDE_BY_ZERO), "Divide by zero error" }, + }; + + LONG CALLBACK FatalConditionHandler::handleVectoredException(PEXCEPTION_POINTERS ExceptionInfo) { + for (auto const& def : signalDefs) { + if (ExceptionInfo->ExceptionRecord->ExceptionCode == def.id) { + reportFatal(def.name); + } + } + // If its not an exception we care about, pass it along. + // This stops us from eating debugger breaks etc. + return EXCEPTION_CONTINUE_SEARCH; } - std::string AssertionResult::getTestMacroName() const { - return m_info.macroName; + FatalConditionHandler::FatalConditionHandler() { + isSet = true; + // 32k seems enough for Catch to handle stack overflow, + // but the value was found experimentally, so there is no strong guarantee + guaranteeSize = 32 * 1024; + exceptionHandlerHandle = nullptr; + // Register as first handler in current chain + exceptionHandlerHandle = AddVectoredExceptionHandler(1, handleVectoredException); + // Pass in guarantee size to be filled + SetThreadStackGuarantee(&guaranteeSize); } - void AssertionResult::discardDecomposedExpression() const { - m_resultData.decomposedExpression = CATCH_NULL; + void FatalConditionHandler::reset() { + if (isSet) { + RemoveVectoredExceptionHandler(exceptionHandlerHandle); + SetThreadStackGuarantee(&guaranteeSize); + exceptionHandlerHandle = nullptr; + isSet = false; + } } - void AssertionResult::expandDecomposedExpression() const { - m_resultData.reconstructExpression(); + FatalConditionHandler::~FatalConditionHandler() { + reset(); } -} // end namespace Catch +bool FatalConditionHandler::isSet = false; +ULONG FatalConditionHandler::guaranteeSize = 0; +PVOID FatalConditionHandler::exceptionHandlerHandle = nullptr; -// #included from: catch_test_case_info.hpp -#define TWOBLUECUBES_CATCH_TEST_CASE_INFO_HPP_INCLUDED +} // namespace Catch -#include <cctype> +#elif defined( CATCH_CONFIG_POSIX_SIGNALS ) namespace Catch { - inline TestCaseInfo::SpecialProperties parseSpecialTag( std::string const& tag ) { - if( startsWith( tag, '.' ) || - tag == "hide" || - tag == "!hide" ) - return TestCaseInfo::IsHidden; - else if( tag == "!throws" ) - return TestCaseInfo::Throws; - else if( tag == "!shouldfail" ) - return TestCaseInfo::ShouldFail; - else if( tag == "!mayfail" ) - return TestCaseInfo::MayFail; - else if( tag == "!nonportable" ) - return TestCaseInfo::NonPortable; - else - return TestCaseInfo::None; + struct SignalDefs { + int id; + const char* name; + }; + + // 32kb for the alternate stack seems to be sufficient. However, this value + // is experimentally determined, so that's not guaranteed. + constexpr static std::size_t sigStackSize = 32768 >= MINSIGSTKSZ ? 32768 : MINSIGSTKSZ; + + static SignalDefs signalDefs[] = { + { SIGINT, "SIGINT - Terminal interrupt signal" }, + { SIGILL, "SIGILL - Illegal instruction signal" }, + { SIGFPE, "SIGFPE - Floating point error signal" }, + { SIGSEGV, "SIGSEGV - Segmentation violation signal" }, + { SIGTERM, "SIGTERM - Termination request signal" }, + { SIGABRT, "SIGABRT - Abort (abnormal termination) signal" } + }; + + void FatalConditionHandler::handleSignal( int sig ) { + char const * name = "<unknown signal>"; + for (auto const& def : signalDefs) { + if (sig == def.id) { + name = def.name; + break; + } + } + reset(); + reportFatal(name); + raise( sig ); } - inline bool isReservedTag( std::string const& tag ) { - return parseSpecialTag( tag ) == TestCaseInfo::None && tag.size() > 0 && !std::isalnum( tag[0] ); + + FatalConditionHandler::FatalConditionHandler() { + isSet = true; + stack_t sigStack; + sigStack.ss_sp = altStackMem; + sigStack.ss_size = sigStackSize; + sigStack.ss_flags = 0; + sigaltstack(&sigStack, &oldSigStack); + struct sigaction sa = { }; + + sa.sa_handler = handleSignal; + sa.sa_flags = SA_ONSTACK; + for (std::size_t i = 0; i < sizeof(signalDefs)/sizeof(SignalDefs); ++i) { + sigaction(signalDefs[i].id, &sa, &oldSigActions[i]); + } } - inline void enforceNotReservedTag( std::string const& tag, SourceLineInfo const& _lineInfo ) { - if( isReservedTag( tag ) ) { - { - Colour colourGuard( Colour::Red ); - Catch::cerr() - << "Tag name [" << tag << "] not allowed.\n" - << "Tag names starting with non alpha-numeric characters are reserved\n"; - } - { - Colour colourGuard( Colour::FileName ); - Catch::cerr() << _lineInfo << std::endl; + + FatalConditionHandler::~FatalConditionHandler() { + reset(); + } + + void FatalConditionHandler::reset() { + if( isSet ) { + // Set signals back to previous values -- hopefully nobody overwrote them in the meantime + for( std::size_t i = 0; i < sizeof(signalDefs)/sizeof(SignalDefs); ++i ) { + sigaction(signalDefs[i].id, &oldSigActions[i], nullptr); } - exit(1); + // Return the old stack + sigaltstack(&oldSigStack, nullptr); + isSet = false; } } - TestCase makeTestCase( ITestCase* _testCase, - std::string const& _className, - std::string const& _name, - std::string const& _descOrTags, - SourceLineInfo const& _lineInfo ) + bool FatalConditionHandler::isSet = false; + struct sigaction FatalConditionHandler::oldSigActions[sizeof(signalDefs)/sizeof(SignalDefs)] = {}; + stack_t FatalConditionHandler::oldSigStack = {}; + char FatalConditionHandler::altStackMem[sigStackSize] = {}; + +} // namespace Catch + +#else + +namespace Catch { + void FatalConditionHandler::reset() {} +} + +#endif // signals/SEH handling + +#if defined(__GNUC__) +# pragma GCC diagnostic pop +#endif +// end catch_fatal_condition.cpp +// start catch_generators.cpp + +// start catch_random_number_generator.h + +#include <algorithm> +#include <random> + +namespace Catch { + + struct IConfig; + + std::mt19937& rng(); + void seedRng( IConfig const& config ); + unsigned int rngSeed(); + +} + +// end catch_random_number_generator.h +#include <limits> +#include <set> + +namespace Catch { + +IGeneratorTracker::~IGeneratorTracker() {} + +const char* GeneratorException::what() const noexcept { + return m_msg; +} + +namespace Generators { + + GeneratorUntypedBase::~GeneratorUntypedBase() {} + + auto acquireGeneratorTracker( SourceLineInfo const& lineInfo ) -> IGeneratorTracker& { + return getResultCapture().acquireGeneratorTracker( lineInfo ); + } + +} // namespace Generators +} // namespace Catch +// end catch_generators.cpp +// start catch_interfaces_capture.cpp + +namespace Catch { + IResultCapture::~IResultCapture() = default; +} +// end catch_interfaces_capture.cpp +// start catch_interfaces_config.cpp + +namespace Catch { + IConfig::~IConfig() = default; +} +// end catch_interfaces_config.cpp +// start catch_interfaces_exception.cpp + +namespace Catch { + IExceptionTranslator::~IExceptionTranslator() = default; + IExceptionTranslatorRegistry::~IExceptionTranslatorRegistry() = default; +} +// end catch_interfaces_exception.cpp +// start catch_interfaces_registry_hub.cpp + +namespace Catch { + IRegistryHub::~IRegistryHub() = default; + IMutableRegistryHub::~IMutableRegistryHub() = default; +} +// end catch_interfaces_registry_hub.cpp +// start catch_interfaces_reporter.cpp + +// start catch_reporter_listening.h + +namespace Catch { + + class ListeningReporter : public IStreamingReporter { + using Reporters = std::vector<IStreamingReporterPtr>; + Reporters m_listeners; + IStreamingReporterPtr m_reporter = nullptr; + ReporterPreferences m_preferences; + + public: + ListeningReporter(); + + void addListener( IStreamingReporterPtr&& listener ); + void addReporter( IStreamingReporterPtr&& reporter ); + + public: // IStreamingReporter + + ReporterPreferences getPreferences() const override; + + void noMatchingTestCases( std::string const& spec ) override; + + static std::set<Verbosity> getSupportedVerbosities(); + +#if defined(CATCH_CONFIG_ENABLE_BENCHMARKING) + void benchmarkPreparing(std::string const& name) override; + void benchmarkStarting( BenchmarkInfo const& benchmarkInfo ) override; + void benchmarkEnded( BenchmarkStats<> const& benchmarkStats ) override; + void benchmarkFailed(std::string const&) override; +#endif // CATCH_CONFIG_ENABLE_BENCHMARKING + + void testRunStarting( TestRunInfo const& testRunInfo ) override; + void testGroupStarting( GroupInfo const& groupInfo ) override; + void testCaseStarting( TestCaseInfo const& testInfo ) override; + void sectionStarting( SectionInfo const& sectionInfo ) override; + void assertionStarting( AssertionInfo const& assertionInfo ) override; + + // The return value indicates if the messages buffer should be cleared: + bool assertionEnded( AssertionStats const& assertionStats ) override; + void sectionEnded( SectionStats const& sectionStats ) override; + void testCaseEnded( TestCaseStats const& testCaseStats ) override; + void testGroupEnded( TestGroupStats const& testGroupStats ) override; + void testRunEnded( TestRunStats const& testRunStats ) override; + + void skipTest( TestCaseInfo const& testInfo ) override; + bool isMulti() const override; + + }; + +} // end namespace Catch + +// end catch_reporter_listening.h +namespace Catch { + + ReporterConfig::ReporterConfig( IConfigPtr const& _fullConfig ) + : m_stream( &_fullConfig->stream() ), m_fullConfig( _fullConfig ) {} + + ReporterConfig::ReporterConfig( IConfigPtr const& _fullConfig, std::ostream& _stream ) + : m_stream( &_stream ), m_fullConfig( _fullConfig ) {} + + std::ostream& ReporterConfig::stream() const { return *m_stream; } + IConfigPtr ReporterConfig::fullConfig() const { return m_fullConfig; } + + TestRunInfo::TestRunInfo( std::string const& _name ) : name( _name ) {} + + GroupInfo::GroupInfo( std::string const& _name, + std::size_t _groupIndex, + std::size_t _groupsCount ) + : name( _name ), + groupIndex( _groupIndex ), + groupsCounts( _groupsCount ) + {} + + AssertionStats::AssertionStats( AssertionResult const& _assertionResult, + std::vector<MessageInfo> const& _infoMessages, + Totals const& _totals ) + : assertionResult( _assertionResult ), + infoMessages( _infoMessages ), + totals( _totals ) { - bool isHidden( startsWith( _name, "./" ) ); // Legacy support + assertionResult.m_resultData.lazyExpression.m_transientExpression = _assertionResult.m_resultData.lazyExpression.m_transientExpression; - // Parse out tags - std::set<std::string> tags; - std::string desc, tag; - bool inTag = false; - for( std::size_t i = 0; i < _descOrTags.size(); ++i ) { - char c = _descOrTags[i]; - if( !inTag ) { - if( c == '[' ) - inTag = true; - else - desc += c; - } - else { - if( c == ']' ) { - TestCaseInfo::SpecialProperties prop = parseSpecialTag( tag ); - if( prop == TestCaseInfo::IsHidden ) - isHidden = true; - else if( prop == TestCaseInfo::None ) - enforceNotReservedTag( tag, _lineInfo ); + if( assertionResult.hasMessage() ) { + // Copy message into messages list. + // !TBD This should have been done earlier, somewhere + MessageBuilder builder( assertionResult.getTestMacroName(), assertionResult.getSourceInfo(), assertionResult.getResultType() ); + builder << assertionResult.getMessage(); + builder.m_info.message = builder.m_stream.str(); - tags.insert( tag ); - tag.clear(); - inTag = false; - } - else - tag += c; + infoMessages.push_back( builder.m_info ); + } + } + + AssertionStats::~AssertionStats() = default; + + SectionStats::SectionStats( SectionInfo const& _sectionInfo, + Counts const& _assertions, + double _durationInSeconds, + bool _missingAssertions ) + : sectionInfo( _sectionInfo ), + assertions( _assertions ), + durationInSeconds( _durationInSeconds ), + missingAssertions( _missingAssertions ) + {} + + SectionStats::~SectionStats() = default; + + TestCaseStats::TestCaseStats( TestCaseInfo const& _testInfo, + Totals const& _totals, + std::string const& _stdOut, + std::string const& _stdErr, + bool _aborting ) + : testInfo( _testInfo ), + totals( _totals ), + stdOut( _stdOut ), + stdErr( _stdErr ), + aborting( _aborting ) + {} + + TestCaseStats::~TestCaseStats() = default; + + TestGroupStats::TestGroupStats( GroupInfo const& _groupInfo, + Totals const& _totals, + bool _aborting ) + : groupInfo( _groupInfo ), + totals( _totals ), + aborting( _aborting ) + {} + + TestGroupStats::TestGroupStats( GroupInfo const& _groupInfo ) + : groupInfo( _groupInfo ), + aborting( false ) + {} + + TestGroupStats::~TestGroupStats() = default; + + TestRunStats::TestRunStats( TestRunInfo const& _runInfo, + Totals const& _totals, + bool _aborting ) + : runInfo( _runInfo ), + totals( _totals ), + aborting( _aborting ) + {} + + TestRunStats::~TestRunStats() = default; + + void IStreamingReporter::fatalErrorEncountered( StringRef ) {} + bool IStreamingReporter::isMulti() const { return false; } + + IReporterFactory::~IReporterFactory() = default; + IReporterRegistry::~IReporterRegistry() = default; + +} // end namespace Catch +// end catch_interfaces_reporter.cpp +// start catch_interfaces_runner.cpp + +namespace Catch { + IRunner::~IRunner() = default; +} +// end catch_interfaces_runner.cpp +// start catch_interfaces_testcase.cpp + +namespace Catch { + ITestInvoker::~ITestInvoker() = default; + ITestCaseRegistry::~ITestCaseRegistry() = default; +} +// end catch_interfaces_testcase.cpp +// start catch_leak_detector.cpp + +#ifdef CATCH_CONFIG_WINDOWS_CRTDBG +#include <crtdbg.h> + +namespace Catch { + + LeakDetector::LeakDetector() { + int flag = _CrtSetDbgFlag(_CRTDBG_REPORT_FLAG); + flag |= _CRTDBG_LEAK_CHECK_DF; + flag |= _CRTDBG_ALLOC_MEM_DF; + _CrtSetDbgFlag(flag); + _CrtSetReportMode(_CRT_WARN, _CRTDBG_MODE_FILE | _CRTDBG_MODE_DEBUG); + _CrtSetReportFile(_CRT_WARN, _CRTDBG_FILE_STDERR); + // Change this to leaking allocation's number to break there + _CrtSetBreakAlloc(-1); + } +} + +#else + + Catch::LeakDetector::LeakDetector() {} + +#endif + +Catch::LeakDetector::~LeakDetector() { + Catch::cleanUp(); +} +// end catch_leak_detector.cpp +// start catch_list.cpp + +// start catch_list.h + +#include <set> + +namespace Catch { + + std::size_t listTests( Config const& config ); + + std::size_t listTestsNamesOnly( Config const& config ); + + struct TagInfo { + void add( std::string const& spelling ); + std::string all() const; + + std::set<std::string> spellings; + std::size_t count = 0; + }; + + std::size_t listTags( Config const& config ); + + std::size_t listReporters(); + + Option<std::size_t> list( std::shared_ptr<Config> const& config ); + +} // end namespace Catch + +// end catch_list.h +// start catch_text.h + +namespace Catch { + using namespace clara::TextFlow; +} + +// end catch_text.h +#include <limits> +#include <algorithm> +#include <iomanip> + +namespace Catch { + + std::size_t listTests( Config const& config ) { + TestSpec testSpec = config.testSpec(); + if( config.hasTestFilters() ) + Catch::cout() << "Matching test cases:\n"; + else { + Catch::cout() << "All available test cases:\n"; + } + + auto matchedTestCases = filterTests( getAllTestCasesSorted( config ), testSpec, config ); + for( auto const& testCaseInfo : matchedTestCases ) { + Colour::Code colour = testCaseInfo.isHidden() + ? Colour::SecondaryText + : Colour::None; + Colour colourGuard( colour ); + + Catch::cout() << Column( testCaseInfo.name ).initialIndent( 2 ).indent( 4 ) << "\n"; + if( config.verbosity() >= Verbosity::High ) { + Catch::cout() << Column( Catch::Detail::stringify( testCaseInfo.lineInfo ) ).indent(4) << std::endl; + std::string description = testCaseInfo.description; + if( description.empty() ) + description = "(NO DESCRIPTION)"; + Catch::cout() << Column( description ).indent(4) << std::endl; } + if( !testCaseInfo.tags.empty() ) + Catch::cout() << Column( testCaseInfo.tagsAsString() ).indent( 6 ) << "\n"; } - if( isHidden ) { - tags.insert( "hide" ); - tags.insert( "." ); + + if( !config.hasTestFilters() ) + Catch::cout() << pluralise( matchedTestCases.size(), "test case" ) << '\n' << std::endl; + else + Catch::cout() << pluralise( matchedTestCases.size(), "matching test case" ) << '\n' << std::endl; + return matchedTestCases.size(); + } + + std::size_t listTestsNamesOnly( Config const& config ) { + TestSpec testSpec = config.testSpec(); + std::size_t matchedTests = 0; + std::vector<TestCase> matchedTestCases = filterTests( getAllTestCasesSorted( config ), testSpec, config ); + for( auto const& testCaseInfo : matchedTestCases ) { + matchedTests++; + if( startsWith( testCaseInfo.name, '#' ) ) + Catch::cout() << '"' << testCaseInfo.name << '"'; + else + Catch::cout() << testCaseInfo.name; + if ( config.verbosity() >= Verbosity::High ) + Catch::cout() << "\t@" << testCaseInfo.lineInfo; + Catch::cout() << std::endl; } + return matchedTests; + } - TestCaseInfo info( _name, _className, desc, tags, _lineInfo ); - return TestCase( _testCase, info ); + void TagInfo::add( std::string const& spelling ) { + ++count; + spellings.insert( spelling ); } - void setTags( TestCaseInfo& testCaseInfo, std::set<std::string> const& tags ) - { - testCaseInfo.tags = tags; - testCaseInfo.lcaseTags.clear(); + std::string TagInfo::all() const { + std::string out; + for( auto const& spelling : spellings ) + out += "[" + spelling + "]"; + return out; + } - std::ostringstream oss; - for( std::set<std::string>::const_iterator it = tags.begin(), itEnd = tags.end(); it != itEnd; ++it ) { - oss << '[' << *it << ']'; - std::string lcaseTag = toLower( *it ); - testCaseInfo.properties = static_cast<TestCaseInfo::SpecialProperties>( testCaseInfo.properties | parseSpecialTag( lcaseTag ) ); - testCaseInfo.lcaseTags.insert( lcaseTag ); + std::size_t listTags( Config const& config ) { + TestSpec testSpec = config.testSpec(); + if( config.hasTestFilters() ) + Catch::cout() << "Tags for matching test cases:\n"; + else { + Catch::cout() << "All available tags:\n"; + } + + std::map<std::string, TagInfo> tagCounts; + + std::vector<TestCase> matchedTestCases = filterTests( getAllTestCasesSorted( config ), testSpec, config ); + for( auto const& testCase : matchedTestCases ) { + for( auto const& tagName : testCase.getTestCaseInfo().tags ) { + std::string lcaseTagName = toLower( tagName ); + auto countIt = tagCounts.find( lcaseTagName ); + if( countIt == tagCounts.end() ) + countIt = tagCounts.insert( std::make_pair( lcaseTagName, TagInfo() ) ).first; + countIt->second.add( tagName ); + } + } + + for( auto const& tagCount : tagCounts ) { + ReusableStringStream rss; + rss << " " << std::setw(2) << tagCount.second.count << " "; + auto str = rss.str(); + auto wrapper = Column( tagCount.second.all() ) + .initialIndent( 0 ) + .indent( str.size() ) + .width( CATCH_CONFIG_CONSOLE_WIDTH-10 ); + Catch::cout() << str << wrapper << '\n'; } - testCaseInfo.tagsAsString = oss.str(); + Catch::cout() << pluralise( tagCounts.size(), "tag" ) << '\n' << std::endl; + return tagCounts.size(); } - TestCaseInfo::TestCaseInfo( std::string const& _name, - std::string const& _className, - std::string const& _description, - std::set<std::string> const& _tags, - SourceLineInfo const& _lineInfo ) - : name( _name ), - className( _className ), - description( _description ), - lineInfo( _lineInfo ), - properties( None ) - { - setTags( *this, _tags ); + std::size_t listReporters() { + Catch::cout() << "Available reporters:\n"; + IReporterRegistry::FactoryMap const& factories = getRegistryHub().getReporterRegistry().getFactories(); + std::size_t maxNameLen = 0; + for( auto const& factoryKvp : factories ) + maxNameLen = (std::max)( maxNameLen, factoryKvp.first.size() ); + + for( auto const& factoryKvp : factories ) { + Catch::cout() + << Column( factoryKvp.first + ":" ) + .indent(2) + .width( 5+maxNameLen ) + + Column( factoryKvp.second->getDescription() ) + .initialIndent(0) + .indent(2) + .width( CATCH_CONFIG_CONSOLE_WIDTH - maxNameLen-8 ) + << "\n"; + } + Catch::cout() << std::endl; + return factories.size(); } - TestCaseInfo::TestCaseInfo( TestCaseInfo const& other ) - : name( other.name ), - className( other.className ), - description( other.description ), - tags( other.tags ), - lcaseTags( other.lcaseTags ), - tagsAsString( other.tagsAsString ), - lineInfo( other.lineInfo ), - properties( other.properties ) - {} + Option<std::size_t> list( std::shared_ptr<Config> const& config ) { + Option<std::size_t> listedCount; + getCurrentMutableContext().setConfig( config ); + if( config->listTests() ) + listedCount = listedCount.valueOr(0) + listTests( *config ); + if( config->listTestNamesOnly() ) + listedCount = listedCount.valueOr(0) + listTestsNamesOnly( *config ); + if( config->listTags() ) + listedCount = listedCount.valueOr(0) + listTags( *config ); + if( config->listReporters() ) + listedCount = listedCount.valueOr(0) + listReporters(); + return listedCount; + } - bool TestCaseInfo::isHidden() const { - return ( properties & IsHidden ) != 0; +} // end namespace Catch +// end catch_list.cpp +// start catch_matchers.cpp + +namespace Catch { +namespace Matchers { + namespace Impl { + + std::string MatcherUntypedBase::toString() const { + if( m_cachedToString.empty() ) + m_cachedToString = describe(); + return m_cachedToString; + } + + MatcherUntypedBase::~MatcherUntypedBase() = default; + + } // namespace Impl +} // namespace Matchers + +using namespace Matchers; +using Matchers::Impl::MatcherBase; + +} // namespace Catch +// end catch_matchers.cpp +// start catch_matchers_floating.cpp + +// start catch_polyfills.hpp + +namespace Catch { + bool isnan(float f); + bool isnan(double d); +} + +// end catch_polyfills.hpp +// start catch_to_string.hpp + +#include <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 } - bool TestCaseInfo::throws() const { - return ( properties & Throws ) != 0; +} // end namespace Catch + +// end catch_to_string.hpp +#include <cstdlib> +#include <cstdint> +#include <cstring> +#include <sstream> +#include <iomanip> +#include <limits> + +namespace Catch { +namespace Matchers { +namespace Floating { +enum class FloatingPointKind : uint8_t { + Float, + Double +}; +} +} +} + +namespace { + +template <typename T> +struct Converter; + +template <> +struct Converter<float> { + static_assert(sizeof(float) == sizeof(int32_t), "Important ULP matcher assumption violated"); + Converter(float f) { + std::memcpy(&i, &f, sizeof(f)); } - bool TestCaseInfo::okToFail() const { - return ( properties & (ShouldFail | MayFail ) ) != 0; + int32_t i; +}; + +template <> +struct Converter<double> { + static_assert(sizeof(double) == sizeof(int64_t), "Important ULP matcher assumption violated"); + Converter(double d) { + std::memcpy(&i, &d, sizeof(d)); } - bool TestCaseInfo::expectedToFail() const { - return ( properties & (ShouldFail ) ) != 0; + int64_t i; +}; + +template <typename T> +auto convert(T t) -> Converter<T> { + return Converter<T>(t); +} + +template <typename FP> +bool almostEqualUlps(FP lhs, FP rhs, int maxUlpDiff) { + // Comparison with NaN should always be false. + // This way we can rule it out before getting into the ugly details + if (Catch::isnan(lhs) || Catch::isnan(rhs)) { + return false; } - TestCase::TestCase( ITestCase* testCase, TestCaseInfo const& info ) : TestCaseInfo( info ), test( testCase ) {} + auto lc = convert(lhs); + auto rc = convert(rhs); - TestCase::TestCase( TestCase const& other ) - : TestCaseInfo( other ), - test( other.test ) - {} + if ((lc.i < 0) != (rc.i < 0)) { + // Potentially we can have +0 and -0 + return lhs == rhs; + } - TestCase TestCase::withName( std::string const& _newName ) const { - TestCase other( *this ); - other.name = _newName; - return other; + auto ulpDiff = std::abs(lc.i - rc.i); + return ulpDiff <= maxUlpDiff; +} + +template <typename FP> +FP step(FP start, FP direction, int steps) { + for (int i = 0; i < steps; ++i) { + start = std::nextafter(start, direction); } + return start; +} + +} // end anonymous namespace - void TestCase::swap( TestCase& other ) { - test.swap( other.test ); - name.swap( other.name ); - className.swap( other.className ); - description.swap( other.description ); - tags.swap( other.tags ); - lcaseTags.swap( other.lcaseTags ); - tagsAsString.swap( other.tagsAsString ); - std::swap( TestCaseInfo::properties, static_cast<TestCaseInfo&>( other ).properties ); - std::swap( lineInfo, other.lineInfo ); +namespace Catch { +namespace Matchers { +namespace Floating { + WithinAbsMatcher::WithinAbsMatcher(double target, double margin) + :m_target{ target }, m_margin{ margin } { + CATCH_ENFORCE(margin >= 0, "Invalid margin: " << margin << '.' + << " Margin has to be non-negative."); } - void TestCase::invoke() const { - test->invoke(); + // Performs equivalent check of std::fabs(lhs - rhs) <= margin + // But without the subtraction to allow for INFINITY in comparison + bool WithinAbsMatcher::match(double const& matchee) const { + return (matchee + m_margin >= m_target) && (m_target + m_margin >= matchee); } - bool TestCase::operator == ( TestCase const& other ) const { - return test.get() == other.test.get() && - name == other.name && - className == other.className; + std::string WithinAbsMatcher::describe() const { + return "is within " + ::Catch::Detail::stringify(m_margin) + " of " + ::Catch::Detail::stringify(m_target); } - bool TestCase::operator < ( TestCase const& other ) const { - return name < other.name; + WithinUlpsMatcher::WithinUlpsMatcher(double target, int ulps, FloatingPointKind baseType) + :m_target{ target }, m_ulps{ ulps }, m_type{ baseType } { + CATCH_ENFORCE(ulps >= 0, "Invalid ULP setting: " << ulps << '.' + << " ULPs have to be non-negative."); } - TestCase& TestCase::operator = ( TestCase const& other ) { - TestCase temp( other ); - swap( temp ); - return *this; + +#if defined(__clang__) +#pragma clang diagnostic push +// Clang <3.5 reports on the default branch in the switch below +#pragma clang diagnostic ignored "-Wunreachable-code" +#endif + + bool WithinUlpsMatcher::match(double const& matchee) const { + switch (m_type) { + case FloatingPointKind::Float: + return almostEqualUlps<float>(static_cast<float>(matchee), static_cast<float>(m_target), m_ulps); + case FloatingPointKind::Double: + return almostEqualUlps<double>(matchee, m_target, m_ulps); + default: + CATCH_INTERNAL_ERROR( "Unknown FloatingPointKind value" ); + } } - TestCaseInfo const& TestCase::getTestCaseInfo() const - { - return *this; +#if defined(__clang__) +#pragma clang diagnostic pop +#endif + + std::string WithinUlpsMatcher::describe() const { + std::stringstream ret; + + ret << "is within " << m_ulps << " ULPs of " << ::Catch::Detail::stringify(m_target); + + if (m_type == FloatingPointKind::Float) { + ret << 'f'; + } + + ret << " (["; + ret << std::fixed << std::setprecision(std::numeric_limits<double>::max_digits10); + if (m_type == FloatingPointKind::Double) { + ret << step(m_target, static_cast<double>(-INFINITY), m_ulps) + << ", " + << step(m_target, static_cast<double>(INFINITY), m_ulps); + } else { + ret << step<float>(static_cast<float>(m_target), -INFINITY, m_ulps) + << ", " + << step<float>(static_cast<float>(m_target), INFINITY, m_ulps); + } + ret << "])"; + + return ret.str(); + //return "is within " + Catch::to_string(m_ulps) + " ULPs of " + ::Catch::Detail::stringify(m_target) + ((m_type == FloatingPointKind::Float)? "f" : ""); } -} // end namespace Catch +}// namespace Floating + +Floating::WithinUlpsMatcher WithinULP(double target, int maxUlpDiff) { + return Floating::WithinUlpsMatcher(target, maxUlpDiff, Floating::FloatingPointKind::Double); +} + +Floating::WithinUlpsMatcher WithinULP(float target, int maxUlpDiff) { + return Floating::WithinUlpsMatcher(target, maxUlpDiff, Floating::FloatingPointKind::Float); +} -// #included from: catch_version.hpp -#define TWOBLUECUBES_CATCH_VERSION_HPP_INCLUDED +Floating::WithinAbsMatcher WithinAbs(double target, double margin) { + return Floating::WithinAbsMatcher(target, margin); +} + +} // namespace Matchers +} // namespace Catch + +// end catch_matchers_floating.cpp +// start catch_matchers_generic.cpp + +std::string Catch::Matchers::Generic::Detail::finalizeDescription(const std::string& desc) { + if (desc.empty()) { + return "matches undescribed predicate"; + } else { + return "matches predicate: \"" + desc + '"'; + } +} +// end catch_matchers_generic.cpp +// start catch_matchers_string.cpp + +#include <regex> namespace Catch { +namespace Matchers { - Version::Version - ( unsigned int _majorVersion, - unsigned int _minorVersion, - unsigned int _patchNumber, - std::string const& _branchName, - unsigned int _buildNumber ) - : majorVersion( _majorVersion ), - minorVersion( _minorVersion ), - patchNumber( _patchNumber ), - branchName( _branchName ), - buildNumber( _buildNumber ) - {} + namespace StdString { - std::ostream& operator << ( std::ostream& os, Version const& version ) { - os << version.majorVersion << '.' - << version.minorVersion << '.' - << version.patchNumber; + CasedString::CasedString( std::string const& str, CaseSensitive::Choice caseSensitivity ) + : m_caseSensitivity( caseSensitivity ), + m_str( adjustString( str ) ) + {} + std::string CasedString::adjustString( std::string const& str ) const { + return m_caseSensitivity == CaseSensitive::No + ? toLower( str ) + : str; + } + std::string CasedString::caseSensitivitySuffix() const { + return m_caseSensitivity == CaseSensitive::No + ? " (case insensitive)" + : std::string(); + } - if( !version.branchName.empty() ) { - os << '-' << version.branchName - << '.' << version.buildNumber; + StringMatcherBase::StringMatcherBase( std::string const& operation, CasedString const& comparator ) + : m_comparator( comparator ), + m_operation( operation ) { } - return os; + + std::string StringMatcherBase::describe() const { + std::string description; + description.reserve(5 + m_operation.size() + m_comparator.m_str.size() + + m_comparator.caseSensitivitySuffix().size()); + description += m_operation; + description += ": \""; + description += m_comparator.m_str; + description += "\""; + description += m_comparator.caseSensitivitySuffix(); + return description; + } + + EqualsMatcher::EqualsMatcher( CasedString const& comparator ) : StringMatcherBase( "equals", comparator ) {} + + bool EqualsMatcher::match( std::string const& source ) const { + return m_comparator.adjustString( source ) == m_comparator.m_str; + } + + ContainsMatcher::ContainsMatcher( CasedString const& comparator ) : StringMatcherBase( "contains", comparator ) {} + + bool ContainsMatcher::match( std::string const& source ) const { + return contains( m_comparator.adjustString( source ), m_comparator.m_str ); + } + + StartsWithMatcher::StartsWithMatcher( CasedString const& comparator ) : StringMatcherBase( "starts with", comparator ) {} + + bool StartsWithMatcher::match( std::string const& source ) const { + return startsWith( m_comparator.adjustString( source ), m_comparator.m_str ); + } + + EndsWithMatcher::EndsWithMatcher( CasedString const& comparator ) : StringMatcherBase( "ends with", comparator ) {} + + bool EndsWithMatcher::match( std::string const& source ) const { + return endsWith( m_comparator.adjustString( source ), m_comparator.m_str ); + } + + RegexMatcher::RegexMatcher(std::string regex, CaseSensitive::Choice caseSensitivity): m_regex(std::move(regex)), m_caseSensitivity(caseSensitivity) {} + + bool RegexMatcher::match(std::string const& matchee) const { + auto flags = std::regex::ECMAScript; // ECMAScript is the default syntax option anyway + if (m_caseSensitivity == CaseSensitive::Choice::No) { + flags |= std::regex::icase; + } + auto reg = std::regex(m_regex, flags); + return std::regex_match(matchee, reg); + } + + std::string RegexMatcher::describe() const { + return "matches " + ::Catch::Detail::stringify(m_regex) + ((m_caseSensitivity == CaseSensitive::Choice::Yes)? " case sensitively" : " case insensitively"); + } + + } // namespace StdString + + StdString::EqualsMatcher Equals( std::string const& str, CaseSensitive::Choice caseSensitivity ) { + return StdString::EqualsMatcher( StdString::CasedString( str, caseSensitivity) ); + } + StdString::ContainsMatcher Contains( std::string const& str, CaseSensitive::Choice caseSensitivity ) { + return StdString::ContainsMatcher( StdString::CasedString( str, caseSensitivity) ); + } + StdString::EndsWithMatcher EndsWith( std::string const& str, CaseSensitive::Choice caseSensitivity ) { + return StdString::EndsWithMatcher( StdString::CasedString( str, caseSensitivity) ); + } + StdString::StartsWithMatcher StartsWith( std::string const& str, CaseSensitive::Choice caseSensitivity ) { + return StdString::StartsWithMatcher( StdString::CasedString( str, caseSensitivity) ); } - Version libraryVersion( 1, 7, 2, "", 0 ); + StdString::RegexMatcher Matches(std::string const& regex, CaseSensitive::Choice caseSensitivity) { + return StdString::RegexMatcher(regex, caseSensitivity); + } -} +} // namespace Matchers +} // namespace Catch +// end catch_matchers_string.cpp +// start catch_message.cpp -// #included from: catch_message.hpp -#define TWOBLUECUBES_CATCH_MESSAGE_HPP_INCLUDED +// start catch_uncaught_exceptions.h namespace Catch { + bool uncaught_exceptions(); +} // end namespace Catch - MessageInfo::MessageInfo( std::string const& _macroName, +// end catch_uncaught_exceptions.h +#include <cassert> +#include <stack> + +namespace Catch { + + MessageInfo::MessageInfo( StringRef const& _macroName, SourceLineInfo const& _lineInfo, ResultWas::OfType _type ) : macroName( _macroName ), @@ -8061,212 +11240,1834 @@ namespace Catch { sequence( ++globalCount ) {} + bool MessageInfo::operator==( MessageInfo const& other ) const { + return sequence == other.sequence; + } + + bool MessageInfo::operator<( MessageInfo const& other ) const { + return sequence < other.sequence; + } + // This may need protecting if threading support is added unsigned int MessageInfo::globalCount = 0; //////////////////////////////////////////////////////////////////////////// + Catch::MessageBuilder::MessageBuilder( StringRef const& macroName, + SourceLineInfo const& lineInfo, + ResultWas::OfType type ) + :m_info(macroName, lineInfo, type) {} + + //////////////////////////////////////////////////////////////////////////// + ScopedMessage::ScopedMessage( MessageBuilder const& builder ) - : m_info( builder.m_info ) + : m_info( builder.m_info ), m_moved() { m_info.message = builder.m_stream.str(); getResultCapture().pushScopedMessage( m_info ); } - ScopedMessage::ScopedMessage( ScopedMessage const& other ) - : m_info( other.m_info ) - {} + + ScopedMessage::ScopedMessage( ScopedMessage&& old ) + : m_info( old.m_info ), m_moved() + { + old.m_moved = true; + } ScopedMessage::~ScopedMessage() { - getResultCapture().popScopedMessage( m_info ); + if ( !uncaught_exceptions() && !m_moved ){ + getResultCapture().popScopedMessage(m_info); + } + } + + Capturer::Capturer( StringRef macroName, SourceLineInfo const& lineInfo, ResultWas::OfType resultType, StringRef names ) { + auto trimmed = [&] (size_t start, size_t end) { + while (names[start] == ',' || isspace(names[start])) { + ++start; + } + while (names[end] == ',' || isspace(names[end])) { + --end; + } + return names.substr(start, end - start + 1); + }; + auto skipq = [&] (size_t start, char quote) { + for (auto i = start + 1; i < names.size() ; ++i) { + if (names[i] == quote) + return i; + if (names[i] == '\\') + ++i; + } + CATCH_INTERNAL_ERROR("CAPTURE parsing encountered unmatched quote"); + }; + + size_t start = 0; + std::stack<char> openings; + for (size_t pos = 0; pos < names.size(); ++pos) { + char c = names[pos]; + switch (c) { + case '[': + case '{': + case '(': + // It is basically impossible to disambiguate between + // comparison and start of template args in this context +// case '<': + openings.push(c); + break; + case ']': + case '}': + case ')': +// case '>': + openings.pop(); + break; + case '"': + case '\'': + pos = skipq(pos, c); + break; + case ',': + if (start != pos && openings.size() == 0) { + m_messages.emplace_back(macroName, lineInfo, resultType); + m_messages.back().message = trimmed(start, pos); + m_messages.back().message += " := "; + start = pos; + } + } + } + assert(openings.size() == 0 && "Mismatched openings"); + m_messages.emplace_back(macroName, lineInfo, resultType); + m_messages.back().message = trimmed(start, names.size() - 1); + m_messages.back().message += " := "; + } + Capturer::~Capturer() { + if ( !uncaught_exceptions() ){ + assert( m_captured == m_messages.size() ); + for( size_t i = 0; i < m_captured; ++i ) + m_resultCapture.popScopedMessage( m_messages[i] ); + } + } + + void Capturer::captureValue( size_t index, std::string const& value ) { + assert( index < m_messages.size() ); + m_messages[index].message += value; + m_resultCapture.pushScopedMessage( m_messages[index] ); + m_captured++; } } // end namespace Catch +// end catch_message.cpp +// start catch_output_redirect.cpp -// #included from: catch_legacy_reporter_adapter.hpp -#define TWOBLUECUBES_CATCH_LEGACY_REPORTER_ADAPTER_HPP_INCLUDED +// start catch_output_redirect.h +#ifndef TWOBLUECUBES_CATCH_OUTPUT_REDIRECT_H +#define TWOBLUECUBES_CATCH_OUTPUT_REDIRECT_H -// #included from: catch_legacy_reporter_adapter.h -#define TWOBLUECUBES_CATCH_LEGACY_REPORTER_ADAPTER_H_INCLUDED +#include <cstdio> +#include <iosfwd> +#include <string> -namespace Catch -{ - // Deprecated - struct IReporter : IShared { - virtual ~IReporter(); - - virtual bool shouldRedirectStdout() const = 0; - - virtual void StartTesting() = 0; - virtual void EndTesting( Totals const& totals ) = 0; - virtual void StartGroup( std::string const& groupName ) = 0; - virtual void EndGroup( std::string const& groupName, Totals const& totals ) = 0; - virtual void StartTestCase( TestCaseInfo const& testInfo ) = 0; - virtual void EndTestCase( TestCaseInfo const& testInfo, Totals const& totals, std::string const& stdOut, std::string const& stdErr ) = 0; - virtual void StartSection( std::string const& sectionName, std::string const& description ) = 0; - virtual void EndSection( std::string const& sectionName, Counts const& assertions ) = 0; - virtual void NoAssertionsInSection( std::string const& sectionName ) = 0; - virtual void NoAssertionsInTestCase( std::string const& testName ) = 0; - virtual void Aborted() = 0; - virtual void Result( AssertionResult const& result ) = 0; - }; - - class LegacyReporterAdapter : public SharedImpl<IStreamingReporter> +namespace Catch { + + class RedirectedStream { + std::ostream& m_originalStream; + std::ostream& m_redirectionStream; + std::streambuf* m_prevBuf; + + public: + RedirectedStream( std::ostream& originalStream, std::ostream& redirectionStream ); + ~RedirectedStream(); + }; + + class RedirectedStdOut { + ReusableStringStream m_rss; + RedirectedStream m_cout; + public: + RedirectedStdOut(); + auto str() const -> std::string; + }; + + // StdErr has two constituent streams in C++, std::cerr and std::clog + // This means that we need to redirect 2 streams into 1 to keep proper + // order of writes + class RedirectedStdErr { + ReusableStringStream m_rss; + RedirectedStream m_cerr; + RedirectedStream m_clog; + public: + RedirectedStdErr(); + auto str() const -> std::string; + }; + + class RedirectedStreams { + public: + RedirectedStreams(RedirectedStreams const&) = delete; + RedirectedStreams& operator=(RedirectedStreams const&) = delete; + RedirectedStreams(RedirectedStreams&&) = delete; + RedirectedStreams& operator=(RedirectedStreams&&) = delete; + + RedirectedStreams(std::string& redirectedCout, std::string& redirectedCerr); + ~RedirectedStreams(); + private: + std::string& m_redirectedCout; + std::string& m_redirectedCerr; + RedirectedStdOut m_redirectedStdOut; + RedirectedStdErr m_redirectedStdErr; + }; + +#if defined(CATCH_CONFIG_NEW_CAPTURE) + + // Windows's implementation of std::tmpfile is terrible (it tries + // to create a file inside system folder, thus requiring elevated + // privileges for the binary), so we have to use tmpnam(_s) and + // create the file ourselves there. + class TempFile { + public: + TempFile(TempFile const&) = delete; + TempFile& operator=(TempFile const&) = delete; + TempFile(TempFile&&) = delete; + TempFile& operator=(TempFile&&) = delete; + + TempFile(); + ~TempFile(); + + std::FILE* getFile(); + std::string getContents(); + + private: + std::FILE* m_file = nullptr; + #if defined(_MSC_VER) + char m_buffer[L_tmpnam] = { 0 }; + #endif + }; + + class OutputRedirect { + public: + OutputRedirect(OutputRedirect const&) = delete; + OutputRedirect& operator=(OutputRedirect const&) = delete; + OutputRedirect(OutputRedirect&&) = delete; + OutputRedirect& operator=(OutputRedirect&&) = delete; + + OutputRedirect(std::string& stdout_dest, std::string& stderr_dest); + ~OutputRedirect(); + + private: + int m_originalStdout = -1; + int m_originalStderr = -1; + TempFile m_stdoutFile; + TempFile m_stderrFile; + std::string& m_stdoutDest; + std::string& m_stderrDest; + }; + +#endif + +} // end namespace Catch + +#endif // TWOBLUECUBES_CATCH_OUTPUT_REDIRECT_H +// end catch_output_redirect.h +#include <cstdio> +#include <cstring> +#include <fstream> +#include <sstream> +#include <stdexcept> + +#if defined(CATCH_CONFIG_NEW_CAPTURE) + #if defined(_MSC_VER) + #include <io.h> //_dup and _dup2 + #define dup _dup + #define dup2 _dup2 + #define fileno _fileno + #else + #include <unistd.h> // dup and dup2 + #endif +#endif + +namespace Catch { + + RedirectedStream::RedirectedStream( std::ostream& originalStream, std::ostream& redirectionStream ) + : m_originalStream( originalStream ), + m_redirectionStream( redirectionStream ), + m_prevBuf( m_originalStream.rdbuf() ) { + m_originalStream.rdbuf( m_redirectionStream.rdbuf() ); + } + + RedirectedStream::~RedirectedStream() { + m_originalStream.rdbuf( m_prevBuf ); + } + + RedirectedStdOut::RedirectedStdOut() : m_cout( Catch::cout(), m_rss.get() ) {} + auto RedirectedStdOut::str() const -> std::string { return m_rss.str(); } + + RedirectedStdErr::RedirectedStdErr() + : m_cerr( Catch::cerr(), m_rss.get() ), + m_clog( Catch::clog(), m_rss.get() ) + {} + auto RedirectedStdErr::str() const -> std::string { return m_rss.str(); } + + RedirectedStreams::RedirectedStreams(std::string& redirectedCout, std::string& redirectedCerr) + : m_redirectedCout(redirectedCout), + m_redirectedCerr(redirectedCerr) + {} + + RedirectedStreams::~RedirectedStreams() { + m_redirectedCout += m_redirectedStdOut.str(); + m_redirectedCerr += m_redirectedStdErr.str(); + } + +#if defined(CATCH_CONFIG_NEW_CAPTURE) + +#if defined(_MSC_VER) + TempFile::TempFile() { + if (tmpnam_s(m_buffer)) { + CATCH_RUNTIME_ERROR("Could not get a temp filename"); + } + if (fopen_s(&m_file, m_buffer, "w")) { + char buffer[100]; + if (strerror_s(buffer, errno)) { + CATCH_RUNTIME_ERROR("Could not translate errno to a string"); + } + CATCH_RUNTIME_ERROR("Could not open the temp file: '" << m_buffer << "' because: " << buffer); + } + } +#else + TempFile::TempFile() { + m_file = std::tmpfile(); + if (!m_file) { + CATCH_RUNTIME_ERROR("Could not create a temp file."); + } + } + +#endif + + TempFile::~TempFile() { + // TBD: What to do about errors here? + std::fclose(m_file); + // We manually create the file on Windows only, on Linux + // it will be autodeleted +#if defined(_MSC_VER) + std::remove(m_buffer); +#endif + } + + FILE* TempFile::getFile() { + return m_file; + } + + std::string TempFile::getContents() { + std::stringstream sstr; + char buffer[100] = {}; + std::rewind(m_file); + while (std::fgets(buffer, sizeof(buffer), m_file)) { + sstr << buffer; + } + return sstr.str(); + } + + OutputRedirect::OutputRedirect(std::string& stdout_dest, std::string& stderr_dest) : + m_originalStdout(dup(1)), + m_originalStderr(dup(2)), + m_stdoutDest(stdout_dest), + m_stderrDest(stderr_dest) { + dup2(fileno(m_stdoutFile.getFile()), 1); + dup2(fileno(m_stderrFile.getFile()), 2); + } + + OutputRedirect::~OutputRedirect() { + Catch::cout() << std::flush; + fflush(stdout); + // Since we support overriding these streams, we flush cerr + // even though std::cerr is unbuffered + Catch::cerr() << std::flush; + Catch::clog() << std::flush; + fflush(stderr); + + dup2(m_originalStdout, 1); + dup2(m_originalStderr, 2); + + m_stdoutDest += m_stdoutFile.getContents(); + m_stderrDest += m_stderrFile.getContents(); + } + +#endif // CATCH_CONFIG_NEW_CAPTURE + +} // namespace Catch + +#if defined(CATCH_CONFIG_NEW_CAPTURE) + #if defined(_MSC_VER) + #undef dup + #undef dup2 + #undef fileno + #endif +#endif +// end catch_output_redirect.cpp +// start catch_polyfills.cpp + +#include <cmath> + +namespace Catch { + +#if !defined(CATCH_CONFIG_POLYFILL_ISNAN) + bool isnan(float f) { + return std::isnan(f); + } + bool isnan(double d) { + return std::isnan(d); + } +#else + // For now we only use this for embarcadero + bool isnan(float f) { + return std::_isnan(f); + } + bool isnan(double d) { + return std::_isnan(d); + } +#endif + +} // end namespace Catch +// end catch_polyfills.cpp +// start catch_random_number_generator.cpp + +namespace Catch { + + std::mt19937& rng() { + static std::mt19937 s_rng; + return s_rng; + } + + void seedRng( IConfig const& config ) { + if( config.rngSeed() != 0 ) { + std::srand( config.rngSeed() ); + rng().seed( config.rngSeed() ); + } + } + + unsigned int rngSeed() { + return getCurrentContext().getConfig()->rngSeed(); + } +} +// end catch_random_number_generator.cpp +// start catch_registry_hub.cpp + +// start catch_test_case_registry_impl.h + +#include <vector> +#include <set> +#include <algorithm> +#include <ios> + +namespace Catch { + + class TestCase; + struct IConfig; + + std::vector<TestCase> sortTests( IConfig const& config, std::vector<TestCase> const& unsortedTestCases ); + bool matchTest( TestCase const& testCase, TestSpec const& testSpec, IConfig const& config ); + + void enforceNoDuplicateTestCases( std::vector<TestCase> const& functions ); + + std::vector<TestCase> filterTests( std::vector<TestCase> const& testCases, TestSpec const& testSpec, IConfig const& config ); + std::vector<TestCase> const& getAllTestCasesSorted( IConfig const& config ); + + class TestRegistry : public ITestCaseRegistry { + public: + virtual ~TestRegistry() = default; + + virtual void registerTest( TestCase const& testCase ); + + std::vector<TestCase> const& getAllTests() const override; + std::vector<TestCase> const& getAllTestsSorted( IConfig const& config ) const override; + + private: + std::vector<TestCase> m_functions; + mutable RunTests::InWhatOrder m_currentSortOrder = RunTests::InDeclarationOrder; + mutable std::vector<TestCase> m_sortedFunctions; + std::size_t m_unnamedCount = 0; + std::ios_base::Init m_ostreamInit; // Forces cout/ cerr to be initialised + }; + + /////////////////////////////////////////////////////////////////////////// + + class TestInvokerAsFunction : public ITestInvoker { + void(*m_testAsFunction)(); + public: + TestInvokerAsFunction( void(*testAsFunction)() ) noexcept; + + void invoke() const override; + }; + + std::string extractClassName( StringRef const& classOrQualifiedMethodName ); + + /////////////////////////////////////////////////////////////////////////// + +} // end namespace Catch + +// end catch_test_case_registry_impl.h +// start catch_reporter_registry.h + +#include <map> + +namespace Catch { + + class ReporterRegistry : public IReporterRegistry { + public: - LegacyReporterAdapter( Ptr<IReporter> const& legacyReporter ); - virtual ~LegacyReporterAdapter(); - - virtual ReporterPreferences getPreferences() const; - virtual void noMatchingTestCases( std::string const& ); - virtual void testRunStarting( TestRunInfo const& ); - virtual void testGroupStarting( GroupInfo const& groupInfo ); - virtual void testCaseStarting( TestCaseInfo const& testInfo ); - virtual void sectionStarting( SectionInfo const& sectionInfo ); - virtual void assertionStarting( AssertionInfo const& ); - virtual bool assertionEnded( AssertionStats const& assertionStats ); - virtual void sectionEnded( SectionStats const& sectionStats ); - virtual void testCaseEnded( TestCaseStats const& testCaseStats ); - virtual void testGroupEnded( TestGroupStats const& testGroupStats ); - virtual void testRunEnded( TestRunStats const& testRunStats ); - virtual void skipTest( TestCaseInfo const& ); + + ~ReporterRegistry() override; + + IStreamingReporterPtr create( std::string const& name, IConfigPtr const& config ) const override; + + void registerReporter( std::string const& name, IReporterFactoryPtr const& factory ); + void registerListener( IReporterFactoryPtr const& factory ); + + FactoryMap const& getFactories() const override; + Listeners const& getListeners() const override; private: - Ptr<IReporter> m_legacyReporter; + FactoryMap m_factories; + Listeners m_listeners; }; } -namespace Catch -{ - LegacyReporterAdapter::LegacyReporterAdapter( Ptr<IReporter> const& legacyReporter ) - : m_legacyReporter( legacyReporter ) - {} - LegacyReporterAdapter::~LegacyReporterAdapter() {} +// end catch_reporter_registry.h +// start catch_tag_alias_registry.h + +// start catch_tag_alias.h + +#include <string> + +namespace Catch { + + struct TagAlias { + TagAlias(std::string const& _tag, SourceLineInfo _lineInfo); + + std::string tag; + SourceLineInfo lineInfo; + }; + +} // end namespace Catch + +// end catch_tag_alias.h +#include <map> + +namespace Catch { + + class TagAliasRegistry : public ITagAliasRegistry { + public: + ~TagAliasRegistry() override; + TagAlias const* find( std::string const& alias ) const override; + std::string expandAliases( std::string const& unexpandedTestSpec ) const override; + void add( std::string const& alias, std::string const& tag, SourceLineInfo const& lineInfo ); + + private: + std::map<std::string, TagAlias> m_registry; + }; + +} // end namespace Catch + +// end catch_tag_alias_registry.h +// start catch_startup_exception_registry.h + +#include <vector> +#include <exception> + +namespace Catch { + + class StartupExceptionRegistry { + 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; + }; + +} // end namespace Catch + +// end catch_startup_exception_registry.h +// start catch_singletons.hpp + +namespace Catch { + + struct ISingleton { + virtual ~ISingleton(); + }; + + void addSingleton( ISingleton* singleton ); + void cleanupSingletons(); + + template<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 + +// end catch_singletons.hpp +namespace Catch { + + namespace { + + class RegistryHub : public IRegistryHub, public IMutableRegistryHub, + private NonCopyable { + + public: // IRegistryHub + RegistryHub() = default; + IReporterRegistry const& getReporterRegistry() const override { + return m_reporterRegistry; + } + ITestCaseRegistry const& getTestCaseRegistry() const override { + return m_testCaseRegistry; + } + IExceptionTranslatorRegistry const& getExceptionTranslatorRegistry() const override { + return m_exceptionTranslatorRegistry; + } + ITagAliasRegistry const& getTagAliasRegistry() const override { + return m_tagAliasRegistry; + } + StartupExceptionRegistry const& getStartupExceptionRegistry() const override { + return m_exceptionRegistry; + } + + public: // IMutableRegistryHub + void registerReporter( std::string const& name, IReporterFactoryPtr const& factory ) override { + m_reporterRegistry.registerReporter( name, factory ); + } + void registerListener( IReporterFactoryPtr const& factory ) override { + m_reporterRegistry.registerListener( factory ); + } + void registerTest( TestCase const& testInfo ) override { + m_testCaseRegistry.registerTest( testInfo ); + } + void registerTranslator( const IExceptionTranslator* translator ) override { + m_exceptionTranslatorRegistry.registerTranslator( translator ); + } + void registerTagAlias( std::string const& alias, std::string const& tag, SourceLineInfo const& lineInfo ) override { + m_tagAliasRegistry.add( alias, tag, lineInfo ); + } + void registerStartupException() noexcept override { + m_exceptionRegistry.add(std::current_exception()); + } + IMutableEnumValuesRegistry& getMutableEnumValuesRegistry() override { + return m_enumValuesRegistry; + } + + private: + TestRegistry m_testCaseRegistry; + ReporterRegistry m_reporterRegistry; + ExceptionTranslatorRegistry m_exceptionTranslatorRegistry; + TagAliasRegistry m_tagAliasRegistry; + StartupExceptionRegistry m_exceptionRegistry; + Detail::EnumValuesRegistry m_enumValuesRegistry; + }; + } + + using RegistryHubSingleton = Singleton<RegistryHub, IRegistryHub, IMutableRegistryHub>; + + IRegistryHub const& getRegistryHub() { + return RegistryHubSingleton::get(); + } + IMutableRegistryHub& getMutableRegistryHub() { + return RegistryHubSingleton::getMutable(); + } + void cleanUp() { + cleanupSingletons(); + cleanUpContext(); + } + std::string translateActiveException() { + return getRegistryHub().getExceptionTranslatorRegistry().translateActiveException(); + } + +} // end namespace Catch +// end catch_registry_hub.cpp +// start catch_reporter_registry.cpp + +namespace Catch { + + ReporterRegistry::~ReporterRegistry() = default; + + IStreamingReporterPtr ReporterRegistry::create( std::string const& name, IConfigPtr const& config ) const { + auto it = m_factories.find( name ); + if( it == m_factories.end() ) + return nullptr; + return it->second->create( ReporterConfig( config ) ); + } - ReporterPreferences LegacyReporterAdapter::getPreferences() const { - ReporterPreferences prefs; - prefs.shouldRedirectStdOut = m_legacyReporter->shouldRedirectStdout(); - return prefs; + void ReporterRegistry::registerReporter( std::string const& name, IReporterFactoryPtr const& factory ) { + m_factories.emplace(name, factory); + } + void ReporterRegistry::registerListener( IReporterFactoryPtr const& factory ) { + m_listeners.push_back( factory ); } - void LegacyReporterAdapter::noMatchingTestCases( std::string const& ) {} - void LegacyReporterAdapter::testRunStarting( TestRunInfo const& ) { - m_legacyReporter->StartTesting(); + IReporterRegistry::FactoryMap const& ReporterRegistry::getFactories() const { + return m_factories; } - void LegacyReporterAdapter::testGroupStarting( GroupInfo const& groupInfo ) { - m_legacyReporter->StartGroup( groupInfo.name ); + IReporterRegistry::Listeners const& ReporterRegistry::getListeners() const { + return m_listeners; } - void LegacyReporterAdapter::testCaseStarting( TestCaseInfo const& testInfo ) { - m_legacyReporter->StartTestCase( testInfo ); + +} +// end catch_reporter_registry.cpp +// start catch_result_type.cpp + +namespace Catch { + + bool isOk( ResultWas::OfType resultType ) { + return ( resultType & ResultWas::FailureBit ) == 0; } - void LegacyReporterAdapter::sectionStarting( SectionInfo const& sectionInfo ) { - m_legacyReporter->StartSection( sectionInfo.name, sectionInfo.description ); + bool isJustInfo( int flags ) { + return flags == ResultWas::Info; } - void LegacyReporterAdapter::assertionStarting( AssertionInfo const& ) { - // Not on legacy interface + + ResultDisposition::Flags operator | ( ResultDisposition::Flags lhs, ResultDisposition::Flags rhs ) { + return static_cast<ResultDisposition::Flags>( static_cast<int>( lhs ) | static_cast<int>( rhs ) ); } - bool LegacyReporterAdapter::assertionEnded( AssertionStats const& assertionStats ) { - if( assertionStats.assertionResult.getResultType() != ResultWas::Ok ) { - for( std::vector<MessageInfo>::const_iterator it = assertionStats.infoMessages.begin(), itEnd = assertionStats.infoMessages.end(); - it != itEnd; - ++it ) { - if( it->type == ResultWas::Info ) { - ResultBuilder rb( it->macroName.c_str(), it->lineInfo, "", ResultDisposition::Normal ); - rb << it->message; - rb.setResultType( ResultWas::Info ); - AssertionResult result = rb.build(); - m_legacyReporter->Result( result ); + bool shouldContinueOnFailure( int flags ) { return ( flags & ResultDisposition::ContinueOnFailure ) != 0; } + bool shouldSuppressFailure( int flags ) { return ( flags & ResultDisposition::SuppressFail ) != 0; } + +} // end namespace Catch +// end catch_result_type.cpp +// start catch_run_context.cpp + +#include <cassert> +#include <algorithm> +#include <sstream> + +namespace Catch { + + namespace Generators { + struct GeneratorTracker : TestCaseTracking::TrackerBase, IGeneratorTracker { + GeneratorBasePtr m_generator; + + GeneratorTracker( TestCaseTracking::NameAndLocation const& nameAndLocation, TrackerContext& ctx, ITracker* parent ) + : TrackerBase( nameAndLocation, ctx, parent ) + {} + ~GeneratorTracker(); + + static GeneratorTracker& acquire( TrackerContext& ctx, TestCaseTracking::NameAndLocation const& nameAndLocation ) { + std::shared_ptr<GeneratorTracker> tracker; + + ITracker& currentTracker = ctx.currentTracker(); + if( TestCaseTracking::ITrackerPtr childTracker = currentTracker.findChild( nameAndLocation ) ) { + assert( childTracker ); + assert( childTracker->isGeneratorTracker() ); + tracker = std::static_pointer_cast<GeneratorTracker>( childTracker ); + } + else { + tracker = std::make_shared<GeneratorTracker>( nameAndLocation, ctx, ¤tTracker ); + currentTracker.addChild( tracker ); + } + + if( !ctx.completedCycle() && !tracker->isComplete() ) { + tracker->open(); + } + + return *tracker; + } + + // TrackerBase interface + bool isGeneratorTracker() const override { return true; } + auto hasGenerator() const -> bool override { + return !!m_generator; + } + void close() override { + TrackerBase::close(); + // Generator interface only finds out if it has another item on atual move + if (m_runState == CompletedSuccessfully && m_generator->next()) { + m_children.clear(); + m_runState = Executing; } } + + // IGeneratorTracker interface + auto getGenerator() const -> GeneratorBasePtr const& override { + return m_generator; + } + void setGenerator( GeneratorBasePtr&& generator ) override { + m_generator = std::move( generator ); + } + }; + GeneratorTracker::~GeneratorTracker() {} + } + + RunContext::RunContext(IConfigPtr const& _config, IStreamingReporterPtr&& reporter) + : m_runInfo(_config->name()), + m_context(getCurrentMutableContext()), + m_config(_config), + m_reporter(std::move(reporter)), + m_lastAssertionInfo{ StringRef(), SourceLineInfo("",0), StringRef(), ResultDisposition::Normal }, + m_includeSuccessfulResults( m_config->includeSuccessfulResults() || m_reporter->getPreferences().shouldReportAllAssertions ) + { + m_context.setRunner(this); + m_context.setConfig(m_config); + m_context.setResultCapture(this); + m_reporter->testRunStarting(m_runInfo); + } + + RunContext::~RunContext() { + m_reporter->testRunEnded(TestRunStats(m_runInfo, m_totals, aborting())); + } + + void RunContext::testGroupStarting(std::string const& testSpec, std::size_t groupIndex, std::size_t groupsCount) { + m_reporter->testGroupStarting(GroupInfo(testSpec, groupIndex, groupsCount)); + } + + void RunContext::testGroupEnded(std::string const& testSpec, Totals const& totals, std::size_t groupIndex, std::size_t groupsCount) { + m_reporter->testGroupEnded(TestGroupStats(GroupInfo(testSpec, groupIndex, groupsCount), totals, aborting())); + } + + Totals RunContext::runTest(TestCase const& testCase) { + Totals prevTotals = m_totals; + + std::string redirectedCout; + std::string redirectedCerr; + + auto const& testInfo = testCase.getTestCaseInfo(); + + m_reporter->testCaseStarting(testInfo); + + m_activeTestCase = &testCase; + + ITracker& rootTracker = m_trackerContext.startRun(); + assert(rootTracker.isSectionTracker()); + static_cast<SectionTracker&>(rootTracker).addInitialFilters(m_config->getSectionsToRun()); + do { + m_trackerContext.startCycle(); + m_testCaseTracker = &SectionTracker::acquire(m_trackerContext, TestCaseTracking::NameAndLocation(testInfo.name, testInfo.lineInfo)); + runCurrentTest(redirectedCout, redirectedCerr); + } while (!m_testCaseTracker->isSuccessfullyCompleted() && !aborting()); + + Totals deltaTotals = m_totals.delta(prevTotals); + if (testInfo.expectedToFail() && deltaTotals.testCases.passed > 0) { + deltaTotals.assertions.failed++; + deltaTotals.testCases.passed--; + deltaTotals.testCases.failed++; + } + m_totals.testCases += deltaTotals.testCases; + m_reporter->testCaseEnded(TestCaseStats(testInfo, + deltaTotals, + redirectedCout, + redirectedCerr, + aborting())); + + m_activeTestCase = nullptr; + m_testCaseTracker = nullptr; + + return deltaTotals; + } + + IConfigPtr RunContext::config() const { + return m_config; + } + + IStreamingReporter& RunContext::reporter() const { + return *m_reporter; + } + + void RunContext::assertionEnded(AssertionResult const & result) { + if (result.getResultType() == ResultWas::Ok) { + m_totals.assertions.passed++; + m_lastAssertionPassed = true; + } else if (!result.isOk()) { + m_lastAssertionPassed = false; + if( m_activeTestCase->getTestCaseInfo().okToFail() ) + m_totals.assertions.failedButOk++; + else + m_totals.assertions.failed++; } - m_legacyReporter->Result( assertionStats.assertionResult ); + else { + m_lastAssertionPassed = true; + } + + // We have no use for the return value (whether messages should be cleared), because messages were made scoped + // and should be let to clear themselves out. + static_cast<void>(m_reporter->assertionEnded(AssertionStats(result, m_messages, m_totals))); + + if (result.getResultType() != ResultWas::Warning) + m_messageScopes.clear(); + + // Reset working state + resetAssertionInfo(); + m_lastResult = result; + } + void RunContext::resetAssertionInfo() { + m_lastAssertionInfo.macroName = StringRef(); + m_lastAssertionInfo.capturedExpression = "{Unknown expression after the reported line}"_sr; + } + + bool RunContext::sectionStarted(SectionInfo const & sectionInfo, Counts & assertions) { + ITracker& sectionTracker = SectionTracker::acquire(m_trackerContext, TestCaseTracking::NameAndLocation(sectionInfo.name, sectionInfo.lineInfo)); + if (!sectionTracker.isOpen()) + return false; + m_activeSections.push_back(§ionTracker); + + m_lastAssertionInfo.lineInfo = sectionInfo.lineInfo; + + m_reporter->sectionStarting(sectionInfo); + + assertions = m_totals.assertions; + return true; } - void LegacyReporterAdapter::sectionEnded( SectionStats const& sectionStats ) { - if( sectionStats.missingAssertions ) - m_legacyReporter->NoAssertionsInSection( sectionStats.sectionInfo.name ); - m_legacyReporter->EndSection( sectionStats.sectionInfo.name, sectionStats.assertions ); + auto RunContext::acquireGeneratorTracker( SourceLineInfo const& lineInfo ) -> IGeneratorTracker& { + using namespace Generators; + GeneratorTracker& tracker = GeneratorTracker::acquire( m_trackerContext, TestCaseTracking::NameAndLocation( "generator", lineInfo ) ); + assert( tracker.isOpen() ); + m_lastAssertionInfo.lineInfo = lineInfo; + return tracker; + } + + bool RunContext::testForMissingAssertions(Counts& assertions) { + if (assertions.total() != 0) + return false; + if (!m_config->warnAboutMissingAssertions()) + return false; + if (m_trackerContext.currentTracker().hasChildren()) + return false; + m_totals.assertions.failed++; + assertions.failed++; + return true; } - void LegacyReporterAdapter::testCaseEnded( TestCaseStats const& testCaseStats ) { - m_legacyReporter->EndTestCase - ( testCaseStats.testInfo, - testCaseStats.totals, - testCaseStats.stdOut, - testCaseStats.stdErr ); + + void RunContext::sectionEnded(SectionEndInfo const & endInfo) { + Counts assertions = m_totals.assertions - endInfo.prevAssertions; + bool missingAssertions = testForMissingAssertions(assertions); + + if (!m_activeSections.empty()) { + m_activeSections.back()->close(); + m_activeSections.pop_back(); + } + + m_reporter->sectionEnded(SectionStats(endInfo.sectionInfo, assertions, endInfo.durationInSeconds, missingAssertions)); + m_messages.clear(); + m_messageScopes.clear(); } - void LegacyReporterAdapter::testGroupEnded( TestGroupStats const& testGroupStats ) { - if( testGroupStats.aborting ) - m_legacyReporter->Aborted(); - m_legacyReporter->EndGroup( testGroupStats.groupInfo.name, testGroupStats.totals ); + + void RunContext::sectionEndedEarly(SectionEndInfo const & endInfo) { + if (m_unfinishedSections.empty()) + m_activeSections.back()->fail(); + else + m_activeSections.back()->close(); + m_activeSections.pop_back(); + + m_unfinishedSections.push_back(endInfo); } - void LegacyReporterAdapter::testRunEnded( TestRunStats const& testRunStats ) { - m_legacyReporter->EndTesting( testRunStats.totals ); + +#if defined(CATCH_CONFIG_ENABLE_BENCHMARKING) + void RunContext::benchmarkPreparing(std::string const& name) { + m_reporter->benchmarkPreparing(name); + } + void RunContext::benchmarkStarting( BenchmarkInfo const& info ) { + m_reporter->benchmarkStarting( info ); } - void LegacyReporterAdapter::skipTest( TestCaseInfo const& ) { + void RunContext::benchmarkEnded( BenchmarkStats<> const& stats ) { + m_reporter->benchmarkEnded( stats ); } -} + void RunContext::benchmarkFailed(std::string const & error) { + m_reporter->benchmarkFailed(error); + } +#endif // CATCH_CONFIG_ENABLE_BENCHMARKING -// #included from: catch_timer.hpp + void RunContext::pushScopedMessage(MessageInfo const & message) { + m_messages.push_back(message); + } -#ifdef __clang__ -#pragma clang diagnostic push -#pragma clang diagnostic ignored "-Wc++11-long-long" -#endif + void RunContext::popScopedMessage(MessageInfo const & message) { + m_messages.erase(std::remove(m_messages.begin(), m_messages.end(), message), m_messages.end()); + } -#ifdef CATCH_PLATFORM_WINDOWS + void RunContext::emplaceUnscopedMessage( MessageBuilder const& builder ) { + m_messageScopes.emplace_back( builder ); + } + + std::string RunContext::getCurrentTestName() const { + return m_activeTestCase + ? m_activeTestCase->getTestCaseInfo().name + : std::string(); + } + + const AssertionResult * RunContext::getLastResult() const { + return &(*m_lastResult); + } + + void RunContext::exceptionEarlyReported() { + m_shouldReportUnexpected = false; + } + + void RunContext::handleFatalErrorCondition( StringRef message ) { + // First notify reporter that bad things happened + m_reporter->fatalErrorEncountered(message); + + // Don't rebuild the result -- the stringification itself can cause more fatal errors + // Instead, fake a result data. + AssertionResultData tempResult( ResultWas::FatalErrorCondition, { false } ); + tempResult.message = message; + AssertionResult result(m_lastAssertionInfo, tempResult); + + assertionEnded(result); + + handleUnfinishedSections(); + + // Recreate section for test case (as we will lose the one that was in scope) + auto const& testCaseInfo = m_activeTestCase->getTestCaseInfo(); + SectionInfo testCaseSection(testCaseInfo.lineInfo, testCaseInfo.name); + + Counts assertions; + assertions.failed = 1; + SectionStats testCaseSectionStats(testCaseSection, assertions, 0, false); + m_reporter->sectionEnded(testCaseSectionStats); + + auto const& testInfo = m_activeTestCase->getTestCaseInfo(); + + Totals deltaTotals; + deltaTotals.testCases.failed = 1; + deltaTotals.assertions.failed = 1; + m_reporter->testCaseEnded(TestCaseStats(testInfo, + deltaTotals, + std::string(), + std::string(), + false)); + m_totals.testCases.failed++; + testGroupEnded(std::string(), m_totals, 1, 1); + m_reporter->testRunEnded(TestRunStats(m_runInfo, m_totals, false)); + } + + bool RunContext::lastAssertionPassed() { + return m_lastAssertionPassed; + } + + void RunContext::assertionPassed() { + m_lastAssertionPassed = true; + ++m_totals.assertions.passed; + resetAssertionInfo(); + m_messageScopes.clear(); + } + + bool RunContext::aborting() const { + return m_totals.assertions.failed >= static_cast<std::size_t>(m_config->abortAfter()); + } + + void RunContext::runCurrentTest(std::string & redirectedCout, std::string & redirectedCerr) { + auto const& testCaseInfo = m_activeTestCase->getTestCaseInfo(); + SectionInfo testCaseSection(testCaseInfo.lineInfo, testCaseInfo.name); + m_reporter->sectionStarting(testCaseSection); + Counts prevAssertions = m_totals.assertions; + double duration = 0; + m_shouldReportUnexpected = true; + m_lastAssertionInfo = { "TEST_CASE"_sr, testCaseInfo.lineInfo, StringRef(), ResultDisposition::Normal }; + + seedRng(*m_config); + + Timer timer; + CATCH_TRY { + if (m_reporter->getPreferences().shouldRedirectStdOut) { +#if !defined(CATCH_CONFIG_EXPERIMENTAL_REDIRECT) + RedirectedStreams redirectedStreams(redirectedCout, redirectedCerr); + + timer.start(); + invokeActiveTestCase(); #else -#include <sys/time.h> + OutputRedirect r(redirectedCout, redirectedCerr); + timer.start(); + invokeActiveTestCase(); #endif + } else { + timer.start(); + invokeActiveTestCase(); + } + duration = timer.getElapsedSeconds(); + } CATCH_CATCH_ANON (TestFailureException&) { + // This just means the test was aborted due to failure + } CATCH_CATCH_ALL { + // Under CATCH_CONFIG_FAST_COMPILE, unexpected exceptions under REQUIRE assertions + // are reported without translation at the point of origin. + if( m_shouldReportUnexpected ) { + AssertionReaction dummyReaction; + handleUnexpectedInflightException( m_lastAssertionInfo, translateActiveException(), dummyReaction ); + } + } + Counts assertions = m_totals.assertions - prevAssertions; + bool missingAssertions = testForMissingAssertions(assertions); + + m_testCaseTracker->close(); + handleUnfinishedSections(); + m_messages.clear(); + m_messageScopes.clear(); + + SectionStats testCaseSectionStats(testCaseSection, assertions, duration, missingAssertions); + m_reporter->sectionEnded(testCaseSectionStats); + } + + void RunContext::invokeActiveTestCase() { + FatalConditionHandler fatalConditionHandler; // Handle signals + m_activeTestCase->invoke(); + fatalConditionHandler.reset(); + } + + void RunContext::handleUnfinishedSections() { + // If sections ended prematurely due to an exception we stored their + // infos here so we can tear them down outside the unwind process. + for (auto it = m_unfinishedSections.rbegin(), + itEnd = m_unfinishedSections.rend(); + it != itEnd; + ++it) + sectionEnded(*it); + m_unfinishedSections.clear(); + } + + void RunContext::handleExpr( + AssertionInfo const& info, + ITransientExpression const& expr, + AssertionReaction& reaction + ) { + m_reporter->assertionStarting( info ); + + bool negated = isFalseTest( info.resultDisposition ); + bool result = expr.getResult() != negated; + + if( result ) { + if (!m_includeSuccessfulResults) { + assertionPassed(); + } + else { + reportExpr(info, ResultWas::Ok, &expr, negated); + } + } + else { + reportExpr(info, ResultWas::ExpressionFailed, &expr, negated ); + populateReaction( reaction ); + } + } + void RunContext::reportExpr( + AssertionInfo const &info, + ResultWas::OfType resultType, + ITransientExpression const *expr, + bool negated ) { + + m_lastAssertionInfo = info; + AssertionResultData data( resultType, LazyExpression( negated ) ); + + AssertionResult assertionResult{ info, data }; + assertionResult.m_resultData.lazyExpression.m_transientExpression = expr; + + assertionEnded( assertionResult ); + } + + void RunContext::handleMessage( + AssertionInfo const& info, + ResultWas::OfType resultType, + StringRef const& message, + AssertionReaction& reaction + ) { + m_reporter->assertionStarting( info ); + + m_lastAssertionInfo = info; + + AssertionResultData data( resultType, LazyExpression( false ) ); + data.message = message; + AssertionResult assertionResult{ m_lastAssertionInfo, data }; + assertionEnded( assertionResult ); + if( !assertionResult.isOk() ) + populateReaction( reaction ); + } + void RunContext::handleUnexpectedExceptionNotThrown( + AssertionInfo const& info, + AssertionReaction& reaction + ) { + handleNonExpr(info, Catch::ResultWas::DidntThrowException, reaction); + } + + void RunContext::handleUnexpectedInflightException( + AssertionInfo const& info, + std::string const& message, + AssertionReaction& reaction + ) { + m_lastAssertionInfo = info; + + AssertionResultData data( ResultWas::ThrewException, LazyExpression( false ) ); + data.message = message; + AssertionResult assertionResult{ info, data }; + assertionEnded( assertionResult ); + populateReaction( reaction ); + } + + void RunContext::populateReaction( AssertionReaction& reaction ) { + reaction.shouldDebugBreak = m_config->shouldDebugBreak(); + reaction.shouldThrow = aborting() || (m_lastAssertionInfo.resultDisposition & ResultDisposition::Normal); + } + + void RunContext::handleIncomplete( + AssertionInfo const& info + ) { + m_lastAssertionInfo = info; + + AssertionResultData data( ResultWas::ThrewException, LazyExpression( false ) ); + data.message = "Exception translation was disabled by CATCH_CONFIG_FAST_COMPILE"; + AssertionResult assertionResult{ info, data }; + assertionEnded( assertionResult ); + } + void RunContext::handleNonExpr( + AssertionInfo const &info, + ResultWas::OfType resultType, + AssertionReaction &reaction + ) { + m_lastAssertionInfo = info; + + AssertionResultData data( resultType, LazyExpression( false ) ); + AssertionResult assertionResult{ info, data }; + assertionEnded( assertionResult ); + + if( !assertionResult.isOk() ) + populateReaction( reaction ); + } + + IResultCapture& getResultCapture() { + if (auto* capture = getCurrentContext().getResultCapture()) + return *capture; + else + CATCH_INTERNAL_ERROR("No result capture instance"); + } +} +// end catch_run_context.cpp +// start catch_section.cpp + +namespace Catch { + + Section::Section( SectionInfo const& info ) + : m_info( info ), + m_sectionIncluded( getResultCapture().sectionStarted( m_info, m_assertions ) ) + { + m_timer.start(); + } + + Section::~Section() { + if( m_sectionIncluded ) { + SectionEndInfo endInfo{ m_info, m_assertions, m_timer.getElapsedSeconds() }; + if( uncaught_exceptions() ) + getResultCapture().sectionEndedEarly( endInfo ); + else + getResultCapture().sectionEnded( endInfo ); + } + } + + // This indicates whether the section should be executed or not + Section::operator bool() const { + return m_sectionIncluded; + } + +} // end namespace Catch +// end catch_section.cpp +// start catch_section_info.cpp + +namespace Catch { + + SectionInfo::SectionInfo + ( SourceLineInfo const& _lineInfo, + std::string const& _name ) + : name( _name ), + lineInfo( _lineInfo ) + {} + +} // end namespace Catch +// end catch_section_info.cpp +// start catch_session.cpp + +// start catch_session.h + +#include <memory> + +namespace Catch { + + class Session : NonCopyable { + public: + + Session(); + ~Session() override; + + void showHelp() const; + void libIdentify(); + + int applyCommandLine( int argc, char const * const * argv ); + #if defined(CATCH_CONFIG_WCHAR) && defined(WIN32) && defined(UNICODE) + int applyCommandLine( int argc, wchar_t const * const * argv ); + #endif + + void useConfigData( ConfigData const& configData ); + + template<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; + std::shared_ptr<Config> m_config; + bool m_startupExceptions = false; + }; + +} // end namespace Catch + +// end catch_session.h +// start catch_version.h + +#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(); +} + +// end catch_version.h +#include <cstdlib> +#include <iomanip> namespace Catch { namespace { -#ifdef CATCH_PLATFORM_WINDOWS - uint64_t getCurrentTicks() { - static uint64_t hz=0, hzo=0; - if (!hz) { - QueryPerformanceFrequency( reinterpret_cast<LARGE_INTEGER*>( &hz ) ); - QueryPerformanceCounter( reinterpret_cast<LARGE_INTEGER*>( &hzo ) ); + const int MaxExitCode = 255; + + IStreamingReporterPtr createReporter(std::string const& reporterName, IConfigPtr const& config) { + auto reporter = Catch::getRegistryHub().getReporterRegistry().create(reporterName, config); + CATCH_ENFORCE(reporter, "No reporter registered with name: '" << reporterName << "'"); + + return reporter; + } + + IStreamingReporterPtr makeReporter(std::shared_ptr<Config> const& config) { + if (Catch::getRegistryHub().getReporterRegistry().getListeners().empty()) { + return createReporter(config->getReporterName(), config); + } + + // On older platforms, returning std::unique_ptr<ListeningReporter> + // when the return type is std::unique_ptr<IStreamingReporter> + // doesn't compile without a std::move call. However, this causes + // a warning on newer platforms. Thus, we have to work around + // it a bit and downcast the pointer manually. + auto ret = std::unique_ptr<IStreamingReporter>(new ListeningReporter); + auto& multi = static_cast<ListeningReporter&>(*ret); + auto const& listeners = Catch::getRegistryHub().getReporterRegistry().getListeners(); + for (auto const& listener : listeners) { + multi.addListener(listener->create(Catch::ReporterConfig(config))); } - uint64_t t; - QueryPerformanceCounter( reinterpret_cast<LARGE_INTEGER*>( &t ) ); - return ((t-hzo)*1000000)/hz; + multi.addReporter(createReporter(config->getReporterName(), config)); + return ret; } -#else - uint64_t getCurrentTicks() { - timeval t; - gettimeofday(&t,CATCH_NULL); - return static_cast<uint64_t>( t.tv_sec ) * 1000000ull + static_cast<uint64_t>( t.tv_usec ); + + Catch::Totals runTests(std::shared_ptr<Config> const& config) { + auto reporter = makeReporter(config); + + RunContext context(config, std::move(reporter)); + + Totals totals; + + context.testGroupStarting(config->name(), 1, 1); + + TestSpec testSpec = config->testSpec(); + + auto const& allTestCases = getAllTestCasesSorted(*config); + for (auto const& testCase : allTestCases) { + bool matching = (!testSpec.hasFilters() && !testCase.isHidden()) || + (testSpec.hasFilters() && matchTest(testCase, testSpec, *config)); + + if (!context.aborting() && matching) + totals += context.runTest(testCase); + else + context.reporter().skipTest(testCase); + } + + if (config->warnAboutNoTests() && totals.testCases.total() == 0) { + ReusableStringStream testConfig; + + bool first = true; + for (const auto& input : config->getTestsOrTags()) { + if (!first) { testConfig << ' '; } + first = false; + testConfig << input; + } + + context.reporter().noMatchingTestCases(testConfig.str()); + totals.error = -1; + } + + context.testGroupEnded(config->name(), totals, 1, 1); + return totals; + } + + void applyFilenamesAsTags(Catch::IConfig const& config) { + auto& tests = const_cast<std::vector<TestCase>&>(getAllTestCasesSorted(config)); + for (auto& testCase : tests) { + auto tags = testCase.tags; + + std::string filename = testCase.lineInfo.file; + auto lastSlash = filename.find_last_of("\\/"); + if (lastSlash != std::string::npos) { + filename.erase(0, lastSlash); + filename[0] = '#'; + } + + auto lastDot = filename.find_last_of('.'); + if (lastDot != std::string::npos) { + filename.erase(lastDot); + } + + tags.push_back(std::move(filename)); + setTags(testCase, tags); + } + } + + } // anon namespace + + Session::Session() { + static bool alreadyInstantiated = false; + if( alreadyInstantiated ) { + CATCH_TRY { CATCH_INTERNAL_ERROR( "Only one instance of Catch::Session can ever be used" ); } + CATCH_CATCH_ALL { getMutableRegistryHub().registerStartupException(); } + } + + // There cannot be exceptions at startup in no-exception mode. +#if !defined(CATCH_CONFIG_DISABLE_EXCEPTIONS) + const auto& exceptions = getRegistryHub().getStartupExceptionRegistry().getExceptions(); + if ( !exceptions.empty() ) { + config(); + getCurrentMutableContext().setConfig(m_config); + + m_startupExceptions = true; + Colour colourGuard( Colour::Red ); + Catch::cerr() << "Errors occurred during startup!" << '\n'; + // iterate over all exceptions and notify user + for ( const auto& ex_ptr : exceptions ) { + try { + std::rethrow_exception(ex_ptr); + } catch ( std::exception const& ex ) { + Catch::cerr() << Column( ex.what() ).indent(2) << '\n'; + } + } } #endif + + alreadyInstantiated = true; + m_cli = makeCommandLineParser( m_configData ); + } + Session::~Session() { + Catch::cleanUp(); } - void Timer::start() { - m_ticks = getCurrentTicks(); + void Session::showHelp() const { + Catch::cout() + << "\nCatch v" << libraryVersion() << "\n" + << m_cli << std::endl + << "For more detailed usage please see the project docs\n" << std::endl; } - unsigned int Timer::getElapsedMicroseconds() const { - return static_cast<unsigned int>(getCurrentTicks() - m_ticks); + void Session::libIdentify() { + Catch::cout() + << std::left << std::setw(16) << "description: " << "A Catch test executable\n" + << std::left << std::setw(16) << "category: " << "testframework\n" + << std::left << std::setw(16) << "framework: " << "Catch Test\n" + << std::left << std::setw(16) << "version: " << libraryVersion() << std::endl; } - unsigned int Timer::getElapsedMilliseconds() const { - return static_cast<unsigned int>(getElapsedMicroseconds()/1000); + + int Session::applyCommandLine( int argc, char const * const * argv ) { + if( m_startupExceptions ) + return 1; + + auto result = m_cli.parse( clara::Args( argc, argv ) ); + if( !result ) { + config(); + getCurrentMutableContext().setConfig(m_config); + Catch::cerr() + << Colour( Colour::Red ) + << "\nError(s) in input:\n" + << Column( result.errorMessage() ).indent( 2 ) + << "\n\n"; + Catch::cerr() << "Run with -? for usage\n" << std::endl; + return MaxExitCode; + } + + if( m_configData.showHelp ) + showHelp(); + if( m_configData.libIdentify ) + libIdentify(); + m_config.reset(); + return 0; } - double Timer::getElapsedSeconds() const { - return getElapsedMicroseconds()/1000000.0; + +#if defined(CATCH_CONFIG_WCHAR) && defined(WIN32) && defined(UNICODE) + int Session::applyCommandLine( int argc, wchar_t const * const * argv ) { + + char **utf8Argv = new char *[ argc ]; + + for ( int i = 0; i < argc; ++i ) { + int bufSize = WideCharToMultiByte( CP_UTF8, 0, argv[i], -1, NULL, 0, NULL, NULL ); + + utf8Argv[ i ] = new char[ bufSize ]; + + WideCharToMultiByte( CP_UTF8, 0, argv[i], -1, utf8Argv[i], bufSize, NULL, NULL ); + } + + int returnCode = applyCommandLine( argc, utf8Argv ); + + for ( int i = 0; i < argc; ++i ) + delete [] utf8Argv[ i ]; + + delete [] utf8Argv; + + return returnCode; + } +#endif + + void Session::useConfigData( ConfigData const& configData ) { + m_configData = configData; + m_config.reset(); + } + + int Session::run() { + if( ( m_configData.waitForKeypress & WaitForKeypress::BeforeStart ) != 0 ) { + Catch::cout() << "...waiting for enter/ return before starting" << std::endl; + static_cast<void>(std::getchar()); + } + int exitCode = runInternal(); + if( ( m_configData.waitForKeypress & WaitForKeypress::BeforeExit ) != 0 ) { + Catch::cout() << "...waiting for enter/ return before exiting, with code: " << exitCode << std::endl; + static_cast<void>(std::getchar()); + } + return exitCode; + } + + clara::Parser const& Session::cli() const { + return m_cli; + } + void Session::cli( clara::Parser const& newParser ) { + m_cli = newParser; + } + ConfigData& Session::configData() { + return m_configData; + } + Config& Session::config() { + if( !m_config ) + m_config = std::make_shared<Config>( m_configData ); + return *m_config; + } + + int Session::runInternal() { + if( m_startupExceptions ) + return 1; + + if (m_configData.showHelp || m_configData.libIdentify) { + return 0; + } + + CATCH_TRY { + config(); // Force config to be constructed + + seedRng( *m_config ); + + if( m_configData.filenamesAsTags ) + applyFilenamesAsTags( *m_config ); + + // Handle list request + if( Option<std::size_t> listed = list( m_config ) ) + return static_cast<int>( *listed ); + + auto totals = runTests( m_config ); + // Note that on unices only the lower 8 bits are usually used, clamping + // the return value to 255 prevents false negative when some multiple + // of 256 tests has failed + return (std::min) (MaxExitCode, (std::max) (totals.error, static_cast<int>(totals.assertions.failed))); + } +#if !defined(CATCH_CONFIG_DISABLE_EXCEPTIONS) + catch( std::exception& ex ) { + Catch::cerr() << ex.what() << std::endl; + return MaxExitCode; + } +#endif + } + +} // end namespace Catch +// end catch_session.cpp +// start catch_singletons.cpp + +#include <vector> + +namespace Catch { + + namespace { + static auto getSingletons() -> std::vector<ISingleton*>*& { + static std::vector<ISingleton*>* g_singletons = nullptr; + if( !g_singletons ) + g_singletons = new std::vector<ISingleton*>(); + return g_singletons; + } + } + + ISingleton::~ISingleton() {} + + void addSingleton(ISingleton* singleton ) { + getSingletons()->push_back( singleton ); + } + void cleanupSingletons() { + auto& singletons = getSingletons(); + for( auto singleton : *singletons ) + delete singleton; + delete singletons; + singletons = nullptr; } } // namespace Catch +// end catch_singletons.cpp +// start catch_startup_exception_registry.cpp -#ifdef __clang__ -#pragma clang diagnostic pop +namespace Catch { +void StartupExceptionRegistry::add( std::exception_ptr const& exception ) noexcept { + CATCH_TRY { + m_exceptions.push_back(exception); + } CATCH_CATCH_ALL { + // If we run out of memory during start-up there's really not a lot more we can do about it + std::terminate(); + } + } + + std::vector<std::exception_ptr> const& StartupExceptionRegistry::getExceptions() const noexcept { + return m_exceptions; + } + +} // end namespace Catch +// end catch_startup_exception_registry.cpp +// start catch_stream.cpp + +#include <cstdio> +#include <iostream> +#include <fstream> +#include <sstream> +#include <vector> +#include <memory> + +namespace Catch { + + Catch::IStream::~IStream() = default; + + namespace Detail { namespace { + template<typename WriterF, std::size_t bufferSize=256> + class StreamBufImpl : public std::streambuf { + char data[bufferSize]; + WriterF m_writer; + + public: + StreamBufImpl() { + setp( data, data + sizeof(data) ); + } + + ~StreamBufImpl() noexcept { + StreamBufImpl::sync(); + } + + private: + int overflow( int c ) override { + sync(); + + if( c != EOF ) { + if( pbase() == epptr() ) + m_writer( std::string( 1, static_cast<char>( c ) ) ); + else + sputc( static_cast<char>( c ) ); + } + return 0; + } + + int sync() override { + if( pbase() != pptr() ) { + m_writer( std::string( pbase(), static_cast<std::string::size_type>( pptr() - pbase() ) ) ); + setp( pbase(), epptr() ); + } + return 0; + } + }; + + /////////////////////////////////////////////////////////////////////////// + + struct OutputDebugWriter { + + void operator()( std::string const&str ) { + writeToDebugConsole( str ); + } + }; + + /////////////////////////////////////////////////////////////////////////// + + class FileStream : public IStream { + mutable std::ofstream m_ofs; + public: + FileStream( StringRef filename ) { + m_ofs.open( filename.c_str() ); + CATCH_ENFORCE( !m_ofs.fail(), "Unable to open file: '" << filename << "'" ); + } + ~FileStream() override = default; + public: // IStream + std::ostream& stream() const override { + return m_ofs; + } + }; + + /////////////////////////////////////////////////////////////////////////// + + class CoutStream : public IStream { + mutable std::ostream m_os; + public: + // Store the streambuf from cout up-front because + // cout may get redirected when running tests + CoutStream() : m_os( Catch::cout().rdbuf() ) {} + ~CoutStream() override = default; + + public: // IStream + std::ostream& stream() const override { return m_os; } + }; + + /////////////////////////////////////////////////////////////////////////// + + class DebugOutStream : public IStream { + std::unique_ptr<StreamBufImpl<OutputDebugWriter>> m_streamBuf; + mutable std::ostream m_os; + public: + DebugOutStream() + : m_streamBuf( new StreamBufImpl<OutputDebugWriter>() ), + m_os( m_streamBuf.get() ) + {} + + ~DebugOutStream() override = default; + + public: // IStream + std::ostream& stream() const override { return m_os; } + }; + + }} // namespace anon::detail + + /////////////////////////////////////////////////////////////////////////// + + auto makeStream( StringRef const &filename ) -> IStream const* { + if( filename.empty() ) + return new Detail::CoutStream(); + else if( filename[0] == '%' ) { + if( filename == "%debug" ) + return new Detail::DebugOutStream(); + else + CATCH_ERROR( "Unrecognised stream: '" << filename << "'" ); + } + else + return new Detail::FileStream( filename ); + } + + // This class encapsulates the idea of a pool of ostringstreams that can be reused. + struct StringStreams { + std::vector<std::unique_ptr<std::ostringstream>> m_streams; + std::vector<std::size_t> m_unused; + std::ostringstream m_referenceStream; // Used for copy state/ flags from + + auto add() -> std::size_t { + if( m_unused.empty() ) { + m_streams.push_back( std::unique_ptr<std::ostringstream>( new std::ostringstream ) ); + return m_streams.size()-1; + } + else { + auto index = m_unused.back(); + m_unused.pop_back(); + return index; + } + } + + void release( std::size_t index ) { + m_streams[index]->copyfmt( m_referenceStream ); // Restore initial flags and other state + m_unused.push_back(index); + } + }; + + ReusableStringStream::ReusableStringStream() + : m_index( Singleton<StringStreams>::getMutable().add() ), + m_oss( Singleton<StringStreams>::getMutable().m_streams[m_index].get() ) + {} + + ReusableStringStream::~ReusableStringStream() { + static_cast<std::ostringstream*>( m_oss )->str(""); + m_oss->clear(); + Singleton<StringStreams>::getMutable().release( m_index ); + } + + auto ReusableStringStream::str() const -> std::string { + return static_cast<std::ostringstream*>( m_oss )->str(); + } + + /////////////////////////////////////////////////////////////////////////// + +#ifndef CATCH_CONFIG_NOSTDOUT // If you #define this you must implement these functions + std::ostream& cout() { return std::cout; } + std::ostream& cerr() { return std::cerr; } + std::ostream& clog() { return std::clog; } #endif -// #included from: catch_common.hpp -#define TWOBLUECUBES_CATCH_COMMON_HPP_INCLUDED +} +// end catch_stream.cpp +// start catch_string_manip.cpp +#include <algorithm> +#include <ostream> #include <cstring> #include <cctype> +#include <vector> namespace Catch { + namespace { + char toLowerCh(char c) { + return static_cast<char>( std::tolower( c ) ); + } + } + bool startsWith( std::string const& s, std::string const& prefix ) { return s.size() >= prefix.size() && std::equal(prefix.begin(), prefix.end(), s.begin()); } @@ -8282,9 +13083,6 @@ namespace Catch { bool contains( std::string const& s, std::string const& infix ) { return s.find( infix ) != std::string::npos; } - char toLowerCh(char c) { - return static_cast<char>( std::tolower( c ) ); - } void toLowerInPlace( std::string& s ) { std::transform( s.begin(), s.end(), s.begin(), toLowerCh ); } @@ -8315,6 +13113,21 @@ namespace Catch { return replaced; } + std::vector<StringRef> splitStringRef( StringRef str, char delimiter ) { + std::vector<StringRef> subStrings; + std::size_t start = 0; + for(std::size_t pos = 0; pos < str.size(); ++pos ) { + if( str[pos] == delimiter ) { + if( pos - start > 1 ) + subStrings.push_back( str.substr( start, pos-start ) ); + start = pos+1; + } + } + if( start < str.size() ) + subStrings.push_back( str.substr( start, str.size()-start ) ); + return subStrings; + } + pluralise::pluralise( std::size_t count, std::string const& label ) : m_count( count ), m_label( label ) @@ -8327,200 +13140,946 @@ namespace Catch { return os; } - SourceLineInfo::SourceLineInfo() : file(""), line( 0 ){} - SourceLineInfo::SourceLineInfo( char const* _file, std::size_t _line ) - : file( _file ), - line( _line ) +} +// end catch_string_manip.cpp +// start catch_stringref.cpp + +#if defined(__clang__) +# pragma clang diagnostic push +# pragma clang diagnostic ignored "-Wexit-time-destructors" +#endif + +#include <ostream> +#include <cstring> +#include <cstdint> + +namespace { + const uint32_t byte_2_lead = 0xC0; + const uint32_t byte_3_lead = 0xE0; + const uint32_t byte_4_lead = 0xF0; +} + +namespace Catch { + StringRef::StringRef( char const* rawChars ) noexcept + : StringRef( rawChars, static_cast<StringRef::size_type>(std::strlen(rawChars) ) ) {} - bool SourceLineInfo::empty() const { - return file[0] == '\0'; + + StringRef::operator std::string() const { + return std::string( m_start, m_size ); } - bool SourceLineInfo::operator == ( SourceLineInfo const& other ) const { - return line == other.line && (file == other.file || std::strcmp(file, other.file) == 0); + + void StringRef::swap( StringRef& other ) noexcept { + std::swap( m_start, other.m_start ); + std::swap( m_size, other.m_size ); + std::swap( m_data, other.m_data ); } - bool SourceLineInfo::operator < ( SourceLineInfo const& other ) const { - return line < other.line || ( line == other.line && (std::strcmp(file, other.file) < 0)); + + auto StringRef::c_str() const -> char const* { + if( !isSubstring() ) + return m_start; + + const_cast<StringRef *>( this )->takeOwnership(); + return m_data; + } + auto StringRef::currentData() const noexcept -> char const* { + return m_start; } - void seedRng( IConfig const& config ) { - if( config.rngSeed() != 0 ) - std::srand( config.rngSeed() ); + auto StringRef::isOwned() const noexcept -> bool { + return m_data != nullptr; } - unsigned int rngSeed() { - return getCurrentContext().getConfig()->rngSeed(); + auto StringRef::isSubstring() const noexcept -> bool { + return m_start[m_size] != '\0'; } - std::ostream& operator << ( std::ostream& os, SourceLineInfo const& info ) { -#ifndef __GNUG__ - os << info.file << '(' << info.line << ')'; -#else - os << info.file << ':' << info.line; -#endif - return os; + void StringRef::takeOwnership() { + if( !isOwned() ) { + m_data = new char[m_size+1]; + memcpy( m_data, m_start, m_size ); + m_data[m_size] = '\0'; + } + } + auto StringRef::substr( size_type start, size_type size ) const noexcept -> StringRef { + if( start < m_size ) + return StringRef( m_start+start, size ); + else + return StringRef(); + } + auto StringRef::operator == ( StringRef const& other ) const noexcept -> bool { + return + size() == other.size() && + (std::strncmp( m_start, other.m_start, size() ) == 0); + } + auto StringRef::operator != ( StringRef const& other ) const noexcept -> bool { + return !operator==( other ); + } + + auto StringRef::operator[](size_type index) const noexcept -> char { + return m_start[index]; + } + + auto StringRef::numberOfCharacters() const noexcept -> size_type { + size_type noChars = m_size; + // Make adjustments for uft encodings + for( size_type i=0; i < m_size; ++i ) { + char c = m_start[i]; + if( ( c & byte_2_lead ) == byte_2_lead ) { + noChars--; + if (( c & byte_3_lead ) == byte_3_lead ) + noChars--; + if( ( c & byte_4_lead ) == byte_4_lead ) + noChars--; + } + } + return noChars; } - void throwLogicError( std::string const& message, SourceLineInfo const& locationInfo ) { - std::ostringstream oss; - oss << locationInfo << ": Internal Catch error: '" << message << '\''; - if( alwaysTrue() ) - throw std::logic_error( oss.str() ); + auto operator + ( StringRef const& lhs, StringRef const& rhs ) -> std::string { + std::string str; + str.reserve( lhs.size() + rhs.size() ); + str += lhs; + str += rhs; + return str; + } + auto operator + ( StringRef const& lhs, const char* rhs ) -> std::string { + return std::string( lhs ) + std::string( rhs ); + } + auto operator + ( char const* lhs, StringRef const& rhs ) -> std::string { + return std::string( lhs ) + std::string( rhs ); + } + + auto operator << ( std::ostream& os, StringRef const& str ) -> std::ostream& { + return os.write(str.currentData(), str.size()); + } + + auto operator+=( std::string& lhs, StringRef const& rhs ) -> std::string& { + lhs.append(rhs.currentData(), rhs.size()); + return lhs; } + +} // namespace Catch + +#if defined(__clang__) +# pragma clang diagnostic pop +#endif +// end catch_stringref.cpp +// start catch_tag_alias.cpp + +namespace Catch { + TagAlias::TagAlias(std::string const & _tag, SourceLineInfo _lineInfo): tag(_tag), lineInfo(_lineInfo) {} } +// end catch_tag_alias.cpp +// start catch_tag_alias_autoregistrar.cpp -// #included from: catch_section.hpp -#define TWOBLUECUBES_CATCH_SECTION_HPP_INCLUDED +namespace Catch { + + RegistrarForTagAliases::RegistrarForTagAliases(char const* alias, char const* tag, SourceLineInfo const& lineInfo) { + CATCH_TRY { + getMutableRegistryHub().registerTagAlias(alias, tag, lineInfo); + } CATCH_CATCH_ALL { + // Do not throw when constructing global objects, instead register the exception to be processed later + getMutableRegistryHub().registerStartupException(); + } + } + +} +// end catch_tag_alias_autoregistrar.cpp +// start catch_tag_alias_registry.cpp + +#include <sstream> namespace Catch { - SectionInfo::SectionInfo - ( SourceLineInfo const& _lineInfo, - std::string const& _name, - std::string const& _description ) + TagAliasRegistry::~TagAliasRegistry() {} + + TagAlias const* TagAliasRegistry::find( std::string const& alias ) const { + auto it = m_registry.find( alias ); + if( it != m_registry.end() ) + return &(it->second); + else + return nullptr; + } + + std::string TagAliasRegistry::expandAliases( std::string const& unexpandedTestSpec ) const { + std::string expandedTestSpec = unexpandedTestSpec; + for( auto const& registryKvp : m_registry ) { + std::size_t pos = expandedTestSpec.find( registryKvp.first ); + if( pos != std::string::npos ) { + expandedTestSpec = expandedTestSpec.substr( 0, pos ) + + registryKvp.second.tag + + expandedTestSpec.substr( pos + registryKvp.first.size() ); + } + } + return expandedTestSpec; + } + + void TagAliasRegistry::add( std::string const& alias, std::string const& tag, SourceLineInfo const& lineInfo ) { + CATCH_ENFORCE( startsWith(alias, "[@") && endsWith(alias, ']'), + "error: tag alias, '" << alias << "' is not of the form [@alias name].\n" << lineInfo ); + + CATCH_ENFORCE( m_registry.insert(std::make_pair(alias, TagAlias(tag, lineInfo))).second, + "error: tag alias, '" << alias << "' already registered.\n" + << "\tFirst seen at: " << find(alias)->lineInfo << "\n" + << "\tRedefined at: " << lineInfo ); + } + + ITagAliasRegistry::~ITagAliasRegistry() {} + + ITagAliasRegistry const& ITagAliasRegistry::get() { + return getRegistryHub().getTagAliasRegistry(); + } + +} // end namespace Catch +// end catch_tag_alias_registry.cpp +// start catch_test_case_info.cpp + +#include <cctype> +#include <exception> +#include <algorithm> +#include <sstream> + +namespace Catch { + + namespace { + TestCaseInfo::SpecialProperties parseSpecialTag( std::string const& tag ) { + if( startsWith( tag, '.' ) || + tag == "!hide" ) + return TestCaseInfo::IsHidden; + else if( tag == "!throws" ) + return TestCaseInfo::Throws; + else if( tag == "!shouldfail" ) + return TestCaseInfo::ShouldFail; + else if( tag == "!mayfail" ) + return TestCaseInfo::MayFail; + else if( tag == "!nonportable" ) + return TestCaseInfo::NonPortable; + else if( tag == "!benchmark" ) + return static_cast<TestCaseInfo::SpecialProperties>( TestCaseInfo::Benchmark | TestCaseInfo::IsHidden ); + else + return TestCaseInfo::None; + } + bool isReservedTag( std::string const& tag ) { + return parseSpecialTag( tag ) == TestCaseInfo::None && tag.size() > 0 && !std::isalnum( static_cast<unsigned char>(tag[0]) ); + } + void enforceNotReservedTag( std::string const& tag, SourceLineInfo const& _lineInfo ) { + CATCH_ENFORCE( !isReservedTag(tag), + "Tag name: [" << tag << "] is not allowed.\n" + << "Tag names starting with non alphanumeric characters are reserved\n" + << _lineInfo ); + } + } + + TestCase makeTestCase( ITestInvoker* _testCase, + std::string const& _className, + NameAndTags const& nameAndTags, + SourceLineInfo const& _lineInfo ) + { + bool isHidden = false; + + // Parse out tags + std::vector<std::string> tags; + std::string desc, tag; + bool inTag = false; + std::string _descOrTags = nameAndTags.tags; + for (char c : _descOrTags) { + if( !inTag ) { + if( c == '[' ) + inTag = true; + else + desc += c; + } + else { + if( c == ']' ) { + TestCaseInfo::SpecialProperties prop = parseSpecialTag( tag ); + if( ( prop & TestCaseInfo::IsHidden ) != 0 ) + isHidden = true; + else if( prop == TestCaseInfo::None ) + enforceNotReservedTag( tag, _lineInfo ); + + // Merged hide tags like `[.approvals]` should be added as + // `[.][approvals]`. The `[.]` is added at later point, so + // we only strip the prefix + if (startsWith(tag, '.') && tag.size() > 1) { + tag.erase(0, 1); + } + tags.push_back( tag ); + tag.clear(); + inTag = false; + } + else + tag += c; + } + } + if( isHidden ) { + tags.push_back( "." ); + } + + TestCaseInfo info( nameAndTags.name, _className, desc, tags, _lineInfo ); + return TestCase( _testCase, std::move(info) ); + } + + void setTags( TestCaseInfo& testCaseInfo, std::vector<std::string> tags ) { + std::sort(begin(tags), end(tags)); + tags.erase(std::unique(begin(tags), end(tags)), end(tags)); + testCaseInfo.lcaseTags.clear(); + + for( auto const& tag : tags ) { + std::string lcaseTag = toLower( tag ); + testCaseInfo.properties = static_cast<TestCaseInfo::SpecialProperties>( testCaseInfo.properties | parseSpecialTag( lcaseTag ) ); + testCaseInfo.lcaseTags.push_back( lcaseTag ); + } + testCaseInfo.tags = std::move(tags); + } + + TestCaseInfo::TestCaseInfo( std::string const& _name, + std::string const& _className, + std::string const& _description, + std::vector<std::string> const& _tags, + SourceLineInfo const& _lineInfo ) : name( _name ), + className( _className ), description( _description ), - lineInfo( _lineInfo ) - {} + lineInfo( _lineInfo ), + properties( None ) + { + setTags( *this, _tags ); + } - Section::Section( SectionInfo const& info ) - : m_info( info ), - m_sectionIncluded( getResultCapture().sectionStarted( m_info, m_assertions ) ) + bool TestCaseInfo::isHidden() const { + return ( properties & IsHidden ) != 0; + } + bool TestCaseInfo::throws() const { + return ( properties & Throws ) != 0; + } + bool TestCaseInfo::okToFail() const { + return ( properties & (ShouldFail | MayFail ) ) != 0; + } + bool TestCaseInfo::expectedToFail() const { + return ( properties & (ShouldFail ) ) != 0; + } + + std::string TestCaseInfo::tagsAsString() const { + std::string ret; + // '[' and ']' per tag + std::size_t full_size = 2 * tags.size(); + for (const auto& tag : tags) { + full_size += tag.size(); + } + ret.reserve(full_size); + for (const auto& tag : tags) { + ret.push_back('['); + ret.append(tag); + ret.push_back(']'); + } + + return ret; + } + + TestCase::TestCase( ITestInvoker* testCase, TestCaseInfo&& info ) : TestCaseInfo( std::move(info) ), test( testCase ) {} + + TestCase TestCase::withName( std::string const& _newName ) const { + TestCase other( *this ); + other.name = _newName; + return other; + } + + void TestCase::invoke() const { + test->invoke(); + } + + bool TestCase::operator == ( TestCase const& other ) const { + return test.get() == other.test.get() && + name == other.name && + className == other.className; + } + + bool TestCase::operator < ( TestCase const& other ) const { + return name < other.name; + } + + TestCaseInfo const& TestCase::getTestCaseInfo() const { - m_timer.start(); + return *this; } - Section::~Section() { - if( m_sectionIncluded ) { - SectionEndInfo endInfo( m_info, m_assertions, m_timer.getElapsedSeconds() ); - if( std::uncaught_exception() ) - getResultCapture().sectionEndedEarly( endInfo ); - else - getResultCapture().sectionEnded( endInfo ); +} // end namespace Catch +// end catch_test_case_info.cpp +// start catch_test_case_registry_impl.cpp + +#include <sstream> + +namespace Catch { + + std::vector<TestCase> sortTests( IConfig const& config, std::vector<TestCase> const& unsortedTestCases ) { + + std::vector<TestCase> sorted = unsortedTestCases; + + switch( config.runOrder() ) { + case RunTests::InLexicographicalOrder: + std::sort( sorted.begin(), sorted.end() ); + break; + case RunTests::InRandomOrder: + seedRng( config ); + std::shuffle( sorted.begin(), sorted.end(), rng() ); + break; + case RunTests::InDeclarationOrder: + // already in declaration order + break; + } + return sorted; + } + bool matchTest( TestCase const& testCase, TestSpec const& testSpec, IConfig const& config ) { + return testSpec.matches( testCase ) && ( config.allowThrows() || !testCase.throws() ); + } + + void enforceNoDuplicateTestCases( std::vector<TestCase> const& functions ) { + std::set<TestCase> seenFunctions; + for( auto const& function : functions ) { + auto prev = seenFunctions.insert( function ); + CATCH_ENFORCE( prev.second, + "error: TEST_CASE( \"" << function.name << "\" ) already defined.\n" + << "\tFirst seen at " << prev.first->getTestCaseInfo().lineInfo << "\n" + << "\tRedefined at " << function.getTestCaseInfo().lineInfo ); } } - // This indicates whether the section should be executed or not - Section::operator bool() const { - return m_sectionIncluded; + std::vector<TestCase> filterTests( std::vector<TestCase> const& testCases, TestSpec const& testSpec, IConfig const& config ) { + std::vector<TestCase> filtered; + filtered.reserve( testCases.size() ); + for (auto const& testCase : testCases) { + if ((!testSpec.hasFilters() && !testCase.isHidden()) || + (testSpec.hasFilters() && matchTest(testCase, testSpec, config))) { + filtered.push_back(testCase); + } + } + return filtered; + } + std::vector<TestCase> const& getAllTestCasesSorted( IConfig const& config ) { + return getRegistryHub().getTestCaseRegistry().getAllTestsSorted( config ); + } + + void TestRegistry::registerTest( TestCase const& testCase ) { + std::string name = testCase.getTestCaseInfo().name; + if( name.empty() ) { + ReusableStringStream rss; + rss << "Anonymous test case " << ++m_unnamedCount; + return registerTest( testCase.withName( rss.str() ) ); + } + m_functions.push_back( testCase ); + } + + std::vector<TestCase> const& TestRegistry::getAllTests() const { + return m_functions; + } + std::vector<TestCase> const& TestRegistry::getAllTestsSorted( IConfig const& config ) const { + if( m_sortedFunctions.empty() ) + enforceNoDuplicateTestCases( m_functions ); + + if( m_currentSortOrder != config.runOrder() || m_sortedFunctions.empty() ) { + m_sortedFunctions = sortTests( config, m_functions ); + m_currentSortOrder = config.runOrder(); + } + return m_sortedFunctions; + } + + /////////////////////////////////////////////////////////////////////////// + TestInvokerAsFunction::TestInvokerAsFunction( void(*testAsFunction)() ) noexcept : m_testAsFunction( testAsFunction ) {} + + void TestInvokerAsFunction::invoke() const { + m_testAsFunction(); + } + + std::string extractClassName( StringRef const& classOrQualifiedMethodName ) { + std::string className = classOrQualifiedMethodName; + if( startsWith( className, '&' ) ) + { + std::size_t lastColons = className.rfind( "::" ); + std::size_t penultimateColons = className.rfind( "::", lastColons-1 ); + if( penultimateColons == std::string::npos ) + penultimateColons = 1; + className = className.substr( penultimateColons, lastColons-penultimateColons ); + } + return className; } } // end namespace Catch +// end catch_test_case_registry_impl.cpp +// start catch_test_case_tracker.cpp -// #included from: catch_debugger.hpp -#define TWOBLUECUBES_CATCH_DEBUGGER_HPP_INCLUDED +#include <algorithm> +#include <cassert> +#include <stdexcept> +#include <memory> +#include <sstream> -#ifdef CATCH_PLATFORM_MAC +#if defined(__clang__) +# pragma clang diagnostic push +# pragma clang diagnostic ignored "-Wexit-time-destructors" +#endif - #include <assert.h> - #include <stdbool.h> - #include <sys/types.h> - #include <unistd.h> - #include <sys/sysctl.h> +namespace Catch { +namespace TestCaseTracking { - namespace Catch{ + NameAndLocation::NameAndLocation( std::string const& _name, SourceLineInfo const& _location ) + : name( _name ), + location( _location ) + {} - // The following function is taken directly from the following technical note: - // http://developer.apple.com/library/mac/#qa/qa2004/qa1361.html + ITracker::~ITracker() = default; - // Returns true if the current process is being debugged (either - // running under the debugger or has a debugger attached post facto). - bool isDebuggerActive(){ + ITracker& TrackerContext::startRun() { + m_rootTracker = std::make_shared<SectionTracker>( NameAndLocation( "{root}", CATCH_INTERNAL_LINEINFO ), *this, nullptr ); + m_currentTracker = nullptr; + m_runState = Executing; + return *m_rootTracker; + } - int mib[4]; - struct kinfo_proc info; - size_t size; + void TrackerContext::endRun() { + m_rootTracker.reset(); + m_currentTracker = nullptr; + m_runState = NotStarted; + } - // Initialize the flags so that, if sysctl fails for some bizarre - // reason, we get a predictable result. + void TrackerContext::startCycle() { + m_currentTracker = m_rootTracker.get(); + m_runState = Executing; + } + void TrackerContext::completeCycle() { + m_runState = CompletedCycle; + } - info.kp_proc.p_flag = 0; + bool TrackerContext::completedCycle() const { + return m_runState == CompletedCycle; + } + ITracker& TrackerContext::currentTracker() { + return *m_currentTracker; + } + void TrackerContext::setCurrentTracker( ITracker* tracker ) { + m_currentTracker = tracker; + } - // Initialize mib, which tells sysctl the info we want, in this case - // we're looking for information about a specific process ID. + TrackerBase::TrackerBase( NameAndLocation const& nameAndLocation, TrackerContext& ctx, ITracker* parent ) + : m_nameAndLocation( nameAndLocation ), + m_ctx( ctx ), + m_parent( parent ) + {} - mib[0] = CTL_KERN; - mib[1] = KERN_PROC; - mib[2] = KERN_PROC_PID; - mib[3] = getpid(); + NameAndLocation const& TrackerBase::nameAndLocation() const { + return m_nameAndLocation; + } + bool TrackerBase::isComplete() const { + return m_runState == CompletedSuccessfully || m_runState == Failed; + } + bool TrackerBase::isSuccessfullyCompleted() const { + return m_runState == CompletedSuccessfully; + } + bool TrackerBase::isOpen() const { + return m_runState != NotStarted && !isComplete(); + } + bool TrackerBase::hasChildren() const { + return !m_children.empty(); + } - // Call sysctl. + void TrackerBase::addChild( ITrackerPtr const& child ) { + m_children.push_back( child ); + } - size = sizeof(info); - if( sysctl(mib, sizeof(mib) / sizeof(*mib), &info, &size, CATCH_NULL, 0) != 0 ) { - Catch::cerr() << "\n** Call to sysctl failed - unable to determine if debugger is active **\n" << std::endl; - return false; - } + ITrackerPtr TrackerBase::findChild( NameAndLocation const& nameAndLocation ) { + auto it = std::find_if( m_children.begin(), m_children.end(), + [&nameAndLocation]( ITrackerPtr const& tracker ){ + return + tracker->nameAndLocation().location == nameAndLocation.location && + tracker->nameAndLocation().name == nameAndLocation.name; + } ); + return( it != m_children.end() ) + ? *it + : nullptr; + } + ITracker& TrackerBase::parent() { + assert( m_parent ); // Should always be non-null except for root + return *m_parent; + } - // We're being debugged if the P_TRACED flag is set. + void TrackerBase::openChild() { + if( m_runState != ExecutingChildren ) { + m_runState = ExecutingChildren; + if( m_parent ) + m_parent->openChild(); + } + } - return ( (info.kp_proc.p_flag & P_TRACED) != 0 ); + bool TrackerBase::isSectionTracker() const { return false; } + bool TrackerBase::isGeneratorTracker() const { return false; } + + void TrackerBase::open() { + m_runState = Executing; + moveToThis(); + if( m_parent ) + m_parent->openChild(); + } + + void TrackerBase::close() { + + // Close any still open children (e.g. generators) + while( &m_ctx.currentTracker() != this ) + m_ctx.currentTracker().close(); + + switch( m_runState ) { + case NeedsAnotherRun: + break; + + case Executing: + m_runState = CompletedSuccessfully; + break; + case ExecutingChildren: + if( m_children.empty() || m_children.back()->isComplete() ) + m_runState = CompletedSuccessfully; + break; + + case NotStarted: + case CompletedSuccessfully: + case Failed: + CATCH_INTERNAL_ERROR( "Illogical state: " << m_runState ); + + default: + CATCH_INTERNAL_ERROR( "Unknown state: " << m_runState ); } - } // namespace Catch + moveToParent(); + m_ctx.completeCycle(); + } + void TrackerBase::fail() { + m_runState = Failed; + if( m_parent ) + m_parent->markAsNeedingAnotherRun(); + moveToParent(); + m_ctx.completeCycle(); + } + void TrackerBase::markAsNeedingAnotherRun() { + m_runState = NeedsAnotherRun; + } -#elif defined(CATCH_PLATFORM_LINUX) - #include <fstream> - #include <string> + void TrackerBase::moveToParent() { + assert( m_parent ); + m_ctx.setCurrentTracker( m_parent ); + } + void TrackerBase::moveToThis() { + m_ctx.setCurrentTracker( this ); + } - namespace Catch{ - // The standard POSIX way of detecting a debugger is to attempt to - // ptrace() the process, but this needs to be done from a child and not - // this process itself to still allow attaching to this process later - // if wanted, so is rather heavy. Under Linux we have the PID of the - // "debugger" (which doesn't need to be gdb, of course, it could also - // be strace, for example) in /proc/$PID/status, so just get it from - // there instead. - bool isDebuggerActive(){ - std::ifstream in("/proc/self/status"); - for( std::string line; std::getline(in, line); ) { - static const int PREFIX_LEN = 11; - if( line.compare(0, PREFIX_LEN, "TracerPid:\t") == 0 ) { - // We're traced if the PID is not 0 and no other PID starts - // with 0 digit, so it's enough to check for just a single - // character. - return line.length() > PREFIX_LEN && line[PREFIX_LEN] != '0'; - } - } + SectionTracker::SectionTracker( NameAndLocation const& nameAndLocation, TrackerContext& ctx, ITracker* parent ) + : TrackerBase( nameAndLocation, ctx, parent ) + { + if( parent ) { + while( !parent->isSectionTracker() ) + parent = &parent->parent(); - return false; + SectionTracker& parentSection = static_cast<SectionTracker&>( *parent ); + addNextFilters( parentSection.m_filters ); } - } // namespace Catch -#elif defined(_MSC_VER) - extern "C" __declspec(dllimport) int __stdcall IsDebuggerPresent(); - namespace Catch { - bool isDebuggerActive() { - return IsDebuggerPresent() != 0; + } + + bool SectionTracker::isComplete() const { + bool complete = true; + + if ((m_filters.empty() || m_filters[0] == "") || + std::find(m_filters.begin(), m_filters.end(), + m_nameAndLocation.name) != m_filters.end()) + complete = TrackerBase::isComplete(); + return complete; + + } + + bool SectionTracker::isSectionTracker() const { return true; } + + SectionTracker& SectionTracker::acquire( TrackerContext& ctx, NameAndLocation const& nameAndLocation ) { + std::shared_ptr<SectionTracker> section; + + ITracker& currentTracker = ctx.currentTracker(); + if( ITrackerPtr childTracker = currentTracker.findChild( nameAndLocation ) ) { + assert( childTracker ); + assert( childTracker->isSectionTracker() ); + section = std::static_pointer_cast<SectionTracker>( childTracker ); } + else { + section = std::make_shared<SectionTracker>( nameAndLocation, ctx, ¤tTracker ); + currentTracker.addChild( section ); + } + if( !ctx.completedCycle() ) + section->tryOpen(); + return *section; } -#elif defined(__MINGW32__) - extern "C" __declspec(dllimport) int __stdcall IsDebuggerPresent(); - namespace Catch { - bool isDebuggerActive() { - return IsDebuggerPresent() != 0; + + void SectionTracker::tryOpen() { + if( !isComplete() && (m_filters.empty() || m_filters[0].empty() || m_filters[0] == m_nameAndLocation.name ) ) + open(); + } + + void SectionTracker::addInitialFilters( std::vector<std::string> const& filters ) { + if( !filters.empty() ) { + m_filters.push_back(""); // Root - should never be consulted + m_filters.push_back(""); // Test Case - not a section filter + m_filters.insert( m_filters.end(), filters.begin(), filters.end() ); } } -#else - namespace Catch { - inline bool isDebuggerActive() { return false; } + void SectionTracker::addNextFilters( std::vector<std::string> const& filters ) { + if( filters.size() > 1 ) + m_filters.insert( m_filters.end(), ++filters.begin(), filters.end() ); } -#endif // Platform -#ifdef CATCH_PLATFORM_WINDOWS +} // namespace TestCaseTracking - namespace Catch { - void writeToDebugConsole( std::string const& text ) { - ::OutputDebugStringA( text.c_str() ); +using TestCaseTracking::ITracker; +using TestCaseTracking::TrackerContext; +using TestCaseTracking::SectionTracker; + +} // namespace Catch + +#if defined(__clang__) +# pragma clang diagnostic pop +#endif +// end catch_test_case_tracker.cpp +// start catch_test_registry.cpp + +namespace Catch { + + auto makeTestInvoker( void(*testAsFunction)() ) noexcept -> ITestInvoker* { + return new(std::nothrow) TestInvokerAsFunction( testAsFunction ); + } + + NameAndTags::NameAndTags( StringRef const& name_ , StringRef const& tags_ ) noexcept : name( name_ ), tags( tags_ ) {} + + AutoReg::AutoReg( ITestInvoker* invoker, SourceLineInfo const& lineInfo, StringRef const& classOrMethod, NameAndTags const& nameAndTags ) noexcept { + CATCH_TRY { + getMutableRegistryHub() + .registerTest( + makeTestCase( + invoker, + extractClassName( classOrMethod ), + nameAndTags, + lineInfo)); + } CATCH_CATCH_ALL { + // Do not throw when constructing global objects, instead register the exception to be processed later + getMutableRegistryHub().registerStartupException(); } } -#else - namespace Catch { - void writeToDebugConsole( std::string const& text ) { - // !TBD: Need a version for Mac/ XCode and other IDEs - Catch::cout() << text; + + AutoReg::~AutoReg() = default; +} +// end catch_test_registry.cpp +// start catch_test_spec.cpp + +#include <algorithm> +#include <string> +#include <vector> +#include <memory> + +namespace Catch { + + TestSpec::Pattern::~Pattern() = default; + TestSpec::NamePattern::~NamePattern() = default; + TestSpec::TagPattern::~TagPattern() = default; + TestSpec::ExcludedPattern::~ExcludedPattern() = default; + + TestSpec::NamePattern::NamePattern( std::string const& name ) + : m_wildcardPattern( toLower( name ), CaseSensitive::No ) + {} + bool TestSpec::NamePattern::matches( TestCaseInfo const& testCase ) const { + return m_wildcardPattern.matches( toLower( testCase.name ) ); + } + + TestSpec::TagPattern::TagPattern( std::string const& tag ) : m_tag( toLower( tag ) ) {} + bool TestSpec::TagPattern::matches( TestCaseInfo const& testCase ) const { + return std::find(begin(testCase.lcaseTags), + end(testCase.lcaseTags), + m_tag) != end(testCase.lcaseTags); + } + + TestSpec::ExcludedPattern::ExcludedPattern( PatternPtr const& underlyingPattern ) : m_underlyingPattern( underlyingPattern ) {} + bool TestSpec::ExcludedPattern::matches( TestCaseInfo const& testCase ) const { return !m_underlyingPattern->matches( testCase ); } + + bool TestSpec::Filter::matches( TestCaseInfo const& testCase ) const { + // All patterns in a filter must match for the filter to be a match + for( auto const& pattern : m_patterns ) { + if( !pattern->matches( testCase ) ) + return false; } + return true; } -#endif // Platform -// #included from: catch_tostring.hpp -#define TWOBLUECUBES_CATCH_TOSTRING_HPP_INCLUDED + bool TestSpec::hasFilters() const { + return !m_filters.empty(); + } + bool TestSpec::matches( TestCaseInfo const& testCase ) const { + // A TestSpec matches if any filter matches + for( auto const& filter : m_filters ) + if( filter.matches( testCase ) ) + return true; + return false; + } +} +// end catch_test_spec.cpp +// start catch_test_spec_parser.cpp + +namespace Catch { + + TestSpecParser::TestSpecParser( ITagAliasRegistry const& tagAliases ) : m_tagAliases( &tagAliases ) {} + + TestSpecParser& TestSpecParser::parse( std::string const& arg ) { + m_mode = None; + m_exclusion = false; + m_start = std::string::npos; + m_arg = m_tagAliases->expandAliases( arg ); + m_escapeChars.clear(); + for( m_pos = 0; m_pos < m_arg.size(); ++m_pos ) + visitChar( m_arg[m_pos] ); + if( m_mode == Name ) + addPattern<TestSpec::NamePattern>(); + return *this; + } + TestSpec TestSpecParser::testSpec() { + addFilter(); + return m_testSpec; + } + + void TestSpecParser::visitChar( char c ) { + if( m_mode == None ) { + switch( c ) { + case ' ': return; + case '~': m_exclusion = true; return; + case '[': return startNewMode( Tag, ++m_pos ); + case '"': return startNewMode( QuotedName, ++m_pos ); + case '\\': return escape(); + default: startNewMode( Name, m_pos ); break; + } + } + if( m_mode == Name ) { + if( c == ',' ) { + addPattern<TestSpec::NamePattern>(); + addFilter(); + } + else if( c == '[' ) { + if( subString() == "exclude:" ) + m_exclusion = true; + else + addPattern<TestSpec::NamePattern>(); + startNewMode( Tag, ++m_pos ); + } + else if( c == '\\' ) + escape(); + } + else if( m_mode == EscapedName ) + m_mode = Name; + else if( m_mode == QuotedName && c == '"' ) + addPattern<TestSpec::NamePattern>(); + else if( m_mode == Tag && c == ']' ) + addPattern<TestSpec::TagPattern>(); + } + void TestSpecParser::startNewMode( Mode mode, std::size_t start ) { + m_mode = mode; + m_start = start; + } + void TestSpecParser::escape() { + if( m_mode == None ) + m_start = m_pos; + m_mode = EscapedName; + m_escapeChars.push_back( m_pos ); + } + std::string TestSpecParser::subString() const { return m_arg.substr( m_start, m_pos - m_start ); } + + void TestSpecParser::addFilter() { + if( !m_currentFilter.m_patterns.empty() ) { + m_testSpec.m_filters.push_back( m_currentFilter ); + m_currentFilter = TestSpec::Filter(); + } + } + + TestSpec parseTestSpec( std::string const& arg ) { + return TestSpecParser( ITagAliasRegistry::get() ).parse( arg ).testSpec(); + } + +} // namespace Catch +// end catch_test_spec_parser.cpp +// start catch_timer.cpp + +#include <chrono> + +static const uint64_t nanosecondsInSecond = 1000000000; + +namespace Catch { + + auto getCurrentNanosecondsSinceEpoch() -> uint64_t { + return std::chrono::duration_cast<std::chrono::nanoseconds>( std::chrono::high_resolution_clock::now().time_since_epoch() ).count(); + } + + namespace { + auto estimateClockResolution() -> uint64_t { + uint64_t sum = 0; + static const uint64_t iterations = 1000000; + + auto startTime = getCurrentNanosecondsSinceEpoch(); + + for( std::size_t i = 0; i < iterations; ++i ) { + + uint64_t ticks; + uint64_t baseTicks = getCurrentNanosecondsSinceEpoch(); + do { + ticks = getCurrentNanosecondsSinceEpoch(); + } while( ticks == baseTicks ); + + auto delta = ticks - baseTicks; + sum += delta; + + // If we have been calibrating for over 3 seconds -- the clock + // is terrible and we should move on. + // TBD: How to signal that the measured resolution is probably wrong? + if (ticks > startTime + 3 * nanosecondsInSecond) { + return sum / ( i + 1u ); + } + } + + // We're just taking the mean, here. To do better we could take the std. dev and exclude outliers + // - and potentially do more iterations if there's a high variance. + return sum/iterations; + } + } + auto getEstimatedClockResolution() -> uint64_t { + static auto s_resolution = estimateClockResolution(); + return s_resolution; + } + + void Timer::start() { + m_nanoseconds = getCurrentNanosecondsSinceEpoch(); + } + auto Timer::getElapsedNanoseconds() const -> uint64_t { + return getCurrentNanosecondsSinceEpoch() - m_nanoseconds; + } + auto Timer::getElapsedMicroseconds() const -> uint64_t { + return getElapsedNanoseconds()/1000; + } + auto Timer::getElapsedMilliseconds() const -> unsigned int { + return static_cast<unsigned int>(getElapsedMicroseconds()/1000); + } + auto Timer::getElapsedSeconds() const -> double { + return getElapsedMicroseconds()/1000000.0; + } + +} // namespace Catch +// end catch_timer.cpp +// start catch_tostring.cpp + +#if defined(__clang__) +# pragma clang diagnostic push +# pragma clang diagnostic ignored "-Wexit-time-destructors" +# pragma clang diagnostic ignored "-Wglobal-constructors" +#endif + +// Enable specific decls locally +#if !defined(CATCH_CONFIG_ENABLE_CHRONO_STRINGMAKER) +#define CATCH_CONFIG_ENABLE_CHRONO_STRINGMAKER +#endif + +#include <cmath> +#include <iomanip> namespace Catch { @@ -8546,8 +14105,7 @@ namespace Detail { }; } - std::string rawMemoryToString( const void *object, std::size_t size ) - { + std::string rawMemoryToString( const void *object, std::size_t size ) { // Reverse order for little endian architectures int i = 0, end = static_cast<int>( size ), inc = 1; if( Endianness::which() == Endianness::Little ) { @@ -8556,1308 +14114,1642 @@ namespace Detail { } unsigned char const *bytes = static_cast<unsigned char const *>(object); - std::ostringstream os; - os << "0x" << std::setfill('0') << std::hex; + ReusableStringStream rss; + rss << "0x" << std::setfill('0') << std::hex; for( ; i != end; i += inc ) - os << std::setw(2) << static_cast<unsigned>(bytes[i]); - return os.str(); + rss << std::setw(2) << static_cast<unsigned>(bytes[i]); + return rss.str(); } } -std::string toString( std::string const& value ) { - std::string s = value; - if( getCurrentContext().getConfig()->showInvisibles() ) { - for(size_t i = 0; i < s.size(); ++i ) { - std::string subs; - switch( s[i] ) { - case '\n': subs = "\\n"; break; - case '\t': subs = "\\t"; break; - default: break; - } - if( !subs.empty() ) { - s = s.substr( 0, i ) + subs + s.substr( i+1 ); - ++i; - } - } +template<typename T> +std::string fpToString( T value, int precision ) { + if (Catch::isnan(value)) { + return "nan"; } - return '"' + s + '"'; -} -std::string toString( std::wstring const& value ) { - std::string s; - s.reserve( value.size() ); - for(size_t i = 0; i < value.size(); ++i ) - s += value[i] <= 0xff ? static_cast<char>( value[i] ) : '?'; - return Catch::toString( s ); + ReusableStringStream rss; + rss << std::setprecision( precision ) + << std::fixed + << value; + std::string d = rss.str(); + std::size_t i = d.find_last_not_of( '0' ); + if( i != std::string::npos && i != d.size()-1 ) { + if( d[i] == '.' ) + i++; + d = d.substr( 0, i+1 ); + } + return d; } -std::string toString( const char* const value ) { - return value ? Catch::toString( std::string( value ) ) : std::string( "{null string}" ); -} +//// ======================================================= //// +// +// Out-of-line defs for full specialization of StringMaker +// +//// ======================================================= //// + +std::string StringMaker<std::string>::convert(const std::string& str) { + if (!getCurrentContext().getConfig()->showInvisibles()) { + return '"' + str + '"'; + } -std::string toString( char* const value ) { - return Catch::toString( static_cast<const char*>( value ) ); + std::string s("\""); + for (char c : str) { + switch (c) { + case '\n': + s.append("\\n"); + break; + case '\t': + s.append("\\t"); + break; + default: + s.push_back(c); + break; + } + } + s.append("\""); + return s; } -std::string toString( const wchar_t* const value ) -{ - return value ? Catch::toString( std::wstring(value) ) : std::string( "{null string}" ); +#ifdef CATCH_CONFIG_CPP17_STRING_VIEW +std::string StringMaker<std::string_view>::convert(std::string_view str) { + return ::Catch::Detail::stringify(std::string{ str }); } +#endif -std::string toString( wchar_t* const value ) -{ - return Catch::toString( static_cast<const wchar_t*>( value ) ); +std::string StringMaker<char const*>::convert(char const* str) { + if (str) { + return ::Catch::Detail::stringify(std::string{ str }); + } else { + return{ "{null string}" }; + } +} +std::string StringMaker<char*>::convert(char* str) { + if (str) { + return ::Catch::Detail::stringify(std::string{ str }); + } else { + return{ "{null string}" }; + } } -std::string toString( int value ) { - std::ostringstream oss; - oss << value; - if( value > Detail::hexThreshold ) - oss << " (0x" << std::hex << value << ')'; - return oss.str(); +#ifdef CATCH_CONFIG_WCHAR +std::string StringMaker<std::wstring>::convert(const std::wstring& wstr) { + std::string s; + s.reserve(wstr.size()); + for (auto c : wstr) { + s += (c <= 0xff) ? static_cast<char>(c) : '?'; + } + return ::Catch::Detail::stringify(s); } -std::string toString( unsigned long value ) { - std::ostringstream oss; - oss << value; - if( value > Detail::hexThreshold ) - oss << " (0x" << std::hex << value << ')'; - return oss.str(); +# ifdef CATCH_CONFIG_CPP17_STRING_VIEW +std::string StringMaker<std::wstring_view>::convert(std::wstring_view str) { + return StringMaker<std::wstring>::convert(std::wstring(str)); } +# endif -std::string toString( unsigned int value ) { - return Catch::toString( static_cast<unsigned long>( value ) ); +std::string StringMaker<wchar_t const*>::convert(wchar_t const * str) { + if (str) { + return ::Catch::Detail::stringify(std::wstring{ str }); + } else { + return{ "{null string}" }; + } +} +std::string StringMaker<wchar_t *>::convert(wchar_t * str) { + if (str) { + return ::Catch::Detail::stringify(std::wstring{ str }); + } else { + return{ "{null string}" }; + } } +#endif -template<typename T> -std::string fpToString( T value, int precision ) { - std::ostringstream oss; - oss << std::setprecision( precision ) - << std::fixed - << value; - std::string d = oss.str(); - std::size_t i = d.find_last_not_of( '0' ); - if( i != std::string::npos && i != d.size()-1 ) { - if( d[i] == '.' ) - i++; - d = d.substr( 0, i+1 ); +std::string StringMaker<int>::convert(int value) { + return ::Catch::Detail::stringify(static_cast<long long>(value)); +} +std::string StringMaker<long>::convert(long value) { + return ::Catch::Detail::stringify(static_cast<long long>(value)); +} +std::string StringMaker<long long>::convert(long long value) { + ReusableStringStream rss; + rss << value; + if (value > Detail::hexThreshold) { + rss << " (0x" << std::hex << value << ')'; } - return d; + return rss.str(); } -std::string toString( const double value ) { - return fpToString( value, 10 ); +std::string StringMaker<unsigned int>::convert(unsigned int value) { + return ::Catch::Detail::stringify(static_cast<unsigned long long>(value)); } -std::string toString( const float value ) { - return fpToString( value, 5 ) + 'f'; +std::string StringMaker<unsigned long>::convert(unsigned long value) { + return ::Catch::Detail::stringify(static_cast<unsigned long long>(value)); +} +std::string StringMaker<unsigned long long>::convert(unsigned long long value) { + ReusableStringStream rss; + rss << value; + if (value > Detail::hexThreshold) { + rss << " (0x" << std::hex << value << ')'; + } + return rss.str(); } -std::string toString( bool value ) { - return value ? "true" : "false"; +std::string StringMaker<bool>::convert(bool b) { + return b ? "true" : "false"; } -std::string toString( char value ) { - if ( value == '\r' ) +std::string StringMaker<signed char>::convert(signed char value) { + if (value == '\r') { return "'\\r'"; - if ( value == '\f' ) + } else if (value == '\f') { return "'\\f'"; - if ( value == '\n' ) + } else if (value == '\n') { return "'\\n'"; - if ( value == '\t' ) + } else if (value == '\t') { return "'\\t'"; - if ( '\0' <= value && value < ' ' ) - return toString( static_cast<unsigned int>( value ) ); - char chstr[] = "' '"; - chstr[1] = value; - return chstr; + } else if ('\0' <= value && value < ' ') { + return ::Catch::Detail::stringify(static_cast<unsigned int>(value)); + } else { + char chstr[] = "' '"; + chstr[1] = value; + return chstr; + } } - -std::string toString( signed char value ) { - return toString( static_cast<char>( value ) ); +std::string StringMaker<char>::convert(char c) { + return ::Catch::Detail::stringify(static_cast<signed char>(c)); } - -std::string toString( unsigned char value ) { - return toString( static_cast<char>( value ) ); +std::string StringMaker<unsigned char>::convert(unsigned char c) { + return ::Catch::Detail::stringify(static_cast<char>(c)); } -#ifdef CATCH_CONFIG_CPP11_LONG_LONG -std::string toString( long long value ) { - std::ostringstream oss; - oss << value; - if( value > Detail::hexThreshold ) - oss << " (0x" << std::hex << value << ')'; - return oss.str(); +std::string StringMaker<std::nullptr_t>::convert(std::nullptr_t) { + return "nullptr"; } -std::string toString( unsigned long long value ) { - std::ostringstream oss; - oss << value; - if( value > Detail::hexThreshold ) - oss << " (0x" << std::hex << value << ')'; - return oss.str(); + +int StringMaker<float>::precision = 5; + +std::string StringMaker<float>::convert(float value) { + return fpToString(value, precision) + 'f'; } -#endif -#ifdef CATCH_CONFIG_CPP11_NULLPTR -std::string toString( std::nullptr_t ) { - return "nullptr"; +int StringMaker<double>::precision = 10; + +std::string StringMaker<double>::convert(double value) { + return fpToString(value, precision); } -#endif -#ifdef __OBJC__ - std::string toString( NSString const * const& nsstring ) { - if( !nsstring ) - return "nil"; - return "@" + toString([nsstring UTF8String]); - } - std::string toString( NSString * CATCH_ARC_STRONG const& nsstring ) { - if( !nsstring ) - return "nil"; - return "@" + toString([nsstring UTF8String]); - } - std::string toString( NSObject* const& nsObject ) { - return toString( [nsObject description] ); - } -#endif +std::string ratio_string<std::atto>::symbol() { return "a"; } +std::string ratio_string<std::femto>::symbol() { return "f"; } +std::string ratio_string<std::pico>::symbol() { return "p"; } +std::string ratio_string<std::nano>::symbol() { return "n"; } +std::string ratio_string<std::micro>::symbol() { return "u"; } +std::string ratio_string<std::milli>::symbol() { return "m"; } } // end namespace Catch -// #included from: catch_result_builder.hpp -#define TWOBLUECUBES_CATCH_RESULT_BUILDER_HPP_INCLUDED +#if defined(__clang__) +# pragma clang diagnostic pop +#endif + +// end catch_tostring.cpp +// start catch_totals.cpp namespace Catch { - std::string capturedExpressionWithSecondArgument( std::string const& capturedExpression, std::string const& secondArg ) { - return secondArg.empty() || secondArg == "\"\"" - ? capturedExpression - : capturedExpression + ", " + secondArg; + Counts Counts::operator - ( Counts const& other ) const { + Counts diff; + diff.passed = passed - other.passed; + diff.failed = failed - other.failed; + diff.failedButOk = failedButOk - other.failedButOk; + return diff; } - ResultBuilder::ResultBuilder( char const* macroName, - SourceLineInfo const& lineInfo, - char const* capturedExpression, - ResultDisposition::Flags resultDisposition, - char const* secondArg ) - : m_assertionInfo( macroName, lineInfo, capturedExpressionWithSecondArgument( capturedExpression, secondArg ), resultDisposition ), - m_shouldDebugBreak( false ), - m_shouldThrow( false ) - {} - ResultBuilder& ResultBuilder::setResultType( ResultWas::OfType result ) { - m_data.resultType = result; - return *this; - } - ResultBuilder& ResultBuilder::setResultType( bool result ) { - m_data.resultType = result ? ResultWas::Ok : ResultWas::ExpressionFailed; + Counts& Counts::operator += ( Counts const& other ) { + passed += other.passed; + failed += other.failed; + failedButOk += other.failedButOk; return *this; } - void ResultBuilder::endExpression( DecomposedExpression const& expr ) { - AssertionResult result = build( expr ); - handleResult( result ); + std::size_t Counts::total() const { + return passed + failed + failedButOk; + } + bool Counts::allPassed() const { + return failed == 0 && failedButOk == 0; + } + bool Counts::allOk() const { + return failed == 0; } - void ResultBuilder::useActiveException( ResultDisposition::Flags resultDisposition ) { - m_assertionInfo.resultDisposition = resultDisposition; - m_stream.oss << Catch::translateActiveException(); - captureResult( ResultWas::ThrewException ); + Totals Totals::operator - ( Totals const& other ) const { + Totals diff; + diff.assertions = assertions - other.assertions; + diff.testCases = testCases - other.testCases; + return diff; } - void ResultBuilder::captureResult( ResultWas::OfType resultType ) { - setResultType( resultType ); - captureExpression(); + Totals& Totals::operator += ( Totals const& other ) { + assertions += other.assertions; + testCases += other.testCases; + return *this; } - void ResultBuilder::captureExpectedException( std::string const& expectedMessage ) { - if( expectedMessage.empty() ) - captureExpectedException( Matchers::Impl::Generic::AllOf<std::string>() ); + Totals Totals::delta( Totals const& prevTotals ) const { + Totals diff = *this - prevTotals; + if( diff.assertions.failed > 0 ) + ++diff.testCases.failed; + else if( diff.assertions.failedButOk > 0 ) + ++diff.testCases.failedButOk; else - captureExpectedException( Matchers::Equals( expectedMessage ) ); + ++diff.testCases.passed; + return diff; } - void ResultBuilder::captureExpectedException( Matchers::Impl::Matcher<std::string> const& matcher ) { +} +// end catch_totals.cpp +// start catch_uncaught_exceptions.cpp - assert( !isFalseTest( m_assertionInfo.resultDisposition ) ); - AssertionResultData data = m_data; - data.resultType = ResultWas::Ok; - data.reconstructedExpression = m_assertionInfo.capturedExpression; +#include <exception> - std::string actualMessage = Catch::translateActiveException(); - if( !matcher.match( actualMessage ) ) { - data.resultType = ResultWas::ExpressionFailed; - data.reconstructedExpression = actualMessage; +namespace Catch { + bool uncaught_exceptions() { +#if defined(CATCH_CONFIG_CPP17_UNCAUGHT_EXCEPTIONS) + return std::uncaught_exceptions() > 0; +#else + return std::uncaught_exception(); +#endif + } +} // end namespace Catch +// end catch_uncaught_exceptions.cpp +// start catch_version.cpp + +#include <ostream> + +namespace Catch { + + Version::Version + ( unsigned int _majorVersion, + unsigned int _minorVersion, + unsigned int _patchNumber, + char const * const _branchName, + unsigned int _buildNumber ) + : majorVersion( _majorVersion ), + minorVersion( _minorVersion ), + patchNumber( _patchNumber ), + branchName( _branchName ), + buildNumber( _buildNumber ) + {} + + std::ostream& operator << ( std::ostream& os, Version const& version ) { + os << version.majorVersion << '.' + << version.minorVersion << '.' + << version.patchNumber; + // branchName is never null -> 0th char is \0 if it is empty + if (version.branchName[0]) { + os << '-' << version.branchName + << '.' << version.buildNumber; } - AssertionResult result( m_assertionInfo, data ); - handleResult( result ); + return os; } - void ResultBuilder::captureExpression() { - AssertionResult result = build(); - handleResult( result ); + Version const& libraryVersion() { + static Version version( 2, 9, 0, "", 0 ); + return version; } - void ResultBuilder::handleResult( AssertionResult const& result ) +} +// end catch_version.cpp +// start catch_wildcard_pattern.cpp + +#include <sstream> + +namespace Catch { + + WildcardPattern::WildcardPattern( std::string const& pattern, + CaseSensitive::Choice caseSensitivity ) + : m_caseSensitivity( caseSensitivity ), + m_pattern( adjustCase( pattern ) ) { - getResultCapture().assertionEnded( result ); + if( startsWith( m_pattern, '*' ) ) { + m_pattern = m_pattern.substr( 1 ); + m_wildcard = WildcardAtStart; + } + if( endsWith( m_pattern, '*' ) ) { + m_pattern = m_pattern.substr( 0, m_pattern.size()-1 ); + m_wildcard = static_cast<WildcardPosition>( m_wildcard | WildcardAtEnd ); + } + } - if( !result.isOk() ) { - if( getCurrentContext().getConfig()->shouldDebugBreak() ) - m_shouldDebugBreak = true; - if( getCurrentContext().getRunner()->aborting() || (m_assertionInfo.resultDisposition & ResultDisposition::Normal) ) - m_shouldThrow = true; + bool WildcardPattern::matches( std::string const& str ) const { + switch( m_wildcard ) { + case NoWildcard: + return m_pattern == adjustCase( str ); + case WildcardAtStart: + return endsWith( adjustCase( str ), m_pattern ); + case WildcardAtEnd: + return startsWith( adjustCase( str ), m_pattern ); + case WildcardAtBothEnds: + return contains( adjustCase( str ), m_pattern ); + default: + CATCH_INTERNAL_ERROR( "Unknown enum" ); } } - void ResultBuilder::react() { - if( m_shouldThrow ) - throw Catch::TestFailureException(); + std::string WildcardPattern::adjustCase( std::string const& str ) const { + return m_caseSensitivity == CaseSensitive::No ? toLower( str ) : str; } +} +// end catch_wildcard_pattern.cpp +// start catch_xmlwriter.cpp - bool ResultBuilder::shouldDebugBreak() const { return m_shouldDebugBreak; } - bool ResultBuilder::allowThrows() const { return getCurrentContext().getConfig()->allowThrows(); } +#include <iomanip> - AssertionResult ResultBuilder::build() const - { - return build( *this ); - } +using uchar = unsigned char; - // CAVEAT: The returned AssertionResult stores a pointer to the argument expr, - // a temporary DecomposedExpression, which in turn holds references to - // operands, possibly temporary as well. - // It should immediately be passed to handleResult; if the expression - // needs to be reported, its string expansion must be composed before - // the temporaries are destroyed. - AssertionResult ResultBuilder::build( DecomposedExpression const& expr ) const - { - assert( m_data.resultType != ResultWas::Unknown ); - AssertionResultData data = m_data; +namespace Catch { - // Flip bool results if FalseTest flag is set - if( isFalseTest( m_assertionInfo.resultDisposition ) ) { - data.negate( expr.isBinaryExpression() ); +namespace { + + size_t trailingBytes(unsigned char c) { + if ((c & 0xE0) == 0xC0) { + return 2; + } + if ((c & 0xF0) == 0xE0) { + return 3; + } + if ((c & 0xF8) == 0xF0) { + return 4; } + CATCH_INTERNAL_ERROR("Invalid multibyte utf-8 start byte encountered"); + } - data.message = m_stream.oss.str(); - data.decomposedExpression = &expr; // for lazy reconstruction - return AssertionResult( m_assertionInfo, data ); + uint32_t headerValue(unsigned char c) { + if ((c & 0xE0) == 0xC0) { + return c & 0x1F; + } + if ((c & 0xF0) == 0xE0) { + return c & 0x0F; + } + if ((c & 0xF8) == 0xF0) { + return c & 0x07; + } + CATCH_INTERNAL_ERROR("Invalid multibyte utf-8 start byte encountered"); } - void ResultBuilder::reconstructExpression( std::string& dest ) const { - dest = m_assertionInfo.capturedExpression; + void hexEscapeChar(std::ostream& os, unsigned char c) { + std::ios_base::fmtflags f(os.flags()); + os << "\\x" + << std::uppercase << std::hex << std::setfill('0') << std::setw(2) + << static_cast<int>(c); + os.flags(f); } -} // end namespace Catch +} // anonymous namespace -// #included from: catch_tag_alias_registry.hpp -#define TWOBLUECUBES_CATCH_TAG_ALIAS_REGISTRY_HPP_INCLUDED + XmlEncode::XmlEncode( std::string const& str, ForWhat forWhat ) + : m_str( str ), + m_forWhat( forWhat ) + {} -// #included from: catch_tag_alias_registry.h -#define TWOBLUECUBES_CATCH_TAG_ALIAS_REGISTRY_H_INCLUDED + void XmlEncode::encodeTo( std::ostream& os ) const { + // Apostrophe escaping not necessary if we always use " to write attributes + // (see: http://www.w3.org/TR/xml/#syntax) -#include <map> + for( std::size_t idx = 0; idx < m_str.size(); ++ idx ) { + uchar c = m_str[idx]; + switch (c) { + case '<': os << "<"; break; + case '&': os << "&"; break; -namespace Catch { + case '>': + // See: http://www.w3.org/TR/xml/#syntax + if (idx > 2 && m_str[idx - 1] == ']' && m_str[idx - 2] == ']') + os << ">"; + else + os << c; + break; - class TagAliasRegistry : public ITagAliasRegistry { - public: - virtual ~TagAliasRegistry(); - virtual Option<TagAlias> find( std::string const& alias ) const; - virtual std::string expandAliases( std::string const& unexpandedTestSpec ) const; - void add( char const* alias, char const* tag, SourceLineInfo const& lineInfo ); - static TagAliasRegistry& get(); + case '\"': + if (m_forWhat == ForAttributes) + os << """; + else + os << c; + break; - private: - std::map<std::string, TagAlias> m_registry; - }; + default: + // Check for control characters and invalid utf-8 -} // end namespace Catch + // Escape control characters in standard ascii + // see http://stackoverflow.com/questions/404107/why-are-control-characters-illegal-in-xml-1-0 + if (c < 0x09 || (c > 0x0D && c < 0x20) || c == 0x7F) { + hexEscapeChar(os, c); + break; + } -namespace Catch { + // Plain ASCII: Write it to stream + if (c < 0x7F) { + os << c; + break; + } - TagAliasRegistry::~TagAliasRegistry() {} + // UTF-8 territory + // Check if the encoding is valid and if it is not, hex escape bytes. + // Important: We do not check the exact decoded values for validity, only the encoding format + // First check that this bytes is a valid lead byte: + // This means that it is not encoded as 1111 1XXX + // Or as 10XX XXXX + if (c < 0xC0 || + c >= 0xF8) { + hexEscapeChar(os, c); + break; + } - Option<TagAlias> TagAliasRegistry::find( std::string const& alias ) const { - std::map<std::string, TagAlias>::const_iterator it = m_registry.find( alias ); - if( it != m_registry.end() ) - return it->second; - else - return Option<TagAlias>(); - } + auto encBytes = trailingBytes(c); + // Are there enough bytes left to avoid accessing out-of-bounds memory? + if (idx + encBytes - 1 >= m_str.size()) { + hexEscapeChar(os, c); + break; + } + // The header is valid, check data + // The next encBytes bytes must together be a valid utf-8 + // This means: bitpattern 10XX XXXX and the extracted value is sane (ish) + bool valid = true; + uint32_t value = headerValue(c); + for (std::size_t n = 1; n < encBytes; ++n) { + uchar nc = m_str[idx + n]; + valid &= ((nc & 0xC0) == 0x80); + value = (value << 6) | (nc & 0x3F); + } - std::string TagAliasRegistry::expandAliases( std::string const& unexpandedTestSpec ) const { - std::string expandedTestSpec = unexpandedTestSpec; - for( std::map<std::string, TagAlias>::const_iterator it = m_registry.begin(), itEnd = m_registry.end(); - it != itEnd; - ++it ) { - std::size_t pos = expandedTestSpec.find( it->first ); - if( pos != std::string::npos ) { - expandedTestSpec = expandedTestSpec.substr( 0, pos ) + - it->second.tag + - expandedTestSpec.substr( pos + it->first.size() ); + if ( + // Wrong bit pattern of following bytes + (!valid) || + // Overlong encodings + (value < 0x80) || + (0x80 <= value && value < 0x800 && encBytes > 2) || + (0x800 < value && value < 0x10000 && encBytes > 3) || + // Encoded value out of range + (value >= 0x110000) + ) { + hexEscapeChar(os, c); + break; + } + + // If we got here, this is in fact a valid(ish) utf-8 sequence + for (std::size_t n = 0; n < encBytes; ++n) { + os << m_str[idx + n]; + } + idx += encBytes - 1; + break; } } - return expandedTestSpec; } - void TagAliasRegistry::add( char const* alias, char const* tag, SourceLineInfo const& lineInfo ) { - - if( !startsWith( alias, "[@" ) || !endsWith( alias, ']' ) ) { - std::ostringstream oss; - oss << "error: tag alias, \"" << alias << "\" is not of the form [@alias name].\n" << lineInfo; - throw std::domain_error( oss.str().c_str() ); - } - if( !m_registry.insert( std::make_pair( alias, TagAlias( tag, lineInfo ) ) ).second ) { - std::ostringstream oss; - oss << "error: tag alias, \"" << alias << "\" already registered.\n" - << "\tFirst seen at " << find(alias)->lineInfo << '\n' - << "\tRedefined at " << lineInfo; - throw std::domain_error( oss.str().c_str() ); - } + std::ostream& operator << ( std::ostream& os, XmlEncode const& xmlEncode ) { + xmlEncode.encodeTo( os ); + return os; } - TagAliasRegistry& TagAliasRegistry::get() { - static TagAliasRegistry instance; - return instance; + XmlWriter::ScopedElement::ScopedElement( XmlWriter* writer ) + : m_writer( writer ) + {} + XmlWriter::ScopedElement::ScopedElement( ScopedElement&& other ) noexcept + : m_writer( other.m_writer ){ + other.m_writer = nullptr; } - - ITagAliasRegistry::~ITagAliasRegistry() {} - ITagAliasRegistry const& ITagAliasRegistry::get() { return TagAliasRegistry::get(); } - - RegistrarForTagAliases::RegistrarForTagAliases( char const* alias, char const* tag, SourceLineInfo const& lineInfo ) { - try { - TagAliasRegistry::get().add( alias, tag, lineInfo ); - } - catch( std::exception& ex ) { - Colour colourGuard( Colour::Red ); - Catch::cerr() << ex.what() << std::endl; - exit(1); + XmlWriter::ScopedElement& XmlWriter::ScopedElement::operator=( ScopedElement&& other ) noexcept { + if ( m_writer ) { + m_writer->endElement(); } + m_writer = other.m_writer; + other.m_writer = nullptr; + return *this; } -} // end namespace Catch - -// #included from: ../reporters/catch_reporter_multi.hpp -#define TWOBLUECUBES_CATCH_REPORTER_MULTI_HPP_INCLUDED - -namespace Catch { - -class MultipleReporters : public SharedImpl<IStreamingReporter> { - typedef std::vector<Ptr<IStreamingReporter> > Reporters; - Reporters m_reporters; - -public: - void add( Ptr<IStreamingReporter> const& reporter ) { - m_reporters.push_back( reporter ); + XmlWriter::ScopedElement::~ScopedElement() { + if( m_writer ) + m_writer->endElement(); } -public: // IStreamingReporter - - virtual ReporterPreferences getPreferences() const CATCH_OVERRIDE { - return m_reporters[0]->getPreferences(); + XmlWriter::ScopedElement& XmlWriter::ScopedElement::writeText( std::string const& text, bool indent ) { + m_writer->writeText( text, indent ); + return *this; } - virtual void noMatchingTestCases( std::string const& spec ) CATCH_OVERRIDE { - for( Reporters::const_iterator it = m_reporters.begin(), itEnd = m_reporters.end(); - it != itEnd; - ++it ) - (*it)->noMatchingTestCases( spec ); + XmlWriter::XmlWriter( std::ostream& os ) : m_os( os ) + { + writeDeclaration(); } - virtual void testRunStarting( TestRunInfo const& testRunInfo ) CATCH_OVERRIDE { - for( Reporters::const_iterator it = m_reporters.begin(), itEnd = m_reporters.end(); - it != itEnd; - ++it ) - (*it)->testRunStarting( testRunInfo ); + XmlWriter::~XmlWriter() { + while( !m_tags.empty() ) + endElement(); } - virtual void testGroupStarting( GroupInfo const& groupInfo ) CATCH_OVERRIDE { - for( Reporters::const_iterator it = m_reporters.begin(), itEnd = m_reporters.end(); - it != itEnd; - ++it ) - (*it)->testGroupStarting( groupInfo ); + XmlWriter& XmlWriter::startElement( std::string const& name ) { + ensureTagClosed(); + newlineIfNecessary(); + m_os << m_indent << '<' << name; + m_tags.push_back( name ); + m_indent += " "; + m_tagIsOpen = true; + return *this; } - virtual void testCaseStarting( TestCaseInfo const& testInfo ) CATCH_OVERRIDE { - for( Reporters::const_iterator it = m_reporters.begin(), itEnd = m_reporters.end(); - it != itEnd; - ++it ) - (*it)->testCaseStarting( testInfo ); + XmlWriter::ScopedElement XmlWriter::scopedElement( std::string const& name ) { + ScopedElement scoped( this ); + startElement( name ); + return scoped; } - virtual void sectionStarting( SectionInfo const& sectionInfo ) CATCH_OVERRIDE { - for( Reporters::const_iterator it = m_reporters.begin(), itEnd = m_reporters.end(); - it != itEnd; - ++it ) - (*it)->sectionStarting( sectionInfo ); + XmlWriter& XmlWriter::endElement() { + newlineIfNecessary(); + m_indent = m_indent.substr( 0, m_indent.size()-2 ); + if( m_tagIsOpen ) { + m_os << "/>"; + m_tagIsOpen = false; + } + else { + m_os << m_indent << "</" << m_tags.back() << ">"; + } + m_os << std::endl; + m_tags.pop_back(); + return *this; } - virtual void assertionStarting( AssertionInfo const& assertionInfo ) CATCH_OVERRIDE { - for( Reporters::const_iterator it = m_reporters.begin(), itEnd = m_reporters.end(); - it != itEnd; - ++it ) - (*it)->assertionStarting( assertionInfo ); + XmlWriter& XmlWriter::writeAttribute( std::string const& name, std::string const& attribute ) { + if( !name.empty() && !attribute.empty() ) + m_os << ' ' << name << "=\"" << XmlEncode( attribute, XmlEncode::ForAttributes ) << '"'; + return *this; } - // The return value indicates if the messages buffer should be cleared: - virtual bool assertionEnded( AssertionStats const& assertionStats ) CATCH_OVERRIDE { - bool clearBuffer = false; - for( Reporters::const_iterator it = m_reporters.begin(), itEnd = m_reporters.end(); - it != itEnd; - ++it ) - clearBuffer |= (*it)->assertionEnded( assertionStats ); - return clearBuffer; + XmlWriter& XmlWriter::writeAttribute( std::string const& name, bool attribute ) { + m_os << ' ' << name << "=\"" << ( attribute ? "true" : "false" ) << '"'; + return *this; } - virtual void sectionEnded( SectionStats const& sectionStats ) CATCH_OVERRIDE { - for( Reporters::const_iterator it = m_reporters.begin(), itEnd = m_reporters.end(); - it != itEnd; - ++it ) - (*it)->sectionEnded( sectionStats ); + XmlWriter& XmlWriter::writeText( std::string const& text, bool indent ) { + if( !text.empty() ){ + bool tagWasOpen = m_tagIsOpen; + ensureTagClosed(); + if( tagWasOpen && indent ) + m_os << m_indent; + m_os << XmlEncode( text ); + m_needsNewline = true; + } + return *this; } - virtual void testCaseEnded( TestCaseStats const& testCaseStats ) CATCH_OVERRIDE { - for( Reporters::const_iterator it = m_reporters.begin(), itEnd = m_reporters.end(); - it != itEnd; - ++it ) - (*it)->testCaseEnded( testCaseStats ); + XmlWriter& XmlWriter::writeComment( std::string const& text ) { + ensureTagClosed(); + m_os << m_indent << "<!--" << text << "-->"; + m_needsNewline = true; + return *this; } - virtual void testGroupEnded( TestGroupStats const& testGroupStats ) CATCH_OVERRIDE { - for( Reporters::const_iterator it = m_reporters.begin(), itEnd = m_reporters.end(); - it != itEnd; - ++it ) - (*it)->testGroupEnded( testGroupStats ); + void XmlWriter::writeStylesheetRef( std::string const& url ) { + m_os << "<?xml-stylesheet type=\"text/xsl\" href=\"" << url << "\"?>\n"; } - virtual void testRunEnded( TestRunStats const& testRunStats ) CATCH_OVERRIDE { - for( Reporters::const_iterator it = m_reporters.begin(), itEnd = m_reporters.end(); - it != itEnd; - ++it ) - (*it)->testRunEnded( testRunStats ); + XmlWriter& XmlWriter::writeBlankLine() { + ensureTagClosed(); + m_os << '\n'; + return *this; } - virtual void skipTest( TestCaseInfo const& testInfo ) CATCH_OVERRIDE { - for( Reporters::const_iterator it = m_reporters.begin(), itEnd = m_reporters.end(); - it != itEnd; - ++it ) - (*it)->skipTest( testInfo ); + void XmlWriter::ensureTagClosed() { + if( m_tagIsOpen ) { + m_os << ">" << std::endl; + m_tagIsOpen = false; + } } - virtual MultipleReporters* tryAsMulti() CATCH_OVERRIDE { - return this; + void XmlWriter::writeDeclaration() { + m_os << "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n"; } -}; - -Ptr<IStreamingReporter> addReporter( Ptr<IStreamingReporter> const& existingReporter, Ptr<IStreamingReporter> const& additionalReporter ) { - Ptr<IStreamingReporter> resultingReporter; - - if( existingReporter ) { - MultipleReporters* multi = existingReporter->tryAsMulti(); - if( !multi ) { - multi = new MultipleReporters; - resultingReporter = Ptr<IStreamingReporter>( multi ); - if( existingReporter ) - multi->add( existingReporter ); + void XmlWriter::newlineIfNecessary() { + if( m_needsNewline ) { + m_os << std::endl; + m_needsNewline = false; } - else - resultingReporter = existingReporter; - multi->add( additionalReporter ); } - else - resultingReporter = additionalReporter; - - return resultingReporter; } - -} // end namespace Catch - -// #included from: ../reporters/catch_reporter_xml.hpp -#define TWOBLUECUBES_CATCH_REPORTER_XML_HPP_INCLUDED - -// #included from: catch_reporter_bases.hpp -#define TWOBLUECUBES_CATCH_REPORTER_BASES_HPP_INCLUDED +// end catch_xmlwriter.cpp +// start catch_reporter_bases.cpp #include <cstring> +#include <cfloat> +#include <cstdio> +#include <cassert> +#include <memory> namespace Catch { + void prepareExpandedExpression(AssertionResult& result) { + result.getExpandedExpression(); + } + + // Because formatting using c++ streams is stateful, drop down to C is required + // Alternatively we could use stringstream, but its performance is... not good. + std::string getFormattedDuration( double duration ) { + // Max exponent + 1 is required to represent the whole part + // + 1 for decimal point + // + 3 for the 3 decimal places + // + 1 for null terminator + const std::size_t maxDoubleSize = DBL_MAX_10_EXP + 1 + 1 + 3 + 1; + char buffer[maxDoubleSize]; + + // Save previous errno, to prevent sprintf from overwriting it + ErrnoGuard guard; +#ifdef _MSC_VER + sprintf_s(buffer, "%.3f", duration); +#else + std::sprintf(buffer, "%.3f", duration); +#endif + return std::string(buffer); + } - struct StreamingReporterBase : SharedImpl<IStreamingReporter> { - - StreamingReporterBase( ReporterConfig const& _config ) - : m_config( _config.fullConfig() ), - stream( _config.stream() ) + std::string serializeFilters( std::vector<std::string> const& container ) { + ReusableStringStream oss; + bool first = true; + for (auto&& filter : container) { - m_reporterPrefs.shouldRedirectStdOut = false; - } + if (!first) + oss << ' '; + else + first = false; - virtual ReporterPreferences getPreferences() const CATCH_OVERRIDE { - return m_reporterPrefs; + oss << filter; } + return oss.str(); + } - virtual ~StreamingReporterBase() CATCH_OVERRIDE; + TestEventListenerBase::TestEventListenerBase(ReporterConfig const & _config) + :StreamingReporterBase(_config) {} - virtual void noMatchingTestCases( std::string const& ) CATCH_OVERRIDE {} + std::set<Verbosity> TestEventListenerBase::getSupportedVerbosities() { + return { Verbosity::Quiet, Verbosity::Normal, Verbosity::High }; + } - virtual void testRunStarting( TestRunInfo const& _testRunInfo ) CATCH_OVERRIDE { - currentTestRunInfo = _testRunInfo; - } - virtual void testGroupStarting( GroupInfo const& _groupInfo ) CATCH_OVERRIDE { - currentGroupInfo = _groupInfo; - } + void TestEventListenerBase::assertionStarting(AssertionInfo const &) {} - virtual void testCaseStarting( TestCaseInfo const& _testInfo ) CATCH_OVERRIDE { - currentTestCaseInfo = _testInfo; - } - virtual void sectionStarting( SectionInfo const& _sectionInfo ) CATCH_OVERRIDE { - m_sectionStack.push_back( _sectionInfo ); - } + bool TestEventListenerBase::assertionEnded(AssertionStats const &) { + return false; + } - virtual void sectionEnded( SectionStats const& /* _sectionStats */ ) CATCH_OVERRIDE { - m_sectionStack.pop_back(); - } - virtual void testCaseEnded( TestCaseStats const& /* _testCaseStats */ ) CATCH_OVERRIDE { - currentTestCaseInfo.reset(); - } - virtual void testGroupEnded( TestGroupStats const& /* _testGroupStats */ ) CATCH_OVERRIDE { - currentGroupInfo.reset(); - } - virtual void testRunEnded( TestRunStats const& /* _testRunStats */ ) CATCH_OVERRIDE { - currentTestCaseInfo.reset(); - currentGroupInfo.reset(); - currentTestRunInfo.reset(); - } +} // end namespace Catch +// end catch_reporter_bases.cpp +// start catch_reporter_compact.cpp - virtual void skipTest( TestCaseInfo const& ) CATCH_OVERRIDE { - // Don't do anything with this by default. - // It can optionally be overridden in the derived class. - } +namespace { - Ptr<IConfig const> m_config; - std::ostream& stream; +#ifdef CATCH_PLATFORM_MAC + const char* failedString() { return "FAILED"; } + const char* passedString() { return "PASSED"; } +#else + const char* failedString() { return "failed"; } + const char* passedString() { return "passed"; } +#endif - LazyStat<TestRunInfo> currentTestRunInfo; - LazyStat<GroupInfo> currentGroupInfo; - LazyStat<TestCaseInfo> currentTestCaseInfo; + // Colour::LightGrey + Catch::Colour::Code dimColour() { return Catch::Colour::FileName; } - std::vector<SectionInfo> m_sectionStack; - ReporterPreferences m_reporterPrefs; - }; + std::string bothOrAll( std::size_t count ) { + return count == 1 ? std::string() : + count == 2 ? "both " : "all " ; + } - struct CumulativeReporterBase : SharedImpl<IStreamingReporter> { - template<typename T, typename ChildNodeT> - struct Node : SharedImpl<> { - explicit Node( T const& _value ) : value( _value ) {} - virtual ~Node() {} +} // anon namespace - typedef std::vector<Ptr<ChildNodeT> > ChildNodes; - T value; - ChildNodes children; - }; - struct SectionNode : SharedImpl<> { - explicit SectionNode( SectionStats const& _stats ) : stats( _stats ) {} - virtual ~SectionNode(); +namespace Catch { +namespace { +// Colour, message variants: +// - white: No tests ran. +// - red: Failed [both/all] N test cases, failed [both/all] M assertions. +// - white: Passed [both/all] N test cases (no assertions). +// - red: Failed N tests cases, failed M assertions. +// - green: Passed [both/all] N tests cases with M assertions. +void printTotals(std::ostream& out, const Totals& totals) { + if (totals.testCases.total() == 0) { + out << "No tests ran."; + } else if (totals.testCases.failed == totals.testCases.total()) { + Colour colour(Colour::ResultError); + const std::string qualify_assertions_failed = + totals.assertions.failed == totals.assertions.total() ? + bothOrAll(totals.assertions.failed) : std::string(); + out << + "Failed " << bothOrAll(totals.testCases.failed) + << pluralise(totals.testCases.failed, "test case") << ", " + "failed " << qualify_assertions_failed << + pluralise(totals.assertions.failed, "assertion") << '.'; + } else if (totals.assertions.total() == 0) { + out << + "Passed " << bothOrAll(totals.testCases.total()) + << pluralise(totals.testCases.total(), "test case") + << " (no assertions)."; + } else if (totals.assertions.failed) { + Colour colour(Colour::ResultError); + out << + "Failed " << pluralise(totals.testCases.failed, "test case") << ", " + "failed " << pluralise(totals.assertions.failed, "assertion") << '.'; + } else { + Colour colour(Colour::ResultSuccess); + out << + "Passed " << bothOrAll(totals.testCases.passed) + << pluralise(totals.testCases.passed, "test case") << + " with " << pluralise(totals.assertions.passed, "assertion") << '.'; + } +} - bool operator == ( SectionNode const& other ) const { - return stats.sectionInfo.lineInfo == other.stats.sectionInfo.lineInfo; - } - bool operator == ( Ptr<SectionNode> const& other ) const { - return operator==( *other ); - } +// Implementation of CompactReporter formatting +class AssertionPrinter { +public: + AssertionPrinter& operator= (AssertionPrinter const&) = delete; + AssertionPrinter(AssertionPrinter const&) = delete; + AssertionPrinter(std::ostream& _stream, AssertionStats const& _stats, bool _printInfoMessages) + : stream(_stream) + , result(_stats.assertionResult) + , messages(_stats.infoMessages) + , itMessage(_stats.infoMessages.begin()) + , printInfoMessages(_printInfoMessages) {} + + void print() { + printSourceInfo(); + + itMessage = messages.begin(); + + switch (result.getResultType()) { + case ResultWas::Ok: + printResultType(Colour::ResultSuccess, passedString()); + printOriginalExpression(); + printReconstructedExpression(); + if (!result.hasExpression()) + printRemainingMessages(Colour::None); + else + printRemainingMessages(); + break; + case ResultWas::ExpressionFailed: + if (result.isOk()) + printResultType(Colour::ResultSuccess, failedString() + std::string(" - but was ok")); + else + printResultType(Colour::Error, failedString()); + printOriginalExpression(); + printReconstructedExpression(); + printRemainingMessages(); + break; + case ResultWas::ThrewException: + printResultType(Colour::Error, failedString()); + printIssue("unexpected exception with message:"); + printMessage(); + printExpressionWas(); + printRemainingMessages(); + break; + case ResultWas::FatalErrorCondition: + printResultType(Colour::Error, failedString()); + printIssue("fatal error condition with message:"); + printMessage(); + printExpressionWas(); + printRemainingMessages(); + break; + case ResultWas::DidntThrowException: + printResultType(Colour::Error, failedString()); + printIssue("expected exception, got none"); + printExpressionWas(); + printRemainingMessages(); + break; + case ResultWas::Info: + printResultType(Colour::None, "info"); + printMessage(); + printRemainingMessages(); + break; + case ResultWas::Warning: + printResultType(Colour::None, "warning"); + printMessage(); + printRemainingMessages(); + break; + case ResultWas::ExplicitFailure: + printResultType(Colour::Error, failedString()); + printIssue("explicitly"); + printRemainingMessages(Colour::None); + break; + // These cases are here to prevent compiler warnings + case ResultWas::Unknown: + case ResultWas::FailureBit: + case ResultWas::Exception: + printResultType(Colour::Error, "** internal error **"); + break; + } + } - SectionStats stats; - typedef std::vector<Ptr<SectionNode> > ChildSections; - typedef std::vector<AssertionStats> Assertions; - ChildSections childSections; - Assertions assertions; - std::string stdOut; - std::string stdErr; - }; +private: + void printSourceInfo() const { + Colour colourGuard(Colour::FileName); + stream << result.getSourceInfo() << ':'; + } - struct BySectionInfo { - BySectionInfo( SectionInfo const& other ) : m_other( other ) {} - BySectionInfo( BySectionInfo const& other ) : m_other( other.m_other ) {} - bool operator() ( Ptr<SectionNode> const& node ) const { - return node->stats.sectionInfo.lineInfo == m_other.lineInfo; + void printResultType(Colour::Code colour, std::string const& passOrFail) const { + if (!passOrFail.empty()) { + { + Colour colourGuard(colour); + stream << ' ' << passOrFail; } - private: - void operator=( BySectionInfo const& ); - SectionInfo const& m_other; - }; - - typedef Node<TestCaseStats, SectionNode> TestCaseNode; - typedef Node<TestGroupStats, TestCaseNode> TestGroupNode; - typedef Node<TestRunStats, TestGroupNode> TestRunNode; - - CumulativeReporterBase( ReporterConfig const& _config ) - : m_config( _config.fullConfig() ), - stream( _config.stream() ) - { - m_reporterPrefs.shouldRedirectStdOut = false; + stream << ':'; } - ~CumulativeReporterBase(); - - virtual ReporterPreferences getPreferences() const CATCH_OVERRIDE { - return m_reporterPrefs; - } - - virtual void testRunStarting( TestRunInfo const& ) CATCH_OVERRIDE {} - virtual void testGroupStarting( GroupInfo const& ) CATCH_OVERRIDE {} + } - virtual void testCaseStarting( TestCaseInfo const& ) CATCH_OVERRIDE {} + void printIssue(std::string const& issue) const { + stream << ' ' << issue; + } - virtual void sectionStarting( SectionInfo const& sectionInfo ) CATCH_OVERRIDE { - SectionStats incompleteStats( sectionInfo, Counts(), 0, false ); - Ptr<SectionNode> node; - if( m_sectionStack.empty() ) { - if( !m_rootSection ) - m_rootSection = new SectionNode( incompleteStats ); - node = m_rootSection; - } - else { - SectionNode& parentNode = *m_sectionStack.back(); - SectionNode::ChildSections::const_iterator it = - std::find_if( parentNode.childSections.begin(), - parentNode.childSections.end(), - BySectionInfo( sectionInfo ) ); - if( it == parentNode.childSections.end() ) { - node = new SectionNode( incompleteStats ); - parentNode.childSections.push_back( node ); - } - else - node = *it; + void printExpressionWas() { + if (result.hasExpression()) { + stream << ';'; + { + Colour colour(dimColour()); + stream << " expression was:"; } - m_sectionStack.push_back( node ); - m_deepestSection = node; + printOriginalExpression(); } + } - virtual void assertionStarting( AssertionInfo const& ) CATCH_OVERRIDE {} - - virtual bool assertionEnded( AssertionStats const& assertionStats ) CATCH_OVERRIDE { - assert( !m_sectionStack.empty() ); - SectionNode& sectionNode = *m_sectionStack.back(); - sectionNode.assertions.push_back( assertionStats ); - // AssertionResult holds a pointer to a temporary DecomposedExpression, - // which getExpandedExpression() calls to build the expression string. - // Our section stack copy of the assertionResult will likely outlive the - // temporary, so it must be expanded or discarded now to avoid calling - // a destroyed object later. - prepareExpandedExpression( sectionNode.assertions.back().assertionResult ); - return true; + void printOriginalExpression() const { + if (result.hasExpression()) { + stream << ' ' << result.getExpression(); } - virtual void sectionEnded( SectionStats const& sectionStats ) CATCH_OVERRIDE { - assert( !m_sectionStack.empty() ); - SectionNode& node = *m_sectionStack.back(); - node.stats = sectionStats; - m_sectionStack.pop_back(); - } - virtual void testCaseEnded( TestCaseStats const& testCaseStats ) CATCH_OVERRIDE { - Ptr<TestCaseNode> node = new TestCaseNode( testCaseStats ); - assert( m_sectionStack.size() == 0 ); - node->children.push_back( m_rootSection ); - m_testCases.push_back( node ); - m_rootSection.reset(); + } - assert( m_deepestSection ); - m_deepestSection->stdOut = testCaseStats.stdOut; - m_deepestSection->stdErr = testCaseStats.stdErr; - } - virtual void testGroupEnded( TestGroupStats const& testGroupStats ) CATCH_OVERRIDE { - Ptr<TestGroupNode> node = new TestGroupNode( testGroupStats ); - node->children.swap( m_testCases ); - m_testGroups.push_back( node ); - } - virtual void testRunEnded( TestRunStats const& testRunStats ) CATCH_OVERRIDE { - Ptr<TestRunNode> node = new TestRunNode( testRunStats ); - node->children.swap( m_testGroups ); - m_testRuns.push_back( node ); - testRunEndedCumulative(); + void printReconstructedExpression() const { + if (result.hasExpandedExpression()) { + { + Colour colour(dimColour()); + stream << " for: "; + } + stream << result.getExpandedExpression(); } - virtual void testRunEndedCumulative() = 0; - - virtual void skipTest( TestCaseInfo const& ) CATCH_OVERRIDE {} + } - virtual void prepareExpandedExpression( AssertionResult& result ) const { - if( result.isOk() ) - result.discardDecomposedExpression(); - else - result.expandDecomposedExpression(); + void printMessage() { + if (itMessage != messages.end()) { + stream << " '" << itMessage->message << '\''; + ++itMessage; } + } - Ptr<IConfig const> m_config; - std::ostream& stream; - std::vector<AssertionStats> m_assertions; - std::vector<std::vector<Ptr<SectionNode> > > m_sections; - std::vector<Ptr<TestCaseNode> > m_testCases; - std::vector<Ptr<TestGroupNode> > m_testGroups; - - std::vector<Ptr<TestRunNode> > m_testRuns; - - Ptr<SectionNode> m_rootSection; - Ptr<SectionNode> m_deepestSection; - std::vector<Ptr<SectionNode> > m_sectionStack; - ReporterPreferences m_reporterPrefs; + void printRemainingMessages(Colour::Code colour = dimColour()) { + if (itMessage == messages.end()) + return; - }; + // using messages.end() directly yields (or auto) compilation error: + std::vector<MessageInfo>::const_iterator itEnd = messages.end(); + const std::size_t N = static_cast<std::size_t>(std::distance(itMessage, itEnd)); - template<char C> - char const* getLineOfChars() { - static char line[CATCH_CONFIG_CONSOLE_WIDTH] = {0}; - if( !*line ) { - std::memset( line, C, CATCH_CONFIG_CONSOLE_WIDTH-1 ); - line[CATCH_CONFIG_CONSOLE_WIDTH-1] = 0; + { + Colour colourGuard(colour); + stream << " with " << pluralise(N, "message") << ':'; } - return line; - } - - struct TestEventListenerBase : StreamingReporterBase { - TestEventListenerBase( ReporterConfig const& _config ) - : StreamingReporterBase( _config ) - {} - virtual void assertionStarting( AssertionInfo const& ) CATCH_OVERRIDE {} - virtual bool assertionEnded( AssertionStats const& ) CATCH_OVERRIDE { - return false; + for (; itMessage != itEnd; ) { + // If this assertion is a warning ignore any INFO messages + if (printInfoMessages || itMessage->type != ResultWas::Info) { + stream << " '" << itMessage->message << '\''; + if (++itMessage != itEnd) { + Colour colourGuard(dimColour()); + stream << " and"; + } + } } - }; - -} // end namespace Catch - -// #included from: ../internal/catch_reporter_registrars.hpp -#define TWOBLUECUBES_CATCH_REPORTER_REGISTRARS_HPP_INCLUDED - -namespace Catch { + } - template<typename T> - class LegacyReporterRegistrar { +private: + std::ostream& stream; + AssertionResult const& result; + std::vector<MessageInfo> messages; + std::vector<MessageInfo>::const_iterator itMessage; + bool printInfoMessages; +}; - class ReporterFactory : public IReporterFactory { - virtual IStreamingReporter* create( ReporterConfig const& config ) const { - return new LegacyReporterAdapter( new T( config ) ); - } +} // anon namespace - virtual std::string getDescription() const { - return T::getDescription(); - } - }; + std::string CompactReporter::getDescription() { + return "Reports test results on a single line, suitable for IDEs"; + } - public: + ReporterPreferences CompactReporter::getPreferences() const { + return m_reporterPrefs; + } - LegacyReporterRegistrar( std::string const& name ) { - getMutableRegistryHub().registerReporter( name, new ReporterFactory() ); + void CompactReporter::noMatchingTestCases( std::string const& spec ) { + stream << "No test cases matched '" << spec << '\'' << std::endl; } - }; - template<typename T> - class ReporterRegistrar { + void CompactReporter::assertionStarting( AssertionInfo const& ) {} - class ReporterFactory : public SharedImpl<IReporterFactory> { + bool CompactReporter::assertionEnded( AssertionStats const& _assertionStats ) { + AssertionResult const& result = _assertionStats.assertionResult; - // *** Please Note ***: - // - If you end up here looking at a compiler error because it's trying to register - // your custom reporter class be aware that the native reporter interface has changed - // to IStreamingReporter. The "legacy" interface, IReporter, is still supported via - // an adapter. Just use REGISTER_LEGACY_REPORTER to take advantage of the adapter. - // However please consider updating to the new interface as the old one is now - // deprecated and will probably be removed quite soon! - // Please contact me via github if you have any questions at all about this. - // In fact, ideally, please contact me anyway to let me know you've hit this - as I have - // no idea who is actually using custom reporters at all (possibly no-one!). - // The new interface is designed to minimise exposure to interface changes in the future. - virtual IStreamingReporter* create( ReporterConfig const& config ) const { - return new T( config ); - } + bool printInfoMessages = true; - virtual std::string getDescription() const { - return T::getDescription(); + // Drop out if result was successful and we're not printing those + if( !m_config->includeSuccessfulResults() && result.isOk() ) { + if( result.getResultType() != ResultWas::Warning ) + return false; + printInfoMessages = false; } - }; - public: + AssertionPrinter printer( stream, _assertionStats, printInfoMessages ); + printer.print(); - ReporterRegistrar( std::string const& name ) { - getMutableRegistryHub().registerReporter( name, new ReporterFactory() ); + stream << std::endl; + return true; } - }; - - template<typename T> - class ListenerRegistrar { - class ListenerFactory : public SharedImpl<IReporterFactory> { - - virtual IStreamingReporter* create( ReporterConfig const& config ) const { - return new T( config ); - } - virtual std::string getDescription() const { - return std::string(); + void CompactReporter::sectionEnded(SectionStats const& _sectionStats) { + if (m_config->showDurations() == ShowDurations::Always) { + stream << getFormattedDuration(_sectionStats.durationInSeconds) << " s: " << _sectionStats.sectionInfo.name << std::endl; } - }; - - public: + } - ListenerRegistrar() { - getMutableRegistryHub().registerListener( new ListenerFactory() ); + void CompactReporter::testRunEnded( TestRunStats const& _testRunStats ) { + printTotals( stream, _testRunStats.totals ); + stream << '\n' << std::endl; + StreamingReporterBase::testRunEnded( _testRunStats ); } - }; -} -#define INTERNAL_CATCH_REGISTER_LEGACY_REPORTER( name, reporterType ) \ - namespace{ Catch::LegacyReporterRegistrar<reporterType> catch_internal_RegistrarFor##reporterType( name ); } + CompactReporter::~CompactReporter() {} -#define INTERNAL_CATCH_REGISTER_REPORTER( name, reporterType ) \ - namespace{ Catch::ReporterRegistrar<reporterType> catch_internal_RegistrarFor##reporterType( name ); } + CATCH_REGISTER_REPORTER( "compact", CompactReporter ) -#define INTERNAL_CATCH_REGISTER_LISTENER( listenerType ) \ - namespace{ Catch::ListenerRegistrar<listenerType> catch_internal_RegistrarFor##listenerType; } +} // end namespace Catch +// end catch_reporter_compact.cpp +// start catch_reporter_console.cpp -// #included from: ../internal/catch_xmlwriter.hpp -#define TWOBLUECUBES_CATCH_XMLWRITER_HPP_INCLUDED +#include <cfloat> +#include <cstdio> -#include <sstream> -#include <string> -#include <vector> -#include <iomanip> +#if defined(_MSC_VER) +#pragma warning(push) +#pragma warning(disable:4061) // Not all labels are EXPLICITLY handled in switch + // Note that 4062 (not all labels are handled and default is missing) is enabled +#endif -namespace Catch { +#if defined(__clang__) +# pragma clang diagnostic push +// For simplicity, benchmarking-only helpers are always enabled +# pragma clang diagnostic ignored "-Wunused-function" +#endif - class XmlEncode { - public: - enum ForWhat { ForTextNodes, ForAttributes }; +namespace Catch { - XmlEncode( std::string const& str, ForWhat forWhat = ForTextNodes ) - : m_str( str ), - m_forWhat( forWhat ) - {} +namespace { - void encodeTo( std::ostream& os ) const { - - // Apostrophe escaping not necessary if we always use " to write attributes - // (see: http://www.w3.org/TR/xml/#syntax) - - for( std::size_t i = 0; i < m_str.size(); ++ i ) { - char c = m_str[i]; - switch( c ) { - case '<': os << "<"; break; - case '&': os << "&"; break; - - case '>': - // See: http://www.w3.org/TR/xml/#syntax - if( i > 2 && m_str[i-1] == ']' && m_str[i-2] == ']' ) - os << ">"; - else - os << c; - break; - - case '\"': - if( m_forWhat == ForAttributes ) - os << """; - else - os << c; - break; - - default: - // Escape control chars - based on contribution by @espenalb in PR #465 and - // by @mrpi PR #588 - if ( ( c >= 0 && c < '\x09' ) || ( c > '\x0D' && c < '\x20') || c=='\x7F' ) { - // see http://stackoverflow.com/questions/404107/why-are-control-characters-illegal-in-xml-1-0 - os << "\\x" << std::uppercase << std::hex << std::setfill('0') << std::setw(2) - << static_cast<int>( c ); - } - else - os << c; - } +// Formatter impl for ConsoleReporter +class ConsoleAssertionPrinter { +public: + ConsoleAssertionPrinter& operator= (ConsoleAssertionPrinter const&) = delete; + ConsoleAssertionPrinter(ConsoleAssertionPrinter const&) = delete; + ConsoleAssertionPrinter(std::ostream& _stream, AssertionStats const& _stats, bool _printInfoMessages) + : stream(_stream), + stats(_stats), + result(_stats.assertionResult), + colour(Colour::None), + message(result.getMessage()), + messages(_stats.infoMessages), + printInfoMessages(_printInfoMessages) { + switch (result.getResultType()) { + case ResultWas::Ok: + colour = Colour::Success; + passOrFail = "PASSED"; + //if( result.hasMessage() ) + if (_stats.infoMessages.size() == 1) + messageLabel = "with message"; + if (_stats.infoMessages.size() > 1) + messageLabel = "with messages"; + break; + case ResultWas::ExpressionFailed: + if (result.isOk()) { + colour = Colour::Success; + passOrFail = "FAILED - but was ok"; + } else { + colour = Colour::Error; + passOrFail = "FAILED"; } + if (_stats.infoMessages.size() == 1) + messageLabel = "with message"; + if (_stats.infoMessages.size() > 1) + messageLabel = "with messages"; + break; + case ResultWas::ThrewException: + colour = Colour::Error; + passOrFail = "FAILED"; + messageLabel = "due to unexpected exception with "; + if (_stats.infoMessages.size() == 1) + messageLabel += "message"; + if (_stats.infoMessages.size() > 1) + messageLabel += "messages"; + break; + case ResultWas::FatalErrorCondition: + colour = Colour::Error; + passOrFail = "FAILED"; + messageLabel = "due to a fatal error condition"; + break; + case ResultWas::DidntThrowException: + colour = Colour::Error; + passOrFail = "FAILED"; + messageLabel = "because no exception was thrown where one was expected"; + break; + case ResultWas::Info: + messageLabel = "info"; + break; + case ResultWas::Warning: + messageLabel = "warning"; + break; + case ResultWas::ExplicitFailure: + passOrFail = "FAILED"; + colour = Colour::Error; + if (_stats.infoMessages.size() == 1) + messageLabel = "explicitly with message"; + if (_stats.infoMessages.size() > 1) + messageLabel = "explicitly with messages"; + break; + // These cases are here to prevent compiler warnings + case ResultWas::Unknown: + case ResultWas::FailureBit: + case ResultWas::Exception: + passOrFail = "** internal error **"; + colour = Colour::Error; + break; + } + } + + void print() const { + printSourceInfo(); + if (stats.totals.assertions.total() > 0) { + printResultType(); + printOriginalExpression(); + printReconstructedExpression(); + } else { + stream << '\n'; } + printMessage(); + } - friend std::ostream& operator << ( std::ostream& os, XmlEncode const& xmlEncode ) { - xmlEncode.encodeTo( os ); - return os; +private: + void printResultType() const { + if (!passOrFail.empty()) { + Colour colourGuard(colour); + stream << passOrFail << ":\n"; } + } + void printOriginalExpression() const { + if (result.hasExpression()) { + Colour colourGuard(Colour::OriginalExpression); + stream << " "; + stream << result.getExpressionInMacro(); + stream << '\n'; + } + } + void printReconstructedExpression() const { + if (result.hasExpandedExpression()) { + stream << "with expansion:\n"; + Colour colourGuard(Colour::ReconstructedExpression); + stream << Column(result.getExpandedExpression()).indent(2) << '\n'; + } + } + void printMessage() const { + if (!messageLabel.empty()) + stream << messageLabel << ':' << '\n'; + for (auto const& msg : messages) { + // If this assertion is a warning ignore any INFO messages + if (printInfoMessages || msg.type != ResultWas::Info) + stream << Column(msg.message).indent(2) << '\n'; + } + } + void printSourceInfo() const { + Colour colourGuard(Colour::FileName); + stream << result.getSourceInfo() << ": "; + } - private: - std::string m_str; - ForWhat m_forWhat; - }; - - class XmlWriter { - public: - - class ScopedElement { - public: - ScopedElement( XmlWriter* writer ) - : m_writer( writer ) - {} - - ScopedElement( ScopedElement const& other ) - : m_writer( other.m_writer ){ - other.m_writer = CATCH_NULL; - } - - ~ScopedElement() { - if( m_writer ) - m_writer->endElement(); - } + std::ostream& stream; + AssertionStats const& stats; + AssertionResult const& result; + Colour::Code colour; + std::string passOrFail; + std::string messageLabel; + std::string message; + std::vector<MessageInfo> messages; + bool printInfoMessages; +}; - ScopedElement& writeText( std::string const& text, bool indent = true ) { - m_writer->writeText( text, indent ); - return *this; - } +std::size_t makeRatio(std::size_t number, std::size_t total) { + std::size_t ratio = total > 0 ? CATCH_CONFIG_CONSOLE_WIDTH * number / total : 0; + return (ratio == 0 && number > 0) ? 1 : ratio; +} - template<typename T> - ScopedElement& writeAttribute( std::string const& name, T const& attribute ) { - m_writer->writeAttribute( name, attribute ); - return *this; - } +std::size_t& findMax(std::size_t& i, std::size_t& j, std::size_t& k) { + if (i > j && i > k) + return i; + else if (j > k) + return j; + else + return k; +} - private: - mutable XmlWriter* m_writer; - }; +struct ColumnInfo { + enum Justification { Left, Right }; + std::string name; + int width; + Justification justification; +}; +struct ColumnBreak {}; +struct RowBreak {}; - XmlWriter() - : m_tagIsOpen( false ), - m_needsNewline( false ), - m_os( Catch::cout() ) - { - writeDeclaration(); - } +class Duration { + enum class Unit { + Auto, + Nanoseconds, + Microseconds, + Milliseconds, + Seconds, + Minutes + }; + static const uint64_t s_nanosecondsInAMicrosecond = 1000; + static const uint64_t s_nanosecondsInAMillisecond = 1000 * s_nanosecondsInAMicrosecond; + static const uint64_t s_nanosecondsInASecond = 1000 * s_nanosecondsInAMillisecond; + static const uint64_t s_nanosecondsInAMinute = 60 * s_nanosecondsInASecond; - XmlWriter( std::ostream& os ) - : m_tagIsOpen( false ), - m_needsNewline( false ), - m_os( os ) - { - writeDeclaration(); - } + uint64_t m_inNanoseconds; + Unit m_units; - ~XmlWriter() { - while( !m_tags.empty() ) - endElement(); +public: + explicit Duration(double inNanoseconds, Unit units = Unit::Auto) + : Duration(static_cast<uint64_t>(inNanoseconds), units) { + } + + explicit Duration(uint64_t inNanoseconds, Unit units = Unit::Auto) + : m_inNanoseconds(inNanoseconds), + m_units(units) { + if (m_units == Unit::Auto) { + if (m_inNanoseconds < s_nanosecondsInAMicrosecond) + m_units = Unit::Nanoseconds; + else if (m_inNanoseconds < s_nanosecondsInAMillisecond) + m_units = Unit::Microseconds; + else if (m_inNanoseconds < s_nanosecondsInASecond) + m_units = Unit::Milliseconds; + else if (m_inNanoseconds < s_nanosecondsInAMinute) + m_units = Unit::Seconds; + else + m_units = Unit::Minutes; } - XmlWriter& startElement( std::string const& name ) { - ensureTagClosed(); - newlineIfNecessary(); - m_os << m_indent << '<' << name; - m_tags.push_back( name ); - m_indent += " "; - m_tagIsOpen = true; - return *this; - } + } - ScopedElement scopedElement( std::string const& name ) { - ScopedElement scoped( this ); - startElement( name ); - return scoped; + auto value() const -> double { + switch (m_units) { + case Unit::Microseconds: + return m_inNanoseconds / static_cast<double>(s_nanosecondsInAMicrosecond); + case Unit::Milliseconds: + return m_inNanoseconds / static_cast<double>(s_nanosecondsInAMillisecond); + case Unit::Seconds: + return m_inNanoseconds / static_cast<double>(s_nanosecondsInASecond); + case Unit::Minutes: + return m_inNanoseconds / static_cast<double>(s_nanosecondsInAMinute); + default: + return static_cast<double>(m_inNanoseconds); } - - XmlWriter& endElement() { - newlineIfNecessary(); - m_indent = m_indent.substr( 0, m_indent.size()-2 ); - if( m_tagIsOpen ) { - m_os << "/>"; - m_tagIsOpen = false; - } - else { - m_os << m_indent << "</" << m_tags.back() << ">"; - } - m_os << std::endl; - m_tags.pop_back(); - return *this; + } + auto unitsAsString() const -> std::string { + switch (m_units) { + case Unit::Nanoseconds: + return "ns"; + case Unit::Microseconds: + return "us"; + case Unit::Milliseconds: + return "ms"; + case Unit::Seconds: + return "s"; + case Unit::Minutes: + return "m"; + default: + return "** internal error **"; } - XmlWriter& writeAttribute( std::string const& name, std::string const& attribute ) { - if( !name.empty() && !attribute.empty() ) - m_os << ' ' << name << "=\"" << XmlEncode( attribute, XmlEncode::ForAttributes ) << '"'; - return *this; - } + } + friend auto operator << (std::ostream& os, Duration const& duration) -> std::ostream& { + return os << duration.value() << " " << duration.unitsAsString(); + } +}; +} // end anon namespace - XmlWriter& writeAttribute( std::string const& name, bool attribute ) { - m_os << ' ' << name << "=\"" << ( attribute ? "true" : "false" ) << '"'; - return *this; - } +class TablePrinter { + std::ostream& m_os; + std::vector<ColumnInfo> m_columnInfos; + std::ostringstream m_oss; + int m_currentColumn = -1; + bool m_isOpen = false; - template<typename T> - XmlWriter& writeAttribute( std::string const& name, T const& attribute ) { - std::ostringstream oss; - oss << attribute; - return writeAttribute( name, oss.str() ); - } +public: + TablePrinter( std::ostream& os, std::vector<ColumnInfo> columnInfos ) + : m_os( os ), + m_columnInfos( std::move( columnInfos ) ) {} - XmlWriter& writeText( std::string const& text, bool indent = true ) { - if( !text.empty() ){ - bool tagWasOpen = m_tagIsOpen; - ensureTagClosed(); - if( tagWasOpen && indent ) - m_os << m_indent; - m_os << XmlEncode( text ); - m_needsNewline = true; - } - return *this; - } + auto columnInfos() const -> std::vector<ColumnInfo> const& { + return m_columnInfos; + } - XmlWriter& writeComment( std::string const& text ) { - ensureTagClosed(); - m_os << m_indent << "<!--" << text << "-->"; - m_needsNewline = true; - return *this; - } + void open() { + if (!m_isOpen) { + m_isOpen = true; + *this << RowBreak(); - void writeStylesheetRef( std::string const& url ) { - m_os << "<?xml-stylesheet type=\"text/xsl\" href=\"" << url << "\"?>\n"; - } + Columns headerCols; + Spacer spacer(2); + for (auto const& info : m_columnInfos) { + headerCols += Column(info.name).width(static_cast<std::size_t>(info.width - 2)); + headerCols += spacer; + } + m_os << headerCols << "\n"; - XmlWriter& writeBlankLine() { - ensureTagClosed(); - m_os << '\n'; - return *this; + m_os << Catch::getLineOfChars<'-'>() << "\n"; } - - void ensureTagClosed() { - if( m_tagIsOpen ) { - m_os << ">" << std::endl; - m_tagIsOpen = false; - } + } + void close() { + if (m_isOpen) { + *this << RowBreak(); + m_os << std::endl; + m_isOpen = false; } + } - private: - XmlWriter( XmlWriter const& ); - void operator=( XmlWriter const& ); - - void writeDeclaration() { - m_os << "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n"; - } + template<typename T> + friend TablePrinter& operator << (TablePrinter& tp, T const& value) { + tp.m_oss << value; + return tp; + } + + friend TablePrinter& operator << (TablePrinter& tp, ColumnBreak) { + auto colStr = tp.m_oss.str(); + // This takes account of utf8 encodings + auto strSize = Catch::StringRef(colStr).numberOfCharacters(); + tp.m_oss.str(""); + tp.open(); + if (tp.m_currentColumn == static_cast<int>(tp.m_columnInfos.size() - 1)) { + tp.m_currentColumn = -1; + tp.m_os << "\n"; + } + tp.m_currentColumn++; + + auto colInfo = tp.m_columnInfos[tp.m_currentColumn]; + auto padding = (strSize + 2 < static_cast<std::size_t>(colInfo.width)) + ? std::string(colInfo.width - (strSize + 2), ' ') + : std::string(); + if (colInfo.justification == ColumnInfo::Left) + tp.m_os << colStr << padding << " "; + else + tp.m_os << padding << colStr << " "; + return tp; + } - void newlineIfNecessary() { - if( m_needsNewline ) { - m_os << std::endl; - m_needsNewline = false; - } + friend TablePrinter& operator << (TablePrinter& tp, RowBreak) { + if (tp.m_currentColumn > 0) { + tp.m_os << "\n"; + tp.m_currentColumn = -1; } + return tp; + } +}; - bool m_tagIsOpen; - bool m_needsNewline; - std::vector<std::string> m_tags; - std::string m_indent; - std::ostream& m_os; - }; +ConsoleReporter::ConsoleReporter(ReporterConfig const& config) + : StreamingReporterBase(config), + m_tablePrinter(new TablePrinter(config.stream(), + { + { "benchmark name", CATCH_CONFIG_CONSOLE_WIDTH - 32, ColumnInfo::Left }, + { "samples mean std dev", 14, ColumnInfo::Right }, + { "iterations low mean low std dev", 14, ColumnInfo::Right }, + { "estimated high mean high std dev", 14, ColumnInfo::Right } + })) {} +ConsoleReporter::~ConsoleReporter() = default; + +std::string ConsoleReporter::getDescription() { + return "Reports test results as plain lines of text"; +} +void ConsoleReporter::noMatchingTestCases(std::string const& spec) { + stream << "No test cases matched '" << spec << '\'' << std::endl; } -// #included from: catch_reenable_warnings.h -#define TWOBLUECUBES_CATCH_REENABLE_WARNINGS_H_INCLUDED +void ConsoleReporter::assertionStarting(AssertionInfo const&) {} -#ifdef __clang__ -# ifdef __ICC // icpc defines the __clang__ macro -# pragma warning(pop) -# else -# pragma clang diagnostic pop -# endif -#elif defined __GNUC__ -# pragma GCC diagnostic pop -#endif +bool ConsoleReporter::assertionEnded(AssertionStats const& _assertionStats) { + AssertionResult const& result = _assertionStats.assertionResult; + bool includeResults = m_config->includeSuccessfulResults() || !result.isOk(); -namespace Catch { - class XmlReporter : public StreamingReporterBase { - public: - XmlReporter( ReporterConfig const& _config ) - : StreamingReporterBase( _config ), - m_xml(_config.stream()), - m_sectionDepth( 0 ) - { - m_reporterPrefs.shouldRedirectStdOut = true; - } + // Drop out if result was successful but we're not printing them. + if (!includeResults && result.getResultType() != ResultWas::Warning) + return false; - virtual ~XmlReporter() CATCH_OVERRIDE; + lazyPrint(); - static std::string getDescription() { - return "Reports test results as an XML document"; - } + ConsoleAssertionPrinter printer(stream, _assertionStats, includeResults); + printer.print(); + stream << std::endl; + return true; +} - virtual std::string getStylesheetRef() const { - return std::string(); - } +void ConsoleReporter::sectionStarting(SectionInfo const& _sectionInfo) { + m_tablePrinter->close(); + m_headerPrinted = false; + StreamingReporterBase::sectionStarting(_sectionInfo); +} +void ConsoleReporter::sectionEnded(SectionStats const& _sectionStats) { + m_tablePrinter->close(); + if (_sectionStats.missingAssertions) { + lazyPrint(); + Colour colour(Colour::ResultError); + if (m_sectionStack.size() > 1) + stream << "\nNo assertions in section"; + else + stream << "\nNo assertions in test case"; + stream << " '" << _sectionStats.sectionInfo.name << "'\n" << std::endl; + } + if (m_config->showDurations() == ShowDurations::Always) { + stream << getFormattedDuration(_sectionStats.durationInSeconds) << " s: " << _sectionStats.sectionInfo.name << std::endl; + } + if (m_headerPrinted) { + m_headerPrinted = false; + } + StreamingReporterBase::sectionEnded(_sectionStats); +} - public: // StreamingReporterBase +#if defined(CATCH_CONFIG_ENABLE_BENCHMARKING) +void ConsoleReporter::benchmarkPreparing(std::string const& name) { + lazyPrintWithoutClosingBenchmarkTable(); - virtual void noMatchingTestCases( std::string const& s ) CATCH_OVERRIDE { - StreamingReporterBase::noMatchingTestCases( s ); - } + auto nameCol = Column(name).width(static_cast<std::size_t>(m_tablePrinter->columnInfos()[0].width - 2)); - virtual void testRunStarting( TestRunInfo const& testInfo ) CATCH_OVERRIDE { - StreamingReporterBase::testRunStarting( testInfo ); - std::string stylesheetRef = getStylesheetRef(); - if( !stylesheetRef.empty() ) - m_xml.writeStylesheetRef( stylesheetRef ); - m_xml.startElement( "Catch" ); - if( !m_config->name().empty() ) - m_xml.writeAttribute( "name", m_config->name() ); - } + bool firstLine = true; + for (auto line : nameCol) { + if (!firstLine) + (*m_tablePrinter) << ColumnBreak() << ColumnBreak() << ColumnBreak(); + else + firstLine = false; - virtual void testGroupStarting( GroupInfo const& groupInfo ) CATCH_OVERRIDE { - StreamingReporterBase::testGroupStarting( groupInfo ); - m_xml.startElement( "Group" ) - .writeAttribute( "name", groupInfo.name ); - } + (*m_tablePrinter) << line << ColumnBreak(); + } +} - virtual void testCaseStarting( TestCaseInfo const& testInfo ) CATCH_OVERRIDE { - StreamingReporterBase::testCaseStarting(testInfo); - m_xml.startElement( "TestCase" ) - .writeAttribute( "name", trim( testInfo.name ) ) - .writeAttribute( "description", testInfo.description ) - .writeAttribute( "tags", testInfo.tagsAsString ); +void ConsoleReporter::benchmarkStarting(BenchmarkInfo const& info) { + (*m_tablePrinter) << info.samples << ColumnBreak() + << info.iterations << ColumnBreak() + << Duration(info.estimatedDuration) << ColumnBreak(); +} +void ConsoleReporter::benchmarkEnded(BenchmarkStats<> const& stats) { + (*m_tablePrinter) << ColumnBreak() + << Duration(stats.mean.point.count()) << ColumnBreak() + << Duration(stats.mean.lower_bound.count()) << ColumnBreak() + << Duration(stats.mean.upper_bound.count()) << ColumnBreak() << ColumnBreak() + << Duration(stats.standardDeviation.point.count()) << ColumnBreak() + << Duration(stats.standardDeviation.lower_bound.count()) << ColumnBreak() + << Duration(stats.standardDeviation.upper_bound.count()) << ColumnBreak() << ColumnBreak() << ColumnBreak() << ColumnBreak() << ColumnBreak(); +} - if ( m_config->showDurations() == ShowDurations::Always ) - m_testCaseTimer.start(); - m_xml.ensureTagClosed(); - } +void ConsoleReporter::benchmarkFailed(std::string const& error) { + Colour colour(Colour::Red); + (*m_tablePrinter) + << "Benchmark failed (" << error << ")" + << ColumnBreak() << RowBreak(); +} +#endif // CATCH_CONFIG_ENABLE_BENCHMARKING - virtual void sectionStarting( SectionInfo const& sectionInfo ) CATCH_OVERRIDE { - StreamingReporterBase::sectionStarting( sectionInfo ); - if( m_sectionDepth++ > 0 ) { - m_xml.startElement( "Section" ) - .writeAttribute( "name", trim( sectionInfo.name ) ) - .writeAttribute( "description", sectionInfo.description ); - m_xml.ensureTagClosed(); - } - } +void ConsoleReporter::testCaseEnded(TestCaseStats const& _testCaseStats) { + m_tablePrinter->close(); + StreamingReporterBase::testCaseEnded(_testCaseStats); + m_headerPrinted = false; +} +void ConsoleReporter::testGroupEnded(TestGroupStats const& _testGroupStats) { + if (currentGroupInfo.used) { + printSummaryDivider(); + stream << "Summary for group '" << _testGroupStats.groupInfo.name << "':\n"; + printTotals(_testGroupStats.totals); + stream << '\n' << std::endl; + } + StreamingReporterBase::testGroupEnded(_testGroupStats); +} +void ConsoleReporter::testRunEnded(TestRunStats const& _testRunStats) { + printTotalsDivider(_testRunStats.totals); + printTotals(_testRunStats.totals); + stream << std::endl; + StreamingReporterBase::testRunEnded(_testRunStats); +} +void ConsoleReporter::testRunStarting(TestRunInfo const& _testInfo) { + StreamingReporterBase::testRunStarting(_testInfo); + printTestFilters(); +} - virtual void assertionStarting( AssertionInfo const& ) CATCH_OVERRIDE { } +void ConsoleReporter::lazyPrint() { - virtual bool assertionEnded( AssertionStats const& assertionStats ) CATCH_OVERRIDE { - const AssertionResult& assertionResult = assertionStats.assertionResult; + m_tablePrinter->close(); + lazyPrintWithoutClosingBenchmarkTable(); +} - // Print any info messages in <Info> tags. - if( assertionStats.assertionResult.getResultType() != ResultWas::Ok ) { - for( std::vector<MessageInfo>::const_iterator it = assertionStats.infoMessages.begin(), itEnd = assertionStats.infoMessages.end(); - it != itEnd; - ++it ) { - if( it->type == ResultWas::Info ) { - m_xml.scopedElement( "Info" ) - .writeText( it->message ); - } else if ( it->type == ResultWas::Warning ) { - m_xml.scopedElement( "Warning" ) - .writeText( it->message ); - } - } - } +void ConsoleReporter::lazyPrintWithoutClosingBenchmarkTable() { - // Drop out if result was successful but we're not printing them. - if( !m_config->includeSuccessfulResults() && isOk(assertionResult.getResultType()) ) - return true; + if (!currentTestRunInfo.used) + lazyPrintRunInfo(); + if (!currentGroupInfo.used) + lazyPrintGroupInfo(); - // Print the expression if there is one. - if( assertionResult.hasExpression() ) { - m_xml.startElement( "Expression" ) - .writeAttribute( "success", assertionResult.succeeded() ) - .writeAttribute( "type", assertionResult.getTestMacroName() ) - .writeAttribute( "filename", assertionResult.getSourceInfo().file ) - .writeAttribute( "line", assertionResult.getSourceInfo().line ); + if (!m_headerPrinted) { + printTestCaseAndSectionHeader(); + m_headerPrinted = true; + } +} +void ConsoleReporter::lazyPrintRunInfo() { + stream << '\n' << getLineOfChars<'~'>() << '\n'; + Colour colour(Colour::SecondaryText); + stream << currentTestRunInfo->name + << " is a Catch v" << libraryVersion() << " host application.\n" + << "Run with -? for options\n\n"; - m_xml.scopedElement( "Original" ) - .writeText( assertionResult.getExpression() ); - m_xml.scopedElement( "Expanded" ) - .writeText( assertionResult.getExpandedExpression() ); - } + if (m_config->rngSeed() != 0) + stream << "Randomness seeded to: " << m_config->rngSeed() << "\n\n"; - // And... Print a result applicable to each result type. - switch( assertionResult.getResultType() ) { - case ResultWas::ThrewException: - m_xml.scopedElement( "Exception" ) - .writeAttribute( "filename", assertionResult.getSourceInfo().file ) - .writeAttribute( "line", assertionResult.getSourceInfo().line ) - .writeText( assertionResult.getMessage() ); - break; - case ResultWas::FatalErrorCondition: - m_xml.scopedElement( "FatalErrorCondition" ) - .writeAttribute( "filename", assertionResult.getSourceInfo().file ) - .writeAttribute( "line", assertionResult.getSourceInfo().line ) - .writeText( assertionResult.getMessage() ); - break; - case ResultWas::Info: - m_xml.scopedElement( "Info" ) - .writeText( assertionResult.getMessage() ); - break; - case ResultWas::Warning: - // Warning will already have been written - break; - case ResultWas::ExplicitFailure: - m_xml.scopedElement( "Failure" ) - .writeText( assertionResult.getMessage() ); - break; - default: - break; - } + currentTestRunInfo.used = true; +} +void ConsoleReporter::lazyPrintGroupInfo() { + if (!currentGroupInfo->name.empty() && currentGroupInfo->groupsCounts > 1) { + printClosedHeader("Group: " + currentGroupInfo->name); + currentGroupInfo.used = true; + } +} +void ConsoleReporter::printTestCaseAndSectionHeader() { + assert(!m_sectionStack.empty()); + printOpenHeader(currentTestCaseInfo->name); - if( assertionResult.hasExpression() ) - m_xml.endElement(); + if (m_sectionStack.size() > 1) { + Colour colourGuard(Colour::Headers); - return true; - } + auto + it = m_sectionStack.begin() + 1, // Skip first section (test case) + itEnd = m_sectionStack.end(); + for (; it != itEnd; ++it) + printHeaderString(it->name, 2); + } - virtual void sectionEnded( SectionStats const& sectionStats ) CATCH_OVERRIDE { - StreamingReporterBase::sectionEnded( sectionStats ); - if( --m_sectionDepth > 0 ) { - XmlWriter::ScopedElement e = m_xml.scopedElement( "OverallResults" ); - e.writeAttribute( "successes", sectionStats.assertions.passed ); - e.writeAttribute( "failures", sectionStats.assertions.failed ); - e.writeAttribute( "expectedFailures", sectionStats.assertions.failedButOk ); + SourceLineInfo lineInfo = m_sectionStack.back().lineInfo; - if ( m_config->showDurations() == ShowDurations::Always ) - e.writeAttribute( "durationInSeconds", sectionStats.durationInSeconds ); + if (!lineInfo.empty()) { + stream << getLineOfChars<'-'>() << '\n'; + Colour colourGuard(Colour::FileName); + stream << lineInfo << '\n'; + } + stream << getLineOfChars<'.'>() << '\n' << std::endl; +} - m_xml.endElement(); - } - } +void ConsoleReporter::printClosedHeader(std::string const& _name) { + printOpenHeader(_name); + stream << getLineOfChars<'.'>() << '\n'; +} +void ConsoleReporter::printOpenHeader(std::string const& _name) { + stream << getLineOfChars<'-'>() << '\n'; + { + Colour colourGuard(Colour::Headers); + printHeaderString(_name); + } +} - virtual void testCaseEnded( TestCaseStats const& testCaseStats ) CATCH_OVERRIDE { - StreamingReporterBase::testCaseEnded( testCaseStats ); - XmlWriter::ScopedElement e = m_xml.scopedElement( "OverallResult" ); - e.writeAttribute( "success", testCaseStats.totals.assertions.allOk() ); +// if string has a : in first line will set indent to follow it on +// subsequent lines +void ConsoleReporter::printHeaderString(std::string const& _string, std::size_t indent) { + std::size_t i = _string.find(": "); + if (i != std::string::npos) + i += 2; + else + i = 0; + stream << Column(_string).indent(indent + i).initialIndent(indent) << '\n'; +} - if ( m_config->showDurations() == ShowDurations::Always ) - e.writeAttribute( "durationInSeconds", m_testCaseTimer.getElapsedSeconds() ); +struct SummaryColumn { + + SummaryColumn( std::string _label, Colour::Code _colour ) + : label( std::move( _label ) ), + colour( _colour ) {} + SummaryColumn addRow( std::size_t count ) { + ReusableStringStream rss; + rss << count; + std::string row = rss.str(); + for (auto& oldRow : rows) { + while (oldRow.size() < row.size()) + oldRow = ' ' + oldRow; + while (oldRow.size() > row.size()) + row = ' ' + row; + } + rows.push_back(row); + return *this; + } - if( !testCaseStats.stdOut.empty() ) - m_xml.scopedElement( "StdOut" ).writeText( trim( testCaseStats.stdOut ), false ); - if( !testCaseStats.stdErr.empty() ) - m_xml.scopedElement( "StdErr" ).writeText( trim( testCaseStats.stdErr ), false ); + std::string label; + Colour::Code colour; + std::vector<std::string> rows; - m_xml.endElement(); - } +}; - virtual void testGroupEnded( TestGroupStats const& testGroupStats ) CATCH_OVERRIDE { - StreamingReporterBase::testGroupEnded( testGroupStats ); - // TODO: Check testGroupStats.aborting and act accordingly. - m_xml.scopedElement( "OverallResults" ) - .writeAttribute( "successes", testGroupStats.totals.assertions.passed ) - .writeAttribute( "failures", testGroupStats.totals.assertions.failed ) - .writeAttribute( "expectedFailures", testGroupStats.totals.assertions.failedButOk ); - m_xml.endElement(); +void ConsoleReporter::printTotals( Totals const& totals ) { + if (totals.testCases.total() == 0) { + stream << Colour(Colour::Warning) << "No tests ran\n"; + } else if (totals.assertions.total() > 0 && totals.testCases.allPassed()) { + stream << Colour(Colour::ResultSuccess) << "All tests passed"; + stream << " (" + << pluralise(totals.assertions.passed, "assertion") << " in " + << pluralise(totals.testCases.passed, "test case") << ')' + << '\n'; + } else { + + std::vector<SummaryColumn> columns; + columns.push_back(SummaryColumn("", Colour::None) + .addRow(totals.testCases.total()) + .addRow(totals.assertions.total())); + columns.push_back(SummaryColumn("passed", Colour::Success) + .addRow(totals.testCases.passed) + .addRow(totals.assertions.passed)); + columns.push_back(SummaryColumn("failed", Colour::ResultError) + .addRow(totals.testCases.failed) + .addRow(totals.assertions.failed)); + columns.push_back(SummaryColumn("failed as expected", Colour::ResultExpectedFailure) + .addRow(totals.testCases.failedButOk) + .addRow(totals.assertions.failedButOk)); + + printSummaryRow("test cases", columns, 0); + printSummaryRow("assertions", columns, 1); + } +} +void ConsoleReporter::printSummaryRow(std::string const& label, std::vector<SummaryColumn> const& cols, std::size_t row) { + for (auto col : cols) { + std::string value = col.rows[row]; + if (col.label.empty()) { + stream << label << ": "; + if (value != "0") + stream << value; + else + stream << Colour(Colour::Warning) << "- none -"; + } else if (value != "0") { + stream << Colour(Colour::LightGrey) << " | "; + stream << Colour(col.colour) + << value << ' ' << col.label; } + } + stream << '\n'; +} - virtual void testRunEnded( TestRunStats const& testRunStats ) CATCH_OVERRIDE { - StreamingReporterBase::testRunEnded( testRunStats ); - m_xml.scopedElement( "OverallResults" ) - .writeAttribute( "successes", testRunStats.totals.assertions.passed ) - .writeAttribute( "failures", testRunStats.totals.assertions.failed ) - .writeAttribute( "expectedFailures", testRunStats.totals.assertions.failedButOk ); - m_xml.endElement(); - } +void ConsoleReporter::printTotalsDivider(Totals const& totals) { + if (totals.testCases.total() > 0) { + std::size_t failedRatio = makeRatio(totals.testCases.failed, totals.testCases.total()); + std::size_t failedButOkRatio = makeRatio(totals.testCases.failedButOk, totals.testCases.total()); + std::size_t passedRatio = makeRatio(totals.testCases.passed, totals.testCases.total()); + while (failedRatio + failedButOkRatio + passedRatio < CATCH_CONFIG_CONSOLE_WIDTH - 1) + findMax(failedRatio, failedButOkRatio, passedRatio)++; + while (failedRatio + failedButOkRatio + passedRatio > CATCH_CONFIG_CONSOLE_WIDTH - 1) + findMax(failedRatio, failedButOkRatio, passedRatio)--; + + stream << Colour(Colour::Error) << std::string(failedRatio, '='); + stream << Colour(Colour::ResultExpectedFailure) << std::string(failedButOkRatio, '='); + if (totals.testCases.allPassed()) + stream << Colour(Colour::ResultSuccess) << std::string(passedRatio, '='); + else + stream << Colour(Colour::Success) << std::string(passedRatio, '='); + } else { + stream << Colour(Colour::Warning) << std::string(CATCH_CONFIG_CONSOLE_WIDTH - 1, '='); + } + stream << '\n'; +} +void ConsoleReporter::printSummaryDivider() { + stream << getLineOfChars<'-'>() << '\n'; +} - private: - Timer m_testCaseTimer; - XmlWriter m_xml; - int m_sectionDepth; - }; +void ConsoleReporter::printTestFilters() { + if (m_config->testSpec().hasFilters()) + stream << Colour(Colour::BrightYellow) << "Filters: " << serializeFilters( m_config->getTestsOrTags() ) << '\n'; +} - INTERNAL_CATCH_REGISTER_REPORTER( "xml", XmlReporter ) +CATCH_REGISTER_REPORTER("console", ConsoleReporter) } // end namespace Catch -// #included from: ../reporters/catch_reporter_junit.hpp -#define TWOBLUECUBES_CATCH_REPORTER_JUNIT_HPP_INCLUDED +#if defined(_MSC_VER) +#pragma warning(pop) +#endif -#include <assert.h> +#if defined(__clang__) +# pragma clang diagnostic pop +#endif +// end catch_reporter_console.cpp +// start catch_reporter_junit.cpp + +#include <cassert> +#include <sstream> +#include <ctime> +#include <algorithm> namespace Catch { @@ -9867,7 +15759,7 @@ namespace Catch { // Also, UTC only, again because of backward compatibility (%z is C++11) time_t rawtime; std::time(&rawtime); - const size_t timeStampSize = sizeof("2017-01-16T17:06:45Z"); + auto const timeStampSize = sizeof("2017-01-16T17:06:45Z"); #ifdef _MSC_VER std::tm timeInfo = {}; @@ -9888,1009 +15780,655 @@ namespace Catch { return std::string(timeStamp); } - } + std::string fileNameTag(const std::vector<std::string> &tags) { + auto it = std::find_if(begin(tags), + end(tags), + [] (std::string const& tag) {return tag.front() == '#'; }); + if (it != tags.end()) + return it->substr(1); + return std::string(); + } + } // anonymous namespace - class JunitReporter : public CumulativeReporterBase { - public: - JunitReporter( ReporterConfig const& _config ) + JunitReporter::JunitReporter( ReporterConfig const& _config ) : CumulativeReporterBase( _config ), xml( _config.stream() ) { m_reporterPrefs.shouldRedirectStdOut = true; + m_reporterPrefs.shouldReportAllAssertions = true; } - virtual ~JunitReporter() CATCH_OVERRIDE; + JunitReporter::~JunitReporter() {} - static std::string getDescription() { - return "Reports test results in an XML format that looks like Ant's junitreport target"; - } + std::string JunitReporter::getDescription() { + return "Reports test results in an XML format that looks like Ant's junitreport target"; + } - virtual void noMatchingTestCases( std::string const& /*spec*/ ) CATCH_OVERRIDE {} + void JunitReporter::noMatchingTestCases( std::string const& /*spec*/ ) {} - virtual void testRunStarting( TestRunInfo const& runInfo ) CATCH_OVERRIDE { - CumulativeReporterBase::testRunStarting( runInfo ); - xml.startElement( "testsuites" ); - } + void JunitReporter::testRunStarting( TestRunInfo const& runInfo ) { + CumulativeReporterBase::testRunStarting( runInfo ); + xml.startElement( "testsuites" ); + } - virtual void testGroupStarting( GroupInfo const& groupInfo ) CATCH_OVERRIDE { - suiteTimer.start(); - stdOutForSuite.str(""); - stdErrForSuite.str(""); - unexpectedExceptions = 0; - CumulativeReporterBase::testGroupStarting( groupInfo ); - } + void JunitReporter::testGroupStarting( GroupInfo const& groupInfo ) { + suiteTimer.start(); + stdOutForSuite.clear(); + stdErrForSuite.clear(); + unexpectedExceptions = 0; + CumulativeReporterBase::testGroupStarting( groupInfo ); + } - virtual bool assertionEnded( AssertionStats const& assertionStats ) CATCH_OVERRIDE { - if( assertionStats.assertionResult.getResultType() == ResultWas::ThrewException ) - unexpectedExceptions++; - return CumulativeReporterBase::assertionEnded( assertionStats ); - } + void JunitReporter::testCaseStarting( TestCaseInfo const& testCaseInfo ) { + m_okToFail = testCaseInfo.okToFail(); + } - virtual void testCaseEnded( TestCaseStats const& testCaseStats ) CATCH_OVERRIDE { - stdOutForSuite << testCaseStats.stdOut; - stdErrForSuite << testCaseStats.stdErr; - CumulativeReporterBase::testCaseEnded( testCaseStats ); - } + bool JunitReporter::assertionEnded( AssertionStats const& assertionStats ) { + if( assertionStats.assertionResult.getResultType() == ResultWas::ThrewException && !m_okToFail ) + unexpectedExceptions++; + return CumulativeReporterBase::assertionEnded( assertionStats ); + } - virtual void testGroupEnded( TestGroupStats const& testGroupStats ) CATCH_OVERRIDE { - double suiteTime = suiteTimer.getElapsedSeconds(); - CumulativeReporterBase::testGroupEnded( testGroupStats ); - writeGroup( *m_testGroups.back(), suiteTime ); - } + void JunitReporter::testCaseEnded( TestCaseStats const& testCaseStats ) { + stdOutForSuite += testCaseStats.stdOut; + stdErrForSuite += testCaseStats.stdErr; + CumulativeReporterBase::testCaseEnded( testCaseStats ); + } - virtual void testRunEndedCumulative() CATCH_OVERRIDE { - xml.endElement(); - } + void JunitReporter::testGroupEnded( TestGroupStats const& testGroupStats ) { + double suiteTime = suiteTimer.getElapsedSeconds(); + CumulativeReporterBase::testGroupEnded( testGroupStats ); + writeGroup( *m_testGroups.back(), suiteTime ); + } - void writeGroup( TestGroupNode const& groupNode, double suiteTime ) { - XmlWriter::ScopedElement e = xml.scopedElement( "testsuite" ); - TestGroupStats const& stats = groupNode.value; - xml.writeAttribute( "name", stats.groupInfo.name ); - xml.writeAttribute( "errors", unexpectedExceptions ); - xml.writeAttribute( "failures", stats.totals.assertions.failed-unexpectedExceptions ); - xml.writeAttribute( "tests", stats.totals.assertions.total() ); - xml.writeAttribute( "hostname", "tbd" ); // !TBD - if( m_config->showDurations() == ShowDurations::Never ) - xml.writeAttribute( "time", "" ); - else - xml.writeAttribute( "time", suiteTime ); - xml.writeAttribute( "timestamp", getCurrentTimestamp() ); + void JunitReporter::testRunEndedCumulative() { + xml.endElement(); + } - // Write test cases - for( TestGroupNode::ChildNodes::const_iterator - it = groupNode.children.begin(), itEnd = groupNode.children.end(); - it != itEnd; - ++it ) - writeTestCase( **it ); + void JunitReporter::writeGroup( TestGroupNode const& groupNode, double suiteTime ) { + XmlWriter::ScopedElement e = xml.scopedElement( "testsuite" ); - xml.scopedElement( "system-out" ).writeText( trim( stdOutForSuite.str() ), false ); - xml.scopedElement( "system-err" ).writeText( trim( stdErrForSuite.str() ), false ); + TestGroupStats const& stats = groupNode.value; + xml.writeAttribute( "name", stats.groupInfo.name ); + xml.writeAttribute( "errors", unexpectedExceptions ); + xml.writeAttribute( "failures", stats.totals.assertions.failed-unexpectedExceptions ); + xml.writeAttribute( "tests", stats.totals.assertions.total() ); + xml.writeAttribute( "hostname", "tbd" ); // !TBD + if( m_config->showDurations() == ShowDurations::Never ) + xml.writeAttribute( "time", "" ); + else + xml.writeAttribute( "time", suiteTime ); + xml.writeAttribute( "timestamp", getCurrentTimestamp() ); + + // Write properties if there are any + if (m_config->hasTestFilters() || m_config->rngSeed() != 0) { + auto properties = xml.scopedElement("properties"); + if (m_config->hasTestFilters()) { + xml.scopedElement("property") + .writeAttribute("name", "filters") + .writeAttribute("value", serializeFilters(m_config->getTestsOrTags())); + } + if (m_config->rngSeed() != 0) { + xml.scopedElement("property") + .writeAttribute("name", "random-seed") + .writeAttribute("value", m_config->rngSeed()); + } } - void writeTestCase( TestCaseNode const& testCaseNode ) { - TestCaseStats const& stats = testCaseNode.value; + // Write test cases + for( auto const& child : groupNode.children ) + writeTestCase( *child ); - // All test cases have exactly one section - which represents the - // test case itself. That section may have 0-n nested sections - assert( testCaseNode.children.size() == 1 ); - SectionNode const& rootSection = *testCaseNode.children.front(); - - std::string className = stats.testInfo.className; - - if( className.empty() ) { - if( rootSection.childSections.empty() ) - className = "global"; - } - writeSection( className, "", rootSection ); - } - - void writeSection( std::string const& className, - std::string const& rootName, - SectionNode const& sectionNode ) { - std::string name = trim( sectionNode.stats.sectionInfo.name ); - if( !rootName.empty() ) - name = rootName + '/' + name; - - if( !sectionNode.assertions.empty() || - !sectionNode.stdOut.empty() || - !sectionNode.stdErr.empty() ) { - XmlWriter::ScopedElement e = xml.scopedElement( "testcase" ); - if( className.empty() ) { - xml.writeAttribute( "classname", name ); - xml.writeAttribute( "name", "root" ); - } - else { - xml.writeAttribute( "classname", className ); - xml.writeAttribute( "name", name ); - } - xml.writeAttribute( "time", Catch::toString( sectionNode.stats.durationInSeconds ) ); - - writeAssertions( sectionNode ); - - if( !sectionNode.stdOut.empty() ) - xml.scopedElement( "system-out" ).writeText( trim( sectionNode.stdOut ), false ); - if( !sectionNode.stdErr.empty() ) - xml.scopedElement( "system-err" ).writeText( trim( sectionNode.stdErr ), false ); - } - for( SectionNode::ChildSections::const_iterator - it = sectionNode.childSections.begin(), - itEnd = sectionNode.childSections.end(); - it != itEnd; - ++it ) - if( className.empty() ) - writeSection( name, "", **it ); - else - writeSection( className, name, **it ); - } - - void writeAssertions( SectionNode const& sectionNode ) { - for( SectionNode::Assertions::const_iterator - it = sectionNode.assertions.begin(), itEnd = sectionNode.assertions.end(); - it != itEnd; - ++it ) - writeAssertion( *it ); - } - void writeAssertion( AssertionStats const& stats ) { - AssertionResult const& result = stats.assertionResult; - if( !result.isOk() ) { - std::string elementName; - switch( result.getResultType() ) { - case ResultWas::ThrewException: - case ResultWas::FatalErrorCondition: - elementName = "error"; - break; - case ResultWas::ExplicitFailure: - elementName = "failure"; - break; - case ResultWas::ExpressionFailed: - elementName = "failure"; - break; - case ResultWas::DidntThrowException: - elementName = "failure"; - break; - - // We should never see these here: - case ResultWas::Info: - case ResultWas::Warning: - case ResultWas::Ok: - case ResultWas::Unknown: - case ResultWas::FailureBit: - case ResultWas::Exception: - elementName = "internalError"; - break; - } + xml.scopedElement( "system-out" ).writeText( trim( stdOutForSuite ), false ); + xml.scopedElement( "system-err" ).writeText( trim( stdErrForSuite ), false ); + } - XmlWriter::ScopedElement e = xml.scopedElement( elementName ); + void JunitReporter::writeTestCase( TestCaseNode const& testCaseNode ) { + TestCaseStats const& stats = testCaseNode.value; - xml.writeAttribute( "message", result.getExpandedExpression() ); - xml.writeAttribute( "type", result.getTestMacroName() ); + // All test cases have exactly one section - which represents the + // test case itself. That section may have 0-n nested sections + assert( testCaseNode.children.size() == 1 ); + SectionNode const& rootSection = *testCaseNode.children.front(); - std::ostringstream oss; - if( !result.getMessage().empty() ) - oss << result.getMessage() << '\n'; - for( std::vector<MessageInfo>::const_iterator - it = stats.infoMessages.begin(), - itEnd = stats.infoMessages.end(); - it != itEnd; - ++it ) - if( it->type == ResultWas::Info ) - oss << it->message << '\n'; + std::string className = stats.testInfo.className; - oss << "at " << result.getSourceInfo(); - xml.writeText( oss.str(), false ); - } + if( className.empty() ) { + className = fileNameTag(stats.testInfo.tags); + if ( className.empty() ) + className = "global"; } - XmlWriter xml; - Timer suiteTimer; - std::ostringstream stdOutForSuite; - std::ostringstream stdErrForSuite; - unsigned int unexpectedExceptions; - }; - - INTERNAL_CATCH_REGISTER_REPORTER( "junit", JunitReporter ) + if ( !m_config->name().empty() ) + className = m_config->name() + "." + className; -} // end namespace Catch + writeSection( className, "", rootSection ); + } -// #included from: ../reporters/catch_reporter_console.hpp -#define TWOBLUECUBES_CATCH_REPORTER_CONSOLE_HPP_INCLUDED + void JunitReporter::writeSection( std::string const& className, + std::string const& rootName, + SectionNode const& sectionNode ) { + std::string name = trim( sectionNode.stats.sectionInfo.name ); + if( !rootName.empty() ) + name = rootName + '/' + name; -namespace Catch { + if( !sectionNode.assertions.empty() || + !sectionNode.stdOut.empty() || + !sectionNode.stdErr.empty() ) { + XmlWriter::ScopedElement e = xml.scopedElement( "testcase" ); + if( className.empty() ) { + xml.writeAttribute( "classname", name ); + xml.writeAttribute( "name", "root" ); + } + else { + xml.writeAttribute( "classname", className ); + xml.writeAttribute( "name", name ); + } + xml.writeAttribute( "time", ::Catch::Detail::stringify( sectionNode.stats.durationInSeconds ) ); - struct ConsoleReporter : StreamingReporterBase { - ConsoleReporter( ReporterConfig const& _config ) - : StreamingReporterBase( _config ), - m_headerPrinted( false ) - {} + writeAssertions( sectionNode ); - virtual ~ConsoleReporter() CATCH_OVERRIDE; - static std::string getDescription() { - return "Reports test results as plain lines of text"; + if( !sectionNode.stdOut.empty() ) + xml.scopedElement( "system-out" ).writeText( trim( sectionNode.stdOut ), false ); + if( !sectionNode.stdErr.empty() ) + xml.scopedElement( "system-err" ).writeText( trim( sectionNode.stdErr ), false ); } + for( auto const& childNode : sectionNode.childSections ) + if( className.empty() ) + writeSection( name, "", *childNode ); + else + writeSection( className, name, *childNode ); + } - virtual void noMatchingTestCases( std::string const& spec ) CATCH_OVERRIDE { - stream << "No test cases matched '" << spec << '\'' << std::endl; - } + void JunitReporter::writeAssertions( SectionNode const& sectionNode ) { + for( auto const& assertion : sectionNode.assertions ) + writeAssertion( assertion ); + } - virtual void assertionStarting( AssertionInfo const& ) CATCH_OVERRIDE { - } + void JunitReporter::writeAssertion( AssertionStats const& stats ) { + AssertionResult const& result = stats.assertionResult; + if( !result.isOk() ) { + std::string elementName; + switch( result.getResultType() ) { + case ResultWas::ThrewException: + case ResultWas::FatalErrorCondition: + elementName = "error"; + break; + case ResultWas::ExplicitFailure: + elementName = "failure"; + break; + case ResultWas::ExpressionFailed: + elementName = "failure"; + break; + case ResultWas::DidntThrowException: + elementName = "failure"; + break; - virtual bool assertionEnded( AssertionStats const& _assertionStats ) CATCH_OVERRIDE { - AssertionResult const& result = _assertionStats.assertionResult; + // We should never see these here: + case ResultWas::Info: + case ResultWas::Warning: + case ResultWas::Ok: + case ResultWas::Unknown: + case ResultWas::FailureBit: + case ResultWas::Exception: + elementName = "internalError"; + break; + } - bool printInfoMessages = true; + XmlWriter::ScopedElement e = xml.scopedElement( elementName ); - // Drop out if result was successful and we're not printing those - if( !m_config->includeSuccessfulResults() && result.isOk() ) { - if( result.getResultType() != ResultWas::Warning ) - return false; - printInfoMessages = false; - } + xml.writeAttribute( "message", result.getExpandedExpression() ); + xml.writeAttribute( "type", result.getTestMacroName() ); - lazyPrint(); + ReusableStringStream rss; + if( !result.getMessage().empty() ) + rss << result.getMessage() << '\n'; + for( auto const& msg : stats.infoMessages ) + if( msg.type == ResultWas::Info ) + rss << msg.message << '\n'; - AssertionPrinter printer( stream, _assertionStats, printInfoMessages ); - printer.print(); - stream << std::endl; - return true; + rss << "at " << result.getSourceInfo(); + xml.writeText( rss.str(), false ); } + } - virtual void sectionStarting( SectionInfo const& _sectionInfo ) CATCH_OVERRIDE { - m_headerPrinted = false; - StreamingReporterBase::sectionStarting( _sectionInfo ); - } - virtual void sectionEnded( SectionStats const& _sectionStats ) CATCH_OVERRIDE { - if( _sectionStats.missingAssertions ) { - lazyPrint(); - Colour colour( Colour::ResultError ); - if( m_sectionStack.size() > 1 ) - stream << "\nNo assertions in section"; - else - stream << "\nNo assertions in test case"; - stream << " '" << _sectionStats.sectionInfo.name << "'\n" << std::endl; - } - if( m_headerPrinted ) { - if( m_config->showDurations() == ShowDurations::Always ) - stream << "Completed in " << _sectionStats.durationInSeconds << 's' << std::endl; - m_headerPrinted = false; - } - else { - if( m_config->showDurations() == ShowDurations::Always ) - stream << _sectionStats.sectionInfo.name << " completed in " << _sectionStats.durationInSeconds << 's' << std::endl; - } - StreamingReporterBase::sectionEnded( _sectionStats ); - } + CATCH_REGISTER_REPORTER( "junit", JunitReporter ) - virtual void testCaseEnded( TestCaseStats const& _testCaseStats ) CATCH_OVERRIDE { - StreamingReporterBase::testCaseEnded( _testCaseStats ); - m_headerPrinted = false; - } - virtual void testGroupEnded( TestGroupStats const& _testGroupStats ) CATCH_OVERRIDE { - if( currentGroupInfo.used ) { - printSummaryDivider(); - stream << "Summary for group '" << _testGroupStats.groupInfo.name << "':\n"; - printTotals( _testGroupStats.totals ); - stream << '\n' << std::endl; - } - StreamingReporterBase::testGroupEnded( _testGroupStats ); - } - virtual void testRunEnded( TestRunStats const& _testRunStats ) CATCH_OVERRIDE { - printTotalsDivider( _testRunStats.totals ); - printTotals( _testRunStats.totals ); - stream << std::endl; - StreamingReporterBase::testRunEnded( _testRunStats ); - } +} // end namespace Catch +// end catch_reporter_junit.cpp +// start catch_reporter_listening.cpp - private: +#include <cassert> - class AssertionPrinter { - void operator= ( AssertionPrinter const& ); - public: - AssertionPrinter( std::ostream& _stream, AssertionStats const& _stats, bool _printInfoMessages ) - : stream( _stream ), - stats( _stats ), - result( _stats.assertionResult ), - colour( Colour::None ), - message( result.getMessage() ), - messages( _stats.infoMessages ), - printInfoMessages( _printInfoMessages ) - { - switch( result.getResultType() ) { - case ResultWas::Ok: - colour = Colour::Success; - passOrFail = "PASSED"; - //if( result.hasMessage() ) - if( _stats.infoMessages.size() == 1 ) - messageLabel = "with message"; - if( _stats.infoMessages.size() > 1 ) - messageLabel = "with messages"; - break; - case ResultWas::ExpressionFailed: - if( result.isOk() ) { - colour = Colour::Success; - passOrFail = "FAILED - but was ok"; - } - else { - colour = Colour::Error; - passOrFail = "FAILED"; - } - if( _stats.infoMessages.size() == 1 ) - messageLabel = "with message"; - if( _stats.infoMessages.size() > 1 ) - messageLabel = "with messages"; - break; - case ResultWas::ThrewException: - colour = Colour::Error; - passOrFail = "FAILED"; - messageLabel = "due to unexpected exception with message"; - break; - case ResultWas::FatalErrorCondition: - colour = Colour::Error; - passOrFail = "FAILED"; - messageLabel = "due to a fatal error condition"; - break; - case ResultWas::DidntThrowException: - colour = Colour::Error; - passOrFail = "FAILED"; - messageLabel = "because no exception was thrown where one was expected"; - break; - case ResultWas::Info: - messageLabel = "info"; - break; - case ResultWas::Warning: - messageLabel = "warning"; - break; - case ResultWas::ExplicitFailure: - passOrFail = "FAILED"; - colour = Colour::Error; - if( _stats.infoMessages.size() == 1 ) - messageLabel = "explicitly with message"; - if( _stats.infoMessages.size() > 1 ) - messageLabel = "explicitly with messages"; - break; - // These cases are here to prevent compiler warnings - case ResultWas::Unknown: - case ResultWas::FailureBit: - case ResultWas::Exception: - passOrFail = "** internal error **"; - colour = Colour::Error; - break; - } - } +namespace Catch { - void print() const { - printSourceInfo(); - if( stats.totals.assertions.total() > 0 ) { - if( result.isOk() ) - stream << '\n'; - printResultType(); - printOriginalExpression(); - printReconstructedExpression(); - } - else { - stream << '\n'; - } - printMessage(); - } + ListeningReporter::ListeningReporter() { + // We will assume that listeners will always want all assertions + m_preferences.shouldReportAllAssertions = true; + } - private: - void printResultType() const { - if( !passOrFail.empty() ) { - Colour colourGuard( colour ); - stream << passOrFail << ":\n"; - } - } - void printOriginalExpression() const { - if( result.hasExpression() ) { - Colour colourGuard( Colour::OriginalExpression ); - stream << " "; - stream << result.getExpressionInMacro(); - stream << '\n'; - } - } - void printReconstructedExpression() const { - if( result.hasExpandedExpression() ) { - stream << "with expansion:\n"; - Colour colourGuard( Colour::ReconstructedExpression ); - stream << Text( result.getExpandedExpression(), TextAttributes().setIndent(2) ) << '\n'; - } - } - void printMessage() const { - if( !messageLabel.empty() ) - stream << messageLabel << ':' << '\n'; - for( std::vector<MessageInfo>::const_iterator it = messages.begin(), itEnd = messages.end(); - it != itEnd; - ++it ) { - // If this assertion is a warning ignore any INFO messages - if( printInfoMessages || it->type != ResultWas::Info ) - stream << Text( it->message, TextAttributes().setIndent(2) ) << '\n'; - } - } - void printSourceInfo() const { - Colour colourGuard( Colour::FileName ); - stream << result.getSourceInfo() << ": "; - } + void ListeningReporter::addListener( IStreamingReporterPtr&& listener ) { + m_listeners.push_back( std::move( listener ) ); + } - std::ostream& stream; - AssertionStats const& stats; - AssertionResult const& result; - Colour::Code colour; - std::string passOrFail; - std::string messageLabel; - std::string message; - std::vector<MessageInfo> messages; - bool printInfoMessages; - }; + void ListeningReporter::addReporter(IStreamingReporterPtr&& reporter) { + assert(!m_reporter && "Listening reporter can wrap only 1 real reporter"); + m_reporter = std::move( reporter ); + m_preferences.shouldRedirectStdOut = m_reporter->getPreferences().shouldRedirectStdOut; + } - void lazyPrint() { + ReporterPreferences ListeningReporter::getPreferences() const { + return m_preferences; + } - if( !currentTestRunInfo.used ) - lazyPrintRunInfo(); - if( !currentGroupInfo.used ) - lazyPrintGroupInfo(); + std::set<Verbosity> ListeningReporter::getSupportedVerbosities() { + return std::set<Verbosity>{ }; + } - if( !m_headerPrinted ) { - printTestCaseAndSectionHeader(); - m_headerPrinted = true; - } + void ListeningReporter::noMatchingTestCases( std::string const& spec ) { + for ( auto const& listener : m_listeners ) { + listener->noMatchingTestCases( spec ); } - void lazyPrintRunInfo() { - stream << '\n' << getLineOfChars<'~'>() << '\n'; - Colour colour( Colour::SecondaryText ); - stream << currentTestRunInfo->name - << " is a Catch v" << libraryVersion << " host application.\n" - << "Run with -? for options\n\n"; - - if( m_config->rngSeed() != 0 ) - stream << "Randomness seeded to: " << m_config->rngSeed() << "\n\n"; + m_reporter->noMatchingTestCases( spec ); + } - currentTestRunInfo.used = true; +#if defined(CATCH_CONFIG_ENABLE_BENCHMARKING) + void ListeningReporter::benchmarkPreparing( std::string const& name ) { + for (auto const& listener : m_listeners) { + listener->benchmarkPreparing(name); + } + m_reporter->benchmarkPreparing(name); + } + void ListeningReporter::benchmarkStarting( BenchmarkInfo const& benchmarkInfo ) { + for ( auto const& listener : m_listeners ) { + listener->benchmarkStarting( benchmarkInfo ); } - void lazyPrintGroupInfo() { - if( !currentGroupInfo->name.empty() && currentGroupInfo->groupsCounts > 1 ) { - printClosedHeader( "Group: " + currentGroupInfo->name ); - currentGroupInfo.used = true; - } + m_reporter->benchmarkStarting( benchmarkInfo ); + } + void ListeningReporter::benchmarkEnded( BenchmarkStats<> const& benchmarkStats ) { + for ( auto const& listener : m_listeners ) { + listener->benchmarkEnded( benchmarkStats ); } - void printTestCaseAndSectionHeader() { - assert( !m_sectionStack.empty() ); - printOpenHeader( currentTestCaseInfo->name ); - - if( m_sectionStack.size() > 1 ) { - Colour colourGuard( Colour::Headers ); - - std::vector<SectionInfo>::const_iterator - it = m_sectionStack.begin()+1, // Skip first section (test case) - itEnd = m_sectionStack.end(); - for( ; it != itEnd; ++it ) - printHeaderString( it->name, 2 ); - } + m_reporter->benchmarkEnded( benchmarkStats ); + } - SourceLineInfo lineInfo = m_sectionStack.back().lineInfo; + void ListeningReporter::benchmarkFailed( std::string const& error ) { + for (auto const& listener : m_listeners) { + listener->benchmarkFailed(error); + } + m_reporter->benchmarkFailed(error); + } +#endif // CATCH_CONFIG_ENABLE_BENCHMARKING - if( !lineInfo.empty() ){ - stream << getLineOfChars<'-'>() << '\n'; - Colour colourGuard( Colour::FileName ); - stream << lineInfo << '\n'; - } - stream << getLineOfChars<'.'>() << '\n' << std::endl; + void ListeningReporter::testRunStarting( TestRunInfo const& testRunInfo ) { + for ( auto const& listener : m_listeners ) { + listener->testRunStarting( testRunInfo ); } + m_reporter->testRunStarting( testRunInfo ); + } - void printClosedHeader( std::string const& _name ) { - printOpenHeader( _name ); - stream << getLineOfChars<'.'>() << '\n'; - } - void printOpenHeader( std::string const& _name ) { - stream << getLineOfChars<'-'>() << '\n'; - { - Colour colourGuard( Colour::Headers ); - printHeaderString( _name ); - } + void ListeningReporter::testGroupStarting( GroupInfo const& groupInfo ) { + for ( auto const& listener : m_listeners ) { + listener->testGroupStarting( groupInfo ); } + m_reporter->testGroupStarting( groupInfo ); + } - // 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 ) { - std::size_t i = _string.find( ": " ); - if( i != std::string::npos ) - i+=2; - else - i = 0; - stream << Text( _string, TextAttributes() - .setIndent( indent+i) - .setInitialIndent( indent ) ) << '\n'; + void ListeningReporter::testCaseStarting( TestCaseInfo const& testInfo ) { + for ( auto const& listener : m_listeners ) { + listener->testCaseStarting( testInfo ); } + m_reporter->testCaseStarting( testInfo ); + } - struct SummaryColumn { - - SummaryColumn( std::string const& _label, Colour::Code _colour ) - : label( _label ), - colour( _colour ) - {} - SummaryColumn addRow( std::size_t count ) { - std::ostringstream oss; - oss << count; - std::string row = oss.str(); - for( std::vector<std::string>::iterator it = rows.begin(); it != rows.end(); ++it ) { - while( it->size() < row.size() ) - *it = ' ' + *it; - while( it->size() > row.size() ) - row = ' ' + row; - } - rows.push_back( row ); - return *this; - } - - std::string label; - Colour::Code colour; - std::vector<std::string> rows; + void ListeningReporter::sectionStarting( SectionInfo const& sectionInfo ) { + for ( auto const& listener : m_listeners ) { + listener->sectionStarting( sectionInfo ); + } + m_reporter->sectionStarting( sectionInfo ); + } - }; + void ListeningReporter::assertionStarting( AssertionInfo const& assertionInfo ) { + for ( auto const& listener : m_listeners ) { + listener->assertionStarting( assertionInfo ); + } + m_reporter->assertionStarting( assertionInfo ); + } - void printTotals( Totals const& totals ) { - if( totals.testCases.total() == 0 ) { - stream << Colour( Colour::Warning ) << "No tests ran\n"; - } - else if( totals.assertions.total() > 0 && totals.testCases.allPassed() ) { - stream << Colour( Colour::ResultSuccess ) << "All tests passed"; - stream << " (" - << pluralise( totals.assertions.passed, "assertion" ) << " in " - << pluralise( totals.testCases.passed, "test case" ) << ')' - << '\n'; - } - else { + // The return value indicates if the messages buffer should be cleared: + bool ListeningReporter::assertionEnded( AssertionStats const& assertionStats ) { + for( auto const& listener : m_listeners ) { + static_cast<void>( listener->assertionEnded( assertionStats ) ); + } + return m_reporter->assertionEnded( assertionStats ); + } - std::vector<SummaryColumn> columns; - columns.push_back( SummaryColumn( "", Colour::None ) - .addRow( totals.testCases.total() ) - .addRow( totals.assertions.total() ) ); - columns.push_back( SummaryColumn( "passed", Colour::Success ) - .addRow( totals.testCases.passed ) - .addRow( totals.assertions.passed ) ); - columns.push_back( SummaryColumn( "failed", Colour::ResultError ) - .addRow( totals.testCases.failed ) - .addRow( totals.assertions.failed ) ); - columns.push_back( SummaryColumn( "failed as expected", Colour::ResultExpectedFailure ) - .addRow( totals.testCases.failedButOk ) - .addRow( totals.assertions.failedButOk ) ); - - printSummaryRow( "test cases", columns, 0 ); - printSummaryRow( "assertions", columns, 1 ); - } - } - void printSummaryRow( std::string const& label, std::vector<SummaryColumn> const& cols, std::size_t row ) { - for( std::vector<SummaryColumn>::const_iterator it = cols.begin(); it != cols.end(); ++it ) { - std::string value = it->rows[row]; - if( it->label.empty() ) { - stream << label << ": "; - if( value != "0" ) - stream << value; - else - stream << Colour( Colour::Warning ) << "- none -"; - } - else if( value != "0" ) { - stream << Colour( Colour::LightGrey ) << " | "; - stream << Colour( it->colour ) - << value << ' ' << it->label; - } - } - stream << '\n'; + void ListeningReporter::sectionEnded( SectionStats const& sectionStats ) { + for ( auto const& listener : m_listeners ) { + listener->sectionEnded( sectionStats ); } + m_reporter->sectionEnded( sectionStats ); + } - static std::size_t makeRatio( std::size_t number, std::size_t total ) { - std::size_t ratio = total > 0 ? CATCH_CONFIG_CONSOLE_WIDTH * number/ total : 0; - return ( ratio == 0 && number > 0 ) ? 1 : ratio; + void ListeningReporter::testCaseEnded( TestCaseStats const& testCaseStats ) { + for ( auto const& listener : m_listeners ) { + listener->testCaseEnded( testCaseStats ); } - static std::size_t& findMax( std::size_t& i, std::size_t& j, std::size_t& k ) { - if( i > j && i > k ) - return i; - else if( j > k ) - return j; - else - return k; - } - - void printTotalsDivider( Totals const& totals ) { - if( totals.testCases.total() > 0 ) { - std::size_t failedRatio = makeRatio( totals.testCases.failed, totals.testCases.total() ); - std::size_t failedButOkRatio = makeRatio( totals.testCases.failedButOk, totals.testCases.total() ); - std::size_t passedRatio = makeRatio( totals.testCases.passed, totals.testCases.total() ); - while( failedRatio + failedButOkRatio + passedRatio < CATCH_CONFIG_CONSOLE_WIDTH-1 ) - findMax( failedRatio, failedButOkRatio, passedRatio )++; - while( failedRatio + failedButOkRatio + passedRatio > CATCH_CONFIG_CONSOLE_WIDTH-1 ) - findMax( failedRatio, failedButOkRatio, passedRatio )--; - - stream << Colour( Colour::Error ) << std::string( failedRatio, '=' ); - stream << Colour( Colour::ResultExpectedFailure ) << std::string( failedButOkRatio, '=' ); - if( totals.testCases.allPassed() ) - stream << Colour( Colour::ResultSuccess ) << std::string( passedRatio, '=' ); - else - stream << Colour( Colour::Success ) << std::string( passedRatio, '=' ); - } - else { - stream << Colour( Colour::Warning ) << std::string( CATCH_CONFIG_CONSOLE_WIDTH-1, '=' ); - } - stream << '\n'; + m_reporter->testCaseEnded( testCaseStats ); + } + + void ListeningReporter::testGroupEnded( TestGroupStats const& testGroupStats ) { + for ( auto const& listener : m_listeners ) { + listener->testGroupEnded( testGroupStats ); } - void printSummaryDivider() { - stream << getLineOfChars<'-'>() << '\n'; + m_reporter->testGroupEnded( testGroupStats ); + } + + void ListeningReporter::testRunEnded( TestRunStats const& testRunStats ) { + for ( auto const& listener : m_listeners ) { + listener->testRunEnded( testRunStats ); } + m_reporter->testRunEnded( testRunStats ); + } - private: - bool m_headerPrinted; - }; + void ListeningReporter::skipTest( TestCaseInfo const& testInfo ) { + for ( auto const& listener : m_listeners ) { + listener->skipTest( testInfo ); + } + m_reporter->skipTest( testInfo ); + } - INTERNAL_CATCH_REGISTER_REPORTER( "console", ConsoleReporter ) + bool ListeningReporter::isMulti() const { + return true; + } } // end namespace Catch +// end catch_reporter_listening.cpp +// start catch_reporter_xml.cpp -// #included from: ../reporters/catch_reporter_compact.hpp -#define TWOBLUECUBES_CATCH_REPORTER_COMPACT_HPP_INCLUDED +#if defined(_MSC_VER) +#pragma warning(push) +#pragma warning(disable:4061) // Not all labels are EXPLICITLY handled in switch + // Note that 4062 (not all labels are handled + // and default is missing) is enabled +#endif namespace Catch { + XmlReporter::XmlReporter( ReporterConfig const& _config ) + : StreamingReporterBase( _config ), + m_xml(_config.stream()) + { + m_reporterPrefs.shouldRedirectStdOut = true; + m_reporterPrefs.shouldReportAllAssertions = true; + } - struct CompactReporter : StreamingReporterBase { - - CompactReporter( ReporterConfig const& _config ) - : StreamingReporterBase( _config ) - {} - - virtual ~CompactReporter(); + XmlReporter::~XmlReporter() = default; - static std::string getDescription() { - return "Reports test results on a single line, suitable for IDEs"; - } + std::string XmlReporter::getDescription() { + return "Reports test results as an XML document"; + } - virtual ReporterPreferences getPreferences() const { - ReporterPreferences prefs; - prefs.shouldRedirectStdOut = false; - return prefs; - } + std::string XmlReporter::getStylesheetRef() const { + return std::string(); + } - virtual void noMatchingTestCases( std::string const& spec ) { - stream << "No test cases matched '" << spec << '\'' << std::endl; - } + void XmlReporter::writeSourceInfo( SourceLineInfo const& sourceInfo ) { + m_xml + .writeAttribute( "filename", sourceInfo.file ) + .writeAttribute( "line", sourceInfo.line ); + } - virtual void assertionStarting( AssertionInfo const& ) { - } + void XmlReporter::noMatchingTestCases( std::string const& s ) { + StreamingReporterBase::noMatchingTestCases( s ); + } - virtual bool assertionEnded( AssertionStats const& _assertionStats ) { - AssertionResult const& result = _assertionStats.assertionResult; + void XmlReporter::testRunStarting( TestRunInfo const& testInfo ) { + StreamingReporterBase::testRunStarting( testInfo ); + std::string stylesheetRef = getStylesheetRef(); + if( !stylesheetRef.empty() ) + m_xml.writeStylesheetRef( stylesheetRef ); + m_xml.startElement( "Catch" ); + if( !m_config->name().empty() ) + m_xml.writeAttribute( "name", m_config->name() ); + if (m_config->testSpec().hasFilters()) + m_xml.writeAttribute( "filters", serializeFilters( m_config->getTestsOrTags() ) ); + if( m_config->rngSeed() != 0 ) + m_xml.scopedElement( "Randomness" ) + .writeAttribute( "seed", m_config->rngSeed() ); + } - bool printInfoMessages = true; + void XmlReporter::testGroupStarting( GroupInfo const& groupInfo ) { + StreamingReporterBase::testGroupStarting( groupInfo ); + m_xml.startElement( "Group" ) + .writeAttribute( "name", groupInfo.name ); + } - // Drop out if result was successful and we're not printing those - if( !m_config->includeSuccessfulResults() && result.isOk() ) { - if( result.getResultType() != ResultWas::Warning ) - return false; - printInfoMessages = false; - } + void XmlReporter::testCaseStarting( TestCaseInfo const& testInfo ) { + StreamingReporterBase::testCaseStarting(testInfo); + m_xml.startElement( "TestCase" ) + .writeAttribute( "name", trim( testInfo.name ) ) + .writeAttribute( "description", testInfo.description ) + .writeAttribute( "tags", testInfo.tagsAsString() ); - AssertionPrinter printer( stream, _assertionStats, printInfoMessages ); - printer.print(); + writeSourceInfo( testInfo.lineInfo ); - stream << std::endl; - return true; - } + if ( m_config->showDurations() == ShowDurations::Always ) + m_testCaseTimer.start(); + m_xml.ensureTagClosed(); + } - virtual void testRunEnded( TestRunStats const& _testRunStats ) { - printTotals( _testRunStats.totals ); - stream << '\n' << std::endl; - StreamingReporterBase::testRunEnded( _testRunStats ); + void XmlReporter::sectionStarting( SectionInfo const& sectionInfo ) { + StreamingReporterBase::sectionStarting( sectionInfo ); + if( m_sectionDepth++ > 0 ) { + m_xml.startElement( "Section" ) + .writeAttribute( "name", trim( sectionInfo.name ) ); + writeSourceInfo( sectionInfo.lineInfo ); + m_xml.ensureTagClosed(); } + } - private: - class AssertionPrinter { - void operator= ( AssertionPrinter const& ); - public: - AssertionPrinter( std::ostream& _stream, AssertionStats const& _stats, bool _printInfoMessages ) - : stream( _stream ) - , stats( _stats ) - , result( _stats.assertionResult ) - , messages( _stats.infoMessages ) - , itMessage( _stats.infoMessages.begin() ) - , printInfoMessages( _printInfoMessages ) - {} + void XmlReporter::assertionStarting( AssertionInfo const& ) { } - void print() { - printSourceInfo(); - - itMessage = messages.begin(); - - switch( result.getResultType() ) { - case ResultWas::Ok: - printResultType( Colour::ResultSuccess, passedString() ); - printOriginalExpression(); - printReconstructedExpression(); - if ( ! result.hasExpression() ) - printRemainingMessages( Colour::None ); - else - printRemainingMessages(); - break; - case ResultWas::ExpressionFailed: - if( result.isOk() ) - printResultType( Colour::ResultSuccess, failedString() + std::string( " - but was ok" ) ); - else - printResultType( Colour::Error, failedString() ); - printOriginalExpression(); - printReconstructedExpression(); - printRemainingMessages(); - break; - case ResultWas::ThrewException: - printResultType( Colour::Error, failedString() ); - printIssue( "unexpected exception with message:" ); - printMessage(); - printExpressionWas(); - printRemainingMessages(); - break; - case ResultWas::FatalErrorCondition: - printResultType( Colour::Error, failedString() ); - printIssue( "fatal error condition with message:" ); - printMessage(); - printExpressionWas(); - printRemainingMessages(); - break; - case ResultWas::DidntThrowException: - printResultType( Colour::Error, failedString() ); - printIssue( "expected exception, got none" ); - printExpressionWas(); - printRemainingMessages(); - break; - case ResultWas::Info: - printResultType( Colour::None, "info" ); - printMessage(); - printRemainingMessages(); - break; - case ResultWas::Warning: - printResultType( Colour::None, "warning" ); - printMessage(); - printRemainingMessages(); - break; - case ResultWas::ExplicitFailure: - printResultType( Colour::Error, failedString() ); - printIssue( "explicitly" ); - printRemainingMessages( Colour::None ); - break; - // These cases are here to prevent compiler warnings - case ResultWas::Unknown: - case ResultWas::FailureBit: - case ResultWas::Exception: - printResultType( Colour::Error, "** internal error **" ); - break; - } - } + bool XmlReporter::assertionEnded( AssertionStats const& assertionStats ) { - private: - // Colour::LightGrey + AssertionResult const& result = assertionStats.assertionResult; - static Colour::Code dimColour() { return Colour::FileName; } + bool includeResults = m_config->includeSuccessfulResults() || !result.isOk(); -#ifdef CATCH_PLATFORM_MAC - static const char* failedString() { return "FAILED"; } - static const char* passedString() { return "PASSED"; } -#else - static const char* failedString() { return "failed"; } - static const char* passedString() { return "passed"; } -#endif - - void printSourceInfo() const { - Colour colourGuard( Colour::FileName ); - stream << result.getSourceInfo() << ':'; - } - - void printResultType( Colour::Code colour, std::string passOrFail ) const { - if( !passOrFail.empty() ) { - { - Colour colourGuard( colour ); - stream << ' ' << passOrFail; - } - stream << ':'; + if( includeResults || result.getResultType() == ResultWas::Warning ) { + // Print any info messages in <Info> tags. + for( auto const& msg : assertionStats.infoMessages ) { + if( msg.type == ResultWas::Info && includeResults ) { + m_xml.scopedElement( "Info" ) + .writeText( msg.message ); + } else if ( msg.type == ResultWas::Warning ) { + m_xml.scopedElement( "Warning" ) + .writeText( msg.message ); } } + } - void printIssue( std::string issue ) const { - stream << ' ' << issue; - } - - void printExpressionWas() { - if( result.hasExpression() ) { - stream << ';'; - { - Colour colour( dimColour() ); - stream << " expression was:"; - } - printOriginalExpression(); - } - } + // Drop out if result was successful but we're not printing them. + if( !includeResults && result.getResultType() != ResultWas::Warning ) + return true; - void printOriginalExpression() const { - if( result.hasExpression() ) { - stream << ' ' << result.getExpression(); - } - } + // Print the expression if there is one. + if( result.hasExpression() ) { + m_xml.startElement( "Expression" ) + .writeAttribute( "success", result.succeeded() ) + .writeAttribute( "type", result.getTestMacroName() ); - void printReconstructedExpression() const { - if( result.hasExpandedExpression() ) { - { - Colour colour( dimColour() ); - stream << " for: "; - } - stream << result.getExpandedExpression(); - } - } + writeSourceInfo( result.getSourceInfo() ); - void printMessage() { - if ( itMessage != messages.end() ) { - stream << " '" << itMessage->message << '\''; - ++itMessage; - } - } + m_xml.scopedElement( "Original" ) + .writeText( result.getExpression() ); + m_xml.scopedElement( "Expanded" ) + .writeText( result.getExpandedExpression() ); + } - void printRemainingMessages( Colour::Code colour = dimColour() ) { - if ( itMessage == messages.end() ) - return; + // And... Print a result applicable to each result type. + switch( result.getResultType() ) { + case ResultWas::ThrewException: + m_xml.startElement( "Exception" ); + writeSourceInfo( result.getSourceInfo() ); + m_xml.writeText( result.getMessage() ); + m_xml.endElement(); + break; + case ResultWas::FatalErrorCondition: + m_xml.startElement( "FatalErrorCondition" ); + writeSourceInfo( result.getSourceInfo() ); + m_xml.writeText( result.getMessage() ); + m_xml.endElement(); + break; + case ResultWas::Info: + m_xml.scopedElement( "Info" ) + .writeText( result.getMessage() ); + break; + case ResultWas::Warning: + // Warning will already have been written + break; + case ResultWas::ExplicitFailure: + m_xml.startElement( "Failure" ); + writeSourceInfo( result.getSourceInfo() ); + m_xml.writeText( result.getMessage() ); + m_xml.endElement(); + break; + default: + break; + } - // using messages.end() directly yields compilation error: - std::vector<MessageInfo>::const_iterator itEnd = messages.end(); - const std::size_t N = static_cast<std::size_t>( std::distance( itMessage, itEnd ) ); + if( result.hasExpression() ) + m_xml.endElement(); - { - Colour colourGuard( colour ); - stream << " with " << pluralise( N, "message" ) << ':'; - } + return true; + } - for(; itMessage != itEnd; ) { - // If this assertion is a warning ignore any INFO messages - if( printInfoMessages || itMessage->type != ResultWas::Info ) { - stream << " '" << itMessage->message << '\''; - if ( ++itMessage != itEnd ) { - Colour colourGuard( dimColour() ); - stream << " and"; - } - } - } - } + void XmlReporter::sectionEnded( SectionStats const& sectionStats ) { + StreamingReporterBase::sectionEnded( sectionStats ); + if( --m_sectionDepth > 0 ) { + XmlWriter::ScopedElement e = m_xml.scopedElement( "OverallResults" ); + e.writeAttribute( "successes", sectionStats.assertions.passed ); + e.writeAttribute( "failures", sectionStats.assertions.failed ); + e.writeAttribute( "expectedFailures", sectionStats.assertions.failedButOk ); - private: - std::ostream& stream; - AssertionStats const& stats; - AssertionResult const& result; - std::vector<MessageInfo> messages; - std::vector<MessageInfo>::const_iterator itMessage; - bool printInfoMessages; - }; + if ( m_config->showDurations() == ShowDurations::Always ) + e.writeAttribute( "durationInSeconds", sectionStats.durationInSeconds ); - // Colour, message variants: - // - white: No tests ran. - // - red: Failed [both/all] N test cases, failed [both/all] M assertions. - // - white: Passed [both/all] N test cases (no assertions). - // - red: Failed N tests cases, failed M assertions. - // - green: Passed [both/all] N tests cases with M assertions. - - std::string bothOrAll( std::size_t count ) const { - return count == 1 ? std::string() : count == 2 ? "both " : "all " ; - } - - void printTotals( const Totals& totals ) const { - if( totals.testCases.total() == 0 ) { - stream << "No tests ran."; - } - else if( totals.testCases.failed == totals.testCases.total() ) { - Colour colour( Colour::ResultError ); - const std::string qualify_assertions_failed = - totals.assertions.failed == totals.assertions.total() ? - bothOrAll( totals.assertions.failed ) : std::string(); - stream << - "Failed " << bothOrAll( totals.testCases.failed ) - << pluralise( totals.testCases.failed, "test case" ) << ", " - "failed " << qualify_assertions_failed << - pluralise( totals.assertions.failed, "assertion" ) << '.'; - } - else if( totals.assertions.total() == 0 ) { - stream << - "Passed " << bothOrAll( totals.testCases.total() ) - << pluralise( totals.testCases.total(), "test case" ) - << " (no assertions)."; - } - else if( totals.assertions.failed ) { - Colour colour( Colour::ResultError ); - stream << - "Failed " << pluralise( totals.testCases.failed, "test case" ) << ", " - "failed " << pluralise( totals.assertions.failed, "assertion" ) << '.'; - } - else { - Colour colour( Colour::ResultSuccess ); - stream << - "Passed " << bothOrAll( totals.testCases.passed ) - << pluralise( totals.testCases.passed, "test case" ) << - " with " << pluralise( totals.assertions.passed, "assertion" ) << '.'; - } + m_xml.endElement(); } - }; + } - INTERNAL_CATCH_REGISTER_REPORTER( "compact", CompactReporter ) + void XmlReporter::testCaseEnded( TestCaseStats const& testCaseStats ) { + StreamingReporterBase::testCaseEnded( testCaseStats ); + XmlWriter::ScopedElement e = m_xml.scopedElement( "OverallResult" ); + e.writeAttribute( "success", testCaseStats.totals.assertions.allOk() ); + + if ( m_config->showDurations() == ShowDurations::Always ) + e.writeAttribute( "durationInSeconds", m_testCaseTimer.getElapsedSeconds() ); + + if( !testCaseStats.stdOut.empty() ) + m_xml.scopedElement( "StdOut" ).writeText( trim( testCaseStats.stdOut ), false ); + if( !testCaseStats.stdErr.empty() ) + m_xml.scopedElement( "StdErr" ).writeText( trim( testCaseStats.stdErr ), false ); + + m_xml.endElement(); + } + + void XmlReporter::testGroupEnded( TestGroupStats const& testGroupStats ) { + StreamingReporterBase::testGroupEnded( testGroupStats ); + // TODO: Check testGroupStats.aborting and act accordingly. + m_xml.scopedElement( "OverallResults" ) + .writeAttribute( "successes", testGroupStats.totals.assertions.passed ) + .writeAttribute( "failures", testGroupStats.totals.assertions.failed ) + .writeAttribute( "expectedFailures", testGroupStats.totals.assertions.failedButOk ); + m_xml.endElement(); + } + + void XmlReporter::testRunEnded( TestRunStats const& testRunStats ) { + StreamingReporterBase::testRunEnded( testRunStats ); + m_xml.scopedElement( "OverallResults" ) + .writeAttribute( "successes", testRunStats.totals.assertions.passed ) + .writeAttribute( "failures", testRunStats.totals.assertions.failed ) + .writeAttribute( "expectedFailures", testRunStats.totals.assertions.failedButOk ); + m_xml.endElement(); + } + +#if defined(CATCH_CONFIG_ENABLE_BENCHMARKING) + void XmlReporter::benchmarkStarting(BenchmarkInfo const &info) { + m_xml.startElement("BenchmarkResults") + .writeAttribute("name", info.name) + .writeAttribute("samples", info.samples) + .writeAttribute("resamples", info.resamples) + .writeAttribute("iterations", info.iterations) + .writeAttribute("clockResolution", static_cast<uint64_t>(info.clockResolution)) + .writeAttribute("estimatedDuration", static_cast<uint64_t>(info.estimatedDuration)) + .writeComment("All values in nano seconds"); + } + + void XmlReporter::benchmarkEnded(BenchmarkStats<> const& benchmarkStats) { + m_xml.startElement("mean") + .writeAttribute("value", static_cast<uint64_t>(benchmarkStats.mean.point.count())) + .writeAttribute("lowerBound", static_cast<uint64_t>(benchmarkStats.mean.lower_bound.count())) + .writeAttribute("upperBound", static_cast<uint64_t>(benchmarkStats.mean.upper_bound.count())) + .writeAttribute("ci", benchmarkStats.mean.confidence_interval); + m_xml.endElement(); + m_xml.startElement("standardDeviation") + .writeAttribute("value", benchmarkStats.standardDeviation.point.count()) + .writeAttribute("lowerBound", benchmarkStats.standardDeviation.lower_bound.count()) + .writeAttribute("upperBound", benchmarkStats.standardDeviation.upper_bound.count()) + .writeAttribute("ci", benchmarkStats.standardDeviation.confidence_interval); + m_xml.endElement(); + m_xml.startElement("outliers") + .writeAttribute("variance", benchmarkStats.outlierVariance) + .writeAttribute("lowMild", benchmarkStats.outliers.low_mild) + .writeAttribute("lowSevere", benchmarkStats.outliers.low_severe) + .writeAttribute("highMild", benchmarkStats.outliers.high_mild) + .writeAttribute("highSevere", benchmarkStats.outliers.high_severe); + m_xml.endElement(); + m_xml.endElement(); + } + + void XmlReporter::benchmarkFailed(std::string const &error) { + m_xml.scopedElement("failed"). + writeAttribute("message", error); + m_xml.endElement(); + } +#endif // CATCH_CONFIG_ENABLE_BENCHMARKING + + CATCH_REGISTER_REPORTER( "xml", XmlReporter ) } // end namespace Catch -namespace Catch { - // These are all here to avoid warnings about not having any out of line - // virtual methods - NonCopyable::~NonCopyable() {} - IShared::~IShared() {} - IStream::~IStream() CATCH_NOEXCEPT {} - FileStream::~FileStream() CATCH_NOEXCEPT {} - CoutStream::~CoutStream() CATCH_NOEXCEPT {} - DebugOutStream::~DebugOutStream() CATCH_NOEXCEPT {} - StreamBufBase::~StreamBufBase() CATCH_NOEXCEPT {} - IContext::~IContext() {} - IResultCapture::~IResultCapture() {} - ITestCase::~ITestCase() {} - ITestCaseRegistry::~ITestCaseRegistry() {} - IRegistryHub::~IRegistryHub() {} - IMutableRegistryHub::~IMutableRegistryHub() {} - IExceptionTranslator::~IExceptionTranslator() {} - IExceptionTranslatorRegistry::~IExceptionTranslatorRegistry() {} - IReporter::~IReporter() {} - IReporterFactory::~IReporterFactory() {} - IReporterRegistry::~IReporterRegistry() {} - IStreamingReporter::~IStreamingReporter() {} - AssertionStats::~AssertionStats() {} - SectionStats::~SectionStats() {} - TestCaseStats::~TestCaseStats() {} - TestGroupStats::~TestGroupStats() {} - TestRunStats::~TestRunStats() {} - CumulativeReporterBase::SectionNode::~SectionNode() {} - CumulativeReporterBase::~CumulativeReporterBase() {} - - StreamingReporterBase::~StreamingReporterBase() {} - ConsoleReporter::~ConsoleReporter() {} - CompactReporter::~CompactReporter() {} - IRunner::~IRunner() {} - IMutableContext::~IMutableContext() {} - IConfig::~IConfig() {} - XmlReporter::~XmlReporter() {} - JunitReporter::~JunitReporter() {} - TestRegistry::~TestRegistry() {} - FreeFunctionTestCase::~FreeFunctionTestCase() {} - IGeneratorInfo::~IGeneratorInfo() {} - IGeneratorsForTest::~IGeneratorsForTest() {} - WildcardPattern::~WildcardPattern() {} - TestSpec::Pattern::~Pattern() {} - TestSpec::NamePattern::~NamePattern() {} - TestSpec::TagPattern::~TagPattern() {} - TestSpec::ExcludedPattern::~ExcludedPattern() {} - - Matchers::Impl::StdString::Equals::~Equals() {} - Matchers::Impl::StdString::Contains::~Contains() {} - Matchers::Impl::StdString::StartsWith::~StartsWith() {} - Matchers::Impl::StdString::EndsWith::~EndsWith() {} - - void Config::dummy() {} +#if defined(_MSC_VER) +#pragma warning(pop) +#endif +// end catch_reporter_xml.cpp - namespace TestCaseTracking { - ITracker::~ITracker() {} - TrackerBase::~TrackerBase() {} - SectionTracker::~SectionTracker() {} - IndexTracker::~IndexTracker() {} - } +namespace Catch { + LeakDetector leakDetector; } #ifdef __clang__ #pragma clang diagnostic pop #endif +// end catch_impl.hpp #endif #ifdef CATCH_CONFIG_MAIN -// #included from: internal/catch_default_main.hpp -#define TWOBLUECUBES_CATCH_DEFAULT_MAIN_HPP_INCLUDED +// start catch_default_main.hpp #ifndef __OBJC__ +#if defined(CATCH_CONFIG_WCHAR) && defined(WIN32) && defined(_UNICODE) && !defined(DO_NOT_USE_WMAIN) +// Standard C/C++ Win32 Unicode wmain entry point +extern "C" int wmain (int argc, wchar_t * argv[], wchar_t * []) { +#else // Standard C/C++ main entry point int main (int argc, char * argv[]) { - int result = Catch::Session().run( argc, argv ); - return ( result < 0xff ? result : 0xff ); +#endif + + return Catch::Session().run( argc, argv ); } #else // __OBJC__ @@ -10902,168 +16440,425 @@ int main (int argc, char * const argv[]) { #endif Catch::registerTestMethods(); - int result = Catch::Session().run( argc, (char* const*)argv ); + int result = Catch::Session().run( argc, (char**)argv ); #if !CATCH_ARC_ENABLED [pool drain]; #endif - return ( result < 0xff ? result : 0xff ); + return result; } #endif // __OBJC__ +// end catch_default_main.hpp #endif +#if !defined(CATCH_CONFIG_IMPL_ONLY) + #ifdef CLARA_CONFIG_MAIN_NOT_DEFINED # undef CLARA_CONFIG_MAIN #endif +#if !defined(CATCH_CONFIG_DISABLE) ////// - // If this config identifier is defined then all CATCH macros are prefixed with CATCH_ #ifdef CATCH_CONFIG_PREFIX_ALL -#define CATCH_REQUIRE( expr ) INTERNAL_CATCH_TEST( expr, Catch::ResultDisposition::Normal, "CATCH_REQUIRE" ) -#define CATCH_REQUIRE_FALSE( expr ) INTERNAL_CATCH_TEST( expr, Catch::ResultDisposition::Normal | Catch::ResultDisposition::FalseTest, "CATCH_REQUIRE_FALSE" ) - -#define CATCH_REQUIRE_THROWS( expr ) INTERNAL_CATCH_THROWS( expr, Catch::ResultDisposition::Normal, "", "CATCH_REQUIRE_THROWS" ) -#define CATCH_REQUIRE_THROWS_AS( expr, exceptionType ) INTERNAL_CATCH_THROWS_AS( expr, exceptionType, Catch::ResultDisposition::Normal, "CATCH_REQUIRE_THROWS_AS" ) -#define CATCH_REQUIRE_THROWS_WITH( expr, matcher ) INTERNAL_CATCH_THROWS( expr, Catch::ResultDisposition::Normal, matcher, "CATCH_REQUIRE_THROWS_WITH" ) -#define CATCH_REQUIRE_NOTHROW( expr ) INTERNAL_CATCH_NO_THROW( expr, Catch::ResultDisposition::Normal, "CATCH_REQUIRE_NOTHROW" ) - -#define CATCH_CHECK( expr ) INTERNAL_CATCH_TEST( expr, Catch::ResultDisposition::ContinueOnFailure, "CATCH_CHECK" ) -#define CATCH_CHECK_FALSE( expr ) INTERNAL_CATCH_TEST( expr, Catch::ResultDisposition::ContinueOnFailure | Catch::ResultDisposition::FalseTest, "CATCH_CHECK_FALSE" ) -#define CATCH_CHECKED_IF( expr ) INTERNAL_CATCH_IF( expr, Catch::ResultDisposition::ContinueOnFailure, "CATCH_CHECKED_IF" ) -#define CATCH_CHECKED_ELSE( expr ) INTERNAL_CATCH_ELSE( expr, Catch::ResultDisposition::ContinueOnFailure, "CATCH_CHECKED_ELSE" ) -#define CATCH_CHECK_NOFAIL( expr ) INTERNAL_CATCH_TEST( expr, Catch::ResultDisposition::ContinueOnFailure | Catch::ResultDisposition::SuppressFail, "CATCH_CHECK_NOFAIL" ) - -#define CATCH_CHECK_THROWS( expr ) INTERNAL_CATCH_THROWS( expr, Catch::ResultDisposition::ContinueOnFailure, "", "CATCH_CHECK_THROWS" ) -#define CATCH_CHECK_THROWS_AS( expr, exceptionType ) INTERNAL_CATCH_THROWS_AS( expr, exceptionType, Catch::ResultDisposition::ContinueOnFailure, "CATCH_CHECK_THROWS_AS" ) -#define CATCH_CHECK_THROWS_WITH( expr, matcher ) INTERNAL_CATCH_THROWS( expr, Catch::ResultDisposition::ContinueOnFailure, matcher, "CATCH_CHECK_THROWS_WITH" ) -#define CATCH_CHECK_NOTHROW( expr ) INTERNAL_CATCH_NO_THROW( expr, Catch::ResultDisposition::ContinueOnFailure, "CATCH_CHECK_NOTHROW" ) - -#define CATCH_CHECK_THAT( arg, matcher ) INTERNAL_CHECK_THAT( arg, matcher, Catch::ResultDisposition::ContinueOnFailure, "CATCH_CHECK_THAT" ) -#define CATCH_REQUIRE_THAT( arg, matcher ) INTERNAL_CHECK_THAT( arg, matcher, Catch::ResultDisposition::Normal, "CATCH_REQUIRE_THAT" ) - -#define CATCH_INFO( msg ) INTERNAL_CATCH_INFO( msg, "CATCH_INFO" ) -#define CATCH_WARN( msg ) INTERNAL_CATCH_MSG( Catch::ResultWas::Warning, Catch::ResultDisposition::ContinueOnFailure, "CATCH_WARN", msg ) -#define CATCH_SCOPED_INFO( msg ) INTERNAL_CATCH_INFO( msg, "CATCH_INFO" ) -#define CATCH_CAPTURE( msg ) INTERNAL_CATCH_INFO( #msg " := " << msg, "CATCH_CAPTURE" ) -#define CATCH_SCOPED_CAPTURE( msg ) INTERNAL_CATCH_INFO( #msg " := " << msg, "CATCH_CAPTURE" ) - -#ifdef CATCH_CONFIG_VARIADIC_MACROS - #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_FAIL( ... ) INTERNAL_CATCH_MSG( Catch::ResultWas::ExplicitFailure, Catch::ResultDisposition::Normal, "CATCH_FAIL", __VA_ARGS__ ) - #define CATCH_SUCCEED( ... ) INTERNAL_CATCH_MSG( Catch::ResultWas::Ok, Catch::ResultDisposition::ContinueOnFailure, "CATCH_SUCCEED", __VA_ARGS__ ) +#define CATCH_REQUIRE( ... ) INTERNAL_CATCH_TEST( "CATCH_REQUIRE", Catch::ResultDisposition::Normal, __VA_ARGS__ ) +#define CATCH_REQUIRE_FALSE( ... ) INTERNAL_CATCH_TEST( "CATCH_REQUIRE_FALSE", Catch::ResultDisposition::Normal | Catch::ResultDisposition::FalseTest, __VA_ARGS__ ) + +#define CATCH_REQUIRE_THROWS( ... ) INTERNAL_CATCH_THROWS( "CATCH_REQUIRE_THROWS", Catch::ResultDisposition::Normal, __VA_ARGS__ ) +#define CATCH_REQUIRE_THROWS_AS( expr, exceptionType ) INTERNAL_CATCH_THROWS_AS( "CATCH_REQUIRE_THROWS_AS", exceptionType, Catch::ResultDisposition::Normal, expr ) +#define CATCH_REQUIRE_THROWS_WITH( expr, matcher ) INTERNAL_CATCH_THROWS_STR_MATCHES( "CATCH_REQUIRE_THROWS_WITH", Catch::ResultDisposition::Normal, matcher, expr ) +#if !defined(CATCH_CONFIG_DISABLE_MATCHERS) +#define CATCH_REQUIRE_THROWS_MATCHES( expr, exceptionType, matcher ) INTERNAL_CATCH_THROWS_MATCHES( "CATCH_REQUIRE_THROWS_MATCHES", exceptionType, Catch::ResultDisposition::Normal, matcher, expr ) +#endif// CATCH_CONFIG_DISABLE_MATCHERS +#define CATCH_REQUIRE_NOTHROW( ... ) INTERNAL_CATCH_NO_THROW( "CATCH_REQUIRE_NOTHROW", Catch::ResultDisposition::Normal, __VA_ARGS__ ) + +#define CATCH_CHECK( ... ) INTERNAL_CATCH_TEST( "CATCH_CHECK", Catch::ResultDisposition::ContinueOnFailure, __VA_ARGS__ ) +#define CATCH_CHECK_FALSE( ... ) INTERNAL_CATCH_TEST( "CATCH_CHECK_FALSE", Catch::ResultDisposition::ContinueOnFailure | Catch::ResultDisposition::FalseTest, __VA_ARGS__ ) +#define CATCH_CHECKED_IF( ... ) INTERNAL_CATCH_IF( "CATCH_CHECKED_IF", Catch::ResultDisposition::ContinueOnFailure, __VA_ARGS__ ) +#define CATCH_CHECKED_ELSE( ... ) INTERNAL_CATCH_ELSE( "CATCH_CHECKED_ELSE", Catch::ResultDisposition::ContinueOnFailure, __VA_ARGS__ ) +#define CATCH_CHECK_NOFAIL( ... ) INTERNAL_CATCH_TEST( "CATCH_CHECK_NOFAIL", Catch::ResultDisposition::ContinueOnFailure | Catch::ResultDisposition::SuppressFail, __VA_ARGS__ ) + +#define CATCH_CHECK_THROWS( ... ) INTERNAL_CATCH_THROWS( "CATCH_CHECK_THROWS", Catch::ResultDisposition::ContinueOnFailure, __VA_ARGS__ ) +#define CATCH_CHECK_THROWS_AS( expr, exceptionType ) INTERNAL_CATCH_THROWS_AS( "CATCH_CHECK_THROWS_AS", exceptionType, Catch::ResultDisposition::ContinueOnFailure, expr ) +#define CATCH_CHECK_THROWS_WITH( expr, matcher ) INTERNAL_CATCH_THROWS_STR_MATCHES( "CATCH_CHECK_THROWS_WITH", Catch::ResultDisposition::ContinueOnFailure, matcher, expr ) +#if !defined(CATCH_CONFIG_DISABLE_MATCHERS) +#define CATCH_CHECK_THROWS_MATCHES( expr, exceptionType, matcher ) INTERNAL_CATCH_THROWS_MATCHES( "CATCH_CHECK_THROWS_MATCHES", exceptionType, Catch::ResultDisposition::ContinueOnFailure, matcher, expr ) +#endif // CATCH_CONFIG_DISABLE_MATCHERS +#define CATCH_CHECK_NOTHROW( ... ) INTERNAL_CATCH_NO_THROW( "CATCH_CHECK_NOTHROW", Catch::ResultDisposition::ContinueOnFailure, __VA_ARGS__ ) + +#if !defined(CATCH_CONFIG_DISABLE_MATCHERS) +#define CATCH_CHECK_THAT( arg, matcher ) INTERNAL_CHECK_THAT( "CATCH_CHECK_THAT", matcher, Catch::ResultDisposition::ContinueOnFailure, arg ) + +#define CATCH_REQUIRE_THAT( arg, matcher ) INTERNAL_CHECK_THAT( "CATCH_REQUIRE_THAT", matcher, Catch::ResultDisposition::Normal, arg ) +#endif // CATCH_CONFIG_DISABLE_MATCHERS + +#define CATCH_INFO( msg ) INTERNAL_CATCH_INFO( "CATCH_INFO", msg ) +#define CATCH_UNSCOPED_INFO( msg ) INTERNAL_CATCH_UNSCOPED_INFO( "CATCH_UNSCOPED_INFO", msg ) +#define CATCH_WARN( msg ) INTERNAL_CATCH_MSG( "CATCH_WARN", Catch::ResultWas::Warning, Catch::ResultDisposition::ContinueOnFailure, msg ) +#define CATCH_CAPTURE( ... ) INTERNAL_CATCH_CAPTURE( INTERNAL_CATCH_UNIQUE_NAME(capturer), "CATCH_CAPTURE",__VA_ARGS__ ) + +#define CATCH_TEST_CASE( ... ) INTERNAL_CATCH_TESTCASE( __VA_ARGS__ ) +#define CATCH_TEST_CASE_METHOD( className, ... ) INTERNAL_CATCH_TEST_CASE_METHOD( className, __VA_ARGS__ ) +#define CATCH_METHOD_AS_TEST_CASE( method, ... ) INTERNAL_CATCH_METHOD_AS_TEST_CASE( method, __VA_ARGS__ ) +#define CATCH_REGISTER_TEST_CASE( Function, ... ) INTERNAL_CATCH_REGISTER_TESTCASE( Function, __VA_ARGS__ ) +#define CATCH_SECTION( ... ) INTERNAL_CATCH_SECTION( __VA_ARGS__ ) +#define CATCH_DYNAMIC_SECTION( ... ) INTERNAL_CATCH_DYNAMIC_SECTION( __VA_ARGS__ ) +#define CATCH_FAIL( ... ) INTERNAL_CATCH_MSG( "CATCH_FAIL", Catch::ResultWas::ExplicitFailure, Catch::ResultDisposition::Normal, __VA_ARGS__ ) +#define CATCH_FAIL_CHECK( ... ) INTERNAL_CATCH_MSG( "CATCH_FAIL_CHECK", Catch::ResultWas::ExplicitFailure, Catch::ResultDisposition::ContinueOnFailure, __VA_ARGS__ ) +#define CATCH_SUCCEED( ... ) INTERNAL_CATCH_MSG( "CATCH_SUCCEED", Catch::ResultWas::Ok, Catch::ResultDisposition::ContinueOnFailure, __VA_ARGS__ ) + +#define CATCH_ANON_TEST_CASE() INTERNAL_CATCH_TESTCASE() + +#ifndef CATCH_CONFIG_TRADITIONAL_MSVC_PREPROCESSOR +#define CATCH_TEMPLATE_TEST_CASE( ... ) INTERNAL_CATCH_TEMPLATE_TEST_CASE( __VA_ARGS__ ) +#define CATCH_TEMPLATE_TEST_CASE_SIG( ... ) INTERNAL_CATCH_TEMPLATE_TEST_CASE_SIG( __VA_ARGS__ ) +#define CATCH_TEMPLATE_TEST_CASE_METHOD( className, ... ) INTERNAL_CATCH_TEMPLATE_TEST_CASE_METHOD( className, __VA_ARGS__ ) +#define CATCH_TEMPLATE_TEST_CASE_METHOD_SIG( className, ... ) INTERNAL_CATCH_TEMPLATE_TEST_CASE_METHOD_SIG( className, __VA_ARGS__ ) +#define CATCH_TEMPLATE_PRODUCT_TEST_CASE( ... ) INTERNAL_CATCH_TEMPLATE_PRODUCT_TEST_CASE( __VA_ARGS__ ) +#define CATCH_TEMPLATE_PRODUCT_TEST_CASE_SIG( ... ) INTERNAL_CATCH_TEMPLATE_PRODUCT_TEST_CASE_SIG( __VA_ARGS__ ) +#define CATCH_TEMPLATE_PRODUCT_TEST_CASE_METHOD( className, ... ) INTERNAL_CATCH_TEMPLATE_PRODUCT_TEST_CASE_METHOD( className, __VA_ARGS__ ) +#define CATCH_TEMPLATE_PRODUCT_TEST_CASE_METHOD_SIG( className, ... ) INTERNAL_CATCH_TEMPLATE_PRODUCT_TEST_CASE_METHOD_SIG( className, __VA_ARGS__ ) #else - #define CATCH_TEST_CASE( name, description ) INTERNAL_CATCH_TESTCASE( name, description ) - #define CATCH_TEST_CASE_METHOD( className, name, description ) INTERNAL_CATCH_TEST_CASE_METHOD( className, name, description ) - #define CATCH_METHOD_AS_TEST_CASE( method, name, description ) INTERNAL_CATCH_METHOD_AS_TEST_CASE( method, name, description ) - #define CATCH_REGISTER_TEST_CASE( function, name, description ) INTERNAL_CATCH_REGISTER_TESTCASE( function, name, description ) - #define CATCH_SECTION( name, description ) INTERNAL_CATCH_SECTION( name, description ) - #define CATCH_FAIL( msg ) INTERNAL_CATCH_MSG( Catch::ResultWas::ExplicitFailure, Catch::ResultDisposition::Normal, "CATCH_FAIL", msg ) - #define CATCH_SUCCEED( msg ) INTERNAL_CATCH_MSG( Catch::ResultWas::Ok, Catch::ResultDisposition::ContinueOnFailure, "CATCH_SUCCEED", msg ) +#define CATCH_TEMPLATE_TEST_CASE( ... ) INTERNAL_CATCH_EXPAND_VARGS( INTERNAL_CATCH_TEMPLATE_TEST_CASE( __VA_ARGS__ ) ) +#define CATCH_TEMPLATE_TEST_CASE_SIG( ... ) INTERNAL_CATCH_EXPAND_VARGS( INTERNAL_CATCH_TEMPLATE_TEST_CASE_SIG( __VA_ARGS__ ) ) +#define CATCH_TEMPLATE_TEST_CASE_METHOD( className, ... ) INTERNAL_CATCH_EXPAND_VARGS( INTERNAL_CATCH_TEMPLATE_TEST_CASE_METHOD( className, __VA_ARGS__ ) ) +#define CATCH_TEMPLATE_TEST_CASE_METHOD_SIG( className, ... ) INTERNAL_CATCH_EXPAND_VARGS( INTERNAL_CATCH_TEMPLATE_TEST_CASE_METHOD_SIG( className, __VA_ARGS__ ) ) +#define CATCH_TEMPLATE_PRODUCT_TEST_CASE( ... ) INTERNAL_CATCH_EXPAND_VARGS( INTERNAL_CATCH_TEMPLATE_PRODUCT_TEST_CASE( __VA_ARGS__ ) ) +#define CATCH_TEMPLATE_PRODUCT_TEST_CASE_SIG( ... ) INTERNAL_CATCH_EXPAND_VARGS( INTERNAL_CATCH_TEMPLATE_PRODUCT_TEST_CASE_SIG( __VA_ARGS__ ) ) +#define CATCH_TEMPLATE_PRODUCT_TEST_CASE_METHOD( className, ... ) INTERNAL_CATCH_EXPAND_VARGS( INTERNAL_CATCH_TEMPLATE_PRODUCT_TEST_CASE_METHOD( className, __VA_ARGS__ ) ) +#define CATCH_TEMPLATE_PRODUCT_TEST_CASE_METHOD_SIG( className, ... ) INTERNAL_CATCH_EXPAND_VARGS( INTERNAL_CATCH_TEMPLATE_PRODUCT_TEST_CASE_METHOD_SIG( className, __VA_ARGS__ ) ) #endif -#define CATCH_ANON_TEST_CASE() INTERNAL_CATCH_TESTCASE( "", "" ) -#define CATCH_REGISTER_REPORTER( name, reporterType ) INTERNAL_CATCH_REGISTER_REPORTER( name, reporterType ) -#define CATCH_REGISTER_LEGACY_REPORTER( name, reporterType ) INTERNAL_CATCH_REGISTER_LEGACY_REPORTER( name, reporterType ) - -#define CATCH_GENERATE( expr) INTERNAL_CATCH_GENERATE( expr ) +#if !defined(CATCH_CONFIG_RUNTIME_STATIC_REQUIRE) +#define CATCH_STATIC_REQUIRE( ... ) static_assert( __VA_ARGS__ , #__VA_ARGS__ ); CATCH_SUCCEED( #__VA_ARGS__ ) +#define CATCH_STATIC_REQUIRE_FALSE( ... ) static_assert( !(__VA_ARGS__), "!(" #__VA_ARGS__ ")" ); CATCH_SUCCEED( #__VA_ARGS__ ) +#else +#define CATCH_STATIC_REQUIRE( ... ) CATCH_REQUIRE( __VA_ARGS__ ) +#define CATCH_STATIC_REQUIRE_FALSE( ... ) CATCH_REQUIRE_FALSE( __VA_ARGS__ ) +#endif // "BDD-style" convenience wrappers -#ifdef CATCH_CONFIG_VARIADIC_MACROS #define CATCH_SCENARIO( ... ) CATCH_TEST_CASE( "Scenario: " __VA_ARGS__ ) #define CATCH_SCENARIO_METHOD( className, ... ) INTERNAL_CATCH_TEST_CASE_METHOD( className, "Scenario: " __VA_ARGS__ ) -#else -#define CATCH_SCENARIO( name, tags ) CATCH_TEST_CASE( "Scenario: " name, tags ) -#define CATCH_SCENARIO_METHOD( className, name, tags ) INTERNAL_CATCH_TEST_CASE_METHOD( className, "Scenario: " name, tags ) -#endif -#define CATCH_GIVEN( desc ) CATCH_SECTION( std::string( "Given: ") + desc, "" ) -#define CATCH_WHEN( desc ) CATCH_SECTION( std::string( " When: ") + desc, "" ) -#define CATCH_AND_WHEN( desc ) CATCH_SECTION( std::string( " And: ") + desc, "" ) -#define CATCH_THEN( desc ) CATCH_SECTION( std::string( " Then: ") + desc, "" ) -#define CATCH_AND_THEN( desc ) CATCH_SECTION( std::string( " And: ") + desc, "" ) +#define CATCH_GIVEN( desc ) INTERNAL_CATCH_DYNAMIC_SECTION( " Given: " << desc ) +#define CATCH_AND_GIVEN( desc ) INTERNAL_CATCH_DYNAMIC_SECTION( "And given: " << desc ) +#define CATCH_WHEN( desc ) INTERNAL_CATCH_DYNAMIC_SECTION( " When: " << desc ) +#define CATCH_AND_WHEN( desc ) INTERNAL_CATCH_DYNAMIC_SECTION( " And when: " << desc ) +#define CATCH_THEN( desc ) INTERNAL_CATCH_DYNAMIC_SECTION( " Then: " << desc ) +#define CATCH_AND_THEN( desc ) INTERNAL_CATCH_DYNAMIC_SECTION( " And: " << desc ) + +#if defined(CATCH_CONFIG_ENABLE_BENCHMARKING) +#define CATCH_BENCHMARK(...) \ + INTERNAL_CATCH_BENCHMARK(INTERNAL_CATCH_UNIQUE_NAME(____C_A_T_C_H____B_E_N_C_H____), INTERNAL_CATCH_GET_1_ARG(__VA_ARGS__,,), INTERNAL_CATCH_GET_2_ARG(__VA_ARGS__,,)) +#define CATCH_BENCHMARK_ADVANCED(name) \ + INTERNAL_CATCH_BENCHMARK_ADVANCED(INTERNAL_CATCH_UNIQUE_NAME(____C_A_T_C_H____B_E_N_C_H____), name) +#endif // CATCH_CONFIG_ENABLE_BENCHMARKING // If CATCH_CONFIG_PREFIX_ALL is not defined then the CATCH_ prefix is not required #else -#define REQUIRE( expr ) INTERNAL_CATCH_TEST( expr, Catch::ResultDisposition::Normal, "REQUIRE" ) -#define REQUIRE_FALSE( expr ) INTERNAL_CATCH_TEST( expr, Catch::ResultDisposition::Normal | Catch::ResultDisposition::FalseTest, "REQUIRE_FALSE" ) - -#define REQUIRE_THROWS( expr ) INTERNAL_CATCH_THROWS( expr, Catch::ResultDisposition::Normal, "", "REQUIRE_THROWS" ) -#define REQUIRE_THROWS_AS( expr, exceptionType ) INTERNAL_CATCH_THROWS_AS( expr, exceptionType, Catch::ResultDisposition::Normal, "REQUIRE_THROWS_AS" ) -#define REQUIRE_THROWS_WITH( expr, matcher ) INTERNAL_CATCH_THROWS( expr, Catch::ResultDisposition::Normal, matcher, "REQUIRE_THROWS_WITH" ) -#define REQUIRE_NOTHROW( expr ) INTERNAL_CATCH_NO_THROW( expr, Catch::ResultDisposition::Normal, "REQUIRE_NOTHROW" ) - -#define CHECK( expr ) INTERNAL_CATCH_TEST( expr, Catch::ResultDisposition::ContinueOnFailure, "CHECK" ) -#define CHECK_FALSE( expr ) INTERNAL_CATCH_TEST( expr, Catch::ResultDisposition::ContinueOnFailure | Catch::ResultDisposition::FalseTest, "CHECK_FALSE" ) -#define CHECKED_IF( expr ) INTERNAL_CATCH_IF( expr, Catch::ResultDisposition::ContinueOnFailure, "CHECKED_IF" ) -#define CHECKED_ELSE( expr ) INTERNAL_CATCH_ELSE( expr, Catch::ResultDisposition::ContinueOnFailure, "CHECKED_ELSE" ) -#define CHECK_NOFAIL( expr ) INTERNAL_CATCH_TEST( expr, Catch::ResultDisposition::ContinueOnFailure | Catch::ResultDisposition::SuppressFail, "CHECK_NOFAIL" ) - -#define CHECK_THROWS( expr ) INTERNAL_CATCH_THROWS( expr, Catch::ResultDisposition::ContinueOnFailure, "", "CHECK_THROWS" ) -#define CHECK_THROWS_AS( expr, exceptionType ) INTERNAL_CATCH_THROWS_AS( expr, exceptionType, Catch::ResultDisposition::ContinueOnFailure, "CHECK_THROWS_AS" ) -#define CHECK_THROWS_WITH( expr, matcher ) INTERNAL_CATCH_THROWS( expr, Catch::ResultDisposition::ContinueOnFailure, matcher, "CHECK_THROWS_WITH" ) -#define CHECK_NOTHROW( expr ) INTERNAL_CATCH_NO_THROW( expr, Catch::ResultDisposition::ContinueOnFailure, "CHECK_NOTHROW" ) - -#define CHECK_THAT( arg, matcher ) INTERNAL_CHECK_THAT( arg, matcher, Catch::ResultDisposition::ContinueOnFailure, "CHECK_THAT" ) -#define REQUIRE_THAT( arg, matcher ) INTERNAL_CHECK_THAT( arg, matcher, Catch::ResultDisposition::Normal, "REQUIRE_THAT" ) - -#define INFO( msg ) INTERNAL_CATCH_INFO( msg, "INFO" ) -#define WARN( msg ) INTERNAL_CATCH_MSG( Catch::ResultWas::Warning, Catch::ResultDisposition::ContinueOnFailure, "WARN", msg ) -#define SCOPED_INFO( msg ) INTERNAL_CATCH_INFO( msg, "INFO" ) -#define CAPTURE( msg ) INTERNAL_CATCH_INFO( #msg " := " << msg, "CAPTURE" ) -#define SCOPED_CAPTURE( msg ) INTERNAL_CATCH_INFO( #msg " := " << msg, "CAPTURE" ) - -#ifdef CATCH_CONFIG_VARIADIC_MACROS - #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 FAIL( ... ) INTERNAL_CATCH_MSG( Catch::ResultWas::ExplicitFailure, Catch::ResultDisposition::Normal, "FAIL", __VA_ARGS__ ) - #define SUCCEED( ... ) INTERNAL_CATCH_MSG( Catch::ResultWas::Ok, Catch::ResultDisposition::ContinueOnFailure, "SUCCEED", __VA_ARGS__ ) +#define REQUIRE( ... ) INTERNAL_CATCH_TEST( "REQUIRE", Catch::ResultDisposition::Normal, __VA_ARGS__ ) +#define REQUIRE_FALSE( ... ) INTERNAL_CATCH_TEST( "REQUIRE_FALSE", Catch::ResultDisposition::Normal | Catch::ResultDisposition::FalseTest, __VA_ARGS__ ) + +#define REQUIRE_THROWS( ... ) INTERNAL_CATCH_THROWS( "REQUIRE_THROWS", Catch::ResultDisposition::Normal, __VA_ARGS__ ) +#define REQUIRE_THROWS_AS( expr, exceptionType ) INTERNAL_CATCH_THROWS_AS( "REQUIRE_THROWS_AS", exceptionType, Catch::ResultDisposition::Normal, expr ) +#define REQUIRE_THROWS_WITH( expr, matcher ) INTERNAL_CATCH_THROWS_STR_MATCHES( "REQUIRE_THROWS_WITH", Catch::ResultDisposition::Normal, matcher, expr ) +#if !defined(CATCH_CONFIG_DISABLE_MATCHERS) +#define REQUIRE_THROWS_MATCHES( expr, exceptionType, matcher ) INTERNAL_CATCH_THROWS_MATCHES( "REQUIRE_THROWS_MATCHES", exceptionType, Catch::ResultDisposition::Normal, matcher, expr ) +#endif // CATCH_CONFIG_DISABLE_MATCHERS +#define REQUIRE_NOTHROW( ... ) INTERNAL_CATCH_NO_THROW( "REQUIRE_NOTHROW", Catch::ResultDisposition::Normal, __VA_ARGS__ ) + +#define CHECK( ... ) INTERNAL_CATCH_TEST( "CHECK", Catch::ResultDisposition::ContinueOnFailure, __VA_ARGS__ ) +#define CHECK_FALSE( ... ) INTERNAL_CATCH_TEST( "CHECK_FALSE", Catch::ResultDisposition::ContinueOnFailure | Catch::ResultDisposition::FalseTest, __VA_ARGS__ ) +#define CHECKED_IF( ... ) INTERNAL_CATCH_IF( "CHECKED_IF", Catch::ResultDisposition::ContinueOnFailure, __VA_ARGS__ ) +#define CHECKED_ELSE( ... ) INTERNAL_CATCH_ELSE( "CHECKED_ELSE", Catch::ResultDisposition::ContinueOnFailure, __VA_ARGS__ ) +#define CHECK_NOFAIL( ... ) INTERNAL_CATCH_TEST( "CHECK_NOFAIL", Catch::ResultDisposition::ContinueOnFailure | Catch::ResultDisposition::SuppressFail, __VA_ARGS__ ) + +#define CHECK_THROWS( ... ) INTERNAL_CATCH_THROWS( "CHECK_THROWS", Catch::ResultDisposition::ContinueOnFailure, __VA_ARGS__ ) +#define CHECK_THROWS_AS( expr, exceptionType ) INTERNAL_CATCH_THROWS_AS( "CHECK_THROWS_AS", exceptionType, Catch::ResultDisposition::ContinueOnFailure, expr ) +#define CHECK_THROWS_WITH( expr, matcher ) INTERNAL_CATCH_THROWS_STR_MATCHES( "CHECK_THROWS_WITH", Catch::ResultDisposition::ContinueOnFailure, matcher, expr ) +#if !defined(CATCH_CONFIG_DISABLE_MATCHERS) +#define CHECK_THROWS_MATCHES( expr, exceptionType, matcher ) INTERNAL_CATCH_THROWS_MATCHES( "CHECK_THROWS_MATCHES", exceptionType, Catch::ResultDisposition::ContinueOnFailure, matcher, expr ) +#endif // CATCH_CONFIG_DISABLE_MATCHERS +#define CHECK_NOTHROW( ... ) INTERNAL_CATCH_NO_THROW( "CHECK_NOTHROW", Catch::ResultDisposition::ContinueOnFailure, __VA_ARGS__ ) + +#if !defined(CATCH_CONFIG_DISABLE_MATCHERS) +#define CHECK_THAT( arg, matcher ) INTERNAL_CHECK_THAT( "CHECK_THAT", matcher, Catch::ResultDisposition::ContinueOnFailure, arg ) + +#define REQUIRE_THAT( arg, matcher ) INTERNAL_CHECK_THAT( "REQUIRE_THAT", matcher, Catch::ResultDisposition::Normal, arg ) +#endif // CATCH_CONFIG_DISABLE_MATCHERS + +#define INFO( msg ) INTERNAL_CATCH_INFO( "INFO", msg ) +#define UNSCOPED_INFO( msg ) INTERNAL_CATCH_UNSCOPED_INFO( "UNSCOPED_INFO", msg ) +#define WARN( msg ) INTERNAL_CATCH_MSG( "WARN", Catch::ResultWas::Warning, Catch::ResultDisposition::ContinueOnFailure, msg ) +#define CAPTURE( ... ) INTERNAL_CATCH_CAPTURE( INTERNAL_CATCH_UNIQUE_NAME(capturer), "CAPTURE",__VA_ARGS__ ) + +#define TEST_CASE( ... ) INTERNAL_CATCH_TESTCASE( __VA_ARGS__ ) +#define TEST_CASE_METHOD( className, ... ) INTERNAL_CATCH_TEST_CASE_METHOD( className, __VA_ARGS__ ) +#define METHOD_AS_TEST_CASE( method, ... ) INTERNAL_CATCH_METHOD_AS_TEST_CASE( method, __VA_ARGS__ ) +#define REGISTER_TEST_CASE( Function, ... ) INTERNAL_CATCH_REGISTER_TESTCASE( Function, __VA_ARGS__ ) +#define SECTION( ... ) INTERNAL_CATCH_SECTION( __VA_ARGS__ ) +#define DYNAMIC_SECTION( ... ) INTERNAL_CATCH_DYNAMIC_SECTION( __VA_ARGS__ ) +#define FAIL( ... ) INTERNAL_CATCH_MSG( "FAIL", Catch::ResultWas::ExplicitFailure, Catch::ResultDisposition::Normal, __VA_ARGS__ ) +#define FAIL_CHECK( ... ) INTERNAL_CATCH_MSG( "FAIL_CHECK", Catch::ResultWas::ExplicitFailure, Catch::ResultDisposition::ContinueOnFailure, __VA_ARGS__ ) +#define SUCCEED( ... ) INTERNAL_CATCH_MSG( "SUCCEED", Catch::ResultWas::Ok, Catch::ResultDisposition::ContinueOnFailure, __VA_ARGS__ ) +#define ANON_TEST_CASE() INTERNAL_CATCH_TESTCASE() + +#ifndef CATCH_CONFIG_TRADITIONAL_MSVC_PREPROCESSOR +#define TEMPLATE_TEST_CASE( ... ) INTERNAL_CATCH_TEMPLATE_TEST_CASE( __VA_ARGS__ ) +#define TEMPLATE_TEST_CASE_SIG( ... ) INTERNAL_CATCH_TEMPLATE_TEST_CASE_SIG( __VA_ARGS__ ) +#define TEMPLATE_TEST_CASE_METHOD( className, ... ) INTERNAL_CATCH_TEMPLATE_TEST_CASE_METHOD( className, __VA_ARGS__ ) +#define TEMPLATE_TEST_CASE_METHOD_SIG( className, ... ) INTERNAL_CATCH_TEMPLATE_TEST_CASE_METHOD_SIG( className, __VA_ARGS__ ) +#define TEMPLATE_PRODUCT_TEST_CASE( ... ) INTERNAL_CATCH_TEMPLATE_PRODUCT_TEST_CASE( __VA_ARGS__ ) +#define TEMPLATE_PRODUCT_TEST_CASE_SIG( ... ) INTERNAL_CATCH_TEMPLATE_PRODUCT_TEST_CASE_SIG( __VA_ARGS__ ) +#define TEMPLATE_PRODUCT_TEST_CASE_METHOD( className, ... ) INTERNAL_CATCH_TEMPLATE_PRODUCT_TEST_CASE_METHOD( className, __VA_ARGS__ ) +#define TEMPLATE_PRODUCT_TEST_CASE_METHOD_SIG( className, ... ) INTERNAL_CATCH_TEMPLATE_PRODUCT_TEST_CASE_METHOD_SIG( className, __VA_ARGS__ ) +#define TEMPLATE_LIST_TEST_CASE( ... ) INTERNAL_CATCH_TEMPLATE_LIST_TEST_CASE(__VA_ARGS__) +#define TEMPLATE_LIST_TEST_CASE_METHOD( className, ... ) INTERNAL_CATCH_TEMPLATE_LIST_TEST_CASE_METHOD( className, __VA_ARGS__ ) #else - #define TEST_CASE( name, description ) INTERNAL_CATCH_TESTCASE( name, description ) - #define TEST_CASE_METHOD( className, name, description ) INTERNAL_CATCH_TEST_CASE_METHOD( className, name, description ) - #define METHOD_AS_TEST_CASE( method, name, description ) INTERNAL_CATCH_METHOD_AS_TEST_CASE( method, name, description ) - #define REGISTER_TEST_CASE( method, name, description ) INTERNAL_CATCH_REGISTER_TESTCASE( method, name, description ) - #define SECTION( name, description ) INTERNAL_CATCH_SECTION( name, description ) - #define FAIL( msg ) INTERNAL_CATCH_MSG( Catch::ResultWas::ExplicitFailure, Catch::ResultDisposition::Normal, "FAIL", msg ) - #define SUCCEED( msg ) INTERNAL_CATCH_MSG( Catch::ResultWas::Ok, Catch::ResultDisposition::ContinueOnFailure, "SUCCEED", msg ) +#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 -#define ANON_TEST_CASE() INTERNAL_CATCH_TESTCASE( "", "" ) - -#define REGISTER_REPORTER( name, reporterType ) INTERNAL_CATCH_REGISTER_REPORTER( name, reporterType ) -#define REGISTER_LEGACY_REPORTER( name, reporterType ) INTERNAL_CATCH_REGISTER_LEGACY_REPORTER( name, reporterType ) -#define GENERATE( expr) INTERNAL_CATCH_GENERATE( expr ) +#if !defined(CATCH_CONFIG_RUNTIME_STATIC_REQUIRE) +#define STATIC_REQUIRE( ... ) static_assert( __VA_ARGS__, #__VA_ARGS__ ); SUCCEED( #__VA_ARGS__ ) +#define STATIC_REQUIRE_FALSE( ... ) static_assert( !(__VA_ARGS__), "!(" #__VA_ARGS__ ")" ); SUCCEED( "!(" #__VA_ARGS__ ")" ) +#else +#define STATIC_REQUIRE( ... ) REQUIRE( __VA_ARGS__ ) +#define STATIC_REQUIRE_FALSE( ... ) REQUIRE_FALSE( __VA_ARGS__ ) +#endif #endif #define CATCH_TRANSLATE_EXCEPTION( signature ) INTERNAL_CATCH_TRANSLATE_EXCEPTION( signature ) // "BDD-style" convenience wrappers -#ifdef CATCH_CONFIG_VARIADIC_MACROS #define SCENARIO( ... ) TEST_CASE( "Scenario: " __VA_ARGS__ ) #define SCENARIO_METHOD( className, ... ) INTERNAL_CATCH_TEST_CASE_METHOD( className, "Scenario: " __VA_ARGS__ ) + +#define GIVEN( desc ) INTERNAL_CATCH_DYNAMIC_SECTION( " Given: " << desc ) +#define AND_GIVEN( desc ) INTERNAL_CATCH_DYNAMIC_SECTION( "And given: " << desc ) +#define WHEN( desc ) INTERNAL_CATCH_DYNAMIC_SECTION( " When: " << desc ) +#define AND_WHEN( desc ) INTERNAL_CATCH_DYNAMIC_SECTION( " And when: " << desc ) +#define THEN( desc ) INTERNAL_CATCH_DYNAMIC_SECTION( " Then: " << desc ) +#define AND_THEN( desc ) INTERNAL_CATCH_DYNAMIC_SECTION( " And: " << desc ) + +#if defined(CATCH_CONFIG_ENABLE_BENCHMARKING) +#define BENCHMARK(...) \ + INTERNAL_CATCH_BENCHMARK(INTERNAL_CATCH_UNIQUE_NAME(____C_A_T_C_H____B_E_N_C_H____), INTERNAL_CATCH_GET_1_ARG(__VA_ARGS__,,), INTERNAL_CATCH_GET_2_ARG(__VA_ARGS__,,)) +#define BENCHMARK_ADVANCED(name) \ + INTERNAL_CATCH_BENCHMARK_ADVANCED(INTERNAL_CATCH_UNIQUE_NAME(____C_A_T_C_H____B_E_N_C_H____), name) +#endif // CATCH_CONFIG_ENABLE_BENCHMARKING + +using Catch::Detail::Approx; + +#else // CATCH_CONFIG_DISABLE + +////// +// If this config identifier is defined then all CATCH macros are prefixed with CATCH_ +#ifdef CATCH_CONFIG_PREFIX_ALL + +#define CATCH_REQUIRE( ... ) (void)(0) +#define CATCH_REQUIRE_FALSE( ... ) (void)(0) + +#define CATCH_REQUIRE_THROWS( ... ) (void)(0) +#define CATCH_REQUIRE_THROWS_AS( expr, exceptionType ) (void)(0) +#define CATCH_REQUIRE_THROWS_WITH( expr, matcher ) (void)(0) +#if !defined(CATCH_CONFIG_DISABLE_MATCHERS) +#define CATCH_REQUIRE_THROWS_MATCHES( expr, exceptionType, matcher ) (void)(0) +#endif// CATCH_CONFIG_DISABLE_MATCHERS +#define CATCH_REQUIRE_NOTHROW( ... ) (void)(0) + +#define CATCH_CHECK( ... ) (void)(0) +#define CATCH_CHECK_FALSE( ... ) (void)(0) +#define CATCH_CHECKED_IF( ... ) if (__VA_ARGS__) +#define CATCH_CHECKED_ELSE( ... ) if (!(__VA_ARGS__)) +#define CATCH_CHECK_NOFAIL( ... ) (void)(0) + +#define CATCH_CHECK_THROWS( ... ) (void)(0) +#define CATCH_CHECK_THROWS_AS( expr, exceptionType ) (void)(0) +#define CATCH_CHECK_THROWS_WITH( expr, matcher ) (void)(0) +#if !defined(CATCH_CONFIG_DISABLE_MATCHERS) +#define CATCH_CHECK_THROWS_MATCHES( expr, exceptionType, matcher ) (void)(0) +#endif // CATCH_CONFIG_DISABLE_MATCHERS +#define CATCH_CHECK_NOTHROW( ... ) (void)(0) + +#if !defined(CATCH_CONFIG_DISABLE_MATCHERS) +#define CATCH_CHECK_THAT( arg, matcher ) (void)(0) + +#define CATCH_REQUIRE_THAT( arg, matcher ) (void)(0) +#endif // CATCH_CONFIG_DISABLE_MATCHERS + +#define CATCH_INFO( msg ) (void)(0) +#define CATCH_UNSCOPED_INFO( msg ) (void)(0) +#define CATCH_WARN( msg ) (void)(0) +#define CATCH_CAPTURE( msg ) (void)(0) + +#define CATCH_TEST_CASE( ... ) INTERNAL_CATCH_TESTCASE_NO_REGISTRATION(INTERNAL_CATCH_UNIQUE_NAME( ____C_A_T_C_H____T_E_S_T____ )) +#define CATCH_TEST_CASE_METHOD( className, ... ) INTERNAL_CATCH_TESTCASE_NO_REGISTRATION(INTERNAL_CATCH_UNIQUE_NAME( ____C_A_T_C_H____T_E_S_T____ )) +#define CATCH_METHOD_AS_TEST_CASE( method, ... ) +#define CATCH_REGISTER_TEST_CASE( Function, ... ) (void)(0) +#define CATCH_SECTION( ... ) +#define CATCH_DYNAMIC_SECTION( ... ) +#define CATCH_FAIL( ... ) (void)(0) +#define CATCH_FAIL_CHECK( ... ) (void)(0) +#define CATCH_SUCCEED( ... ) (void)(0) + +#define CATCH_ANON_TEST_CASE() INTERNAL_CATCH_TESTCASE_NO_REGISTRATION(INTERNAL_CATCH_UNIQUE_NAME( ____C_A_T_C_H____T_E_S_T____ )) + +#ifndef CATCH_CONFIG_TRADITIONAL_MSVC_PREPROCESSOR +#define CATCH_TEMPLATE_TEST_CASE( ... ) INTERNAL_CATCH_TEMPLATE_TEST_CASE_NO_REGISTRATION(__VA_ARGS__) +#define CATCH_TEMPLATE_TEST_CASE_SIG( ... ) INTERNAL_CATCH_TEMPLATE_TEST_CASE_SIG_NO_REGISTRATION(__VA_ARGS__) +#define CATCH_TEMPLATE_TEST_CASE_METHOD( className, ... ) INTERNAL_CATCH_TEMPLATE_TEST_CASE_METHOD_NO_REGISTRATION(className, __VA_ARGS__) +#define CATCH_TEMPLATE_TEST_CASE_METHOD_SIG( className, ... ) INTERNAL_CATCH_TEMPLATE_TEST_CASE_METHOD_SIG_NO_REGISTRATION(className, __VA_ARGS__ ) +#define CATCH_TEMPLATE_PRODUCT_TEST_CASE( ... ) CATCH_TEMPLATE_TEST_CASE( __VA_ARGS__ ) +#define CATCH_TEMPLATE_PRODUCT_TEST_CASE_SIG( ... ) CATCH_TEMPLATE_TEST_CASE( __VA_ARGS__ ) +#define CATCH_TEMPLATE_PRODUCT_TEST_CASE_METHOD( className, ... ) CATCH_TEMPLATE_TEST_CASE_METHOD( className, __VA_ARGS__ ) +#define CATCH_TEMPLATE_PRODUCT_TEST_CASE_METHOD_SIG( className, ... ) CATCH_TEMPLATE_TEST_CASE_METHOD( className, __VA_ARGS__ ) #else -#define SCENARIO( name, tags ) TEST_CASE( "Scenario: " name, tags ) -#define SCENARIO_METHOD( className, name, tags ) INTERNAL_CATCH_TEST_CASE_METHOD( className, "Scenario: " name, tags ) +#define CATCH_TEMPLATE_TEST_CASE( ... ) INTERNAL_CATCH_EXPAND_VARGS( INTERNAL_CATCH_TEMPLATE_TEST_CASE_NO_REGISTRATION(__VA_ARGS__) ) +#define CATCH_TEMPLATE_TEST_CASE_SIG( ... ) INTERNAL_CATCH_EXPAND_VARGS( INTERNAL_CATCH_TEMPLATE_TEST_CASE_SIG_NO_REGISTRATION(__VA_ARGS__) ) +#define CATCH_TEMPLATE_TEST_CASE_METHOD( className, ... ) INTERNAL_CATCH_EXPAND_VARGS( INTERNAL_CATCH_TEMPLATE_TEST_CASE_METHOD_NO_REGISTRATION(className, __VA_ARGS__ ) ) +#define CATCH_TEMPLATE_TEST_CASE_METHOD_SIG( className, ... ) INTERNAL_CATCH_EXPAND_VARGS( INTERNAL_CATCH_TEMPLATE_TEST_CASE_METHOD_SIG_NO_REGISTRATION(className, __VA_ARGS__ ) ) +#define CATCH_TEMPLATE_PRODUCT_TEST_CASE( ... ) CATCH_TEMPLATE_TEST_CASE( __VA_ARGS__ ) +#define CATCH_TEMPLATE_PRODUCT_TEST_CASE_SIG( ... ) CATCH_TEMPLATE_TEST_CASE( __VA_ARGS__ ) +#define CATCH_TEMPLATE_PRODUCT_TEST_CASE_METHOD( className, ... ) CATCH_TEMPLATE_TEST_CASE_METHOD( className, __VA_ARGS__ ) +#define CATCH_TEMPLATE_PRODUCT_TEST_CASE_METHOD_SIG( className, ... ) CATCH_TEMPLATE_TEST_CASE_METHOD( className, __VA_ARGS__ ) #endif -#define GIVEN( desc ) SECTION( std::string(" Given: ") + desc, "" ) -#define WHEN( desc ) SECTION( std::string(" When: ") + desc, "" ) -#define AND_WHEN( desc ) SECTION( std::string("And when: ") + desc, "" ) -#define THEN( desc ) SECTION( std::string(" Then: ") + desc, "" ) -#define AND_THEN( desc ) SECTION( std::string(" And: ") + desc, "" ) + +// "BDD-style" convenience wrappers +#define CATCH_SCENARIO( ... ) INTERNAL_CATCH_TESTCASE_NO_REGISTRATION(INTERNAL_CATCH_UNIQUE_NAME( ____C_A_T_C_H____T_E_S_T____ )) +#define CATCH_SCENARIO_METHOD( className, ... ) INTERNAL_CATCH_TESTCASE_METHOD_NO_REGISTRATION(INTERNAL_CATCH_UNIQUE_NAME( ____C_A_T_C_H____T_E_S_T____ ), className ) +#define CATCH_GIVEN( desc ) +#define CATCH_AND_GIVEN( desc ) +#define CATCH_WHEN( desc ) +#define CATCH_AND_WHEN( desc ) +#define CATCH_THEN( desc ) +#define CATCH_AND_THEN( desc ) + +#define CATCH_STATIC_REQUIRE( ... ) (void)(0) +#define CATCH_STATIC_REQUIRE_FALSE( ... ) (void)(0) + +// If CATCH_CONFIG_PREFIX_ALL is not defined then the CATCH_ prefix is not required +#else + +#define REQUIRE( ... ) (void)(0) +#define REQUIRE_FALSE( ... ) (void)(0) + +#define REQUIRE_THROWS( ... ) (void)(0) +#define REQUIRE_THROWS_AS( expr, exceptionType ) (void)(0) +#define REQUIRE_THROWS_WITH( expr, matcher ) (void)(0) +#if !defined(CATCH_CONFIG_DISABLE_MATCHERS) +#define REQUIRE_THROWS_MATCHES( expr, exceptionType, matcher ) (void)(0) +#endif // CATCH_CONFIG_DISABLE_MATCHERS +#define REQUIRE_NOTHROW( ... ) (void)(0) + +#define CHECK( ... ) (void)(0) +#define CHECK_FALSE( ... ) (void)(0) +#define CHECKED_IF( ... ) if (__VA_ARGS__) +#define CHECKED_ELSE( ... ) if (!(__VA_ARGS__)) +#define CHECK_NOFAIL( ... ) (void)(0) + +#define CHECK_THROWS( ... ) (void)(0) +#define CHECK_THROWS_AS( expr, exceptionType ) (void)(0) +#define CHECK_THROWS_WITH( expr, matcher ) (void)(0) +#if !defined(CATCH_CONFIG_DISABLE_MATCHERS) +#define CHECK_THROWS_MATCHES( expr, exceptionType, matcher ) (void)(0) +#endif // CATCH_CONFIG_DISABLE_MATCHERS +#define CHECK_NOTHROW( ... ) (void)(0) + +#if !defined(CATCH_CONFIG_DISABLE_MATCHERS) +#define CHECK_THAT( arg, matcher ) (void)(0) + +#define REQUIRE_THAT( arg, matcher ) (void)(0) +#endif // CATCH_CONFIG_DISABLE_MATCHERS + +#define INFO( msg ) (void)(0) +#define UNSCOPED_INFO( msg ) (void)(0) +#define WARN( msg ) (void)(0) +#define CAPTURE( msg ) (void)(0) + +#define TEST_CASE( ... ) INTERNAL_CATCH_TESTCASE_NO_REGISTRATION(INTERNAL_CATCH_UNIQUE_NAME( ____C_A_T_C_H____T_E_S_T____ )) +#define TEST_CASE_METHOD( className, ... ) INTERNAL_CATCH_TESTCASE_NO_REGISTRATION(INTERNAL_CATCH_UNIQUE_NAME( ____C_A_T_C_H____T_E_S_T____ )) +#define METHOD_AS_TEST_CASE( method, ... ) +#define REGISTER_TEST_CASE( Function, ... ) (void)(0) +#define SECTION( ... ) +#define DYNAMIC_SECTION( ... ) +#define FAIL( ... ) (void)(0) +#define FAIL_CHECK( ... ) (void)(0) +#define SUCCEED( ... ) (void)(0) +#define ANON_TEST_CASE() INTERNAL_CATCH_TESTCASE_NO_REGISTRATION(INTERNAL_CATCH_UNIQUE_NAME( ____C_A_T_C_H____T_E_S_T____ )) + +#ifndef CATCH_CONFIG_TRADITIONAL_MSVC_PREPROCESSOR +#define TEMPLATE_TEST_CASE( ... ) INTERNAL_CATCH_TEMPLATE_TEST_CASE_NO_REGISTRATION(__VA_ARGS__) +#define TEMPLATE_TEST_CASE_SIG( ... ) INTERNAL_CATCH_TEMPLATE_TEST_CASE_SIG_NO_REGISTRATION(__VA_ARGS__) +#define TEMPLATE_TEST_CASE_METHOD( className, ... ) INTERNAL_CATCH_TEMPLATE_TEST_CASE_METHOD_NO_REGISTRATION(className, __VA_ARGS__) +#define TEMPLATE_TEST_CASE_METHOD_SIG( className, ... ) INTERNAL_CATCH_TEMPLATE_TEST_CASE_METHOD_SIG_NO_REGISTRATION(className, __VA_ARGS__ ) +#define TEMPLATE_PRODUCT_TEST_CASE( ... ) TEMPLATE_TEST_CASE( __VA_ARGS__ ) +#define TEMPLATE_PRODUCT_TEST_CASE_SIG( ... ) TEMPLATE_TEST_CASE( __VA_ARGS__ ) +#define TEMPLATE_PRODUCT_TEST_CASE_METHOD( className, ... ) TEMPLATE_TEST_CASE_METHOD( className, __VA_ARGS__ ) +#define TEMPLATE_PRODUCT_TEST_CASE_METHOD_SIG( className, ... ) TEMPLATE_TEST_CASE_METHOD( className, __VA_ARGS__ ) +#else +#define TEMPLATE_TEST_CASE( ... ) INTERNAL_CATCH_EXPAND_VARGS( INTERNAL_CATCH_TEMPLATE_TEST_CASE_NO_REGISTRATION(__VA_ARGS__) ) +#define TEMPLATE_TEST_CASE_SIG( ... ) INTERNAL_CATCH_EXPAND_VARGS( INTERNAL_CATCH_TEMPLATE_TEST_CASE_SIG_NO_REGISTRATION(__VA_ARGS__) ) +#define TEMPLATE_TEST_CASE_METHOD( className, ... ) INTERNAL_CATCH_EXPAND_VARGS( INTERNAL_CATCH_TEMPLATE_TEST_CASE_METHOD_NO_REGISTRATION(className, __VA_ARGS__ ) ) +#define TEMPLATE_TEST_CASE_METHOD_SIG( className, ... ) INTERNAL_CATCH_EXPAND_VARGS( INTERNAL_CATCH_TEMPLATE_TEST_CASE_METHOD_SIG_NO_REGISTRATION(className, __VA_ARGS__ ) ) +#define TEMPLATE_PRODUCT_TEST_CASE( ... ) TEMPLATE_TEST_CASE( __VA_ARGS__ ) +#define TEMPLATE_PRODUCT_TEST_CASE_SIG( ... ) TEMPLATE_TEST_CASE( __VA_ARGS__ ) +#define TEMPLATE_PRODUCT_TEST_CASE_METHOD( className, ... ) TEMPLATE_TEST_CASE_METHOD( className, __VA_ARGS__ ) +#define TEMPLATE_PRODUCT_TEST_CASE_METHOD_SIG( className, ... ) TEMPLATE_TEST_CASE_METHOD( className, __VA_ARGS__ ) +#endif + +#define STATIC_REQUIRE( ... ) (void)(0) +#define STATIC_REQUIRE_FALSE( ... ) (void)(0) + +#endif + +#define CATCH_TRANSLATE_EXCEPTION( signature ) INTERNAL_CATCH_TRANSLATE_EXCEPTION_NO_REG( INTERNAL_CATCH_UNIQUE_NAME( catch_internal_ExceptionTranslator ), signature ) + +// "BDD-style" convenience wrappers +#define SCENARIO( ... ) INTERNAL_CATCH_TESTCASE_NO_REGISTRATION(INTERNAL_CATCH_UNIQUE_NAME( ____C_A_T_C_H____T_E_S_T____ ) ) +#define SCENARIO_METHOD( className, ... ) INTERNAL_CATCH_TESTCASE_METHOD_NO_REGISTRATION(INTERNAL_CATCH_UNIQUE_NAME( ____C_A_T_C_H____T_E_S_T____ ), className ) + +#define GIVEN( desc ) +#define AND_GIVEN( desc ) +#define WHEN( desc ) +#define AND_WHEN( desc ) +#define THEN( desc ) +#define AND_THEN( desc ) using Catch::Detail::Approx; +#endif + +#endif // ! CATCH_CONFIG_IMPL_ONLY + +// start catch_reenable_warnings.h + + +#ifdef __clang__ +# ifdef __ICC // icpc defines the __clang__ macro +# pragma warning(pop) +# else +# pragma clang diagnostic pop +# endif +#elif defined __GNUC__ +# pragma GCC diagnostic pop +#endif + +// end catch_reenable_warnings.h +// end catch.hpp #endif // TWOBLUECUBES_SINGLE_INCLUDE_CATCH_HPP_INCLUDED diff --git a/3rdparty/bx/LICENSE b/3rdparty/bx/LICENSE index 3df43e30a2a..1c8e1e05472 100644 --- a/3rdparty/bx/LICENSE +++ b/3rdparty/bx/LICENSE @@ -1,4 +1,4 @@ -Copyright 2010-2018 Branimir Karadzic. All rights reserved. +Copyright 2010-2019 Branimir Karadzic Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: @@ -10,13 +10,13 @@ are permitted provided that the following conditions are met: this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. -THIS SOFTWARE IS PROVIDED BY COPYRIGHT HOLDER ``AS IS'' AND ANY EXPRESS OR -IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF -MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT -SHALL COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, -INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT -LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR -PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, -WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND +ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED +WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. +IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, +INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, +BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY +OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. diff --git a/3rdparty/bx/README.md b/3rdparty/bx/README.md index 1d987c34f7f..00d6d8a0205 100644 --- a/3rdparty/bx/README.md +++ b/3rdparty/bx/README.md @@ -23,27 +23,25 @@ https://github.com/bkaradzic/bx <img align="right" src="http://opensource.org/trademarks/opensource/OSI-Approved-License-100x137.png"> </a> - Copyright 2010-2018 Branimir Karadzic. All rights reserved. + Copyright 2010-2019 Branimir Karadzic - https://github.com/bkaradzic/bx + Redistribution and use in source and binary forms, with or without modification, + are permitted provided that the following conditions are met: - Redistribution and use in source and binary forms, with or without - modification, are permitted provided that the following conditions are met: - - 1. Redistributions of source code must retain the above copyright notice, - this list of conditions and the following disclaimer. + 1. Redistributions of source code must retain the above copyright notice, this + list of conditions and the following disclaimer. 2. Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. - THIS SOFTWARE IS PROVIDED BY COPYRIGHT HOLDER ``AS IS'' AND ANY EXPRESS OR - IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF - MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO - EVENT SHALL COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, + THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND + ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. + IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, - DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF - LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE - OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF - ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY + OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE + OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED + OF THE POSSIBILITY OF SUCH DAMAGE. diff --git a/3rdparty/bx/include/bx/allocator.h b/3rdparty/bx/include/bx/allocator.h index cfd1475ab52..7a140b0b562 100644 --- a/3rdparty/bx/include/bx/allocator.h +++ b/3rdparty/bx/include/bx/allocator.h @@ -1,5 +1,5 @@ /* - * Copyright 2010-2018 Branimir Karadzic. All rights reserved. + * Copyright 2010-2019 Branimir Karadzic. All rights reserved. * License: https://github.com/bkaradzic/bx#license-bsd-2-clause */ diff --git a/3rdparty/bx/include/bx/bx.h b/3rdparty/bx/include/bx/bx.h index 7c2d06162aa..2cfc676d795 100644 --- a/3rdparty/bx/include/bx/bx.h +++ b/3rdparty/bx/include/bx/bx.h @@ -1,5 +1,5 @@ /* - * Copyright 2010-2018 Branimir Karadzic. All rights reserved. + * Copyright 2010-2019 Branimir Karadzic. All rights reserved. * License: https://github.com/bkaradzic/bx#license-bsd-2-clause */ @@ -73,27 +73,76 @@ namespace bx template<typename Ty> constexpr bool isPowerOf2(Ty _a); + /// Copy memory block. + /// + /// @param _dst Destination pointer. + /// @param _src Source pointer. + /// @param _numBytes Number of bytes to copy. + /// + /// @remark Source and destination memory blocks must not overlap. /// void memCopy(void* _dst, const void* _src, size_t _numBytes); + /// Copy strided memory block. /// - void memCopy(void* _dst, const void* _src, uint32_t _size, uint32_t _num, uint32_t _srcPitch, uint32_t _dstPitch); - + /// @param _dst Destination pointer. + /// @param _dstStride Destination stride. + /// @param _src Source pointer. + /// @param _srcStride Source stride. + /// @param _stride Number of bytes per stride to copy. + /// @param _num Number of strides. /// - void gather(void* _dst, const void* _src, uint32_t _size, uint32_t _num, uint32_t _srcPitch); - + /// @remark Source and destination memory blocks must not overlap. /// - void scatter(void* _dst, const void* _src, uint32_t _size, uint32_t _num, uint32_t _dstPitch); + void memCopy( + void* _dst + , uint32_t _dstStride + , const void* _src + , uint32_t _srcStride + , uint32_t _stride + , uint32_t _num + ); /// void memMove(void* _dst, const void* _src, size_t _numBytes); /// + void memMove( + void* _dst + , uint32_t _dstStride + , const void* _src + , uint32_t _srcStride + , uint32_t _stride + , uint32_t _num + ); + + /// void memSet(void* _dst, uint8_t _ch, size_t _numBytes); /// + void memSet(void* _dst, uint32_t _dstStride, uint8_t _ch, uint32_t _stride, uint32_t _num); + + /// int32_t memCmp(const void* _lhs, const void* _rhs, size_t _numBytes); + /// + void gather( + void* _dst + , const void* _src + , uint32_t _srcStride + , uint32_t _stride + , uint32_t _num + ); + + /// + void scatter( + void* _dst + , uint32_t _dstStride + , const void* _src + , uint32_t _stride + , uint32_t _num + ); + } // namespace bx #include "inline/bx.inl" diff --git a/3rdparty/bx/include/bx/commandline.h b/3rdparty/bx/include/bx/commandline.h index dbd2af39427..058b51c5a5a 100644 --- a/3rdparty/bx/include/bx/commandline.h +++ b/3rdparty/bx/include/bx/commandline.h @@ -1,5 +1,5 @@ /* - * Copyright 2010-2018 Branimir Karadzic. All rights reserved. + * Copyright 2010-2019 Branimir Karadzic. All rights reserved. * License: https://github.com/bkaradzic/bx#license-bsd-2-clause */ diff --git a/3rdparty/bx/include/bx/config.h b/3rdparty/bx/include/bx/config.h index 787859de6cf..247968a04d7 100644 --- a/3rdparty/bx/include/bx/config.h +++ b/3rdparty/bx/include/bx/config.h @@ -1,5 +1,5 @@ /* - * Copyright 2010-2018 Branimir Karadzic. All rights reserved. + * Copyright 2010-2019 Branimir Karadzic. All rights reserved. * License: https://github.com/bkaradzic/bx#license-bsd-2-clause */ diff --git a/3rdparty/bx/include/bx/cpu.h b/3rdparty/bx/include/bx/cpu.h index a2e81a1a1ca..9cadf408bd0 100644 --- a/3rdparty/bx/include/bx/cpu.h +++ b/3rdparty/bx/include/bx/cpu.h @@ -1,5 +1,5 @@ /* - * Copyright 2010-2018 Branimir Karadzic. All rights reserved. + * Copyright 2010-2019 Branimir Karadzic. All rights reserved. * License: https://github.com/bkaradzic/bx#license-bsd-2-clause */ diff --git a/3rdparty/bx/include/bx/debug.h b/3rdparty/bx/include/bx/debug.h index 80efaf97c87..b5a9bc0534e 100644 --- a/3rdparty/bx/include/bx/debug.h +++ b/3rdparty/bx/include/bx/debug.h @@ -1,5 +1,5 @@ /* - * Copyright 2010-2018 Branimir Karadzic. All rights reserved. + * Copyright 2010-2019 Branimir Karadzic. All rights reserved. * License: https://github.com/bkaradzic/bx#license-bsd-2-clause */ diff --git a/3rdparty/bx/include/bx/easing.h b/3rdparty/bx/include/bx/easing.h index 8581b975c9c..034999081cf 100644 --- a/3rdparty/bx/include/bx/easing.h +++ b/3rdparty/bx/include/bx/easing.h @@ -1,5 +1,5 @@ /* - * Copyright 2011-2018 Branimir Karadzic. All rights reserved. + * Copyright 2011-2019 Branimir Karadzic. All rights reserved. * License: https://github.com/bkaradzic/bx#license-bsd-2-clause */ diff --git a/3rdparty/bx/include/bx/endian.h b/3rdparty/bx/include/bx/endian.h index 22965b5f31a..f9eeba0ec54 100644 --- a/3rdparty/bx/include/bx/endian.h +++ b/3rdparty/bx/include/bx/endian.h @@ -1,5 +1,5 @@ /* - * Copyright 2010-2018 Branimir Karadzic. All rights reserved. + * Copyright 2010-2019 Branimir Karadzic. All rights reserved. * License: https://github.com/bkaradzic/bx#license-bsd-2-clause */ diff --git a/3rdparty/bx/include/bx/error.h b/3rdparty/bx/include/bx/error.h index 1eb220e7be0..f1416b64a7b 100644 --- a/3rdparty/bx/include/bx/error.h +++ b/3rdparty/bx/include/bx/error.h @@ -1,5 +1,5 @@ /* - * Copyright 2010-2018 Branimir Karadzic. All rights reserved. + * Copyright 2010-2019 Branimir Karadzic. All rights reserved. * License: https://github.com/bkaradzic/bx#license-bsd-2-clause */ diff --git a/3rdparty/bx/include/bx/file.h b/3rdparty/bx/include/bx/file.h index 4c0dd415f1b..42c15ba9622 100644 --- a/3rdparty/bx/include/bx/file.h +++ b/3rdparty/bx/include/bx/file.h @@ -1,5 +1,5 @@ /* - * Copyright 2010-2018 Branimir Karadzic. All rights reserved. + * Copyright 2010-2019 Branimir Karadzic. All rights reserved. * License: https://github.com/bkaradzic/bx#license-bsd-2-clause */ @@ -11,19 +11,19 @@ namespace bx { - /// + /// Returns standard input reader. ReaderI* getStdIn(); - /// + /// Returns standard output writer. WriterI* getStdOut(); - /// + /// Returns standard error writer. WriterI* getStdErr(); - /// + /// Returns null output writer. WriterI* getNullOut(); - /// + /// File reader. class FileReader : public FileReaderI { public: @@ -49,7 +49,7 @@ namespace bx BX_ALIGN_DECL(16, uint8_t) m_internal[64]; }; - /// + /// File writer. class FileWriter : public FileWriterI { public: @@ -75,23 +75,68 @@ namespace bx BX_ALIGN_DECL(16, uint8_t) m_internal[64]; }; - /// - struct FileInfo + /// File type. + struct FileType { + /// File types: enum Enum { - Regular, - Directory, + File, //!< File. + Dir, //!< Directory. Count }; + }; + + /// File info. + struct FileInfo + { + FilePath filePath; //!< File path. + uint64_t size; //!< File size. + FileType::Enum type; //!< File type. + }; + + /// Directory reader. + class DirectoryReader : public ReaderOpenI, public CloserI, public ReaderI + { + public: + /// + DirectoryReader(); + + /// + virtual ~DirectoryReader(); + + /// + virtual bool open(const FilePath& _filePath, Error* _err) override; + + /// + virtual void close() override; + + /// + virtual int32_t read(void* _data, int32_t _size, Error* _err) override; - uint64_t m_size; - Enum m_type; + private: + BX_ALIGN_DECL(16, uint8_t) m_internal[sizeof(FilePath)+sizeof(FileInfo)+16]; }; + /// FIle stat. + bool stat(FileInfo& _outFileInfo, const FilePath& _filePath); + + /// Creates a directory named `_filePath`. + /// + bool make(const FilePath& _filePath, Error* _err = NULL); + + /// Creates a directory named `_filePath` along with all necessary parents. + /// + bool makeAll(const FilePath& _filePath, Error* _err = NULL); + + /// Removes file or directory. + /// + bool remove(const FilePath& _filePath, Error* _err = NULL); + + /// Removes file or directory recursivelly. /// - bool stat(const FilePath& _filePath, FileInfo& _outFileInfo); + bool removeAll(const FilePath& _filePath, Error* _err = NULL); } // namespace bx diff --git a/3rdparty/bx/include/bx/filepath.h b/3rdparty/bx/include/bx/filepath.h index c71a1695cfb..920c0b4bbe2 100644 --- a/3rdparty/bx/include/bx/filepath.h +++ b/3rdparty/bx/include/bx/filepath.h @@ -1,5 +1,5 @@ /* - * Copyright 2010-2018 Branimir Karadzic. All rights reserved. + * Copyright 2010-2019 Branimir Karadzic. All rights reserved. * License: https://github.com/bkaradzic/bx#license-bsd-2-clause */ @@ -79,9 +79,13 @@ namespace bx /// void join(const StringView& _str); - /// Returns C string to file path. + /// Implicitly converts FilePath to StringView. /// - const char* get() const; + operator StringView() const; + + /// Returns zero-terminated C string pointer to file path. + /// + const char* getCPtr() const; /// If path is `/abv/gd/555/333/pod.mac` returns `/abv/gd/555/333/`. /// @@ -111,22 +115,6 @@ namespace bx char m_filePath[kMaxFilePath]; }; - /// Creates a directory named `_filePath`. - /// - bool make(const FilePath& _filePath, Error* _err = NULL); - - /// Creates a directory named `_filePath` along with all necessary parents. - /// - bool makeAll(const FilePath& _filePath, Error* _err = NULL); - - /// Removes file or directory. - /// - bool remove(const FilePath& _filePath, Error* _err = NULL); - - /// Removes file or directory recursivelly. - /// - bool removeAll(const FilePath& _filePath, Error* _err = NULL); - } // namespace bx #endif // BX_FILEPATH_H_HEADER_GUARD diff --git a/3rdparty/bx/include/bx/float4x4_t.h b/3rdparty/bx/include/bx/float4x4_t.h index 3250d929db3..9ec367f0739 100644 --- a/3rdparty/bx/include/bx/float4x4_t.h +++ b/3rdparty/bx/include/bx/float4x4_t.h @@ -1,5 +1,5 @@ /* - * Copyright 2010-2018 Branimir Karadzic. All rights reserved. + * Copyright 2010-2019 Branimir Karadzic. All rights reserved. * License: https://github.com/bkaradzic/bx#license-bsd-2-clause */ diff --git a/3rdparty/bx/include/bx/handlealloc.h b/3rdparty/bx/include/bx/handlealloc.h index 21b07ccca63..a4432b9d176 100644 --- a/3rdparty/bx/include/bx/handlealloc.h +++ b/3rdparty/bx/include/bx/handlealloc.h @@ -1,5 +1,5 @@ /* - * Copyright 2010-2018 Branimir Karadzic. All rights reserved. + * Copyright 2010-2019 Branimir Karadzic. All rights reserved. * License: https://github.com/bkaradzic/bx#license-bsd-2-clause */ diff --git a/3rdparty/bx/include/bx/hash.h b/3rdparty/bx/include/bx/hash.h index 9ced6947c9f..976fa21acb7 100644 --- a/3rdparty/bx/include/bx/hash.h +++ b/3rdparty/bx/include/bx/hash.h @@ -1,5 +1,5 @@ /* - * Copyright 2010-2018 Branimir Karadzic. All rights reserved. + * Copyright 2010-2019 Branimir Karadzic. All rights reserved. * License: https://github.com/bkaradzic/bx#license-bsd-2-clause */ diff --git a/3rdparty/bx/include/bx/inline/allocator.inl b/3rdparty/bx/include/bx/inline/allocator.inl index 1ff5f647345..ce4ccc92a72 100644 --- a/3rdparty/bx/include/bx/inline/allocator.inl +++ b/3rdparty/bx/include/bx/inline/allocator.inl @@ -1,5 +1,5 @@ /* - * Copyright 2010-2018 Branimir Karadzic. All rights reserved. + * Copyright 2010-2019 Branimir Karadzic. All rights reserved. * License: https://github.com/bkaradzic/bx#license-bsd-2-clause */ diff --git a/3rdparty/bx/include/bx/inline/bx.inl b/3rdparty/bx/include/bx/inline/bx.inl index c17649a5d69..b0083571ac4 100644 --- a/3rdparty/bx/include/bx/inline/bx.inl +++ b/3rdparty/bx/include/bx/inline/bx.inl @@ -1,5 +1,5 @@ /* - * Copyright 2010-2018 Branimir Karadzic. All rights reserved. + * Copyright 2010-2019 Branimir Karadzic. All rights reserved. * License: https://github.com/bkaradzic/bx#license-bsd-2-clause */ diff --git a/3rdparty/bx/include/bx/inline/cpu.inl b/3rdparty/bx/include/bx/inline/cpu.inl index 3ff2a231fd3..b5a83a4f68d 100644 --- a/3rdparty/bx/include/bx/inline/cpu.inl +++ b/3rdparty/bx/include/bx/inline/cpu.inl @@ -1,5 +1,5 @@ /* - * Copyright 2010-2018 Branimir Karadzic. All rights reserved. + * Copyright 2010-2019 Branimir Karadzic. All rights reserved. * License: https://github.com/bkaradzic/bx#license-bsd-2-clause */ @@ -12,7 +12,9 @@ # include <windows.h> # endif // BX_PLATFORM_WINRT -# include <emmintrin.h> // _mm_fence +# if BX_CPU_X86 +# include <emmintrin.h> // _mm_fence +# endif extern "C" void _ReadBarrier(); # pragma intrinsic(_ReadBarrier) diff --git a/3rdparty/bx/include/bx/inline/easing.inl b/3rdparty/bx/include/bx/inline/easing.inl index 3cdab9a77cb..02bd7d4e225 100644 --- a/3rdparty/bx/include/bx/inline/easing.inl +++ b/3rdparty/bx/include/bx/inline/easing.inl @@ -1,5 +1,5 @@ /* - * Copyright 2011-2018 Branimir Karadzic. All rights reserved. + * Copyright 2011-2019 Branimir Karadzic. All rights reserved. * License: https://github.com/bkaradzic/bx#license-bsd-2-clause */ diff --git a/3rdparty/bx/include/bx/inline/endian.inl b/3rdparty/bx/include/bx/inline/endian.inl index 332e2b6102a..c0fd7480c7a 100644 --- a/3rdparty/bx/include/bx/inline/endian.inl +++ b/3rdparty/bx/include/bx/inline/endian.inl @@ -1,5 +1,5 @@ /* - * Copyright 2010-2018 Branimir Karadzic. All rights reserved. + * Copyright 2010-2019 Branimir Karadzic. All rights reserved. * License: https://github.com/bkaradzic/bx#license-bsd-2-clause */ @@ -46,7 +46,7 @@ namespace bx } template <typename Ty> - inline Ty toLittleEndian(const Ty _in) + inline Ty toLittleEndian(Ty _in) { #if BX_CPU_ENDIAN_BIG return endianSwap(_in); @@ -56,7 +56,7 @@ namespace bx } template <typename Ty> - inline Ty toBigEndian(const Ty _in) + inline Ty toBigEndian(Ty _in) { #if BX_CPU_ENDIAN_LITTLE return endianSwap(_in); @@ -66,7 +66,7 @@ namespace bx } template <typename Ty> - inline Ty toHostEndian(const Ty _in, bool _fromLittleEndian) + inline Ty toHostEndian(Ty _in, bool _fromLittleEndian) { #if BX_CPU_ENDIAN_LITTLE return _fromLittleEndian ? _in : endianSwap(_in); diff --git a/3rdparty/bx/include/bx/inline/error.inl b/3rdparty/bx/include/bx/inline/error.inl index af5c31a1991..badb75633b0 100644 --- a/3rdparty/bx/include/bx/inline/error.inl +++ b/3rdparty/bx/include/bx/inline/error.inl @@ -1,5 +1,5 @@ /* - * Copyright 2010-2018 Branimir Karadzic. All rights reserved. + * Copyright 2010-2019 Branimir Karadzic. All rights reserved. * License: https://github.com/bkaradzic/bx#license-bsd-2-clause */ diff --git a/3rdparty/bx/include/bx/inline/float4x4_t.inl b/3rdparty/bx/include/bx/inline/float4x4_t.inl index 222c111c361..27bf00d5618 100644 --- a/3rdparty/bx/include/bx/inline/float4x4_t.inl +++ b/3rdparty/bx/include/bx/inline/float4x4_t.inl @@ -1,5 +1,5 @@ /* - * Copyright 2010-2018 Branimir Karadzic. All rights reserved. + * Copyright 2010-2019 Branimir Karadzic. All rights reserved. * License: https://github.com/bkaradzic/bx#license-bsd-2-clause */ diff --git a/3rdparty/bx/include/bx/inline/handlealloc.inl b/3rdparty/bx/include/bx/inline/handlealloc.inl index 3bb364b66b3..94b7ebbda4a 100644 --- a/3rdparty/bx/include/bx/inline/handlealloc.inl +++ b/3rdparty/bx/include/bx/inline/handlealloc.inl @@ -1,5 +1,5 @@ /* - * Copyright 2010-2018 Branimir Karadzic. All rights reserved. + * Copyright 2010-2019 Branimir Karadzic. All rights reserved. * License: https://github.com/bkaradzic/bx#license-bsd-2-clause */ diff --git a/3rdparty/bx/include/bx/inline/hash.inl b/3rdparty/bx/include/bx/inline/hash.inl index 18bee143c70..56430e1470b 100644 --- a/3rdparty/bx/include/bx/inline/hash.inl +++ b/3rdparty/bx/include/bx/inline/hash.inl @@ -1,5 +1,5 @@ /* - * Copyright 2010-2018 Branimir Karadzic. All rights reserved. + * Copyright 2010-2019 Branimir Karadzic. All rights reserved. * License: https://github.com/bkaradzic/bx#license-bsd-2-clause */ diff --git a/3rdparty/bx/include/bx/inline/math.inl b/3rdparty/bx/include/bx/inline/math.inl index 29c88797e94..81ce759ec99 100644 --- a/3rdparty/bx/include/bx/inline/math.inl +++ b/3rdparty/bx/include/bx/inline/math.inl @@ -1,5 +1,5 @@ /* - * Copyright 2011-2018 Branimir Karadzic. All rights reserved. + * Copyright 2011-2019 Branimir Karadzic. All rights reserved. * License: https://github.com/bkaradzic/bx#license-bsd-2-clause */ @@ -95,16 +95,32 @@ namespace bx return tmp == UINT64_C(0x7ff0000000000000); } - inline BX_CONST_FUNC float round(float _f) + inline BX_CONSTEXPR_FUNC float floor(float _a) { - return floor(_f + 0.5f); + if (_a < 0.0f) + { + const float fr = fract(-_a); + const float result = -_a - fr; + + return -(0.0f != fr + ? result + 1.0f + : result) + ; + } + + return _a - fract(_a); } - inline BX_CONST_FUNC float ceil(float _a) + inline BX_CONSTEXPR_FUNC float ceil(float _a) { return -floor(-_a); } + inline BX_CONSTEXPR_FUNC float round(float _f) + { + return floor(_f + 0.5f); + } + inline BX_CONSTEXPR_FUNC float lerp(float _a, float _b, float _t) { return _a + (_b - _a) * _t; @@ -261,7 +277,8 @@ namespace bx inline BX_CONSTEXPR_FUNC bool equal(float _a, float _b, float _epsilon) { // Reference(s): - // - https://web.archive.org/web/20181103180318/http://realtimecollisiondetection.net/blog/?p=89 + // - Floating-point tolerances revisited + // https://web.archive.org/web/20181103180318/http://realtimecollisiondetection.net/blog/?p=89 // const float lhs = abs(_a - _b); const float rhs = _epsilon * max(1.0f, abs(_a), abs(_b) ); @@ -331,26 +348,49 @@ namespace bx return _a + angleDiff(_a, _b) * _t; } - inline Vec3 load(const void* _ptr) + template<typename Ty> + inline Ty load(const void* _ptr) { - const float* ptr = reinterpret_cast<const float*>(_ptr); - return - { - ptr[0], - ptr[1], - ptr[2], - }; + Ty result; + memCopy(&result, _ptr, sizeof(Ty) ); + return result; + } + + template<typename Ty> + inline void store(void* _ptr, const Ty& _a) + { + memCopy(_ptr, &_a, sizeof(Ty) ); } - inline void store(void* _ptr, const Vec3& _a) + inline Vec3::Vec3() { - float* ptr = reinterpret_cast<float*>(_ptr); - ptr[0] = _a.x; - ptr[1] = _a.y; - ptr[2] = _a.z; } - inline BX_CONSTEXPR_FUNC Vec3 abs(const Vec3& _a) + constexpr Vec3::Vec3(float _v) + : x(_v) + , y(_v) + , z(_v) + { + } + + constexpr Vec3::Vec3(float _x, float _y, float _z) + : x(_x) + , y(_y) + , z(_z) + { + } + + inline BX_CONSTEXPR_FUNC Vec3 round(const Vec3 _a) + { + return + { + round(_a.x), + round(_a.y), + round(_a.z), + }; + } + + inline BX_CONSTEXPR_FUNC Vec3 abs(const Vec3 _a) { return { @@ -360,7 +400,7 @@ namespace bx }; } - inline BX_CONSTEXPR_FUNC Vec3 neg(const Vec3& _a) + inline BX_CONSTEXPR_FUNC Vec3 neg(const Vec3 _a) { return { @@ -370,7 +410,7 @@ namespace bx }; } - inline BX_CONSTEXPR_FUNC Vec3 add(const Vec3& _a, const Vec3& _b) + inline BX_CONSTEXPR_FUNC Vec3 add(const Vec3 _a, const Vec3 _b) { return { @@ -380,7 +420,7 @@ namespace bx }; } - inline BX_CONSTEXPR_FUNC Vec3 add(const Vec3& _a, float _b) + inline BX_CONSTEXPR_FUNC Vec3 add(const Vec3 _a, float _b) { return { @@ -390,7 +430,7 @@ namespace bx }; } - inline BX_CONSTEXPR_FUNC Vec3 sub(const Vec3& _a, const Vec3& _b) + inline BX_CONSTEXPR_FUNC Vec3 sub(const Vec3 _a, const Vec3 _b) { return { @@ -400,7 +440,7 @@ namespace bx }; } - inline BX_CONSTEXPR_FUNC Vec3 sub(const Vec3& _a, float _b) + inline BX_CONSTEXPR_FUNC Vec3 sub(const Vec3 _a, float _b) { return { @@ -410,7 +450,7 @@ namespace bx }; } - inline BX_CONSTEXPR_FUNC Vec3 mul(const Vec3& _a, const Vec3& _b) + inline BX_CONSTEXPR_FUNC Vec3 mul(const Vec3 _a, const Vec3 _b) { return { @@ -420,7 +460,7 @@ namespace bx }; } - inline BX_CONSTEXPR_FUNC Vec3 mul(const Vec3& _a, float _b) + inline BX_CONSTEXPR_FUNC Vec3 mul(const Vec3 _a, float _b) { return { @@ -430,17 +470,22 @@ namespace bx }; } - inline BX_CONSTEXPR_FUNC Vec3 mad(const Vec3& _a, const Vec3& _b, const Vec3& _c) + inline BX_CONSTEXPR_FUNC Vec3 mad(const Vec3 _a, const float _b, const Vec3 _c) + { + return add(mul(_a, _b), _c); + } + + inline BX_CONSTEXPR_FUNC Vec3 mad(const Vec3 _a, const Vec3 _b, const Vec3 _c) { return add(mul(_a, _b), _c); } - inline BX_CONSTEXPR_FUNC float dot(const Vec3& _a, const Vec3& _b) + inline BX_CONSTEXPR_FUNC float dot(const Vec3 _a, const Vec3 _b) { return _a.x*_b.x + _a.y*_b.y + _a.z*_b.z; } - inline BX_CONSTEXPR_FUNC Vec3 cross(const Vec3& _a, const Vec3& _b) + inline BX_CONSTEXPR_FUNC Vec3 cross(const Vec3 _a, const Vec3 _b) { return { @@ -450,12 +495,23 @@ namespace bx }; } - inline BX_CONST_FUNC float length(const Vec3& _a) + inline BX_CONST_FUNC float length(const Vec3 _a) { return sqrt(dot(_a, _a) ); } - inline BX_CONSTEXPR_FUNC Vec3 lerp(const Vec3& _a, const Vec3& _b, float _t) + inline BX_CONST_FUNC float distanceSq(const Vec3 _a, const Vec3 _b) + { + const Vec3 ba = sub(_b, _a); + return dot(ba, ba); + } + + inline BX_CONST_FUNC float distance(const Vec3 _a, const Vec3 _b) + { + return length(sub(_b, _a) ); + } + + inline BX_CONSTEXPR_FUNC Vec3 lerp(const Vec3 _a, const Vec3 _b, float _t) { return { @@ -465,7 +521,7 @@ namespace bx }; } - inline BX_CONSTEXPR_FUNC Vec3 lerp(const Vec3& _a, const Vec3& _b, const Vec3& _t) + inline BX_CONSTEXPR_FUNC Vec3 lerp(const Vec3 _a, const Vec3 _b, const Vec3 _t) { return { @@ -475,14 +531,14 @@ namespace bx }; } - inline BX_CONST_FUNC Vec3 normalize(const Vec3& _a) + inline BX_CONST_FUNC Vec3 normalize(const Vec3 _a) { const float invLen = 1.0f/length(_a); - const Vec3 result = mul(_a, invLen); + const Vec3 result = mul(_a, invLen); return result; } - inline BX_CONSTEXPR_FUNC Vec3 min(const Vec3& _a, const Vec3& _b) + inline BX_CONSTEXPR_FUNC Vec3 min(const Vec3 _a, const Vec3 _b) { return { @@ -492,7 +548,7 @@ namespace bx }; } - inline BX_CONSTEXPR_FUNC Vec3 max(const Vec3& _a, const Vec3& _b) + inline BX_CONSTEXPR_FUNC Vec3 max(const Vec3 _a, const Vec3 _b) { return { @@ -502,7 +558,7 @@ namespace bx }; } - inline BX_CONSTEXPR_FUNC Vec3 rcp(const Vec3& _a) + inline BX_CONSTEXPR_FUNC Vec3 rcp(const Vec3 _a) { return { @@ -512,7 +568,7 @@ namespace bx }; } - inline void calcTangentFrame(Vec3& _outT, Vec3& _outB, const Vec3& _n) + inline void calcTangentFrame(Vec3& _outT, Vec3& _outB, const Vec3 _n) { const float nx = _n.x; const float ny = _n.y; @@ -536,7 +592,7 @@ namespace bx _outB = cross(_n, _outT); } - inline void calcTangentFrame(Vec3& _outT, Vec3& _outB, const Vec3& _n, float _angle) + inline void calcTangentFrame(Vec3& _outT, Vec3& _outB, const Vec3 _n, float _angle) { calcTangentFrame(_outT, _outB, _n); @@ -567,292 +623,181 @@ namespace bx return result; } - inline void toLatLong(float* _outU, float* _outV, const Vec3& _dir) + inline void toLatLong(float* _outU, float* _outV, const Vec3 _dir) { const float phi = atan2(_dir.x, _dir.z); const float theta = acos(_dir.y); - *_outU = (bx::kPi + phi)/bx::kPi2; - *_outV = theta*bx::kInvPi; - } - - inline void vec3Add(float* _result, const float* _a, const float* _b) - { - _result[0] = _a[0] + _b[0]; - _result[1] = _a[1] + _b[1]; - _result[2] = _a[2] + _b[2]; - } - - inline void vec3Sub(float* _result, const float* _a, const float* _b) - { - _result[0] = _a[0] - _b[0]; - _result[1] = _a[1] - _b[1]; - _result[2] = _a[2] - _b[2]; - } - - inline void vec3Mul(float* _result, const float* _a, const float* _b) - { - _result[0] = _a[0] * _b[0]; - _result[1] = _a[1] * _b[1]; - _result[2] = _a[2] * _b[2]; - } - - inline void vec3Mul(float* _result, const float* _a, float _b) - { - _result[0] = _a[0] * _b; - _result[1] = _a[1] * _b; - _result[2] = _a[2] * _b; - } - - inline float vec3Dot(const float* _a, const float* _b) - { - return _a[0]*_b[0] + _a[1]*_b[1] + _a[2]*_b[2]; - } - - inline void vec3Cross(float* _result, const float* _a, const float* _b) - { - _result[0] = _a[1]*_b[2] - _a[2]*_b[1]; - _result[1] = _a[2]*_b[0] - _a[0]*_b[2]; - _result[2] = _a[0]*_b[1] - _a[1]*_b[0]; - } - - inline float vec3Length(const float* _a) - { - return sqrt(vec3Dot(_a, _a) ); - } - - inline float vec3Norm(float* _result, const float* _a) - { - const float len = vec3Length(_a); - const float invLen = 1.0f/len; - _result[0] = _a[0] * invLen; - _result[1] = _a[1] * invLen; - _result[2] = _a[2] * invLen; - return len; + *_outU = (kPi + phi)/kPi2; + *_outV = theta*kInvPi; } - inline void vec3TangentFrame(const float* _n, float* _t, float* _b) + inline BX_CONSTEXPR_FUNC Quaternion invert(const Quaternion _a) { - const float nx = _n[0]; - const float ny = _n[1]; - const float nz = _n[2]; - - if (abs(nx) > abs(nz) ) - { - float invLen = 1.0f / sqrt(nx*nx + nz*nz); - _t[0] = -nz * invLen; - _t[1] = 0.0f; - _t[2] = nx * invLen; - } - else + return { - float invLen = 1.0f / sqrt(ny*ny + nz*nz); - _t[0] = 0.0f; - _t[1] = nz * invLen; - _t[2] = -ny * invLen; - } - - vec3Cross(_b, _n, _t); - } - - inline void vec3TangentFrame(const float* _n, float* _t, float* _b, float _angle) - { - vec3TangentFrame(_n, _t, _b); - - const float sa = sin(_angle); - const float ca = cos(_angle); - - _t[0] = -sa * _b[0] + ca * _t[0]; - _t[1] = -sa * _b[1] + ca * _t[1]; - _t[2] = -sa * _b[2] + ca * _t[2]; - - vec3Cross(_b, _n, _t); - } - - inline void vec3FromLatLong(float* _vec, float _u, float _v) - { - const float phi = _u * kPi2; - const float theta = _v * kPi; - - const float st = sin(theta); - const float sp = sin(phi); - const float ct = cos(theta); - const float cp = cos(phi); - - _vec[0] = -st*sp; - _vec[1] = ct; - _vec[2] = -st*cp; + -_a.x, + -_a.y, + -_a.z, + _a.w, + }; } - inline void vec3ToLatLong(float* _outU, float* _outV, const float* _dir) + inline BX_CONSTEXPR_FUNC Vec3 mulXyz(const Quaternion _a, const Quaternion _b) { - const float phi = atan2(_dir[0], _dir[2]); - const float theta = acos(_dir[1]); + const float ax = _a.x; + const float ay = _a.y; + const float az = _a.z; + const float aw = _a.w; - *_outU = (bx::kPi + phi)/bx::kPi2; - *_outV = theta*bx::kInvPi; - } - - inline void quatIdentity(float* _result) - { - _result[0] = 0.0f; - _result[1] = 0.0f; - _result[2] = 0.0f; - _result[3] = 1.0f; - } + const float bx = _b.x; + const float by = _b.y; + const float bz = _b.z; + const float bw = _b.w; - inline void quatMove(float* _result, const float* _a) - { - _result[0] = _a[0]; - _result[1] = _a[1]; - _result[2] = _a[2]; - _result[3] = _a[3]; + return + { + aw * bx + ax * bw + ay * bz - az * by, + aw * by - ax * bz + ay * bw + az * bx, + aw * bz + ax * by - ay * bx + az * bw, + }; } - inline void quatMulXYZ(float* _result, const float* _qa, const float* _qb) + inline BX_CONSTEXPR_FUNC Quaternion mul(const Quaternion _a, const Quaternion _b) { - const float ax = _qa[0]; - const float ay = _qa[1]; - const float az = _qa[2]; - const float aw = _qa[3]; + const float ax = _a.x; + const float ay = _a.y; + const float az = _a.z; + const float aw = _a.w; - const float bx = _qb[0]; - const float by = _qb[1]; - const float bz = _qb[2]; - const float bw = _qb[3]; + const float bx = _b.x; + const float by = _b.y; + const float bz = _b.z; + const float bw = _b.w; - _result[0] = aw * bx + ax * bw + ay * bz - az * by; - _result[1] = aw * by - ax * bz + ay * bw + az * bx; - _result[2] = aw * bz + ax * by - ay * bx + az * bw; + return + { + aw * bx + ax * bw + ay * bz - az * by, + aw * by - ax * bz + ay * bw + az * bx, + aw * bz + ax * by - ay * bx + az * bw, + aw * bw - ax * bx - ay * by - az * bz, + }; } - inline void quatMul(float* _result, const float* _qa, const float* _qb) + inline BX_CONSTEXPR_FUNC Vec3 mul(const Vec3 _v, const Quaternion _q) { - const float ax = _qa[0]; - const float ay = _qa[1]; - const float az = _qa[2]; - const float aw = _qa[3]; - - const float bx = _qb[0]; - const float by = _qb[1]; - const float bz = _qb[2]; - const float bw = _qb[3]; - - _result[0] = aw * bx + ax * bw + ay * bz - az * by; - _result[1] = aw * by - ax * bz + ay * bw + az * bx; - _result[2] = aw * bz + ax * by - ay * bx + az * bw; - _result[3] = aw * bw - ax * bx - ay * by - az * bz; - } + const Quaternion tmp0 = invert(_q); + const Quaternion qv = { _v.x, _v.y, _v.z, 0.0f }; + const Quaternion tmp1 = mul(tmp0, qv); + const Vec3 result = mulXyz(tmp1, _q); - inline void quatInvert(float* _result, const float* _quat) - { - _result[0] = -_quat[0]; - _result[1] = -_quat[1]; - _result[2] = -_quat[2]; - _result[3] = _quat[3]; + return result; } - inline float quatDot(const float* _a, const float* _b) + inline BX_CONSTEXPR_FUNC float dot(const Quaternion _a, const Quaternion _b) { - return _a[0]*_b[0] - + _a[1]*_b[1] - + _a[2]*_b[2] - + _a[3]*_b[3] - ; + return + _a.x * _b.x + + _a.y * _b.y + + _a.z * _b.z + + _a.w * _b.w + ; } - inline void quatNorm(float* _result, const float* _quat) + inline BX_CONSTEXPR_FUNC Quaternion normalize(const Quaternion _a) { - const float norm = quatDot(_quat, _quat); + const float norm = dot(_a, _a); if (0.0f < norm) { const float invNorm = 1.0f / sqrt(norm); - _result[0] = _quat[0] * invNorm; - _result[1] = _quat[1] * invNorm; - _result[2] = _quat[2] * invNorm; - _result[3] = _quat[3] * invNorm; + + return + { + _a.x * invNorm, + _a.y * invNorm, + _a.z * invNorm, + _a.w * invNorm, + }; } - else + + return { - quatIdentity(_result); - } + 0.0f, + 0.0f, + 0.0f, + 1.0f, + }; } - inline void quatToEuler(float* _result, const float* _quat) + inline BX_CONST_FUNC Vec3 toEuler(const Quaternion _a) { - const float x = _quat[0]; - const float y = _quat[1]; - const float z = _quat[2]; - const float w = _quat[3]; + const float xx = _a.x; + const float yy = _a.y; + const float zz = _a.z; + const float ww = _a.w; + const float xsq = square(xx); + const float ysq = square(yy); + const float zsq = square(zz); - const float yy = y * y; - const float zz = z * z; - - const float xx = x * x; - _result[0] = atan2(2.0f * (x * w - y * z), 1.0f - 2.0f * (xx + zz) ); - _result[1] = atan2(2.0f * (y * w + x * z), 1.0f - 2.0f * (yy + zz) ); - _result[2] = asin (2.0f * (x * y + z * w) ); + return + { + atan2(2.0f * (xx * ww - yy * zz), 1.0f - 2.0f * (xsq + zsq) ), + atan2(2.0f * (yy * ww + xx * zz), 1.0f - 2.0f * (ysq + zsq) ), + asin( 2.0f * (xx * yy + zz * ww) ), + }; } - inline void quatRotateAxis(float* _result, const float* _axis, float _angle) + inline BX_CONST_FUNC Quaternion rotateAxis(const Vec3 _axis, float _angle) { const float ha = _angle * 0.5f; - const float ca = cos(ha); const float sa = sin(ha); - _result[0] = _axis[0] * sa; - _result[1] = _axis[1] * sa; - _result[2] = _axis[2] * sa; - _result[3] = ca; + + return + { + _axis.x * sa, + _axis.y * sa, + _axis.z * sa, + cos(ha), + }; } - inline void quatRotateX(float* _result, float _ax) + inline BX_CONST_FUNC Quaternion rotateX(float _ax) { const float hx = _ax * 0.5f; - const float cx = cos(hx); - const float sx = sin(hx); - _result[0] = sx; - _result[1] = 0.0f; - _result[2] = 0.0f; - _result[3] = cx; + + return + { + sin(hx), + 0.0f, + 0.0f, + cos(hx), + }; } - inline void quatRotateY(float* _result, float _ay) + inline BX_CONST_FUNC Quaternion rotateY(float _ay) { const float hy = _ay * 0.5f; - const float cy = cos(hy); - const float sy = sin(hy); - _result[0] = 0.0f; - _result[1] = sy; - _result[2] = 0.0f; - _result[3] = cy; - } - inline void quatRotateZ(float* _result, float _az) - { - const float hz = _az * 0.5f; - const float cz = cos(hz); - const float sz = sin(hz); - _result[0] = 0.0f; - _result[1] = 0.0f; - _result[2] = sz; - _result[3] = cz; + return + { + 0.0f, + sin(hy), + 0.0f, + cos(hy), + }; } - inline void vec3MulQuat(float* _result, const float* _vec, const float* _quat) + inline BX_CONST_FUNC Quaternion rotateZ(float _az) { - float tmp0[4]; - quatInvert(tmp0, _quat); - - float qv[4]; - qv[0] = _vec[0]; - qv[1] = _vec[1]; - qv[2] = _vec[2]; - qv[3] = 0.0f; - - float tmp1[4]; - quatMul(tmp1, tmp0, qv); + const float hz = _az * 0.5f; - quatMulXYZ(_result, tmp1, _quat); + return + { + 0.0f, + 0.0f, + sin(hz), + cos(hz), + }; } inline void mtxIdentity(float* _result) @@ -883,63 +828,63 @@ namespace bx mtxScale(_result, _scale, _scale, _scale); } - inline void mtxFromNormal(float* _result, const float* _normal, float _scale, const float* _pos) + inline void mtxFromNormal(float* _result, const Vec3& _normal, float _scale, const Vec3& _pos) { - float tangent[3]; - float bitangent[3]; - vec3TangentFrame(_normal, tangent, bitangent); + Vec3 tangent; + Vec3 bitangent; + calcTangentFrame(tangent, bitangent, _normal); - vec3Mul(&_result[ 0], bitangent, _scale); - vec3Mul(&_result[ 4], _normal, _scale); - vec3Mul(&_result[ 8], tangent, _scale); + store(&_result[ 0], mul(bitangent, _scale) ); + store(&_result[ 4], mul(_normal, _scale) ); + store(&_result[ 8], mul(tangent, _scale) ); _result[ 3] = 0.0f; _result[ 7] = 0.0f; _result[11] = 0.0f; - _result[12] = _pos[0]; - _result[13] = _pos[1]; - _result[14] = _pos[2]; + _result[12] = _pos.x; + _result[13] = _pos.y; + _result[14] = _pos.z; _result[15] = 1.0f; } - inline void mtxFromNormal(float* _result, const float* _normal, float _scale, const float* _pos, float _angle) + inline void mtxFromNormal(float* _result, const Vec3& _normal, float _scale, const Vec3& _pos, float _angle) { - float tangent[3]; - float bitangent[3]; - vec3TangentFrame(_normal, tangent, bitangent, _angle); + Vec3 tangent; + Vec3 bitangent; + calcTangentFrame(tangent, bitangent, _normal, _angle); - vec3Mul(&_result[ 0], bitangent, _scale); - vec3Mul(&_result[ 4], _normal, _scale); - vec3Mul(&_result[ 8], tangent, _scale); + store(&_result[0], mul(bitangent, _scale) ); + store(&_result[4], mul(_normal, _scale) ); + store(&_result[8], mul(tangent, _scale) ); _result[ 3] = 0.0f; _result[ 7] = 0.0f; _result[11] = 0.0f; - _result[12] = _pos[0]; - _result[13] = _pos[1]; - _result[14] = _pos[2]; + _result[12] = _pos.x; + _result[13] = _pos.y; + _result[14] = _pos.z; _result[15] = 1.0f; } - inline void mtxQuat(float* _result, const float* _quat) + inline void mtxQuat(float* _result, const Quaternion& _quat) { - const float x = _quat[0]; - const float y = _quat[1]; - const float z = _quat[2]; - const float w = _quat[3]; + const float qx = _quat.x; + const float qy = _quat.y; + const float qz = _quat.z; + const float qw = _quat.w; - const float x2 = x + x; - const float y2 = y + y; - const float z2 = z + z; - const float x2x = x2 * x; - const float x2y = x2 * y; - const float x2z = x2 * z; - const float x2w = x2 * w; - const float y2y = y2 * y; - const float y2z = y2 * z; - const float y2w = y2 * w; - const float z2z = z2 * z; - const float z2w = z2 * w; + const float x2 = qx + qx; + const float y2 = qy + qy; + const float z2 = qz + qz; + const float x2x = x2 * qx; + const float x2y = x2 * qy; + const float x2z = x2 * qz; + const float x2w = x2 * qw; + const float y2y = y2 * qy; + const float y2z = y2 * qz; + const float y2w = y2 * qw; + const float z2z = z2 * qz; + const float z2w = z2 * qw; _result[ 0] = 1.0f - (y2y + z2z); _result[ 1] = x2y - z2w; @@ -962,21 +907,21 @@ namespace bx _result[15] = 1.0f; } - inline void mtxQuatTranslation(float* _result, const float* _quat, const float* _translation) + inline void mtxQuatTranslation(float* _result, const Quaternion& _quat, const Vec3& _translation) { mtxQuat(_result, _quat); - _result[12] = -(_result[0]*_translation[0] + _result[4]*_translation[1] + _result[ 8]*_translation[2]); - _result[13] = -(_result[1]*_translation[0] + _result[5]*_translation[1] + _result[ 9]*_translation[2]); - _result[14] = -(_result[2]*_translation[0] + _result[6]*_translation[1] + _result[10]*_translation[2]); + store(&_result[12], neg(mulXyz0(_translation, _result) ) ); } - inline void mtxQuatTranslationHMD(float* _result, const float* _quat, const float* _translation) + inline void mtxQuatTranslationHMD(float* _result, const Quaternion& _quat, const Vec3& _translation) { - float quat[4]; - quat[0] = -_quat[0]; - quat[1] = -_quat[1]; - quat[2] = _quat[2]; - quat[3] = _quat[3]; + const Quaternion quat = + { + -_quat.x, + -_quat.y, + _quat.z, + _quat.w, + }; mtxQuatTranslation(_result, quat, _translation); } @@ -1016,48 +961,6 @@ namespace bx return result; } - inline void vec3MulMtx(float* _result, const float* _vec, const float* _mat) - { - _result[0] = _vec[0] * _mat[ 0] + _vec[1] * _mat[4] + _vec[2] * _mat[ 8] + _mat[12]; - _result[1] = _vec[0] * _mat[ 1] + _vec[1] * _mat[5] + _vec[2] * _mat[ 9] + _mat[13]; - _result[2] = _vec[0] * _mat[ 2] + _vec[1] * _mat[6] + _vec[2] * _mat[10] + _mat[14]; - } - - inline void vec3MulMtxXyz0(float* _result, const float* _vec, const float* _mat) - { - _result[0] = _vec[0] * _mat[ 0] + _vec[1] * _mat[4] + _vec[2] * _mat[ 8]; - _result[1] = _vec[0] * _mat[ 1] + _vec[1] * _mat[5] + _vec[2] * _mat[ 9]; - _result[2] = _vec[0] * _mat[ 2] + _vec[1] * _mat[6] + _vec[2] * _mat[10]; - } - - inline void vec3MulMtxH(float* _result, const float* _vec, const float* _mat) - { - float xx = _vec[0] * _mat[ 0] + _vec[1] * _mat[4] + _vec[2] * _mat[ 8] + _mat[12]; - float yy = _vec[0] * _mat[ 1] + _vec[1] * _mat[5] + _vec[2] * _mat[ 9] + _mat[13]; - float zz = _vec[0] * _mat[ 2] + _vec[1] * _mat[6] + _vec[2] * _mat[10] + _mat[14]; - float ww = _vec[0] * _mat[ 3] + _vec[1] * _mat[7] + _vec[2] * _mat[11] + _mat[15]; - float invW = sign(ww)/ww; - _result[0] = xx*invW; - _result[1] = yy*invW; - _result[2] = zz*invW; - } - - inline void vec4Mul(float* _result, const float* _a, const float* _b) - { - _result[0] = _a[0] * _b[0]; - _result[1] = _a[1] * _b[1]; - _result[2] = _a[2] * _b[2]; - _result[3] = _a[3] * _b[3]; - } - - inline void vec4Mul(float* _result, const float* _a, float _b) - { - _result[0] = _a[0] * _b; - _result[1] = _a[1] * _b; - _result[2] = _a[2] * _b; - _result[3] = _a[3] * _b; - } - inline void vec4MulMtx(float* _result, const float* _vec, const float* _mat) { _result[0] = _vec[0] * _mat[ 0] + _vec[1] * _mat[4] + _vec[2] * _mat[ 8] + _vec[3] * _mat[12]; @@ -1094,75 +997,30 @@ namespace bx _result[15] = _a[15]; } - /// Convert LH to RH projection matrix and vice versa. - inline void mtxProjFlipHandedness(float* _dst, const float* _src) + inline Vec3 calcNormal(const Vec3& _va, const Vec3& _vb, const Vec3& _vc) { - _dst[ 0] = -_src[ 0]; - _dst[ 1] = -_src[ 1]; - _dst[ 2] = -_src[ 2]; - _dst[ 3] = -_src[ 3]; - _dst[ 4] = _src[ 4]; - _dst[ 5] = _src[ 5]; - _dst[ 6] = _src[ 6]; - _dst[ 7] = _src[ 7]; - _dst[ 8] = -_src[ 8]; - _dst[ 9] = -_src[ 9]; - _dst[10] = -_src[10]; - _dst[11] = -_src[11]; - _dst[12] = _src[12]; - _dst[13] = _src[13]; - _dst[14] = _src[14]; - _dst[15] = _src[15]; - } + const Vec3 ba = sub(_vb, _va); + const Vec3 ca = sub(_vc, _va); + const Vec3 baxca = cross(ba, ca); - /// Convert LH to RH view matrix and vice versa. - inline void mtxViewFlipHandedness(float* _dst, const float* _src) - { - _dst[ 0] = -_src[ 0]; - _dst[ 1] = _src[ 1]; - _dst[ 2] = -_src[ 2]; - _dst[ 3] = _src[ 3]; - _dst[ 4] = -_src[ 4]; - _dst[ 5] = _src[ 5]; - _dst[ 6] = -_src[ 6]; - _dst[ 7] = _src[ 7]; - _dst[ 8] = -_src[ 8]; - _dst[ 9] = _src[ 9]; - _dst[10] = -_src[10]; - _dst[11] = _src[11]; - _dst[12] = -_src[12]; - _dst[13] = _src[13]; - _dst[14] = -_src[14]; - _dst[15] = _src[15]; + return normalize(baxca); } - inline void calcNormal(float _result[3], const float _va[3], const float _vb[3], const float _vc[3]) + inline void calcPlane(Plane& _outPlane, const Vec3& _va, const Vec3& _vb, const Vec3& _vc) { - float ba[3]; - vec3Sub(ba, _vb, _va); - - float ca[3]; - vec3Sub(ca, _vc, _va); - - float baxca[3]; - vec3Cross(baxca, ba, ca); - - vec3Norm(_result, baxca); + Vec3 normal = calcNormal(_va, _vb, _vc); + calcPlane(_outPlane, normal, _va); } - inline void calcPlane(float _result[4], const float _va[3], const float _vb[3], const float _vc[3]) + inline void calcPlane(Plane& _outPlane, const Vec3& _normal, const Vec3& _pos) { - float normal[3]; - calcNormal(normal, _va, _vb, _vc); - calcPlane(_result, normal, _va); + _outPlane.normal = _normal; + _outPlane.dist = -dot(_normal, _pos); } - inline void calcPlane(float _result[4], const float _normal[3], const float _pos[3]) + inline float distance(const Plane& _plane, const Vec3& _pos) { - _result[0] = _normal[0]; - _result[1] = _normal[1]; - _result[2] = _normal[2]; - _result[3] = -vec3Dot(_normal, _pos); + return dot(_plane.normal, _pos) + _plane.dist; } inline BX_CONST_FUNC float toLinear(float _a) diff --git a/3rdparty/bx/include/bx/inline/mpscqueue.inl b/3rdparty/bx/include/bx/inline/mpscqueue.inl index a22cf283815..fcff39bff86 100644 --- a/3rdparty/bx/include/bx/inline/mpscqueue.inl +++ b/3rdparty/bx/include/bx/inline/mpscqueue.inl @@ -1,5 +1,5 @@ /* - * Copyright 2010-2018 Branimir Karadzic. All rights reserved. + * Copyright 2010-2019 Branimir Karadzic. All rights reserved. * License: https://github.com/bkaradzic/bx#license-bsd-2-clause */ diff --git a/3rdparty/bx/include/bx/inline/mutex.inl b/3rdparty/bx/include/bx/inline/mutex.inl index 971ebcaa6a8..7b1ee37c909 100644 --- a/3rdparty/bx/include/bx/inline/mutex.inl +++ b/3rdparty/bx/include/bx/inline/mutex.inl @@ -1,5 +1,5 @@ /* - * Copyright 2010-2018 Branimir Karadzic. All rights reserved. + * Copyright 2010-2019 Branimir Karadzic. All rights reserved. * License: https://github.com/bkaradzic/bx#license-bsd-2-clause */ diff --git a/3rdparty/bx/include/bx/inline/pixelformat.inl b/3rdparty/bx/include/bx/inline/pixelformat.inl index 8ac02813521..72e2c943c86 100644 --- a/3rdparty/bx/include/bx/inline/pixelformat.inl +++ b/3rdparty/bx/include/bx/inline/pixelformat.inl @@ -1,5 +1,5 @@ /* - * Copyright 2010-2018 Branimir Karadzic. All rights reserved. + * Copyright 2010-2019 Branimir Karadzic. All rights reserved. * License: https://github.com/bkaradzic/bx#license-bsd-2-clause */ @@ -31,6 +31,23 @@ namespace bx return max(-1.0f, float(_value) / _scale); } + // A8 + inline void packA8(void* _dst, const float* _src) + { + uint8_t* dst = (uint8_t*)_dst; + dst[0] = uint8_t(toUnorm(_src[3], 255.0f) ); + } + + inline void unpackA8(float* _dst, const void* _src) + { + const uint8_t* src = (const uint8_t*)_src; + const float aa = fromUnorm(src[0], 255.0f); + _dst[0] = aa; + _dst[1] = aa; + _dst[2] = aa; + _dst[3] = aa; + } + // R8 inline void packR8(void* _dst, const float* _src) { diff --git a/3rdparty/bx/include/bx/inline/readerwriter.inl b/3rdparty/bx/include/bx/inline/readerwriter.inl index 9a365dcee60..66c8d6d45a7 100644 --- a/3rdparty/bx/include/bx/inline/readerwriter.inl +++ b/3rdparty/bx/include/bx/inline/readerwriter.inl @@ -1,5 +1,5 @@ /* - * Copyright 2010-2018 Branimir Karadzic. All rights reserved. + * Copyright 2010-2019 Branimir Karadzic. All rights reserved. * License: https://github.com/bkaradzic/bx#license-bsd-2-clause */ @@ -273,7 +273,7 @@ namespace bx } template<typename Ty> - int32_t read(ReaderI* _reader, Ty& _value, Error* _err) + inline int32_t read(ReaderI* _reader, Ty& _value, Error* _err) { BX_ERROR_SCOPE(_err); BX_STATIC_ASSERT(isTriviallyCopyable<Ty>() ); @@ -281,7 +281,7 @@ namespace bx } template<typename Ty> - int32_t readHE(ReaderI* _reader, Ty& _value, bool _fromLittleEndian, Error* _err) + inline int32_t readHE(ReaderI* _reader, Ty& _value, bool _fromLittleEndian, Error* _err) { BX_ERROR_SCOPE(_err); BX_STATIC_ASSERT(isTriviallyCopyable<Ty>() ); @@ -329,7 +329,7 @@ namespace bx } template<typename Ty> - int32_t write(WriterI* _writer, const Ty& _value, Error* _err) + inline int32_t write(WriterI* _writer, const Ty& _value, Error* _err) { BX_ERROR_SCOPE(_err); BX_STATIC_ASSERT(isTriviallyCopyable<Ty>() ); @@ -337,7 +337,7 @@ namespace bx } template<typename Ty> - int32_t writeLE(WriterI* _writer, const Ty& _value, Error* _err) + inline int32_t writeLE(WriterI* _writer, const Ty& _value, Error* _err) { BX_ERROR_SCOPE(_err); BX_STATIC_ASSERT(isTriviallyCopyable<Ty>() ); @@ -346,8 +346,14 @@ namespace bx return result; } + template<> + inline int32_t writeLE(WriterI* _writer, const float& _value, Error* _err) + { + return writeLE(_writer, floatToBits(_value), _err); + } + template<typename Ty> - int32_t writeBE(WriterI* _writer, const Ty& _value, Error* _err) + inline int32_t writeBE(WriterI* _writer, const Ty& _value, Error* _err) { BX_ERROR_SCOPE(_err); BX_STATIC_ASSERT(isTriviallyCopyable<Ty>() ); @@ -356,6 +362,12 @@ namespace bx return result; } + template<> + inline int32_t writeBE(WriterI* _writer, const float& _value, Error* _err) + { + return writeBE(_writer, floatToBits(_value), _err); + } + inline int64_t skip(SeekerI* _seeker, int64_t _offset) { return _seeker->seek(_offset, Whence::Current); @@ -392,7 +404,7 @@ namespace bx } template<typename Ty> - int32_t peek(ReaderSeekerI* _reader, Ty& _value, Error* _err) + inline int32_t peek(ReaderSeekerI* _reader, Ty& _value, Error* _err) { BX_ERROR_SCOPE(_err); BX_STATIC_ASSERT(isTriviallyCopyable<Ty>() ); diff --git a/3rdparty/bx/include/bx/inline/ringbuffer.inl b/3rdparty/bx/include/bx/inline/ringbuffer.inl index 51f365546db..d0f0a8b2eda 100644 --- a/3rdparty/bx/include/bx/inline/ringbuffer.inl +++ b/3rdparty/bx/include/bx/inline/ringbuffer.inl @@ -1,5 +1,5 @@ /* - * Copyright 2010-2018 Branimir Karadzic. All rights reserved. + * Copyright 2010-2019 Branimir Karadzic. All rights reserved. * License: https://github.com/bkaradzic/bx#license-bsd-2-clause */ diff --git a/3rdparty/bx/include/bx/inline/rng.inl b/3rdparty/bx/include/bx/inline/rng.inl index 9577bb09aa9..4ac29703f0c 100644 --- a/3rdparty/bx/include/bx/inline/rng.inl +++ b/3rdparty/bx/include/bx/inline/rng.inl @@ -1,5 +1,5 @@ /* - * Copyright 2010-2018 Branimir Karadzic. All rights reserved. + * Copyright 2010-2019 Branimir Karadzic. All rights reserved. * License: https://github.com/bkaradzic/bx#license-bsd-2-clause */ @@ -103,6 +103,10 @@ namespace bx inline void generateSphereHammersley(void* _data, uint32_t _stride, uint32_t _num, float _scale) { + // Reference(s): + // - Sampling with Hammersley and Halton Points + // https://web.archive.org/web/20190207230709/http://www.cse.cuhk.edu.hk/~ttwong/papers/udpoint/udpoints.html + uint8_t* data = (uint8_t*)_data; for (uint32_t ii = 0; ii < _num; ii++) diff --git a/3rdparty/bx/include/bx/inline/simd128_langext.inl b/3rdparty/bx/include/bx/inline/simd128_langext.inl index ac08ba7f9de..2c58becd247 100644 --- a/3rdparty/bx/include/bx/inline/simd128_langext.inl +++ b/3rdparty/bx/include/bx/inline/simd128_langext.inl @@ -1,5 +1,5 @@ /* - * Copyright 2010-2018 Branimir Karadzic. All rights reserved. + * Copyright 2010-2019 Branimir Karadzic. All rights reserved. * License: https://github.com/bkaradzic/bx#license-bsd-2-clause */ diff --git a/3rdparty/bx/include/bx/inline/simd128_neon.inl b/3rdparty/bx/include/bx/inline/simd128_neon.inl index 95107eeaa27..1aa40952e31 100644 --- a/3rdparty/bx/include/bx/inline/simd128_neon.inl +++ b/3rdparty/bx/include/bx/inline/simd128_neon.inl @@ -1,5 +1,5 @@ /* - * Copyright 2010-2018 Branimir Karadzic. All rights reserved. + * Copyright 2010-2019 Branimir Karadzic. All rights reserved. * License: https://github.com/bkaradzic/bx#license-bsd-2-clause */ diff --git a/3rdparty/bx/include/bx/inline/simd128_ref.inl b/3rdparty/bx/include/bx/inline/simd128_ref.inl index 6895b24f46a..5cd98c74478 100644 --- a/3rdparty/bx/include/bx/inline/simd128_ref.inl +++ b/3rdparty/bx/include/bx/inline/simd128_ref.inl @@ -1,5 +1,5 @@ /* - * Copyright 2010-2018 Branimir Karadzic. All rights reserved. + * Copyright 2010-2019 Branimir Karadzic. All rights reserved. * License: https://github.com/bkaradzic/bx#license-bsd-2-clause */ diff --git a/3rdparty/bx/include/bx/inline/simd128_sse.inl b/3rdparty/bx/include/bx/inline/simd128_sse.inl index 229c1431fe3..213fe174b98 100644 --- a/3rdparty/bx/include/bx/inline/simd128_sse.inl +++ b/3rdparty/bx/include/bx/inline/simd128_sse.inl @@ -1,5 +1,5 @@ /* - * Copyright 2010-2018 Branimir Karadzic. All rights reserved. + * Copyright 2010-2019 Branimir Karadzic. All rights reserved. * License: https://github.com/bkaradzic/bx#license-bsd-2-clause */ diff --git a/3rdparty/bx/include/bx/inline/simd256_avx.inl b/3rdparty/bx/include/bx/inline/simd256_avx.inl index 4dd9c666f71..221c1d283fc 100644 --- a/3rdparty/bx/include/bx/inline/simd256_avx.inl +++ b/3rdparty/bx/include/bx/inline/simd256_avx.inl @@ -1,5 +1,5 @@ /* - * Copyright 2010-2018 Branimir Karadzic. All rights reserved. + * Copyright 2010-2019 Branimir Karadzic. All rights reserved. * License: https://github.com/bkaradzic/bx#license-bsd-2-clause */ diff --git a/3rdparty/bx/include/bx/inline/simd256_ref.inl b/3rdparty/bx/include/bx/inline/simd256_ref.inl index 0c84d9d82a0..cbcdd726e6a 100644 --- a/3rdparty/bx/include/bx/inline/simd256_ref.inl +++ b/3rdparty/bx/include/bx/inline/simd256_ref.inl @@ -1,5 +1,5 @@ /* - * Copyright 2010-2018 Branimir Karadzic. All rights reserved. + * Copyright 2010-2019 Branimir Karadzic. All rights reserved. * License: https://github.com/bkaradzic/bx#license-bsd-2-clause */ diff --git a/3rdparty/bx/include/bx/inline/simd_ni.inl b/3rdparty/bx/include/bx/inline/simd_ni.inl index eb388513e88..6b78be8a308 100644 --- a/3rdparty/bx/include/bx/inline/simd_ni.inl +++ b/3rdparty/bx/include/bx/inline/simd_ni.inl @@ -1,5 +1,5 @@ /* - * Copyright 2010-2018 Branimir Karadzic. All rights reserved. + * Copyright 2010-2019 Branimir Karadzic. All rights reserved. * License: https://github.com/bkaradzic/bx#license-bsd-2-clause */ diff --git a/3rdparty/bx/include/bx/inline/sort.inl b/3rdparty/bx/include/bx/inline/sort.inl index 27a085362da..85e7b0a9fd0 100644 --- a/3rdparty/bx/include/bx/inline/sort.inl +++ b/3rdparty/bx/include/bx/inline/sort.inl @@ -1,5 +1,5 @@ /* - * Copyright 2010-2018 Branimir Karadzic. All rights reserved. + * Copyright 2010-2019 Branimir Karadzic. All rights reserved. * License: https://github.com/bkaradzic/bx#license-bsd-2-clause */ diff --git a/3rdparty/bx/include/bx/inline/spscqueue.inl b/3rdparty/bx/include/bx/inline/spscqueue.inl index e0fae02ef29..94c05d81712 100644 --- a/3rdparty/bx/include/bx/inline/spscqueue.inl +++ b/3rdparty/bx/include/bx/inline/spscqueue.inl @@ -1,5 +1,5 @@ /* - * Copyright 2010-2018 Branimir Karadzic. All rights reserved. + * Copyright 2010-2019 Branimir Karadzic. All rights reserved. * License: https://github.com/bkaradzic/bx#license-bsd-2-clause */ @@ -9,7 +9,10 @@ namespace bx { - // http://drdobbs.com/article/print?articleId=210604448&siteSectionName= + // Reference(s): + // - Writing Lock-Free Code: A Corrected Queue + // https://web.archive.org/web/20190207230604/http://www.drdobbs.com/parallel/writing-lock-free-code-a-corrected-queue/210604448 + // inline SpScUnboundedQueue::SpScUnboundedQueue(AllocatorI* _allocator) : m_allocator(_allocator) , m_first(BX_NEW(m_allocator, Node)(NULL) ) diff --git a/3rdparty/bx/include/bx/inline/string.inl b/3rdparty/bx/include/bx/inline/string.inl index a387128679e..61c49339cd8 100644 --- a/3rdparty/bx/include/bx/inline/string.inl +++ b/3rdparty/bx/include/bx/inline/string.inl @@ -1,5 +1,5 @@ /* - * Copyright 2010-2018 Branimir Karadzic. All rights reserved. + * Copyright 2010-2019 Branimir Karadzic. All rights reserved. * License: https://github.com/bkaradzic/bx#license-bsd-2-clause */ @@ -241,4 +241,43 @@ namespace bx return StringView(_str, _start, _len); } + inline LineReader::LineReader(const bx::StringView& _str) + : m_str(_str) + { + reset(); + } + + inline void LineReader::reset() + { + m_curr = m_str; + m_line = 0; + } + + inline StringView LineReader::next() + { + if (m_curr.getPtr() != m_str.getTerm() ) + { + ++m_line; + + StringView curr(m_curr); + m_curr = bx::strFindNl(m_curr); + + StringView line(curr.getPtr(), m_curr.getPtr() ); + + return strRTrim(line, "\n\r"); + } + + return m_curr; + } + + inline bool LineReader::isDone() const + { + return m_curr.getPtr() == m_str.getTerm(); + } + + inline uint32_t LineReader::getLine() const + { + return m_line; + } + } // namespace bx diff --git a/3rdparty/bx/include/bx/inline/uint32_t.inl b/3rdparty/bx/include/bx/inline/uint32_t.inl index 2a17582b0db..8fc1af6199b 100644 --- a/3rdparty/bx/include/bx/inline/uint32_t.inl +++ b/3rdparty/bx/include/bx/inline/uint32_t.inl @@ -1,5 +1,5 @@ /* - * Copyright 2010-2018 Branimir Karadzic. All rights reserved. + * Copyright 2010-2019 Branimir Karadzic. All rights reserved. * License: https://github.com/bkaradzic/bx#license-bsd-2-clause */ @@ -307,8 +307,12 @@ namespace bx return result; } + template<> inline BX_CONSTEXPR_FUNC uint32_t uint32_cntbits(uint32_t _val) { +#if BX_COMPILER_GCC || BX_COMPILER_CLANG + return __builtin_popcount(_val); +#else const uint32_t tmp0 = uint32_srl(_val, 1); const uint32_t tmp1 = uint32_and(tmp0, 0x55555555); const uint32_t tmp2 = uint32_sub(_val, tmp1); @@ -328,10 +332,37 @@ namespace bx const uint32_t result = uint32_and(tmpF, 0x3f); return result; +#endif // BX_COMPILER_* } + template<> + inline BX_CONSTEXPR_FUNC uint32_t uint32_cntbits(uint64_t _val) + { +#if BX_COMPILER_GCC || BX_COMPILER_CLANG + return __builtin_popcountll(_val); +#else + const uint32_t lo = uint32_t(_val&UINT32_MAX); + const uint32_t hi = uint32_t(_val>>32); + + const uint32_t total = uint32_cntbits(lo) + + uint32_cntbits(hi); + return total; +#endif // BX_COMPILER_* + } + + template<> inline BX_CONSTEXPR_FUNC uint32_t uint32_cntbits(uint8_t _val) { return uint32_cntbits<uint32_t>(_val); } + template<> inline BX_CONSTEXPR_FUNC uint32_t uint32_cntbits(int8_t _val) { return uint32_cntbits<uint8_t >(_val); } + template<> inline BX_CONSTEXPR_FUNC uint32_t uint32_cntbits(uint16_t _val) { return uint32_cntbits<uint32_t>(_val); } + template<> inline BX_CONSTEXPR_FUNC uint32_t uint32_cntbits(int16_t _val) { return uint32_cntbits<uint16_t>(_val); } + template<> inline BX_CONSTEXPR_FUNC uint32_t uint32_cntbits(int32_t _val) { return uint32_cntbits<uint32_t>(_val); } + template<> inline BX_CONSTEXPR_FUNC uint32_t uint32_cntbits(int64_t _val) { return uint32_cntbits<uint64_t>(_val); } + + template<> inline BX_CONSTEXPR_FUNC uint32_t uint32_cntlz(uint32_t _val) { +#if BX_COMPILER_GCC || BX_COMPILER_CLANG + return 0 == _val ? 32 : __builtin_clz(_val); +#else const uint32_t tmp0 = uint32_srl(_val, 1); const uint32_t tmp1 = uint32_or(tmp0, _val); const uint32_t tmp2 = uint32_srl(tmp1, 2); @@ -346,18 +377,64 @@ namespace bx const uint32_t result = uint32_cntbits(tmpA); return result; +#endif // BX_COMPILER_* } + template<> + inline BX_CONSTEXPR_FUNC uint32_t uint32_cntlz(uint64_t _val) + { +#if BX_COMPILER_GCC || BX_COMPILER_CLANG + return 0 == _val ? 64 : __builtin_clzll(_val); +#else + return _val & UINT64_C(0xffffffff00000000) + ? uint32_cntlz(uint32_t(_val>>32) ) + : uint32_cntlz(uint32_t(_val) ) + 32 + ; +#endif // BX_COMPILER_* + } + + template<> inline BX_CONSTEXPR_FUNC uint32_t uint32_cntlz(uint8_t _val) { return uint32_cntlz<uint32_t>(_val)-24; } + template<> inline BX_CONSTEXPR_FUNC uint32_t uint32_cntlz(int8_t _val) { return uint32_cntlz<uint8_t >(_val); } + template<> inline BX_CONSTEXPR_FUNC uint32_t uint32_cntlz(uint16_t _val) { return uint32_cntlz<uint32_t>(_val)-16; } + template<> inline BX_CONSTEXPR_FUNC uint32_t uint32_cntlz(int16_t _val) { return uint32_cntlz<uint16_t>(_val); } + template<> inline BX_CONSTEXPR_FUNC uint32_t uint32_cntlz(int32_t _val) { return uint32_cntlz<uint32_t>(_val); } + template<> inline BX_CONSTEXPR_FUNC uint32_t uint32_cntlz(int64_t _val) { return uint32_cntlz<uint64_t>(_val); } + + template<> inline BX_CONSTEXPR_FUNC uint32_t uint32_cnttz(uint32_t _val) { +#if BX_COMPILER_GCC || BX_COMPILER_CLANG + return 0 == _val ? 32 : __builtin_ctz(_val); +#else const uint32_t tmp0 = uint32_not(_val); const uint32_t tmp1 = uint32_dec(_val); const uint32_t tmp2 = uint32_and(tmp0, tmp1); const uint32_t result = uint32_cntbits(tmp2); return result; +#endif // BX_COMPILER_* + } + + template<> + inline BX_CONSTEXPR_FUNC uint32_t uint32_cnttz(uint64_t _val) + { +#if BX_COMPILER_GCC || BX_COMPILER_CLANG + return 0 == _val ? 64 : __builtin_ctzll(_val); +#else + return _val & UINT64_C(0xffffffff) + ? uint32_cnttz(uint32_t(_val) ) + : uint32_cnttz(uint32_t(_val>>32) ) + 32 + ; +#endif // BX_COMPILER_* } + template<> inline BX_CONSTEXPR_FUNC uint32_t uint32_cnttz(uint8_t _val) { return bx::min(8u, uint32_cnttz<uint32_t>(_val) ); } + template<> inline BX_CONSTEXPR_FUNC uint32_t uint32_cnttz(int8_t _val) { return uint32_cnttz<uint8_t >(_val); } + template<> inline BX_CONSTEXPR_FUNC uint32_t uint32_cnttz(uint16_t _val) { return bx::min(16u, uint32_cnttz<uint32_t>(_val) ); } + template<> inline BX_CONSTEXPR_FUNC uint32_t uint32_cnttz(int16_t _val) { return uint32_cnttz<uint16_t>(_val); } + template<> inline BX_CONSTEXPR_FUNC uint32_t uint32_cnttz(int32_t _val) { return uint32_cnttz<uint32_t>(_val); } + template<> inline BX_CONSTEXPR_FUNC uint32_t uint32_cnttz(int64_t _val) { return uint32_cnttz<uint64_t>(_val); } + inline BX_CONSTEXPR_FUNC uint32_t uint32_part1by1(uint32_t _a) { // shuffle: @@ -468,6 +545,66 @@ namespace bx ; } + inline BX_CONSTEXPR_FUNC uint64_t uint64_li(uint64_t _a) + { + return _a; + } + + inline BX_CONSTEXPR_FUNC uint64_t uint64_dec(uint64_t _a) + { + return _a - 1; + } + + inline BX_CONSTEXPR_FUNC uint64_t uint64_inc(uint64_t _a) + { + return _a + 1; + } + + inline BX_CONSTEXPR_FUNC uint64_t uint64_not(uint64_t _a) + { + return ~_a; + } + + inline BX_CONSTEXPR_FUNC uint64_t uint64_neg(uint64_t _a) + { + return -(int32_t)_a; + } + + inline BX_CONSTEXPR_FUNC uint64_t uint64_ext(uint64_t _a) + { + return ( (int32_t)_a)>>31; + } + + inline BX_CONSTEXPR_FUNC uint64_t uint64_and(uint64_t _a, uint64_t _b) + { + return _a & _b; + } + + inline BX_CONSTEXPR_FUNC uint64_t uint64_andc(uint64_t _a, uint64_t _b) + { + return _a & ~_b; + } + + inline BX_CONSTEXPR_FUNC uint64_t uint64_xor(uint64_t _a, uint64_t _b) + { + return _a ^ _b; + } + + inline BX_CONSTEXPR_FUNC uint64_t uint64_xorl(uint64_t _a, uint64_t _b) + { + return !_a != !_b; + } + + inline BX_CONSTEXPR_FUNC uint64_t uint64_or(uint64_t _a, uint64_t _b) + { + return _a | _b; + } + + inline BX_CONSTEXPR_FUNC uint64_t uint64_orc(uint64_t _a, uint64_t _b) + { + return _a | ~_b; + } + inline BX_CONSTEXPR_FUNC uint64_t uint64_sll(uint64_t _a, int32_t _sa) { return _a << _sa; @@ -537,21 +674,10 @@ namespace bx return result; } - inline BX_CONSTEXPR_FUNC uint32_t strideAlign16(uint32_t _offset, uint32_t _stride) - { - const uint32_t align = uint32_lcm(16, _stride); - const uint32_t mod = uint32_mod(_offset, align); - const uint32_t mask = uint32_cmpeq(mod, 0); - const uint32_t tmp0 = uint32_selb(mask, 0, align); - const uint32_t tmp1 = uint32_add(_offset, tmp0); - const uint32_t result = uint32_sub(tmp1, mod); - - return result; - } - - inline BX_CONSTEXPR_FUNC uint32_t strideAlign256(uint32_t _offset, uint32_t _stride) + template<uint32_t Min> + inline BX_CONSTEXPR_FUNC uint32_t strideAlign(uint32_t _offset, uint32_t _stride) { - const uint32_t align = uint32_lcm(256, _stride); + const uint32_t align = uint32_lcm(Min, _stride); const uint32_t mod = uint32_mod(_offset, align); const uint32_t mask = uint32_cmpeq(mod, 0); const uint32_t tmp0 = uint32_selb(mask, 0, align); diff --git a/3rdparty/bx/include/bx/macros.h b/3rdparty/bx/include/bx/macros.h index 9c72182486b..2f92c1ff424 100644 --- a/3rdparty/bx/include/bx/macros.h +++ b/3rdparty/bx/include/bx/macros.h @@ -1,5 +1,5 @@ /* - * Copyright 2010-2018 Branimir Karadzic. All rights reserved. + * Copyright 2010-2019 Branimir Karadzic. All rights reserved. * License: https://github.com/bkaradzic/bx#license-bsd-2-clause */ @@ -115,7 +115,11 @@ /// The return value of the function is solely a function of the arguments. /// -#define BX_CONSTEXPR_FUNC constexpr BX_CONST_FUNC +#if __cplusplus < 201402 +# define BX_CONSTEXPR_FUNC BX_CONST_FUNC +#else +# define BX_CONSTEXPR_FUNC constexpr BX_CONST_FUNC +#endif // __cplusplus < 201402 /// #define BX_STATIC_ASSERT(_condition, ...) static_assert(_condition, "" __VA_ARGS__) diff --git a/3rdparty/bx/include/bx/maputil.h b/3rdparty/bx/include/bx/maputil.h index 78ce6b6f84c..fcdc4b58d97 100644 --- a/3rdparty/bx/include/bx/maputil.h +++ b/3rdparty/bx/include/bx/maputil.h @@ -1,5 +1,5 @@ /* - * Copyright 2010-2018 Branimir Karadzic. All rights reserved. + * Copyright 2010-2019 Branimir Karadzic. All rights reserved. * License: https://github.com/bkaradzic/bx#license-bsd-2-clause */ diff --git a/3rdparty/bx/include/bx/math.h b/3rdparty/bx/include/bx/math.h index c751efad50b..6e28839a35f 100644 --- a/3rdparty/bx/include/bx/math.h +++ b/3rdparty/bx/include/bx/math.h @@ -1,10 +1,8 @@ /* - * Copyright 2011-2018 Branimir Karadzic. All rights reserved. + * Copyright 2011-2019 Branimir Karadzic. All rights reserved. * License: https://github.com/bkaradzic/bx#license-bsd-2-clause */ -// FPU math lib - #ifndef BX_MATH_H_HEADER_GUARD #define BX_MATH_H_HEADER_GUARD @@ -55,9 +53,31 @@ namespace bx /// struct Vec3 { + /// + Vec3(); + + /// + explicit constexpr Vec3(float _v); + + /// + constexpr Vec3(float _x, float _y, float _z); + float x, y, z; }; + /// + struct Plane + { + Vec3 normal; + float dist; + }; + + /// + struct Quaternion + { + float x, y, z, w; + }; + /// Returns converted the argument _deg to radians. /// BX_CONST_FUNC float toRad(float _deg); @@ -112,15 +132,15 @@ namespace bx /// Returns the largest integer value not greater than _f. /// - BX_CONST_FUNC float floor(float _f); + BX_CONSTEXPR_FUNC float floor(float _f); /// Returns the smallest integer value not less than _f. /// - BX_CONST_FUNC float ceil(float _f); + BX_CONSTEXPR_FUNC float ceil(float _f); /// Returns the nearest integer value to _f, rounding halfway cases away from zero, /// - BX_CONST_FUNC float round(float _f); + BX_CONSTEXPR_FUNC float round(float _f); /// Returns linear interpolation between two values _a and _b. /// @@ -264,158 +284,123 @@ namespace bx BX_CONST_FUNC float angleLerp(float _a, float _b, float _t); /// - Vec3 load(const void* _ptr); - - /// - void store(void* _ptr, const Vec3& _a); + template<typename Ty> + Ty load(const void* _ptr); /// - BX_CONSTEXPR_FUNC Vec3 abs(const Vec3& _a); + template<typename Ty> + void store(void* _ptr, const Ty& _a); /// - BX_CONSTEXPR_FUNC Vec3 neg(const Vec3& _a); + BX_CONSTEXPR_FUNC Vec3 round(const Vec3 _a); /// - BX_CONSTEXPR_FUNC Vec3 add(const Vec3& _a, const Vec3& _b); + BX_CONSTEXPR_FUNC Vec3 abs(const Vec3 _a); /// - BX_CONSTEXPR_FUNC Vec3 add(const Vec3& _a, float _b); + BX_CONSTEXPR_FUNC Vec3 neg(const Vec3 _a); /// - BX_CONSTEXPR_FUNC Vec3 sub(const Vec3& _a, const Vec3& _b); + BX_CONSTEXPR_FUNC Vec3 add(const Vec3 _a, const Vec3 _b); /// - BX_CONSTEXPR_FUNC Vec3 sub(const Vec3& _a, float _b); + BX_CONSTEXPR_FUNC Vec3 add(const Vec3 _a, float _b); /// - BX_CONSTEXPR_FUNC Vec3 mul(const Vec3& _a, const Vec3& _b); + BX_CONSTEXPR_FUNC Vec3 sub(const Vec3 _a, const Vec3 _b); /// - BX_CONSTEXPR_FUNC Vec3 mul(const Vec3& _a, float _b); + BX_CONSTEXPR_FUNC Vec3 sub(const Vec3 _a, float _b); /// - BX_CONSTEXPR_FUNC Vec3 mad(const Vec3& _a, const Vec3& _b, const Vec3& _c); + BX_CONSTEXPR_FUNC Vec3 mul(const Vec3 _a, const Vec3 _b); /// - BX_CONSTEXPR_FUNC float dot(const Vec3& _a, const Vec3& _b); + BX_CONSTEXPR_FUNC Vec3 mul(const Vec3 _a, float _b); /// - BX_CONSTEXPR_FUNC Vec3 cross(const Vec3& _a, const Vec3& _b); + BX_CONSTEXPR_FUNC Vec3 mad(const Vec3 _a, const float _b, const Vec3 _c); /// - BX_CONST_FUNC float length(const Vec3& _a); + BX_CONSTEXPR_FUNC Vec3 mad(const Vec3 _a, const Vec3 _b, const Vec3 _c); /// - BX_CONSTEXPR_FUNC Vec3 lerp(const Vec3& _a, const Vec3& _b, float _t); + BX_CONSTEXPR_FUNC float dot(const Vec3 _a, const Vec3 _b); /// - BX_CONSTEXPR_FUNC Vec3 lerp(const Vec3& _a, const Vec3& _b, const Vec3& _t); + BX_CONSTEXPR_FUNC Vec3 cross(const Vec3 _a, const Vec3 _b); /// - BX_CONST_FUNC Vec3 normalize(const Vec3& _a); + BX_CONST_FUNC float length(const Vec3 _a); /// - BX_CONSTEXPR_FUNC Vec3 min(const Vec3& _a, const Vec3& _b); + BX_CONST_FUNC float distanceSq(const Vec3 _a, const Vec3 _b); /// - BX_CONSTEXPR_FUNC Vec3 max(const Vec3& _a, const Vec3& _b); + BX_CONST_FUNC float distance(const Vec3 _a, const Vec3 _b); /// - BX_CONSTEXPR_FUNC Vec3 rcp(const Vec3& _a); + BX_CONSTEXPR_FUNC Vec3 lerp(const Vec3 _a, const Vec3 _b, float _t); /// - void calcTangentFrame(Vec3& _outT, Vec3& _outB, const Vec3& _n); + BX_CONSTEXPR_FUNC Vec3 lerp(const Vec3 _a, const Vec3 _b, const Vec3 _t); /// - void calcTangentFrame(Vec3& _outT, Vec3& _outB, const Vec3& _n, float _angle); + BX_CONST_FUNC Vec3 normalize(const Vec3 _a); /// - BX_CONST_FUNC Vec3 fromLatLong(float _u, float _v); - - /// - void toLatLong(float* _outU, float* _outV, const Vec3& _dir); - - /// - void vec3Add(float* _result, const float* _a, const float* _b); - - /// - void vec3Sub(float* _result, const float* _a, const float* _b); + BX_CONSTEXPR_FUNC Vec3 min(const Vec3 _a, const Vec3 _b); /// - void vec3Mul(float* _result, const float* _a, const float* _b); + BX_CONSTEXPR_FUNC Vec3 max(const Vec3 _a, const Vec3 _b); /// - void vec3Mul(float* _result, const float* _a, float _b); + BX_CONSTEXPR_FUNC Vec3 rcp(const Vec3 _a); /// - float vec3Dot(const float* _a, const float* _b); + void calcTangentFrame(Vec3& _outT, Vec3& _outB, const Vec3 _n); /// - void vec3Cross(float* _result, const float* _a, const float* _b); + void calcTangentFrame(Vec3& _outT, Vec3& _outB, const Vec3 _n, float _angle); /// - float vec3Length(const float* _a); - - /// - float vec3Norm(float* _result, const float* _a); - - /// Calculate tangent frame from normal. - /// - void vec3TangentFrame(const float* _n, float* _t, float* _b); - - /// Calculate tangent frame from normal and angle. - /// - void vec3TangentFrame(const float* _n, float* _t, float* _b, float _angle); - - /// - void vec3FromLatLong(float* _vec, float _u, float _v); - - /// Convert direction to 2D latitude and longitude. - /// - /// @param[out] _outU U-coordinate. - /// @param[out] _outV V-coordinate. - /// @param[in] _dir Normalized direction vector. - /// - void vec3ToLatLong(float* _outU, float* _outV, const float* _dir); - - /// - void quatIdentity(float* _result); + BX_CONST_FUNC Vec3 fromLatLong(float _u, float _v); /// - void quatMove(float* _result, const float* _a); + void toLatLong(float* _outU, float* _outV, const Vec3 _dir); /// - void quatMulXYZ(float* _result, const float* _qa, const float* _qb); + BX_CONSTEXPR_FUNC Quaternion invert(const Quaternion _a); /// - void quatMul(float* _result, const float* _qa, const float* _qb); + BX_CONSTEXPR_FUNC Vec3 mulXyz(const Quaternion _a, const Quaternion _b); /// - void quatInvert(float* _result, const float* _quat); + BX_CONSTEXPR_FUNC Quaternion mul(const Quaternion _a, const Quaternion _b); /// - float quatDot(const float* _a, const float* _b); + BX_CONSTEXPR_FUNC Vec3 mul(const Vec3 _v, const Quaternion _q); /// - void quatNorm(float* _result, const float* _quat); + BX_CONSTEXPR_FUNC float dot(const Quaternion _a, const Quaternion _b); /// - void quatToEuler(float* _result, const float* _quat); + BX_CONSTEXPR_FUNC Quaternion normalize(const Quaternion _a); /// - void quatRotateAxis(float* _result, const float* _axis, float _angle); + BX_CONST_FUNC Vec3 toEuler(const Quaternion _a); /// - void quatRotateX(float* _result, float _ax); + BX_CONST_FUNC Quaternion rotateAxis(const Vec3 _axis, float _angle); /// - void quatRotateY(float* _result, float _ay); + BX_CONST_FUNC Quaternion rotateX(float _ax); /// - void quatRotateZ(float* _result, float _az); + BX_CONST_FUNC Quaternion rotateY(float _ay); /// - void vec3MulQuat(float* _result, const float* _vec, const float* _quat); + BX_CONST_FUNC Quaternion rotateZ(float _az); /// void mtxIdentity(float* _result); @@ -430,109 +415,121 @@ namespace bx void mtxScale(float* _result, float _scale); /// - void mtxFromNormal(float* _result, const float* _normal, float _scale, const float* _pos); - - /// - void mtxFromNormal(float* _result, const float* _normal, float _scale, const float* _pos, float _angle); - - /// - void mtxQuat(float* _result, const float* _quat); - - /// - void mtxQuatTranslation(float* _result, const float* _quat, const float* _translation); - - /// - void mtxQuatTranslationHMD(float* _result, const float* _quat, const float* _translation); - - /// - void mtxLookAtLh(float* _result, const Vec3& _eye, const Vec3& _at, const Vec3& _up = { 0.0f, 1.0f, 0.0f }); - - /// - void mtxLookAtRh(float* _result, const Vec3& _eye, const Vec3& _at, const Vec3& _up = { 0.0f, 1.0f, 0.0f }); - - /// - void mtxLookAt(float* _result, const Vec3& _eye, const Vec3& _at, const Vec3& _up = { 0.0f, 1.0f, 0.0f }); - - /// - void mtxProj(float* _result, float _ut, float _dt, float _lt, float _rt, float _near, float _far, bool _oglNdc); - - /// - void mtxProj(float* _result, const float _fov[4], float _near, float _far, bool _oglNdc); - - /// - void mtxProj(float* _result, float _fovy, float _aspect, float _near, float _far, bool _oglNdc); - - /// - void mtxProjLh(float* _result, float _ut, float _dt, float _lt, float _rt, float _near, float _far, bool _oglNdc); - - /// - void mtxProjLh(float* _result, const float _fov[4], float _near, float _far, bool _oglNdc); - - /// - void mtxProjLh(float* _result, float _fovy, float _aspect, float _near, float _far, bool _oglNdc); - - /// - void mtxProjRh(float* _result, float _ut, float _dt, float _lt, float _rt, float _near, float _far, bool _oglNdc); - - /// - void mtxProjRh(float* _result, const float _fov[4], float _near, float _far, bool _oglNdc); - - /// - void mtxProjRh(float* _result, float _fovy, float _aspect, float _near, float _far, bool _oglNdc); - - /// - void mtxProjInf(float* _result, const float _fov[4], float _near, bool _oglNdc); - - /// - void mtxProjInf(float* _result, float _ut, float _dt, float _lt, float _rt, float _near, bool _oglNdc); - - /// - void mtxProjInf(float* _result, float _fovy, float _aspect, float _near, bool _oglNdc); - - /// - void mtxProjInfLh(float* _result, float _ut, float _dt, float _lt, float _rt, float _near, bool _oglNdc); - - /// - void mtxProjInfLh(float* _result, const float _fov[4], float _near, bool _oglNdc); - - /// - void mtxProjInfLh(float* _result, float _fovy, float _aspect, float _near, bool _oglNdc); - - /// - void mtxProjInfRh(float* _result, float _ut, float _dt, float _lt, float _rt, float _near, bool _oglNdc); - - /// - void mtxProjInfRh(float* _result, const float _fov[4], float _near, bool _oglNdc); - - /// - void mtxProjInfRh(float* _result, float _fovy, float _aspect, float _near, bool _oglNdc); - - /// - void mtxProjRevInfLh(float* _result, float _ut, float _dt, float _lt, float _rt, float _near, bool _oglNdc); - - /// - void mtxProjRevInfLh(float* _result, const float _fov[4], float _near, bool _oglNdc); - - /// - void mtxProjRevInfLh(float* _result, float _fovy, float _aspect, float _near, bool _oglNdc); - - /// - void mtxProjRevInfRh(float* _result, float _ut, float _dt, float _lt, float _rt, float _near, bool _oglNdc); - - /// - void mtxProjRevInfRh(float* _result, const float _fov[4], float _near, bool _oglNdc); - - /// - void mtxProjRevInfRh(float* _result, float _fovy, float _aspect, float _near, bool _oglNdc); - - /// - void mtxOrtho(float* _result, float _left, float _right, float _bottom, float _top, float _near, float _far, float _offset, bool _oglNdc); - - /// - void mtxOrthoLh(float* _result, float _left, float _right, float _bottom, float _top, float _near, float _far, float _offset, bool _oglNdc); - - /// - void mtxOrthoRh(float* _result, float _left, float _right, float _bottom, float _top, float _near, float _far, float _offset, bool _oglNdc); + void mtxFromNormal( + float* _result + , const Vec3& _normal + , float _scale + , const Vec3& _pos + ); + + /// + void mtxFromNormal( + float* _result + , const Vec3& _normal + , float _scale + , const Vec3& _pos + , float _angle + ); + + /// + void mtxQuat(float* _result, const Quaternion& _quat); + + /// + void mtxQuatTranslation(float* _result, const Quaternion& _quat, const Vec3& _translation); + + /// + void mtxQuatTranslationHMD(float* _result, const Quaternion& _quat, const Vec3& _translation); + + /// + void mtxLookAt( + float* _result + , const Vec3& _eye + , const Vec3& _at + , const Vec3& _up = { 0.0f, 1.0f, 0.0f } + , Handness::Enum _handness = Handness::Left + ); + + /// + void mtxProj( + float* _result + , float _ut + , float _dt + , float _lt + , float _rt + , float _near + , float _far + , bool _homogeneousNdc + , Handness::Enum _handness = Handness::Left + ); + + /// + void mtxProj( + float* _result + , const float _fov[4] + , float _near + , float _far + , bool _homogeneousNdc + , Handness::Enum _handness = Handness::Left + ); + + /// + void mtxProj( + float* _result + , float _fovy + , float _aspect + , float _near + , float _far + , bool _homogeneousNdc + , Handness::Enum _handness = Handness::Left + ); + + /// + void mtxProjInf( + float* _result + , const float _fov[4] + , float _near + , bool _homogeneousNdc + , Handness::Enum _handness = Handness::Left + , NearFar::Enum _nearFar = NearFar::Default + ); + + /// + void mtxProjInf( + float* _result + , float _ut + , float _dt + , float _lt + , float _rt + , float _near + , bool _homogeneousNdc + , Handness::Enum _handness = Handness::Left + , NearFar::Enum _nearFar = NearFar::Default + ); + + /// + void mtxProjInf( + float* _result + , float _fovy + , float _aspect + , float _near + , bool _homogeneousNdc + , Handness::Enum _handness = Handness::Left + , NearFar::Enum _nearFar = NearFar::Default + ); + + /// + void mtxOrtho( + float* _result + , float _left + , float _right + , float _bottom + , float _top + , float _near + , float _far + , float _offset + , bool _homogeneousNdc + , Handness::Enum _handness = Handness::Left + ); /// void mtxRotateX(float* _result, float _ax); @@ -553,22 +550,27 @@ namespace bx void mtxRotateZYX(float* _result, float _ax, float _ay, float _az); /// - void mtxSRT(float* _result, float _sx, float _sy, float _sz, float _ax, float _ay, float _az, float _tx, float _ty, float _tz); + void mtxSRT( + float* _result + , float _sx + , float _sy + , float _sz + , float _ax + , float _ay + , float _az + , float _tx + , float _ty + , float _tz + ); /// - void vec3MulMtx(float* _result, const float* _vec, const float* _mat); + Vec3 mul(const Vec3& _vec, const float* _mat); /// - void vec3MulMtxXyz0(float* _result, const float* _vec, const float* _mat); + Vec3 mulXyz0(const Vec3& _vec, const float* _mat); /// - void vec3MulMtxH(float* _result, const float* _vec, const float* _mat); - - /// - void vec4Mul(float* _result, const float* _a, const float* _b); - - /// - void vec4Mul(float* _result, const float* _a, float _b); + Vec3 mulH(const Vec3& _vec, const float* _mat); /// void vec4MulMtx(float* _result, const float* _vec, const float* _mat); @@ -585,22 +587,23 @@ namespace bx /// void mtxInverse(float* _result, const float* _a); - /// Convert LH to RH projection matrix and vice versa. /// - void mtxProjFlipHandedness(float* _dst, const float* _src); + void mtx3Cofactor(float* _result, const float* _a); + + /// + void mtxCofactor(float* _result, const float* _a); - /// Convert LH to RH view matrix and vice versa. /// - void mtxViewFlipHandedness(float* _dst, const float* _src); + Vec3 calcNormal(const Vec3& _va, const Vec3& _vb, const Vec3& _vc); /// - void calcNormal(float _result[3], const float _va[3], const float _vb[3], const float _vc[3]); + void calcPlane(Plane& _outPlane, const Vec3& _va, const Vec3& _vb, const Vec3& _vc); /// - void calcPlane(float _result[4], const float _va[3], const float _vb[3], const float _vc[3]); + void calcPlane(Plane& _outPlane, const Vec3& _normal, const Vec3& _pos); /// - void calcPlane(float _result[4], const float _normal[3], const float _pos[3]); + float distance(const Plane& _plane, const Vec3& _pos); /// void calcLinearFit2D(float _result[2], const void* _points, uint32_t _stride, uint32_t _numPoints); diff --git a/3rdparty/bx/include/bx/mpscqueue.h b/3rdparty/bx/include/bx/mpscqueue.h index 003dfe8f4f8..bbc80049872 100644 --- a/3rdparty/bx/include/bx/mpscqueue.h +++ b/3rdparty/bx/include/bx/mpscqueue.h @@ -1,5 +1,5 @@ /* - * Copyright 2010-2018 Branimir Karadzic. All rights reserved. + * Copyright 2010-2019 Branimir Karadzic. All rights reserved. * License: https://github.com/bkaradzic/bx#license-bsd-2-clause */ diff --git a/3rdparty/bx/include/bx/mutex.h b/3rdparty/bx/include/bx/mutex.h index 213fc68c70e..8f8ec18de08 100644 --- a/3rdparty/bx/include/bx/mutex.h +++ b/3rdparty/bx/include/bx/mutex.h @@ -1,5 +1,5 @@ /* - * Copyright 2010-2018 Branimir Karadzic. All rights reserved. + * Copyright 2010-2019 Branimir Karadzic. All rights reserved. * License: https://github.com/bkaradzic/bx#license-bsd-2-clause */ diff --git a/3rdparty/bx/include/bx/os.h b/3rdparty/bx/include/bx/os.h index 92edacd21d1..2a8cb0d6883 100644 --- a/3rdparty/bx/include/bx/os.h +++ b/3rdparty/bx/include/bx/os.h @@ -1,5 +1,5 @@ /* - * Copyright 2010-2018 Branimir Karadzic. All rights reserved. + * Copyright 2010-2019 Branimir Karadzic. All rights reserved. * License: https://github.com/bkaradzic/bx#license-bsd-2-clause */ diff --git a/3rdparty/bx/include/bx/pixelformat.h b/3rdparty/bx/include/bx/pixelformat.h index c75b39836d5..03c5f62415b 100644 --- a/3rdparty/bx/include/bx/pixelformat.h +++ b/3rdparty/bx/include/bx/pixelformat.h @@ -1,5 +1,5 @@ /* - * Copyright 2011-2018 Branimir Karadzic. All rights reserved. + * Copyright 2011-2019 Branimir Karadzic. All rights reserved. * License: https://github.com/bkaradzic/bx#license-bsd-2-clause */ @@ -41,6 +41,10 @@ namespace bx /// float fromSnorm(int32_t _value, float _scale); + // A8 + void packA8(void* _dst, const float* _src); + void unpackA8(float* _dst, const void* _src); + // R8 void packR8(void* _dst, const float* _src); void unpackR8(float* _dst, const void* _src); diff --git a/3rdparty/bx/include/bx/platform.h b/3rdparty/bx/include/bx/platform.h index e0031ec2c38..8195cfe36c8 100644 --- a/3rdparty/bx/include/bx/platform.h +++ b/3rdparty/bx/include/bx/platform.h @@ -1,5 +1,5 @@ /* - * Copyright 2010-2018 Branimir Karadzic. All rights reserved. + * Copyright 2010-2019 Branimir Karadzic. All rights reserved. * License: https://github.com/bkaradzic/bx#license-bsd-2-clause */ @@ -48,6 +48,7 @@ #define BX_PLATFORM_ANDROID 0 #define BX_PLATFORM_BSD 0 #define BX_PLATFORM_EMSCRIPTEN 0 +#define BX_PLATFORM_HAIKU 0 #define BX_PLATFORM_HURD 0 #define BX_PLATFORM_IOS 0 #define BX_PLATFORM_LINUX 0 @@ -133,8 +134,14 @@ #endif // #if BX_CPU_PPC -# undef BX_CPU_ENDIAN_BIG -# define BX_CPU_ENDIAN_BIG 1 +// _LITTLE_ENDIAN exists on ppc64le. +# if _LITTLE_ENDIAN +# undef BX_CPU_ENDIAN_LITTLE +# define BX_CPU_ENDIAN_LITTLE 1 +# else +# undef BX_CPU_ENDIAN_BIG +# define BX_CPU_ENDIAN_BIG 1 +# endif #else # undef BX_CPU_ENDIAN_LITTLE # define BX_CPU_ENDIAN_LITTLE 1 @@ -213,6 +220,9 @@ #elif defined(__NX__) # undef BX_PLATFORM_NX # define BX_PLATFORM_NX 1 +#elif defined(__HAIKU__) +# undef BX_PLATFORM_HAIKU +# define BX_PLATFORM_HAIKU 1 #endif // #if !BX_CRT_NONE @@ -229,7 +239,7 @@ # elif defined(__MINGW32__) || defined(__MINGW64__) # undef BX_CRT_MINGW # define BX_CRT_MINGW 1 -# elif defined(__apple_build_version__) || defined(__ORBIS__) || defined(__EMSCRIPTEN__) || defined(__llvm__) +# elif defined(__apple_build_version__) || defined(__ORBIS__) || defined(__EMSCRIPTEN__) || defined(__llvm__) || defined(__HAIKU__) # undef BX_CRT_LIBCXX # define BX_CRT_LIBCXX 1 # endif // @@ -246,10 +256,12 @@ # endif // BX_CRT_* #endif // !BX_CRT_NONE +/// #define BX_PLATFORM_POSIX (0 \ || BX_PLATFORM_ANDROID \ || BX_PLATFORM_BSD \ || BX_PLATFORM_EMSCRIPTEN \ + || BX_PLATFORM_HAIKU \ || BX_PLATFORM_HURD \ || BX_PLATFORM_IOS \ || BX_PLATFORM_LINUX \ @@ -260,10 +272,12 @@ || BX_PLATFORM_STEAMLINK \ ) +/// #define BX_PLATFORM_NONE !(0 \ || BX_PLATFORM_ANDROID \ || BX_PLATFORM_BSD \ || BX_PLATFORM_EMSCRIPTEN \ + || BX_PLATFORM_HAIKU \ || BX_PLATFORM_HURD \ || BX_PLATFORM_IOS \ || BX_PLATFORM_LINUX \ @@ -277,6 +291,42 @@ || BX_PLATFORM_XBOXONE \ ) +/// +#define BX_PLATFORM_OS_CONSOLE (0 \ + || BX_PLATFORM_NX \ + || BX_PLATFORM_PS4 \ + || BX_PLATFORM_WINRT \ + || BX_PLATFORM_XBOXONE \ + ) + +/// +#define BX_PLATFORM_OS_DESKTOP (0 \ + || BX_PLATFORM_BSD \ + || BX_PLATFORM_HAIKU \ + || BX_PLATFORM_HURD \ + || BX_PLATFORM_LINUX \ + || BX_PLATFORM_OSX \ + || BX_PLATFORM_WINDOWS \ + ) + +/// +#define BX_PLATFORM_OS_EMBEDDED (0 \ + || BX_PLATFORM_RPI \ + || BX_PLATFORM_STEAMLINK \ + ) + +/// +#define BX_PLATFORM_OS_MOBILE (0 \ + || BX_PLATFORM_ANDROID \ + || BX_PLATFORM_IOS \ + ) + +/// +#define BX_PLATFORM_OS_WEB (0 \ + || BX_PLATFORM_EMSCRIPTEN \ + ) + +/// #if BX_COMPILER_GCC # define BX_COMPILER_NAME "GCC " \ BX_STRINGIZE(__GNUC__) "." \ @@ -288,7 +338,9 @@ BX_STRINGIZE(__clang_minor__) "." \ BX_STRINGIZE(__clang_patchlevel__) #elif BX_COMPILER_MSVC -# if BX_COMPILER_MSVC >= 1910 // Visual Studio 2017 +# if BX_COMPILER_MSVC >= 1920 // Visual Studio 2019 +# define BX_COMPILER_NAME "MSVC 16.0" +# elif BX_COMPILER_MSVC >= 1910 // Visual Studio 2017 # define BX_COMPILER_NAME "MSVC 15.0" # elif BX_COMPILER_MSVC >= 1900 // Visual Studio 2015 # define BX_COMPILER_NAME "MSVC 14.0" @@ -315,6 +367,8 @@ BX_STRINGIZE(__EMSCRIPTEN_major__) "." \ BX_STRINGIZE(__EMSCRIPTEN_minor__) "." \ BX_STRINGIZE(__EMSCRIPTEN_tiny__) +#elif BX_PLATFORM_HAIKU +# define BX_PLATFORM_NAME "Haiku" #elif BX_PLATFORM_HURD # define BX_PLATFORM_NAME "Hurd" #elif BX_PLATFORM_IOS @@ -383,4 +437,23 @@ # define BX_ARCH_NAME "64-bit" #endif // BX_ARCH_ +#if BX_COMPILER_MSVC +# define BX_CPP_NAME "C++MsvcUnknown" +#elif defined(__cplusplus) +# if __cplusplus < 201103L +# error "Pre-C++11 compiler is not supported!" +# elif __cplusplus < 201402L +# define BX_CPP_NAME "C++11" +# elif __cplusplus < 201703L +# define BX_CPP_NAME "C++14" +# elif __cplusplus < 201704L +# define BX_CPP_NAME "C++17" +# else +// See: https://gist.github.com/bkaradzic/2e39896bc7d8c34e042b#orthodox-c +# define BX_CPP_NAME "C++WayTooModern" +# endif // BX_CPP_NAME +#else +# define BX_CPP_NAME "C++Unknown" +#endif // defined(__cplusplus) + #endif // BX_PLATFORM_H_HEADER_GUARD diff --git a/3rdparty/bx/include/bx/process.h b/3rdparty/bx/include/bx/process.h index 8369829527b..222b8ed79a8 100644 --- a/3rdparty/bx/include/bx/process.h +++ b/3rdparty/bx/include/bx/process.h @@ -1,5 +1,5 @@ /* - * Copyright 2010-2018 Branimir Karadzic. All rights reserved. + * Copyright 2010-2019 Branimir Karadzic. All rights reserved. * License: https://github.com/bkaradzic/bx#license-bsd-2-clause */ diff --git a/3rdparty/bx/include/bx/readerwriter.h b/3rdparty/bx/include/bx/readerwriter.h index 0d8e13545a8..ed61f46aa82 100644 --- a/3rdparty/bx/include/bx/readerwriter.h +++ b/3rdparty/bx/include/bx/readerwriter.h @@ -1,5 +1,5 @@ /* - * Copyright 2010-2018 Branimir Karadzic. All rights reserved. + * Copyright 2010-2019 Branimir Karadzic. All rights reserved. * License: https://github.com/bkaradzic/bx#license-bsd-2-clause */ @@ -10,6 +10,7 @@ #include "endian.h" #include "error.h" #include "filepath.h" +#include "math.h" #include "string.h" #include "uint32_t.h" @@ -21,94 +22,116 @@ BX_ERROR_RESULT(BX_ERROR_READERWRITER_ALREADY_OPEN, BX_MAKEFOURCC('R', 'W', 0, 5 namespace bx { - /// + /// The position from where offset is added. struct Whence { + /// Whence values: enum Enum { - Begin, - Current, - End, + Begin, //!< From begining of file. + Current, //!< From current position of file. + End, //!< From end of file. }; }; - /// + /// Reader interface. struct BX_NO_VTABLE ReaderI { + /// virtual ~ReaderI() = 0; + + /// virtual int32_t read(void* _data, int32_t _size, Error* _err) = 0; }; - /// + /// Writer interface. struct BX_NO_VTABLE WriterI { + /// virtual ~WriterI() = 0; + + /// virtual int32_t write(const void* _data, int32_t _size, Error* _err) = 0; }; - /// + /// Seeker interface. struct BX_NO_VTABLE SeekerI { + /// virtual ~SeekerI() = 0; + + /// virtual int64_t seek(int64_t _offset = 0, Whence::Enum _whence = Whence::Current) = 0; }; - /// + /// Reader seeker interface. struct BX_NO_VTABLE ReaderSeekerI : public ReaderI, public SeekerI { }; - /// + /// Writer seeker interface. struct BX_NO_VTABLE WriterSeekerI : public WriterI, public SeekerI { }; - /// + /// Open for reading interface. struct BX_NO_VTABLE ReaderOpenI { + /// virtual ~ReaderOpenI() = 0; + + /// virtual bool open(const FilePath& _filePath, Error* _err) = 0; }; - /// + /// Open for writing interface. struct BX_NO_VTABLE WriterOpenI { + /// virtual ~WriterOpenI() = 0; + + /// virtual bool open(const FilePath& _filePath, bool _append, Error* _err) = 0; }; - /// + /// Open process interface. struct BX_NO_VTABLE ProcessOpenI { + /// virtual ~ProcessOpenI() = 0; + + /// virtual bool open(const FilePath& _filePath, const StringView& _args, Error* _err) = 0; }; - /// + /// Closer interface. struct BX_NO_VTABLE CloserI { + /// virtual ~CloserI() = 0; + + /// virtual void close() = 0; }; - /// + /// File reader interface. struct BX_NO_VTABLE FileReaderI : public ReaderOpenI, public CloserI, public ReaderSeekerI { }; - /// + /// File writer interface. struct BX_NO_VTABLE FileWriterI : public WriterOpenI, public CloserI, public WriterSeekerI { }; - /// + /// Memory block interface. struct BX_NO_VTABLE MemoryBlockI { virtual void* more(uint32_t _size = 0) = 0; virtual uint32_t getSize() = 0; }; - /// + /// Static memory block interface. class StaticMemoryBlock : public MemoryBlockI { public: @@ -125,11 +148,11 @@ namespace bx virtual uint32_t getSize() override; private: - void* m_data; + void* m_data; uint32_t m_size; }; - /// + /// Memory block. class MemoryBlock : public MemoryBlockI { public: @@ -147,8 +170,8 @@ namespace bx private: AllocatorI* m_allocator; - void* m_data; - uint32_t m_size; + void* m_data; + uint32_t m_size; }; /// Sizer writer. Dummy writter that only counts number of bytes written into it. @@ -172,7 +195,7 @@ namespace bx int64_t m_top; }; - /// + /// Memory reader. class MemoryReader : public ReaderSeekerI { public: @@ -203,7 +226,7 @@ namespace bx int64_t m_top; }; - /// + /// Memory writer. class MemoryWriter : public WriterSeekerI { public: @@ -222,9 +245,9 @@ namespace bx private: MemoryBlockI* m_memBlock; uint8_t* m_data; - int64_t m_pos; - int64_t m_top; - int64_t m_size; + int64_t m_pos; + int64_t m_top; + int64_t m_size; }; /// Static (fixed size) memory block writer. diff --git a/3rdparty/bx/include/bx/ringbuffer.h b/3rdparty/bx/include/bx/ringbuffer.h index db44da8432e..2a93b985b4a 100644 --- a/3rdparty/bx/include/bx/ringbuffer.h +++ b/3rdparty/bx/include/bx/ringbuffer.h @@ -1,5 +1,5 @@ /* - * Copyright 2010-2018 Branimir Karadzic. All rights reserved. + * Copyright 2010-2019 Branimir Karadzic. All rights reserved. * License: https://github.com/bkaradzic/bx#license-bsd-2-clause */ diff --git a/3rdparty/bx/include/bx/rng.h b/3rdparty/bx/include/bx/rng.h index 87ba511e513..5469b5efd9c 100644 --- a/3rdparty/bx/include/bx/rng.h +++ b/3rdparty/bx/include/bx/rng.h @@ -1,5 +1,5 @@ /* - * Copyright 2010-2018 Branimir Karadzic. All rights reserved. + * Copyright 2010-2019 Branimir Karadzic. All rights reserved. * License: https://github.com/bkaradzic/bx#license-bsd-2-clause */ @@ -67,9 +67,7 @@ namespace bx template <typename Ty> bx::Vec3 randUnitHemisphere(Ty* _rng, const bx::Vec3& _normal); - /// Sampling with Hammersley and Halton Points - /// http://www.cse.cuhk.edu.hk/~ttwong/papers/udpoint/udpoints.html - /// + /// Sampling with Hammersley and Halton Points. void generateSphereHammersley(void* _data, uint32_t _stride, uint32_t _num, float _scale = 1.0f); /// Fisher-Yates shuffle. diff --git a/3rdparty/bx/include/bx/semaphore.h b/3rdparty/bx/include/bx/semaphore.h index 6d944ed2919..e3bd3899305 100644 --- a/3rdparty/bx/include/bx/semaphore.h +++ b/3rdparty/bx/include/bx/semaphore.h @@ -1,5 +1,5 @@ /* - * Copyright 2010-2018 Branimir Karadzic. All rights reserved. + * Copyright 2010-2019 Branimir Karadzic. All rights reserved. * License: https://github.com/bkaradzic/bx#license-bsd-2-clause */ diff --git a/3rdparty/bx/include/bx/settings.h b/3rdparty/bx/include/bx/settings.h index 7797cfac2bf..7dd45e626fb 100644 --- a/3rdparty/bx/include/bx/settings.h +++ b/3rdparty/bx/include/bx/settings.h @@ -1,5 +1,5 @@ /* - * Copyright 2011-2018 Branimir Karadzic. All rights reserved. + * Copyright 2011-2019 Branimir Karadzic. All rights reserved. * License: https://github.com/bkaradzic/bx#license-bsd-2-clause */ diff --git a/3rdparty/bx/include/bx/simd_t.h b/3rdparty/bx/include/bx/simd_t.h index 33c3f48a954..0c41844655b 100644 --- a/3rdparty/bx/include/bx/simd_t.h +++ b/3rdparty/bx/include/bx/simd_t.h @@ -1,5 +1,5 @@ /* - * Copyright 2010-2018 Branimir Karadzic. All rights reserved. + * Copyright 2010-2019 Branimir Karadzic. All rights reserved. * License: https://github.com/bkaradzic/bx#license-bsd-2-clause */ diff --git a/3rdparty/bx/include/bx/sort.h b/3rdparty/bx/include/bx/sort.h index 1506fb8307b..b32febd90ff 100644 --- a/3rdparty/bx/include/bx/sort.h +++ b/3rdparty/bx/include/bx/sort.h @@ -1,5 +1,5 @@ /* - * Copyright 2010-2018 Branimir Karadzic. All rights reserved. + * Copyright 2010-2019 Branimir Karadzic. All rights reserved. * License: https://github.com/bkaradzic/bx#license-bsd-2-clause */ diff --git a/3rdparty/bx/include/bx/spscqueue.h b/3rdparty/bx/include/bx/spscqueue.h index f34b65dfada..7ea7c448750 100644 --- a/3rdparty/bx/include/bx/spscqueue.h +++ b/3rdparty/bx/include/bx/spscqueue.h @@ -1,5 +1,5 @@ /* - * Copyright 2010-2018 Branimir Karadzic. All rights reserved. + * Copyright 2010-2019 Branimir Karadzic. All rights reserved. * License: https://github.com/bkaradzic/bx#license-bsd-2-clause */ diff --git a/3rdparty/bx/include/bx/string.h b/3rdparty/bx/include/bx/string.h index e0800562d71..43798a4f549 100644 --- a/3rdparty/bx/include/bx/string.h +++ b/3rdparty/bx/include/bx/string.h @@ -1,5 +1,5 @@ /* - * Copyright 2010-2018 Branimir Karadzic. All rights reserved. + * Copyright 2010-2019 Branimir Karadzic. All rights reserved. * License: https://github.com/bkaradzic/bx#license-bsd-2-clause */ @@ -21,6 +21,7 @@ namespace bx }; /// Non-zero-terminated string view. + /// class StringView { public: @@ -77,9 +78,16 @@ namespace bx /// void clear(); + /// Returns pointer to non-terminated string. + /// + /// @attention Use of this pointer in standard C/C++ functions is not safe. You must use it + /// in conjunction with `getTerm()` or getLength()`. /// const char* getPtr() const; + /// Returns pointer past last character in string view. + /// + /// @attention Dereferencing this pointer is not safe. /// const char* getTerm() const; @@ -261,7 +269,7 @@ namespace bx StringView findIdentifierMatch(const StringView& _str, const StringView& _word); /// Finds any identifier from NULL terminated array of identifiers. - StringView findIdentifierMatch(const StringView& _str, const char** _words); + StringView findIdentifierMatch(const StringView& _str, const char** _words, int32_t _num = INT32_MAX); /// Cross platform implementation of vsnprintf that returns number of /// characters which would have been written to the final string if @@ -273,6 +281,12 @@ namespace bx /// enough space had been available. int32_t snprintf(char* _out, int32_t _max, const char* _format, ...); + /// + int32_t vprintf(const char* _format, va_list _argList); + + /// + int32_t printf(const char* _format, ...); + /// Templatized snprintf. template <typename Ty> void stringPrintfVargs(Ty& _out, const char* _format, va_list _argList); @@ -321,6 +335,31 @@ namespace bx /// Converts string to 32-bit unsigned integer value. bool fromString(uint32_t* _out, const StringView& _str); + /// + class LineReader + { + public: + /// + LineReader(const bx::StringView& _str); + + /// + void reset(); + + /// + StringView next(); + + /// + bool isDone() const; + + /// + uint32_t getLine() const; + + private: + const bx::StringView m_str; + bx::StringView m_curr; + uint32_t m_line; + }; + } // namespace bx #include "inline/string.inl" diff --git a/3rdparty/bx/include/bx/thread.h b/3rdparty/bx/include/bx/thread.h index 087bd3d4a57..2526741ce08 100644 --- a/3rdparty/bx/include/bx/thread.h +++ b/3rdparty/bx/include/bx/thread.h @@ -1,5 +1,5 @@ /* - * Copyright 2010-2018 Branimir Karadzic. All rights reserved. + * Copyright 2010-2019 Branimir Karadzic. All rights reserved. * License: https://github.com/bkaradzic/bx#license-bsd-2-clause */ diff --git a/3rdparty/bx/include/bx/timer.h b/3rdparty/bx/include/bx/timer.h index b0b0b403a8b..bbe5e084d4f 100644 --- a/3rdparty/bx/include/bx/timer.h +++ b/3rdparty/bx/include/bx/timer.h @@ -1,5 +1,5 @@ /* - * Copyright 2010-2018 Branimir Karadzic. All rights reserved. + * Copyright 2010-2019 Branimir Karadzic. All rights reserved. * License: https://github.com/bkaradzic/bx#license-bsd-2-clause */ diff --git a/3rdparty/bx/include/bx/uint32_t.h b/3rdparty/bx/include/bx/uint32_t.h index 3d0afcb7124..3eb8bdfd3c8 100644 --- a/3rdparty/bx/include/bx/uint32_t.h +++ b/3rdparty/bx/include/bx/uint32_t.h @@ -1,5 +1,5 @@ /* - * Copyright 2010-2018 Branimir Karadzic. All rights reserved. + * Copyright 2010-2019 Branimir Karadzic. All rights reserved. * License: https://github.com/bkaradzic/bx#license-bsd-2-clause */ @@ -149,14 +149,18 @@ namespace bx /// Count number of bits set. /// - BX_CONSTEXPR_FUNC uint32_t uint32_cntbits(uint32_t _val); + template<typename Ty> + BX_CONSTEXPR_FUNC uint32_t uint32_cntbits(Ty _val); /// Count number of leading zeros. /// - BX_CONSTEXPR_FUNC uint32_t uint32_cntlz(uint32_t _val); + template<typename Ty> + BX_CONSTEXPR_FUNC uint32_t uint32_cntlz(Ty _val); + /// Count number of trailing zeros. /// - BX_CONSTEXPR_FUNC uint32_t uint32_cnttz(uint32_t _val); + template<typename Ty> + BX_CONSTEXPR_FUNC uint32_t uint32_cnttz(Ty _val); /// BX_CONSTEXPR_FUNC uint32_t uint32_part1by1(uint32_t _a); @@ -170,16 +174,41 @@ namespace bx /// BX_CONSTEXPR_FUNC uint32_t uint32_nextpow2(uint32_t _a); - /// Count number of bits set. /// - BX_CONSTEXPR_FUNC uint32_t uint64_cntbits(uint64_t _val); + BX_CONSTEXPR_FUNC uint64_t uint64_li(uint64_t _a); + + /// + BX_CONSTEXPR_FUNC uint64_t uint64_dec(uint64_t _a); + + /// + BX_CONSTEXPR_FUNC uint64_t uint64_inc(uint64_t _a); + + /// + BX_CONSTEXPR_FUNC uint64_t uint64_not(uint64_t _a); + + /// + BX_CONSTEXPR_FUNC uint64_t uint64_neg(uint64_t _a); + + /// + BX_CONSTEXPR_FUNC uint64_t uint64_ext(uint64_t _a); - /// Count number of leading zeros. /// - BX_CONSTEXPR_FUNC uint32_t uint64_cntlz(uint64_t _val); + BX_CONSTEXPR_FUNC uint64_t uint64_and(uint64_t _a, uint64_t _b); /// - BX_CONSTEXPR_FUNC uint32_t uint64_cnttz(uint64_t _val); + BX_CONSTEXPR_FUNC uint64_t uint64_andc(uint64_t _a, uint64_t _b); + + /// + BX_CONSTEXPR_FUNC uint64_t uint64_xor(uint64_t _a, uint64_t _b); + + /// + BX_CONSTEXPR_FUNC uint64_t uint64_xorl(uint64_t _a, uint64_t _b); + + /// + BX_CONSTEXPR_FUNC uint64_t uint64_or(uint64_t _a, uint64_t _b); + + /// + BX_CONSTEXPR_FUNC uint64_t uint64_orc(uint64_t _a, uint64_t _b); /// BX_CONSTEXPR_FUNC uint64_t uint64_sll(uint64_t _a, int32_t _sa); @@ -217,13 +246,10 @@ namespace bx /// BX_CONSTEXPR_FUNC uint32_t strideAlign(uint32_t _offset, uint32_t _stride); - /// Align to arbitrary stride and 16-bytes. + /// Align to arbitrary stride and Min bytes. /// - BX_CONSTEXPR_FUNC uint32_t strideAlign16(uint32_t _offset, uint32_t _stride); - - /// Align to arbitrary stride and 256-bytes. - /// - BX_CONSTEXPR_FUNC uint32_t strideAlign256(uint32_t _offset, uint32_t _stride); + template<uint32_t Min> + BX_CONSTEXPR_FUNC uint32_t strideAlign(uint32_t _offset, uint32_t _stride); /// Convert float to half-float. /// diff --git a/3rdparty/bx/include/bx/url.h b/3rdparty/bx/include/bx/url.h index 9e00d8348b4..320db14e398 100644 --- a/3rdparty/bx/include/bx/url.h +++ b/3rdparty/bx/include/bx/url.h @@ -1,5 +1,5 @@ /* - * Copyright 2010-2018 Branimir Karadzic. All rights reserved. + * Copyright 2010-2019 Branimir Karadzic. All rights reserved. * License: https://github.com/bkaradzic/bx#license-bsd-2-clause */ diff --git a/3rdparty/bx/include/compat/msvc/dirent.h b/3rdparty/bx/include/compat/msvc/dirent.h index a08d0fa4df3..3177d335f75 100644 --- a/3rdparty/bx/include/compat/msvc/dirent.h +++ b/3rdparty/bx/include/compat/msvc/dirent.h @@ -1,28 +1,10 @@ /* - * dirent.h - dirent API for Microsoft Visual Studio + * Dirent interface for Microsoft Visual Studio * - * Copyright (C) 2006-2012 Toni Ronkko - * - * Permission is hereby granted, free of charge, to any person obtaining - * a copy of this software and associated documentation files (the - * ``Software''), to deal in the Software without restriction, including - * without limitation the rights to use, copy, modify, merge, publish, - * distribute, sublicense, and/or sell copies of the Software, and to - * permit persons to whom the Software is furnished to do so, subject to - * the following conditions: - * - * The above copyright notice and this permission notice shall be included - * in all copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED ``AS IS'', WITHOUT WARRANTY OF ANY KIND, EXPRESS - * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF - * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. - * IN NO EVENT SHALL TONI RONKKO BE LIABLE FOR ANY CLAIM, DAMAGES OR - * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, - * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR - * OTHER DEALINGS IN THE SOFTWARE. - * - * $Id: dirent.h,v 1.20 2014/03/19 17:52:23 tronkko Exp $ + * Copyright (C) 1998-2019 Toni Ronkko + * This file is part of dirent. Dirent may be freely distributed + * under the MIT license. For all details and documentation, see + * https://github.com/tronkko/dirent */ #ifndef DIRENT_H #define DIRENT_H @@ -36,21 +18,16 @@ #endif // _MSC_VER /* - * Define architecture flags so we don't need to include windows.h. - * Avoiding windows.h makes it simpler to use windows sockets in conjunction - * with dirent.h. + * Include windows.h without Windows Sockets 1.1 to prevent conflicts with + * Windows Sockets 2.0. */ -#if !defined(_68K_) && !defined(_MPPC_) && !defined(_X86_) && !defined(_IA64_) && !defined(_AMD64_) && defined(_M_IX86) -# define _X86_ -#endif -#if !defined(_68K_) && !defined(_MPPC_) && !defined(_X86_) && !defined(_IA64_) && !defined(_AMD64_) && defined(_M_AMD64) -#define _AMD64_ +#ifndef WIN32_LEAN_AND_MEAN +# define WIN32_LEAN_AND_MEAN #endif +#include <windows.h> #include <stdio.h> #include <stdarg.h> -#include <windef.h> -#include <winbase.h> #include <wchar.h> #include <string.h> #include <stdlib.h> @@ -70,54 +47,109 @@ # define FILE_ATTRIBUTE_DEVICE 0x40 #endif -/* File type and permission flags for stat() */ +/* File type and permission flags for stat(), general mask */ #if !defined(S_IFMT) -# define S_IFMT _S_IFMT /* File type mask */ +# define S_IFMT _S_IFMT #endif + +/* Directory bit */ #if !defined(S_IFDIR) -# define S_IFDIR _S_IFDIR /* Directory */ +# define S_IFDIR _S_IFDIR #endif + +/* Character device bit */ #if !defined(S_IFCHR) -# define S_IFCHR _S_IFCHR /* Character device */ +# define S_IFCHR _S_IFCHR #endif + +/* Pipe bit */ #if !defined(S_IFFIFO) -# define S_IFFIFO _S_IFFIFO /* Pipe */ +# define S_IFFIFO _S_IFFIFO #endif + +/* Regular file bit */ #if !defined(S_IFREG) -# define S_IFREG _S_IFREG /* Regular file */ +# define S_IFREG _S_IFREG #endif + +/* Read permission */ #if !defined(S_IREAD) -# define S_IREAD _S_IREAD /* Read permission */ +# define S_IREAD _S_IREAD #endif + +/* Write permission */ #if !defined(S_IWRITE) -# define S_IWRITE _S_IWRITE /* Write permission */ +# define S_IWRITE _S_IWRITE #endif + +/* Execute permission */ #if !defined(S_IEXEC) -# define S_IEXEC _S_IEXEC /* Execute permission */ +# define S_IEXEC _S_IEXEC #endif + +/* Pipe */ #if !defined(S_IFIFO) -# define S_IFIFO _S_IFIFO /* Pipe */ +# define S_IFIFO _S_IFIFO #endif + +/* Block device */ #if !defined(S_IFBLK) -# define S_IFBLK 0 /* Block device */ +# define S_IFBLK 0 #endif + +/* Link */ #if !defined(S_IFLNK) -# define S_IFLNK 0 /* Link */ +# define S_IFLNK 0 #endif + +/* Socket */ #if !defined(S_IFSOCK) -# define S_IFSOCK 0 /* Socket */ +# define S_IFSOCK 0 +#endif + +/* Read user permission */ +#if !defined(S_IRUSR) +# define S_IRUSR S_IREAD +#endif + +/* Write user permission */ +#if !defined(S_IWUSR) +# define S_IWUSR S_IWRITE +#endif + +/* Execute user permission */ +#if !defined(S_IXUSR) +# define S_IXUSR 0 +#endif + +/* Read group permission */ +#if !defined(S_IRGRP) +# define S_IRGRP 0 +#endif + +/* Write group permission */ +#if !defined(S_IWGRP) +# define S_IWGRP 0 #endif -#if defined(_MSC_VER) -# define S_IRUSR S_IREAD /* Read user */ -# define S_IWUSR S_IWRITE /* Write user */ -# define S_IXUSR 0 /* Execute user */ -# define S_IRGRP 0 /* Read group */ -# define S_IWGRP 0 /* Write group */ -# define S_IXGRP 0 /* Execute group */ -# define S_IROTH 0 /* Read others */ -# define S_IWOTH 0 /* Write others */ -# define S_IXOTH 0 /* Execute others */ +/* Execute group permission */ +#if !defined(S_IXGRP) +# define S_IXGRP 0 +#endif + +/* Read others permission */ +#if !defined(S_IROTH) +# define S_IROTH 0 +#endif + +/* Write others permission */ +#if !defined(S_IWOTH) +# define S_IWOTH 0 +#endif + +/* Execute others permission */ +#if !defined(S_IXOTH) +# define S_IXOTH 0 #endif /* Maximum length of file name */ @@ -132,14 +164,14 @@ #endif /* File type flags for d_type */ -#define DT_UNKNOWN 0 -#define DT_REG S_IFREG -#define DT_DIR S_IFDIR -#define DT_FIFO S_IFIFO -#define DT_SOCK S_IFSOCK -#define DT_CHR S_IFCHR -#define DT_BLK S_IFBLK -#define DT_LNK S_IFLNK +#define DT_UNKNOWN 0 +#define DT_REG S_IFREG +#define DT_DIR S_IFDIR +#define DT_FIFO S_IFIFO +#define DT_SOCK S_IFSOCK +#define DT_CHR S_IFCHR +#define DT_BLK S_IFBLK +#define DT_LNK S_IFLNK /* Macros for converting between st_mode and d_type */ #define IFTODT(mode) ((mode) & S_IFMT) @@ -151,39 +183,33 @@ * only defined for compatibility. These macros should always return false * on Windows. */ -#ifndef S_ISFIFO +#if !defined(S_ISFIFO) # define S_ISFIFO(mode) (((mode) & S_IFMT) == S_IFIFO) #endif - -#ifndef S_ISDIR -# define S_ISDIR(mode) (((mode) & S_IFMT) == S_IFDIR) +#if !defined(S_ISDIR) +# define S_ISDIR(mode) (((mode) & S_IFMT) == S_IFDIR) #endif - -#ifndef S_ISREG -# define S_ISREG(mode) (((mode) & S_IFMT) == S_IFREG) +#if !defined(S_ISREG) +# define S_ISREG(mode) (((mode) & S_IFMT) == S_IFREG) #endif - -#ifndef S_ISLNK -# define S_ISLNK(mode) (((mode) & S_IFMT) == S_IFLNK) +#if !defined(S_ISLNK) +# define S_ISLNK(mode) (((mode) & S_IFMT) == S_IFLNK) #endif - -#ifndef S_ISSOCK +#if !defined(S_ISSOCK) # define S_ISSOCK(mode) (((mode) & S_IFMT) == S_IFSOCK) #endif - -#ifndef S_ISCHR -# define S_ISCHR(mode) (((mode) & S_IFMT) == S_IFCHR) +#if !defined(S_ISCHR) +# define S_ISCHR(mode) (((mode) & S_IFMT) == S_IFCHR) #endif - -#ifndef S_ISBLK -# define S_ISBLK(mode) (((mode) & S_IFMT) == S_IFBLK) +#if !defined(S_ISBLK) +# define S_ISBLK(mode) (((mode) & S_IFMT) == S_IFBLK) #endif -/* Return the exact length of d_namlen without zero terminator */ +/* Return the exact length of the file name without zero terminator */ #define _D_EXACT_NAMLEN(p) ((p)->d_namlen) -/* Return number of bytes needed to store d_namlen */ -#define _D_ALLOC_NAMLEN(p) (PATH_MAX) +/* Return the maximum size of a file name */ +#define _D_ALLOC_NAMLEN(p) ((PATH_MAX)+1) #ifdef __cplusplus @@ -193,45 +219,63 @@ extern "C" { /* Wide-character version */ struct _wdirent { - long d_ino; /* Always zero */ - unsigned short d_reclen; /* Structure size */ - size_t d_namlen; /* Length of name without \0 */ - int d_type; /* File type */ - wchar_t d_name[PATH_MAX]; /* File name */ + /* Always zero */ + long d_ino; + + /* File position within stream */ + long d_off; + + /* Structure size */ + unsigned short d_reclen; + + /* Length of name without \0 */ + size_t d_namlen; + + /* File type */ + int d_type; + + /* File name */ + wchar_t d_name[PATH_MAX+1]; }; typedef struct _wdirent _wdirent; struct _WDIR { - struct _wdirent ent; /* Current directory entry */ - WIN32_FIND_DATAW data; /* Private file data */ - int cached; /* True if data is valid */ - HANDLE handle; /* Win32 search handle */ - wchar_t *patt; /* Initial directory name */ + /* Current directory entry */ + struct _wdirent ent; + + /* Private file data */ + WIN32_FIND_DATAW data; + + /* True if data is valid */ + int cached; + + /* Win32 search handle */ + HANDLE handle; + + /* Initial directory name */ + wchar_t *patt; }; typedef struct _WDIR _WDIR; -static _WDIR *_wopendir (const wchar_t *dirname); -static struct _wdirent *_wreaddir (_WDIR *dirp); -static int _wclosedir (_WDIR *dirp); -static void _wrewinddir (_WDIR* dirp); +/* Multi-byte character version */ +struct dirent { + /* Always zero */ + long d_ino; + /* File position within stream */ + long d_off; -/* For compatibility with Symbian */ -#define wdirent _wdirent -#define WDIR _WDIR -#define wopendir _wopendir -#define wreaddir _wreaddir -#define wclosedir _wclosedir -#define wrewinddir _wrewinddir + /* Structure size */ + unsigned short d_reclen; + /* Length of name without \0 */ + size_t d_namlen; -/* Multi-byte character versions */ -struct dirent { - long d_ino; /* Always zero */ - unsigned short d_reclen; /* Structure size */ - size_t d_namlen; /* Length of name without \0 */ - int d_type; /* File type */ - char d_name[PATH_MAX]; /* File name */ + /* File type */ + int d_type; + + /* File name */ + char d_name[PATH_MAX+1]; }; typedef struct dirent dirent; @@ -241,10 +285,41 @@ struct DIR { }; typedef struct DIR DIR; + +/* Dirent functions */ static DIR *opendir (const char *dirname); +static _WDIR *_wopendir (const wchar_t *dirname); + static struct dirent *readdir (DIR *dirp); +static struct _wdirent *_wreaddir (_WDIR *dirp); + +static int readdir_r( + DIR *dirp, struct dirent *entry, struct dirent **result); +static int _wreaddir_r( + _WDIR *dirp, struct _wdirent *entry, struct _wdirent **result); + static int closedir (DIR *dirp); +static int _wclosedir (_WDIR *dirp); + static void rewinddir (DIR* dirp); +static void _wrewinddir (_WDIR* dirp); + +static int scandir (const char *dirname, struct dirent ***namelist, + int (*filter)(const struct dirent*), + int (*compare)(const struct dirent**, const struct dirent**)); + +static int alphasort (const struct dirent **a, const struct dirent **b); + +static int versionsort (const struct dirent **a, const struct dirent **b); + + +/* For compatibility with Symbian */ +#define wdirent _wdirent +#define WDIR _WDIR +#define wopendir _wopendir +#define wreaddir _wreaddir +#define wclosedir _wclosedir +#define wrewinddir _wrewinddir /* Internal utility functions */ @@ -267,6 +342,7 @@ static int dirent_wcstombs_s( static void dirent_set_errno (int error); + /* * Open directory stream DIRNAME for read and return a pointer to the * internal working area that is used to retrieve individual directory @@ -277,7 +353,8 @@ _wopendir( const wchar_t *dirname) { _WDIR *dirp = NULL; - int error; + DWORD n; + wchar_t *p; /* Must have directory name */ if (dirname == NULL || dirname[0] == '\0') { @@ -287,96 +364,120 @@ _wopendir( /* Allocate new _WDIR structure */ dirp = (_WDIR*) malloc (sizeof (struct _WDIR)); - if (dirp != NULL) { - DWORD n; - - /* Reset _WDIR structure */ - dirp->handle = INVALID_HANDLE_VALUE; - dirp->patt = NULL; - dirp->cached = 0; - - /* Compute the length of full path plus zero terminator */ - n = GetFullPathNameW (dirname, 0, NULL, NULL); - - /* Allocate room for absolute directory name and search pattern */ - dirp->patt = (wchar_t*) malloc (sizeof (wchar_t) * n + 16); - if (dirp->patt) { - - /* - * Convert relative directory name to an absolute one. This - * allows rewinddir() to function correctly even when current - * working directory is changed between opendir() and rewinddir(). - */ - n = GetFullPathNameW (dirname, n, dirp->patt, NULL); - if (n > 0) { - wchar_t *p; - - /* Append search pattern \* to the directory name */ - p = dirp->patt + n; - if (dirp->patt < p) { - switch (p[-1]) { - case '\\': - case '/': - case ':': - /* Directory ends in path separator, e.g. c:\temp\ */ - /*NOP*/; - break; - - default: - /* Directory name doesn't end in path separator */ - *p++ = '\\'; - } - } - *p++ = '*'; - *p = '\0'; + if (!dirp) { + return NULL; + } - /* Open directory stream and retrieve the first entry */ - if (dirent_first (dirp)) { - /* Directory stream opened successfully */ - error = 0; - } else { - /* Cannot retrieve first entry */ - error = 1; - dirent_set_errno (ENOENT); - } + /* Reset _WDIR structure */ + dirp->handle = INVALID_HANDLE_VALUE; + dirp->patt = NULL; + dirp->cached = 0; + + /* + * Compute the length of full path plus zero terminator + * + * Note that on WinRT there's no way to convert relative paths + * into absolute paths, so just assume it is an absolute path. + */ +#if defined(WINAPI_FAMILY) && defined(WINAPI_FAMILY_PHONE_APP) && (WINAPI_FAMILY == WINAPI_FAMILY_PHONE_APP) + /* WinRT */ + n = wcslen (dirname); +#else + /* Regular Windows */ + n = GetFullPathNameW (dirname, 0, NULL, NULL); +#endif - } else { - /* Cannot retrieve full path name */ - dirent_set_errno (ENOENT); - error = 1; - } + /* Allocate room for absolute directory name and search pattern */ + dirp->patt = (wchar_t*) malloc (sizeof (wchar_t) * n + 16); + if (dirp->patt == NULL) { + goto exit_closedir; + } - } else { - /* Cannot allocate memory for search pattern */ - error = 1; - } + /* + * Convert relative directory name to an absolute one. This + * allows rewinddir() to function correctly even when current + * working directory is changed between opendir() and rewinddir(). + * + * Note that on WinRT there's no way to convert relative paths + * into absolute paths, so just assume it is an absolute path. + */ +#if defined(WINAPI_FAMILY) && defined(WINAPI_FAMILY_PHONE_APP) && (WINAPI_FAMILY == WINAPI_FAMILY_PHONE_APP) + /* WinRT */ + wcsncpy_s (dirp->patt, n+1, dirname, n); +#else + /* Regular Windows */ + n = GetFullPathNameW (dirname, n, dirp->patt, NULL); + if (n <= 0) { + goto exit_closedir; + } +#endif - } else { - /* Cannot allocate _WDIR structure */ - error = 1; + /* Append search pattern \* to the directory name */ + p = dirp->patt + n; + switch (p[-1]) { + case '\\': + case '/': + case ':': + /* Directory ends in path separator, e.g. c:\temp\ */ + /*NOP*/; + break; + + default: + /* Directory name doesn't end in path separator */ + *p++ = '\\'; } + *p++ = '*'; + *p = '\0'; - /* Clean up in case of error */ - if (error && dirp) { - _wclosedir (dirp); - dirp = NULL; + /* Open directory stream and retrieve the first entry */ + if (!dirent_first (dirp)) { + goto exit_closedir; } + /* Success */ return dirp; + + /* Failure */ +exit_closedir: + _wclosedir (dirp); + return NULL; } /* - * Read next directory entry. The directory entry is returned in dirent - * structure in the d_name field. Individual directory entries returned by - * this function include regular files, sub-directories, pseudo-directories - * "." and ".." as well as volume labels, hidden files and system files. + * Read next directory entry. + * + * Returns pointer to static directory entry which may be overwritten by + * subsequent calls to _wreaddir(). */ static struct _wdirent* _wreaddir( _WDIR *dirp) { + struct _wdirent *entry; + + /* + * Read directory entry to buffer. We can safely ignore the return value + * as entry will be set to NULL in case of error. + */ + (void) _wreaddir_r (dirp, &dirp->ent, &entry); + + /* Return pointer to statically allocated directory entry */ + return entry; +} + +/* + * Read next directory entry. + * + * Returns zero on success. If end of directory stream is reached, then sets + * result to NULL and returns zero. + */ +static int +_wreaddir_r( + _WDIR *dirp, + struct _wdirent *entry, + struct _wdirent **result) +{ WIN32_FIND_DATAW *datap; - struct _wdirent *entp; /* Read next directory entry */ datap = dirent_next (dirp); @@ -384,46 +485,47 @@ _wreaddir( size_t n; DWORD attr; - /* Pointer to directory entry to return */ - entp = &dirp->ent; - /* * Copy file name as wide-character string. If the file name is too * long to fit in to the destination buffer, then truncate file name * to PATH_MAX characters and zero-terminate the buffer. */ n = 0; - while (n + 1 < PATH_MAX && datap->cFileName[n] != 0) { - entp->d_name[n] = datap->cFileName[n]; + while (n < PATH_MAX && datap->cFileName[n] != 0) { + entry->d_name[n] = datap->cFileName[n]; n++; } - dirp->ent.d_name[n] = 0; + entry->d_name[n] = 0; /* Length of file name excluding zero terminator */ - entp->d_namlen = n; + entry->d_namlen = n; /* File type */ attr = datap->dwFileAttributes; if ((attr & FILE_ATTRIBUTE_DEVICE) != 0) { - entp->d_type = DT_CHR; + entry->d_type = DT_CHR; } else if ((attr & FILE_ATTRIBUTE_DIRECTORY) != 0) { - entp->d_type = DT_DIR; + entry->d_type = DT_DIR; } else { - entp->d_type = DT_REG; + entry->d_type = DT_REG; } /* Reset dummy fields */ - entp->d_ino = 0; - entp->d_reclen = sizeof (struct _wdirent); + entry->d_ino = 0; + entry->d_off = 0; + entry->d_reclen = sizeof (struct _wdirent); + + /* Set result address */ + *result = entry; } else { - /* Last directory entry read */ - entp = NULL; + /* Return NULL to indicate end of directory */ + *result = NULL; } - return entp; + return /*OK*/0; } /* @@ -441,23 +543,21 @@ _wclosedir( /* Release search handle */ if (dirp->handle != INVALID_HANDLE_VALUE) { FindClose (dirp->handle); - dirp->handle = INVALID_HANDLE_VALUE; } /* Release search pattern */ - if (dirp->patt) { - free (dirp->patt); - dirp->patt = NULL; - } + free (dirp->patt); /* Release directory structure */ free (dirp); ok = /*success*/0; } else { + /* Invalid directory stream */ dirent_set_errno (EBADF); ok = /*failure*/-1; + } return ok; } @@ -487,9 +587,12 @@ dirent_first( _WDIR *dirp) { WIN32_FIND_DATAW *datap; + DWORD error; /* Open directory and retrieve the first entry */ - dirp->handle = FindFirstFileW (dirp->patt, &dirp->data); + dirp->handle = FindFirstFileExW( + dirp->patt, FindExInfoStandard, &dirp->data, + FindExSearchNameMatch, NULL, 0); if (dirp->handle != INVALID_HANDLE_VALUE) { /* a directory entry is now waiting in memory */ @@ -498,15 +601,38 @@ dirent_first( } else { - /* Failed to re-open directory: no directory entry in memory */ + /* Failed to open directory: no directory entry in memory */ dirp->cached = 0; datap = NULL; + /* Set error code */ + error = GetLastError (); + switch (error) { + case ERROR_ACCESS_DENIED: + /* No read access to directory */ + dirent_set_errno (EACCES); + break; + + case ERROR_DIRECTORY: + /* Directory name is invalid */ + dirent_set_errno (ENOTDIR); + break; + + case ERROR_PATH_NOT_FOUND: + default: + /* Cannot find the file */ + dirent_set_errno (ENOENT); + } + } return datap; } -/* Get next directory entry (internal) */ +/* + * Get next directory entry (internal). + * + * Returns + */ static WIN32_FIND_DATAW* dirent_next( _WDIR *dirp) @@ -527,7 +653,7 @@ dirent_next( /* Got a file */ p = &dirp->data; } else { - /* The very last entry has been processed or an error occured */ + /* The very last entry has been processed or an error occurred */ FindClose (dirp->handle); dirp->handle = INVALID_HANDLE_VALUE; p = NULL; @@ -552,6 +678,8 @@ opendir( { struct DIR *dirp; int error; + wchar_t wname[PATH_MAX + 1]; + size_t n; /* Must have directory name */ if (dirname == NULL || dirname[0] == '\0') { @@ -561,67 +689,71 @@ opendir( /* Allocate memory for DIR structure */ dirp = (DIR*) malloc (sizeof (struct DIR)); - if (dirp) { - wchar_t wname[PATH_MAX]; - size_t n; - - /* Convert directory name to wide-character string */ - error = dirent_mbstowcs_s (&n, wname, PATH_MAX, dirname, PATH_MAX); - if (!error) { - - /* Open directory stream using wide-character name */ - dirp->wdirp = _wopendir (wname); - if (dirp->wdirp) { - /* Directory stream opened */ - error = 0; - } else { - /* Failed to open directory stream */ - error = 1; - } - - } else { - /* - * Cannot convert file name to wide-character string. This - * occurs if the string contains invalid multi-byte sequences or - * the output buffer is too small to contain the resulting - * string. - */ - error = 1; - } + if (!dirp) { + return NULL; + } - } else { - /* Cannot allocate DIR structure */ - error = 1; + /* Convert directory name to wide-character string */ + error = dirent_mbstowcs_s( + &n, wname, PATH_MAX + 1, dirname, PATH_MAX + 1); + if (error) { + /* + * Cannot convert file name to wide-character string. This + * occurs if the string contains invalid multi-byte sequences or + * the output buffer is too small to contain the resulting + * string. + */ + goto exit_free; } - /* Clean up in case of error */ - if (error && dirp) { - free (dirp); - dirp = NULL; + + /* Open directory stream using wide-character name */ + dirp->wdirp = _wopendir (wname); + if (!dirp->wdirp) { + goto exit_free; } + /* Success */ return dirp; + + /* Failure */ +exit_free: + free (dirp); + return NULL; } /* * Read next directory entry. - * - * When working with text consoles, please note that file names returned by - * readdir() are represented in the default ANSI code page while any output to - * console is typically formatted on another code page. Thus, non-ASCII - * characters in file names will not usually display correctly on console. The - * problem can be fixed in two ways: (1) change the character set of console - * to 1252 using chcp utility and use Lucida Console font, or (2) use - * _cprintf function when writing to console. The _cprinf() will re-encode - * ANSI strings to the console code page so many non-ASCII characters will - * display correcly. */ static struct dirent* readdir( DIR *dirp) { + struct dirent *entry; + + /* + * Read directory entry to buffer. We can safely ignore the return value + * as entry will be set to NULL in case of error. + */ + (void) readdir_r (dirp, &dirp->ent, &entry); + + /* Return pointer to statically allocated directory entry */ + return entry; +} + +/* + * Read next directory entry into called-allocated buffer. + * + * Returns zero on success. If the end of directory stream is reached, then + * sets result to NULL and returns zero. + */ +static int +readdir_r( + DIR *dirp, + struct dirent *entry, + struct dirent **result) +{ WIN32_FIND_DATAW *datap; - struct dirent *entp; /* Read next directory entry */ datap = dirent_next (dirp->wdirp); @@ -631,7 +763,7 @@ readdir( /* Attempt to convert file name to multi-byte string */ error = dirent_wcstombs_s( - &n, dirp->ent.d_name, PATH_MAX, datap->cFileName, PATH_MAX); + &n, entry->d_name, PATH_MAX + 1, datap->cFileName, PATH_MAX + 1); /* * If the file name cannot be represented by a multi-byte string, @@ -645,55 +777,60 @@ readdir( */ if (error && datap->cAlternateFileName[0] != '\0') { error = dirent_wcstombs_s( - &n, dirp->ent.d_name, PATH_MAX, - datap->cAlternateFileName, PATH_MAX); + &n, entry->d_name, PATH_MAX + 1, + datap->cAlternateFileName, PATH_MAX + 1); } if (!error) { DWORD attr; - /* Initialize directory entry for return */ - entp = &dirp->ent; - /* Length of file name excluding zero terminator */ - entp->d_namlen = n - 1; + entry->d_namlen = n - 1; /* File attributes */ attr = datap->dwFileAttributes; if ((attr & FILE_ATTRIBUTE_DEVICE) != 0) { - entp->d_type = DT_CHR; + entry->d_type = DT_CHR; } else if ((attr & FILE_ATTRIBUTE_DIRECTORY) != 0) { - entp->d_type = DT_DIR; + entry->d_type = DT_DIR; } else { - entp->d_type = DT_REG; + entry->d_type = DT_REG; } /* Reset dummy fields */ - entp->d_ino = 0; - entp->d_reclen = sizeof (struct dirent); + entry->d_ino = 0; + entry->d_off = 0; + entry->d_reclen = sizeof (struct dirent); } else { + /* * Cannot convert file name to multi-byte string so construct - * an errornous directory entry and return that. Note that + * an erroneous directory entry and return that. Note that * we cannot return NULL as that would stop the processing * of directory entries completely. */ - entp = &dirp->ent; - entp->d_name[0] = '?'; - entp->d_name[1] = '\0'; - entp->d_namlen = 1; - entp->d_type = DT_UNKNOWN; - entp->d_ino = 0; - entp->d_reclen = 0; + entry->d_name[0] = '?'; + entry->d_name[1] = '\0'; + entry->d_namlen = 1; + entry->d_type = DT_UNKNOWN; + entry->d_ino = 0; + entry->d_off = -1; + entry->d_reclen = 0; + } + /* Return pointer to directory entry */ + *result = entry; + } else { + /* No more directory entries */ - entp = NULL; + *result = NULL; + } - return entp; + return /*OK*/0; } /* @@ -734,6 +871,162 @@ rewinddir( _wrewinddir (dirp->wdirp); } +/* + * Scan directory for entries. + */ +static int +scandir( + const char *dirname, + struct dirent ***namelist, + int (*filter)(const struct dirent*), + int (*compare)(const struct dirent**, const struct dirent**)) +{ + struct dirent **files = NULL; + size_t size = 0; + size_t allocated = 0; + const size_t init_size = 1; + DIR *dir = NULL; + struct dirent *entry; + struct dirent *tmp = NULL; + size_t i; + int result = 0; + + /* Open directory stream */ + dir = opendir (dirname); + if (dir) { + + /* Read directory entries to memory */ + while (1) { + + /* Enlarge pointer table to make room for another pointer */ + if (size >= allocated) { + void *p; + size_t num_entries; + + /* Compute number of entries in the enlarged pointer table */ + if (size < init_size) { + /* Allocate initial pointer table */ + num_entries = init_size; + } else { + /* Double the size */ + num_entries = size * 2; + } + + /* Allocate first pointer table or enlarge existing table */ + p = realloc (files, sizeof (void*) * num_entries); + if (p != NULL) { + /* Got the memory */ + files = (dirent**) p; + allocated = num_entries; + } else { + /* Out of memory */ + result = -1; + break; + } + + } + + /* Allocate room for temporary directory entry */ + if (tmp == NULL) { + tmp = (struct dirent*) malloc (sizeof (struct dirent)); + if (tmp == NULL) { + /* Cannot allocate temporary directory entry */ + result = -1; + break; + } + } + + /* Read directory entry to temporary area */ + if (readdir_r (dir, tmp, &entry) == /*OK*/0) { + + /* Did we get an entry? */ + if (entry != NULL) { + int pass; + + /* Determine whether to include the entry in result */ + if (filter) { + /* Let the filter function decide */ + pass = filter (tmp); + } else { + /* No filter function, include everything */ + pass = 1; + } + + if (pass) { + /* Store the temporary entry to pointer table */ + files[size++] = tmp; + tmp = NULL; + + /* Keep up with the number of files */ + result++; + } + + } else { + + /* + * End of directory stream reached => sort entries and + * exit. + */ + qsort (files, size, sizeof (void*), + (int (*) (const void*, const void*)) compare); + break; + + } + + } else { + /* Error reading directory entry */ + result = /*Error*/ -1; + break; + } + + } + + } else { + /* Cannot open directory */ + result = /*Error*/ -1; + } + + /* Release temporary directory entry */ + free (tmp); + + /* Release allocated memory on error */ + if (result < 0) { + for (i = 0; i < size; i++) { + free (files[i]); + } + free (files); + files = NULL; + } + + /* Close directory stream */ + if (dir) { + closedir (dir); + } + + /* Pass pointer table to caller */ + if (namelist) { + *namelist = files; + } + return result; +} + +/* Alphabetical sorting */ +static int +alphasort( + const struct dirent **a, const struct dirent **b) +{ + return strcoll ((*a)->d_name, (*b)->d_name); +} + +/* Sort versions */ +static int +versionsort( + const struct dirent **a, const struct dirent **b) +{ + /* FIXME: implement strverscmp and use that */ + return alphasort (a, b); +} + /* Convert multi-byte string to wide character string */ static int dirent_mbstowcs_s( @@ -744,46 +1037,87 @@ dirent_mbstowcs_s( size_t count) { int error; - -#if defined(_MSC_VER) && _MSC_VER >= 1400 - - /* Microsoft Visual Studio 2005 or later */ - error = mbstowcs_s (pReturnValue, wcstr, sizeInWords, mbstr, count); - + int n; + size_t len; + UINT cp; + DWORD flags; + + /* Determine code page for multi-byte string */ +#if WINAPI_FAMILY_PARTITION(WINAPI_PARTITION_DESKTOP) + if (AreFileApisANSI ()) { + /* Default ANSI code page */ + cp = GetACP (); + } else { + /* Default OEM code page */ + cp = GetOEMCP (); + } #else + cp = CP_ACP; +#endif // WINAPI_FAMILY_PARTITION(WINAPI_PARTITION_DESKTOP) + + /* + * Determine flags based on the character set. For more information, + * please see https://docs.microsoft.com/fi-fi/windows/desktop/api/stringapiset/nf-stringapiset-multibytetowidechar + */ + switch (cp) { + case 42: + case 50220: + case 50221: + case 50222: + case 50225: + case 50227: + case 50229: + case 57002: + case 57003: + case 57004: + case 57005: + case 57006: + case 57007: + case 57008: + case 57009: + case 57010: + case 57011: + case 65000: + /* MultiByteToWideChar does not support MB_ERR_INVALID_CHARS */ + flags = 0; + break; + + default: + /* + * Ask MultiByteToWideChar to return an error if a multi-byte + * character cannot be converted to a wide-character. + */ + flags = MB_ERR_INVALID_CHARS; + } - /* Older Visual Studio or non-Microsoft compiler */ - size_t n; + /* Compute the length of input string without zero-terminator */ + len = 0; + while (mbstr[len] != '\0' && len < count) { + len++; + } - /* Convert to wide-character string (or count characters) */ - n = mbstowcs (wcstr, mbstr, sizeInWords); - if (!wcstr || n < count) { + /* Convert to wide-character string */ + n = MultiByteToWideChar( + /* Source code page */ cp, + /* Flags */ flags, + /* Pointer to string to convert */ mbstr, + /* Size of multi-byte string */ (int) len, + /* Pointer to output buffer */ wcstr, + /* Size of output buffer */ (int)sizeInWords - 1 + ); - /* Zero-terminate output buffer */ - if (wcstr && sizeInWords) { - if (n >= sizeInWords) { - n = sizeInWords - 1; - } - wcstr[n] = 0; - } + /* Ensure that output buffer is zero-terminated */ + wcstr[n] = '\0'; - /* Length of resuting multi-byte string WITH zero terminator */ - if (pReturnValue) { - *pReturnValue = n + 1; - } + /* Return length of wide-character string with zero-terminator */ + *pReturnValue = (size_t) (n + 1); - /* Success */ + /* Return zero if conversion succeeded */ + if (n > 0) { error = 0; - } else { - - /* Could not convert string */ error = 1; - } - -#endif - return error; } @@ -796,47 +1130,82 @@ dirent_wcstombs_s( const wchar_t *wcstr, size_t count) { + int n; int error; - -#if defined(_MSC_VER) && _MSC_VER >= 1400 - - /* Microsoft Visual Studio 2005 or later */ - error = wcstombs_s (pReturnValue, mbstr, sizeInBytes, wcstr, count); - + UINT cp; + size_t len; + BOOL flag = 0; + LPBOOL pflag; + + /* Determine code page for multi-byte string */ +#if WINAPI_FAMILY_PARTITION(WINAPI_PARTITION_DESKTOP) + if (AreFileApisANSI ()) { + /* Default ANSI code page */ + cp = GetACP (); + } else { + /* Default OEM code page */ + cp = GetOEMCP (); + } #else + cp = CP_ACP; +#endif // WINAPI_FAMILY_PARTITION(WINAPI_PARTITION_DESKTOP) - /* Older Visual Studio or non-Microsoft compiler */ - size_t n; - /* Convert to multi-byte string (or count the number of bytes needed) */ - n = wcstombs (mbstr, wcstr, sizeInBytes); - if (!mbstr || n < count) { + /* Compute the length of input string without zero-terminator */ + len = 0; + while (wcstr[len] != '\0' && len < count) { + len++; + } - /* Zero-terminate output buffer */ - if (mbstr && sizeInBytes) { - if (n >= sizeInBytes) { - n = sizeInBytes - 1; - } - mbstr[n] = '\0'; - } + /* + * Determine if we can ask WideCharToMultiByte to return information on + * broken characters. For more information, please see + * https://docs.microsoft.com/en-us/windows/desktop/api/stringapiset/nf-stringapiset-widechartomultibyte + */ + switch (cp) { + case CP_UTF7: + case CP_UTF8: + /* + * WideCharToMultiByte fails if we request information on default + * characters. This is just a nuisance but doesn't affect the + * conversion: if Windows is configured to use UTF-8, then the default + * character should not be needed anyway. + */ + pflag = NULL; + break; - /* Lenght of resulting multi-bytes string WITH zero-terminator */ - if (pReturnValue) { - *pReturnValue = n + 1; - } + default: + /* + * Request that WideCharToMultiByte sets the flag if it uses the + * default character. + */ + pflag = &flag; + } - /* Success */ + /* Convert wide-character string to multi-byte character string */ + n = WideCharToMultiByte( + /* Target code page */ cp, + /* Flags */ 0, + /* Pointer to unicode string */ wcstr, + /* Length of unicode string */ (int) len, + /* Pointer to output buffer */ mbstr, + /* Size of output buffer */ (int)sizeInBytes - 1, + /* Default character */ NULL, + /* Whether default character was used or not */ pflag + ); + + /* Ensure that output buffer is zero-terminated */ + mbstr[n] = '\0'; + + /* Return length of multi-byte string with zero-terminator */ + *pReturnValue = (size_t) (n + 1); + + /* Return zero if conversion succeeded without using default characters */ + if (n > 0 && flag == 0) { error = 0; - } else { - - /* Cannot convert string */ error = 1; - } - -#endif - return error; } @@ -858,6 +1227,7 @@ dirent_set_errno( #endif } + #ifdef __cplusplus } #endif diff --git a/3rdparty/bx/makefile b/3rdparty/bx/makefile index 65e55a7715e..985c94b83f4 100644 --- a/3rdparty/bx/makefile +++ b/3rdparty/bx/makefile @@ -1,5 +1,5 @@ # -# Copyright 2011-2018 Branimir Karadzic. All rights reserved. +# Copyright 2011-2019 Branimir Karadzic. All rights reserved. # License: https://github.com/bkaradzic/bx#license-bsd-2-clause # @@ -8,10 +8,12 @@ GENIE=../bx/tools/bin/$(OS)/genie all: $(GENIE) vs2017 $(GENIE) --gcc=android-arm gmake + $(GENIE) --gcc=android-arm64 gmake $(GENIE) --gcc=android-mips gmake $(GENIE) --gcc=android-x86 gmake $(GENIE) --gcc=mingw-gcc gmake $(GENIE) --gcc=linux-gcc gmake + $(GENIE) --gcc=haiku gmake $(GENIE) --gcc=osx gmake $(GENIE) --gcc=ios-arm gmake $(GENIE) --gcc=ios-simulator gmake @@ -26,6 +28,14 @@ android-arm-release: .build/projects/gmake-android-arm make -R -C .build/projects/gmake-android-arm config=release android-arm: android-arm-debug android-arm-release +.build/projects/gmake-android-arm64: + $(GENIE) --gcc=android-arm64 gmake +android-arm64-debug: .build/projects/gmake-android-arm64 + make -R -C .build/projects/gmake-android-arm64 config=debug +android-arm64-release: .build/projects/gmake-android-arm64 + make -R -C .build/projects/gmake-android-arm64 config=release +android-arm64: android-arm64-debug android-arm64-release + .build/projects/gmake-android-mips: $(GENIE) --gcc=android-mips gmake android-mips-debug: .build/projects/gmake-android-mips @@ -50,6 +60,14 @@ linux-release64: .build/projects/gmake-linux make -R -C .build/projects/gmake-linux config=release64 linux: linux-debug64 linux-release64 +.build/projects/gmake-haiku: + $(GENIE) --gcc=haiku gmake +haiku-debug64: .build/projects/gmake-haiku + make -R -C .build/projects/gmake-haiku config=debug64 +haiku-release64: .build/projects/gmake-haiku + make -R -C .build/projects/gmake-haiku config=release64 +haiku: haiku-debug64 haiku-release64 + .build/projects/gmake-mingw-gcc: $(GENIE) --gcc=mingw-gcc gmake mingw-gcc-debug32: .build/projects/gmake-mingw-gcc @@ -130,7 +148,8 @@ clean: SILENT ?= @ UNAME := $(shell uname) -ifeq ($(UNAME),$(filter $(UNAME),Linux GNU Darwin)) +ifeq ($(UNAME),$(filter $(UNAME),Linux GNU Darwin Haiku)) +ifeq ($(UNAME),$(filter $(UNAME),Darwin Haiku)) ifeq ($(UNAME),$(filter $(UNAME),Darwin)) OS=darwin BUILD_PROJECT_DIR=gmake-osx @@ -138,6 +157,13 @@ BUILD_OUTPUT_DIR=osx64_clang BUILD_TOOLS_CONFIG=release64 EXE= else +OS=haiku +BUILD_PROJECT_DIR=gmake-haiku +BUILD_OUTPUT_DIR=haiku64_gcc +BUILD_TOOLS_CONFIG=release64 +EXE= +endif +else OS=linux BUILD_PROJECT_DIR=gmake-linux BUILD_OUTPUT_DIR=linux64_gcc @@ -165,6 +191,12 @@ tools/bin/darwin/bin2c: .build/osx64_clang/bin/bin2cRelease tools/bin/linux/bin2c: .build/linux64_gcc/bin/bin2cRelease $(SILENT) cp $(<) $(@) +.build/haiku64_gcc/bin/bin2cRelease: .build/projects/gmake-haiku + $(SILENT) make -C .build/projects/gmake-haiku bin2c config=$(BUILD_TOOLS_CONFIG) + +tools/bin/haiku/bin2c: .build/haiku64_gcc/bin/bin2cRelease + $(SILENT) cp $(<) $(@) + .build/win64_mingw-gcc/bin/bin2cRelease.exe: .build/projects/gmake-mingw-gcc $(SILENT) make -C .build/projects/gmake-mingw-gcc bin2c config=$(BUILD_TOOLS_CONFIG) @@ -186,6 +218,12 @@ tools/bin/darwin/lemon: .build/osx64_clang/bin/lemonRelease tools/bin/linux/lemon: .build/linux64_gcc/bin/lemonRelease $(SILENT) cp $(<) $(@) +.build/haiku64_gcc/bin/lemonRelease: .build/projects/gmake-haiku + $(SILENT) make -C .build/projects/gmake-haiku lemon config=$(BUILD_TOOLS_CONFIG) + +tools/bin/haiku/lemon: .build/haiku64_gcc/bin/lemonRelease + $(SILENT) cp $(<) $(@) + .build/win64_mingw-gcc/bin/lemonRelease.exe: .build/projects/gmake-mingw-gcc $(SILENT) make -C .build/projects/gmake-mingw-gcc lemon config=$(BUILD_TOOLS_CONFIG) @@ -199,7 +237,7 @@ lemon: tools/bin/$(OS)/lemon$(EXE) tools/bin/$(OS)/lempar.c tools: bin2c lemon -dist: tools/bin/darwin/bin2c tools/bin/linux/bin2c tools/bin/windows/bin2c.exe +dist: tools/bin/darwin/bin2c tools/bin/linux/bin2c tools/bin/windows/bin2c.exe tools/bin/haiku/bin2c .build/$(BUILD_OUTPUT_DIR)/bin/bx.testRelease$(EXE): .build/projects/$(BUILD_PROJECT_DIR) $(SILENT) make -C .build/projects/$(BUILD_PROJECT_DIR) bx.test config=$(BUILD_TOOLS_CONFIG) diff --git a/3rdparty/bx/scripts/bin2c.lua b/3rdparty/bx/scripts/bin2c.lua index fc4dec96c69..f81031879ca 100644 --- a/3rdparty/bx/scripts/bin2c.lua +++ b/3rdparty/bx/scripts/bin2c.lua @@ -1,5 +1,5 @@ -- --- Copyright 2010-2018 Branimir Karadzic. All rights reserved. +-- Copyright 2010-2019 Branimir Karadzic. All rights reserved. -- License: https://github.com/bkaradzic/bx#license-bsd-2-clause -- diff --git a/3rdparty/bx/scripts/bx.lua b/3rdparty/bx/scripts/bx.lua index bb0d17ade18..60f67641362 100644 --- a/3rdparty/bx/scripts/bx.lua +++ b/3rdparty/bx/scripts/bx.lua @@ -1,5 +1,5 @@ -- --- Copyright 2010-2018 Branimir Karadzic. All rights reserved. +-- Copyright 2010-2019 Branimir Karadzic. All rights reserved. -- License: https://github.com/bkaradzic/bx#license-bsd-2-clause -- diff --git a/3rdparty/bx/scripts/genie.lua b/3rdparty/bx/scripts/genie.lua index 3cdb23e7c60..600bd7fab9c 100644 --- a/3rdparty/bx/scripts/genie.lua +++ b/3rdparty/bx/scripts/genie.lua @@ -1,5 +1,5 @@ -- --- Copyright 2010-2018 Branimir Karadzic. All rights reserved. +-- Copyright 2010-2019 Branimir Karadzic. All rights reserved. -- License: https://github.com/bkaradzic/bx#license-bsd-2-clause -- diff --git a/3rdparty/bx/scripts/lemon.lua b/3rdparty/bx/scripts/lemon.lua index 21024fd80b9..2759ea60972 100644 --- a/3rdparty/bx/scripts/lemon.lua +++ b/3rdparty/bx/scripts/lemon.lua @@ -1,5 +1,5 @@ -- --- Copyright 2010-2018 Branimir Karadzic. All rights reserved. +-- Copyright 2010-2019 Branimir Karadzic. All rights reserved. -- License: https://github.com/bkaradzic/bx#license-bsd-2-clause -- diff --git a/3rdparty/bx/scripts/toolchain.lua b/3rdparty/bx/scripts/toolchain.lua index 03f97089b7b..18eeb91a832 100644 --- a/3rdparty/bx/scripts/toolchain.lua +++ b/3rdparty/bx/scripts/toolchain.lua @@ -1,5 +1,5 @@ -- --- Copyright 2010-2018 Branimir Karadzic. All rights reserved. +-- Copyright 2010-2019 Branimir Karadzic. All rights reserved. -- License: https://github.com/bkaradzic/bx#license-bsd-2-clause -- @@ -50,6 +50,7 @@ function toolchain(_buildDir, _libDir) description = "Choose GCC flavor", allowed = { { "android-arm", "Android - ARM" }, + { "android-arm64", "Android - ARM64" }, { "android-x86", "Android - x86" }, { "asmjs", "Emscripten/asm.js" }, { "freebsd", "FreeBSD" }, @@ -73,6 +74,7 @@ function toolchain(_buildDir, _libDir) { "orbis", "Orbis" }, { "riscv", "RISC-V" }, { "rpi", "RaspberryPi" }, + { "haiku", "Haiku" }, }, } @@ -193,7 +195,9 @@ function toolchain(_buildDir, _libDir) end flags { + "Cpp14", "ExtraWarnings", + "FloatFast", } if _ACTION == "gmake" or _ACTION == "ninja" then @@ -218,6 +222,21 @@ function toolchain(_buildDir, _libDir) premake.gcc.llvm = true location (path.join(_buildDir, "projects", _ACTION .. "-android-arm")) + elseif "android-arm64" == _OPTIONS["gcc"] then + + if not os.getenv("ANDROID_NDK_ARM64") + or not os.getenv("ANDROID_NDK_CLANG") + or not os.getenv("ANDROID_NDK_ROOT") then + print("Set ANDROID_NDK_CLANG, ANDROID_NDK_ARM64, and ANDROID_NDK_ROOT environment variables.") + end + + premake.gcc.cc = "$(ANDROID_NDK_CLANG)/bin/clang" + premake.gcc.cxx = "$(ANDROID_NDK_CLANG)/bin/clang++" + premake.gcc.ar = "$(ANDROID_NDK_ARM64)/bin/aarch64-linux-android-ar" + + premake.gcc.llvm = true + location (path.join(_buildDir, "projects", _ACTION .. "-android-arm64")) + elseif "android-x86" == _OPTIONS["gcc"] then if not os.getenv("ANDROID_NDK_X86") @@ -228,18 +247,19 @@ function toolchain(_buildDir, _libDir) premake.gcc.cc = "$(ANDROID_NDK_CLANG)/bin/clang" premake.gcc.cxx = "$(ANDROID_NDK_CLANG)/bin/clang++" + premake.gcc.ar = "$(ANDROID_NDK_X86)/bin/i686-linux-android-ar" premake.gcc.llvm = true location (path.join(_buildDir, "projects", _ACTION .. "-android-x86")) elseif "asmjs" == _OPTIONS["gcc"] then - if not os.getenv("EMSCRIPTEN") then - print("Set EMSCRIPTEN environment variable.") + if not os.getenv("EMSDK") then + print("Set EMSDK environment variable.") end - premake.gcc.cc = "\"$(EMSCRIPTEN)/emcc\"" - premake.gcc.cxx = "\"$(EMSCRIPTEN)/em++\"" - premake.gcc.ar = "\"$(EMSCRIPTEN)/emar\"" + premake.gcc.cc = "\"$(EMSDK)/fastcomp/bin/emcc\"" + premake.gcc.cxx = "\"$(EMSDK)/fastcomp/bin/em++\"" + premake.gcc.ar = "\"$(EMSDK)/fastcomp/bin/emar\"" premake.gcc.llvm = true location (path.join(_buildDir, "projects", _ACTION .. "-asmjs")) @@ -391,6 +411,7 @@ function toolchain(_buildDir, _libDir) or _ACTION == "vs2013" or _ACTION == "vs2015" or _ACTION == "vs2017" + or _ACTION == "vs2019" then local action = premake.action.current() @@ -633,9 +654,6 @@ function toolchain(_buildDir, _libDir) "-Wunused-value", "-Wundef", } - buildoptions_cpp { - "-std=c++14", - } linkoptions { "-Wl,--gc-sections", "-static", @@ -664,9 +682,9 @@ function toolchain(_buildDir, _libDir) configuration { "mingw-clang" } buildoptions { - "-isystem$(MINGW)/lib/gcc/x86_64-w64-mingw32/4.8.1/include/c++", - "-isystem$(MINGW)/lib/gcc/x86_64-w64-mingw32/4.8.1/include/c++/x86_64-w64-mingw32", - "-isystem$(MINGW)/x86_64-w64-mingw32/include", + "-isystem $(MINGW)/lib/gcc/x86_64-w64-mingw32/4.8.1/include/c++", + "-isystem $(MINGW)/lib/gcc/x86_64-w64-mingw32/4.8.1/include/c++/x86_64-w64-mingw32", + "-isystem $(MINGW)/x86_64-w64-mingw32/include", } linkoptions { "-Qunused-arguments", @@ -722,9 +740,6 @@ function toolchain(_buildDir, _libDir) "-Wundef", -- "-Wuseless-cast", } - buildoptions_cpp { - "-std=c++14", - } links { "rt", "dl", @@ -779,9 +794,6 @@ function toolchain(_buildDir, _libDir) "-Wunused-value", "-Wundef", } - buildoptions_cpp { - "-std=c++14", - } links { "rt", "dl", @@ -798,9 +810,6 @@ function toolchain(_buildDir, _libDir) "-Wunused-value", "-Wundef", } - buildoptions_cpp { - "-std=c++14", - } links { "rt", "dl", @@ -840,9 +849,6 @@ function toolchain(_buildDir, _libDir) "-Wunused-value", "-Wundef", } - buildoptions_cpp { - "-std=c++14", - } linkoptions { "-no-canonical-prefixes", "-Wl,--no-undefined", @@ -860,7 +866,6 @@ function toolchain(_buildDir, _libDir) "__STEAMLINK__=1", -- There is no special prefedined compiler symbol to detect SteamLink, faking it. } buildoptions { - "-std=c++14", "-Wfatal-errors", "-Wunused-value", "-Wundef", @@ -905,6 +910,33 @@ function toolchain(_buildDir, _libDir) "-Wl,--fix-cortex-a8", } + configuration { "android-arm64" } + targetdir (path.join(_buildDir, "android-arm64/bin")) + objdir (path.join(_buildDir, "android-arm64/obj")) + libdirs { + "$(ANDROID_NDK_ROOT)/sources/cxx-stl/llvm-libc++/libs/arm64-v8a", + } + includedirs { + "$(ANDROID_NDK_ROOT)/sysroot/usr/include/aarch64-linux-android", + } + buildoptions { + "-gcc-toolchain $(ANDROID_NDK_ARM64)", + "--sysroot=" .. path.join("$(ANDROID_NDK_ROOT)/platforms", androidPlatform, "arch-arm64"), + "-target aarch64-none-linux-androideabi", + "-march=armv8-a", + "-Wunused-value", + "-Wundef", + } + linkoptions { + "-gcc-toolchain $(ANDROID_NDK_ARM64)", + "--sysroot=" .. path.join("$(ANDROID_NDK_ROOT)/platforms", androidPlatform, "arch-arm64"), + path.join("$(ANDROID_NDK_ROOT)/platforms", androidPlatform, "arch-arm64/usr/lib/crtbegin_so.o"), + path.join("$(ANDROID_NDK_ROOT)/platforms", androidPlatform, "arch-arm64/usr/lib/crtend_so.o"), + "-target aarch64-none-linux-androideabi", + "-march=armv8-a", + "-Wl,--fix-cortex-a8", + } + configuration { "android-x86" } targetdir (path.join(_buildDir, "android-x86/bin")) objdir (path.join(_buildDir, "android-x86/obj")) @@ -939,15 +971,10 @@ function toolchain(_buildDir, _libDir) objdir (path.join(_buildDir, "asmjs/obj")) libdirs { path.join(_libDir, "lib/asmjs") } buildoptions { - "-i\"system$(EMSCRIPTEN)/system/include\"", - "-i\"system$(EMSCRIPTEN)/system/include/libcxx\"", - "-i\"system$(EMSCRIPTEN)/system/include/libc\"", + "-isystem \"$(EMSDK)/fastcomp/emscripten\"", "-Wunused-value", "-Wundef", } - buildoptions_cpp { - "-std=c++14", - } configuration { "freebsd" } targetdir (path.join(_buildDir, "freebsd/bin")) @@ -1005,12 +1032,6 @@ function toolchain(_buildDir, _libDir) objdir (path.join(_buildDir, "osx_universal/bin")) configuration { "osx" } - buildoptions_cpp { - "-std=c++14", - } - buildoptions_objcpp { - "-std=c++14", - } buildoptions { "-Wfatal-errors", "-msse2", @@ -1023,12 +1044,6 @@ function toolchain(_buildDir, _libDir) linkoptions { "-lc++", } - buildoptions_cpp { - "-std=c++14", - } - buildoptions_objcpp { - "-std=c++14", - } buildoptions { "-Wfatal-errors", "-Wunused-value", @@ -1073,6 +1088,7 @@ function toolchain(_buildDir, _libDir) buildoptions { "-miphoneos-version-min=7.0", "--sysroot=/Applications/Xcode.app/Contents/Developer/Platforms/iPhoneOS.platform/Developer/SDKs/iPhoneOS" ..iosPlatform .. ".sdk", + "-fembed-bitcode", } configuration { "ios-simulator" } @@ -1171,9 +1187,6 @@ function toolchain(_buildDir, _libDir) "$(SCE_ORBIS_SDK_DIR)/target/include", "$(SCE_ORBIS_SDK_DIR)/target/include_common", } - buildoptions_cpp { - "-std=c++14", - } configuration { "rpi" } targetdir (path.join(_buildDir, "rpi/bin")) @@ -1190,9 +1203,6 @@ function toolchain(_buildDir, _libDir) "-Wunused-value", "-Wundef", } - buildoptions_cpp { - "-std=c++14", - } includedirs { "/opt/vc/include", "/opt/vc/include/interface/vcos/pthreads", @@ -1222,9 +1232,6 @@ function toolchain(_buildDir, _libDir) "-Wundef", "--sysroot=$(FREEDOM_E_SDK)/work/build/riscv-gnu-toolchain/riscv64-unknown-elf/prefix/riscv64-unknown-elf", } - buildoptions_cpp { - "-std=c++14", - } configuration {} -- reset configuration @@ -1239,6 +1246,12 @@ function strip() "$(SILENT) $(ANDROID_NDK_ARM)/bin/arm-linux-androideabi-strip -s \"$(TARGET)\"" } + configuration { "android-arm64", "Release" } + postbuildcommands { + "$(SILENT) echo Stripping symbols.", + "$(SILENT) $(ANDROID_NDK_ARM64)/bin/aarch64-linux-android-strip -s \"$(TARGET)\"" + } + configuration { "android-x86", "Release" } postbuildcommands { "$(SILENT) echo Stripping symbols.", @@ -1266,7 +1279,7 @@ function strip() configuration { "asmjs" } postbuildcommands { "$(SILENT) echo Running asmjs finalize.", - "$(SILENT) \"$(EMSCRIPTEN)/emcc\" -O2 " + "$(SILENT) \"$(EMSDK)/fastcomp/bin/emcc\" -O2 " -- .. "-s ALLOW_MEMORY_GROWTH=1 " -- .. "-s ASSERTIONS=2 " diff --git a/3rdparty/bx/src/allocator.cpp b/3rdparty/bx/src/allocator.cpp index c26677edb25..50c1505874c 100644 --- a/3rdparty/bx/src/allocator.cpp +++ b/3rdparty/bx/src/allocator.cpp @@ -1,5 +1,5 @@ /* - * Copyright 2010-2018 Branimir Karadzic. All rights reserved. + * Copyright 2010-2019 Branimir Karadzic. All rights reserved. * License: https://github.com/bkaradzic/bx#license-bsd-2-clause */ diff --git a/3rdparty/bx/src/amalgamated.cpp b/3rdparty/bx/src/amalgamated.cpp index d3f896b1416..e274b69019d 100644 --- a/3rdparty/bx/src/amalgamated.cpp +++ b/3rdparty/bx/src/amalgamated.cpp @@ -1,5 +1,5 @@ /* - * Copyright 2011-2018 Branimir Karadzic. All rights reserved. + * Copyright 2011-2019 Branimir Karadzic. All rights reserved. * License: https://github.com/bkaradzic/bx#license-bsd-2-clause */ diff --git a/3rdparty/bx/src/bx.cpp b/3rdparty/bx/src/bx.cpp index 659cb5917cf..ab28bc25d2f 100644 --- a/3rdparty/bx/src/bx.cpp +++ b/3rdparty/bx/src/bx.cpp @@ -1,5 +1,5 @@ /* - * Copyright 2010-2018 Branimir Karadzic. All rights reserved. + * Copyright 2010-2019 Branimir Karadzic. All rights reserved. * License: https://github.com/bkaradzic/bx#license-bsd-2-clause */ @@ -44,29 +44,29 @@ namespace bx #endif // BX_CRT_NONE } - void memCopy(void* _dst, const void* _src, uint32_t _size, uint32_t _num, uint32_t _srcPitch, uint32_t _dstPitch) + void memCopy( + void* _dst + , uint32_t _dstStride + , const void* _src + , uint32_t _srcStride + , uint32_t _stride + , uint32_t _num + ) { - const uint8_t* src = (const uint8_t*)_src; - uint8_t* dst = (uint8_t*)_dst; - - for (uint32_t ii = 0; ii < _num; ++ii) + if (_stride == _srcStride + && _stride == _dstStride) { - memCopy(dst, src, _size); - src += _srcPitch; - dst += _dstPitch; + memCopy(_dst, _src, _stride*_num); + return; } - } - /// - void gather(void* _dst, const void* _src, uint32_t _size, uint32_t _num, uint32_t _srcPitch) - { - memCopy(_dst, _src, _size, _num, _srcPitch, _size); - } + const uint8_t* src = (const uint8_t*)_src; + uint8_t* dst = (uint8_t*)_dst; - /// - void scatter(void* _dst, const void* _src, uint32_t _size, uint32_t _num, uint32_t _dstPitch) - { - memCopy(_dst, _src, _size, _num, _size, _dstPitch); + for (uint32_t ii = 0; ii < _num; ++ii, src += _srcStride, dst += _dstStride) + { + memCopy(dst, src, _stride); + } } void memMoveRef(void* _dst, const void* _src, size_t _numBytes) @@ -102,6 +102,31 @@ namespace bx #endif // BX_CRT_NONE } + void memMove( + void* _dst + , uint32_t _dstStride + , const void* _src + , uint32_t _srcStride + , uint32_t _stride + , uint32_t _num + ) + { + if (_stride == _srcStride + && _stride == _dstStride) + { + memMove(_dst, _src, _stride*_num); + return; + } + + const uint8_t* src = (const uint8_t*)_src; + uint8_t* dst = (uint8_t*)_dst; + + for (uint32_t ii = 0; ii < _num; ++ii, src += _srcStride, dst += _dstStride) + { + memMove(dst, src, _stride); + } + } + void memSetRef(void* _dst, uint8_t _ch, size_t _numBytes) { uint8_t* dst = (uint8_t*)_dst; @@ -121,6 +146,22 @@ namespace bx #endif // BX_CRT_NONE } + void memSet(void* _dst, uint32_t _dstStride, uint8_t _ch, uint32_t _stride, uint32_t _num) + { + if (_stride == _dstStride) + { + memSet(_dst, _ch, _stride*_num); + return; + } + + uint8_t* dst = (uint8_t*)_dst; + + for (uint32_t ii = 0; ii < _num; ++ii, dst += _dstStride) + { + memSet(dst, _ch, _stride); + } + } + int32_t memCmpRef(const void* _lhs, const void* _rhs, size_t _numBytes) { const char* lhs = (const char*)_lhs; @@ -144,4 +185,16 @@ namespace bx #endif // BX_CRT_NONE } + /// + void gather(void* _dst, const void* _src, uint32_t _srcStride, uint32_t _stride, uint32_t _num) + { + memMove(_dst, _stride, _src, _srcStride, _stride, _num); + } + + /// + void scatter(void* _dst, uint32_t _dstStride, const void* _src, uint32_t _stride, uint32_t _num) + { + memMove(_dst, _dstStride, _src, _stride, _num, _stride); + } + } // namespace bx diff --git a/3rdparty/bx/src/bx_p.h b/3rdparty/bx/src/bx_p.h index d4b00084de3..de02ccce2da 100644 --- a/3rdparty/bx/src/bx_p.h +++ b/3rdparty/bx/src/bx_p.h @@ -1,5 +1,5 @@ /* - * Copyright 2010-2018 Branimir Karadzic. All rights reserved. + * Copyright 2010-2019 Branimir Karadzic. All rights reserved. * License: https://github.com/bkaradzic/bx#license-bsd-2-clause */ diff --git a/3rdparty/bx/src/commandline.cpp b/3rdparty/bx/src/commandline.cpp index df322560b56..d9f6fb4e8de 100644 --- a/3rdparty/bx/src/commandline.cpp +++ b/3rdparty/bx/src/commandline.cpp @@ -1,5 +1,5 @@ /* - * Copyright 2010-2018 Branimir Karadzic. All rights reserved. + * Copyright 2010-2019 Branimir Karadzic. All rights reserved. * License: https://github.com/bkaradzic/bx#license-bsd-2-clause */ @@ -39,7 +39,7 @@ namespace bx switch (state) { case SkipWhitespace: - for (; isSpace(*curr); ++curr) {}; // skip whitespace + for (; isSpace(*curr) && *curr!=_term; ++curr) {}; // skip whitespace state = SetTerm; break; diff --git a/3rdparty/bx/src/crtnone.cpp b/3rdparty/bx/src/crtnone.cpp index 93579515c7f..72dd8747eaa 100644 --- a/3rdparty/bx/src/crtnone.cpp +++ b/3rdparty/bx/src/crtnone.cpp @@ -1,5 +1,5 @@ /* - * Copyright 2010-2018 Branimir Karadzic. All rights reserved. + * Copyright 2010-2019 Branimir Karadzic. All rights reserved. * License: https://github.com/bkaradzic/bx#license-bsd-2-clause */ @@ -75,7 +75,7 @@ extern "C" char* strcat(char* _dst, const char* _src) extern "C" const char* strchr(const char* _str, int _ch) { - return bx::strFind(_str, _ch); + return bx::strFind(_str, _ch).getPtr(); } extern "C" int32_t strcmp(const char* _lhs, const char* _rhs) @@ -95,7 +95,7 @@ extern "C" int32_t strcasecmp(const char* _lhs, const char* _rhs) extern "C" const char* strstr(const char* _str, const char* _find) { - return bx::strFind(_str, _find); + return bx::strFind(_str, _find).getPtr(); } extern "C" void qsort(void* _base, size_t _num, size_t _size, bx::ComparisonFn _fn) diff --git a/3rdparty/bx/src/debug.cpp b/3rdparty/bx/src/debug.cpp index 8e8bae122a6..623a2213b47 100644 --- a/3rdparty/bx/src/debug.cpp +++ b/3rdparty/bx/src/debug.cpp @@ -1,5 +1,5 @@ /* - * Copyright 2010-2018 Branimir Karadzic. All rights reserved. + * Copyright 2010-2019 Branimir Karadzic. All rights reserved. * License: https://github.com/bkaradzic/bx#license-bsd-2-clause */ @@ -24,8 +24,8 @@ extern "C" __declspec(dllimport) void __stdcall OutputDebugStringA(const char* _ # include <CoreFoundation/CFString.h> extern "C" void NSLog(CFStringRef _format, ...); # endif // defined(__OBJC__) -#elif 0 // BX_PLATFORM_EMSCRIPTEN -# include <emscripten.h> +#elif BX_PLATFORM_EMSCRIPTEN +# include <emscripten/emscripten.h> #else # include <stdio.h> // fputs, fflush #endif // BX_PLATFORM_WINDOWS @@ -43,6 +43,11 @@ namespace bx // NaCl doesn't like int 3: // NativeClient: NaCl module load failed: Validation failure. File violates Native Client safety rules. __asm__ ("int $3"); +#elif BX_PLATFORM_EMSCRIPTEN + emscripten_log(EM_LOG_CONSOLE | EM_LOG_ERROR | EM_LOG_C_STACK | EM_LOG_JS_STACK | EM_LOG_DEMANGLE, "debugBreak!"); + // Doing emscripten_debugger() disables asm.js validation due to an emscripten bug + //emscripten_debugger(); + EM_ASM({ debugger; }); #else // cross platform implementation int* int3 = (int*)3L; *int3 = 3; @@ -69,7 +74,7 @@ namespace bx # else NSLog(__CFStringMakeConstantString("%s"), _out); # endif // defined(__OBJC__) -#elif 0 // BX_PLATFORM_EMSCRIPTEN +#elif BX_PLATFORM_EMSCRIPTEN emscripten_log(EM_LOG_CONSOLE, "%s", _out); #else fputs(_out, stdout); diff --git a/3rdparty/bx/src/dtoa.cpp b/3rdparty/bx/src/dtoa.cpp index 3c23ea516e1..686d5c6a12d 100644 --- a/3rdparty/bx/src/dtoa.cpp +++ b/3rdparty/bx/src/dtoa.cpp @@ -1,5 +1,5 @@ /* - * Copyright 2010-2018 Branimir Karadzic. All rights reserved. + * Copyright 2010-2019 Branimir Karadzic. All rights reserved. * License: https://github.com/bkaradzic/bx#license-bsd-2-clause */ diff --git a/3rdparty/bx/src/easing.cpp b/3rdparty/bx/src/easing.cpp index e9f82b280de..8984abccd0c 100644 --- a/3rdparty/bx/src/easing.cpp +++ b/3rdparty/bx/src/easing.cpp @@ -1,5 +1,5 @@ /* - * Copyright 2010-2018 Branimir Karadzic. All rights reserved. + * Copyright 2010-2019 Branimir Karadzic. All rights reserved. * License: https://github.com/bkaradzic/bx#license-bsd-2-clause */ diff --git a/3rdparty/bx/src/file.cpp b/3rdparty/bx/src/file.cpp index 8a282cab91f..aea285ea23f 100644 --- a/3rdparty/bx/src/file.cpp +++ b/3rdparty/bx/src/file.cpp @@ -1,24 +1,34 @@ /* - * Copyright 2010-2018 Branimir Karadzic. All rights reserved. + * Copyright 2010-2019 Branimir Karadzic. All rights reserved. * License: https://github.com/bkaradzic/bx#license-bsd-2-clause */ #include "bx_p.h" #include <bx/file.h> +#ifndef BX_CONFIG_CRT_FILE_READER_WRITER +# define BX_CONFIG_CRT_FILE_READER_WRITER !BX_CRT_NONE +#endif // BX_CONFIG_CRT_FILE_READER_WRITER + +#ifndef BX_CONFIG_CRT_DIRECTORY_READER +# define BX_CONFIG_CRT_DIRECTORY_READER (BX_PLATFORM_OS_DESKTOP && !BX_CRT_NONE) +#endif // BX_CONFIG_CRT_DIRECTORY_READER + #if BX_CRT_NONE # include "crt0.h" #else -# include <stdio.h> -# include <sys/stat.h> +# if BX_CONFIG_CRT_DIRECTORY_READER +# include <dirent.h> +# endif // BX_CONFIG_CRT_DIRECTORY_READER +# include <stdio.h> // remove +# include <sys/stat.h> // stat, mkdir +# if BX_CRT_MSVC +# include <direct.h> // _getcwd +# else +# include <unistd.h> // getcwd +# endif // BX_CRT_MSVC #endif // !BX_CRT_NONE -#ifndef BX_CONFIG_CRT_FILE_READER_WRITER -# define BX_CONFIG_CRT_FILE_READER_WRITER !(0 \ - || BX_CRT_NONE \ - ) -#endif // BX_CONFIG_CRT_FILE_READER_WRITER - namespace bx { class NoopWriterImpl : public FileWriterI @@ -64,6 +74,7 @@ namespace bx # elif 0 \ || BX_PLATFORM_ANDROID \ || BX_PLATFORM_BSD \ + || BX_PLATFORM_HAIKU \ || BX_PLATFORM_IOS \ || BX_PLATFORM_OSX # define fseeko64 fseeko @@ -97,7 +108,7 @@ namespace bx return false; } - m_file = fopen(_filePath.get(), "rb"); + m_file = fopen(_filePath.getCPtr(), "rb"); if (NULL == m_file) { BX_ERROR_SET(_err, BX_ERROR_READERWRITER_OPEN, "FileReader: Failed to open file."); @@ -177,7 +188,7 @@ namespace bx return false; } - m_file = fopen(_filePath.get(), _append ? "ab" : "wb"); + m_file = fopen(_filePath.getCPtr(), _append ? "ab" : "wb"); if (NULL == m_file) { @@ -553,18 +564,193 @@ namespace bx return impl->write(_data, _size, _err); } - bool stat(const FilePath& _filePath, FileInfo& _outFileInfo) +#if BX_CONFIG_CRT_DIRECTORY_READER + + class DirectoryReaderImpl : public ReaderOpenI, public CloserI, public ReaderI + { + public: + DirectoryReaderImpl() + : m_dir(NULL) + , m_pos(0) + { + } + + virtual ~DirectoryReaderImpl() + { + close(); + } + + virtual bool open(const FilePath& _filePath, Error* _err) override + { + BX_CHECK(NULL != _err, "Reader/Writer interface calling functions must handle errors."); + + m_dir = opendir(_filePath.getCPtr() ); + + if (NULL == m_dir) + { + BX_ERROR_SET(_err, BX_ERROR_READERWRITER_OPEN, "DirectoryReader: Failed to open directory."); + return false; + } + + m_pos = 0; + + return true; + } + + virtual void close() override + { + if (NULL != m_dir) + { + closedir(m_dir); + m_dir = NULL; + } + } + + virtual int32_t read(void* _data, int32_t _size, Error* _err) override + { + BX_CHECK(NULL != _err, "Reader/Writer interface calling functions must handle errors."); + + int32_t total = 0; + + uint8_t* out = (uint8_t*)_data; + + while (0 < _size) + { + if (0 == m_pos) + { + if (!fetch(m_cache, m_dir) ) + { + BX_ERROR_SET(_err, BX_ERROR_READERWRITER_EOF, "DirectoryReader: EOF."); + return total; + } + } + + const uint8_t* src = (const uint8_t*)&m_cache; + int32_t size = min<int32_t>(_size, sizeof(m_cache)-m_pos); + memCopy(&out[total], &src[m_pos], size); + total += size; + _size -= size; + + m_pos += size; + m_pos %= sizeof(m_cache); + } + + return total; + } + + static bool fetch(FileInfo& _out, DIR* _dir) + { + for (;;) + { + const dirent* item = readdir(_dir); + + if (NULL == item) + { + break; + } + + if (0 != (item->d_type & DT_DIR) ) + { + _out.type = FileType::Dir; + _out.size = UINT64_MAX; + _out.filePath.set(item->d_name); + return true; + } + + if (0 != (item->d_type & DT_REG) ) + { + _out.type = FileType::File; + _out.size = UINT64_MAX; + _out.filePath.set(item->d_name); + return true; + } + } + + return false; + } + + FileInfo m_cache; + DIR* m_dir; + int32_t m_pos; + }; + +#else + + class DirectoryReaderImpl : public ReaderOpenI, public CloserI, public ReaderI + { + public: + DirectoryReaderImpl() + { + } + + virtual ~DirectoryReaderImpl() + { + } + + virtual bool open(const FilePath& _filePath, Error* _err) override + { + BX_UNUSED(_filePath); + BX_ERROR_SET(_err, BX_ERROR_READERWRITER_OPEN, "DirectoryReader: Failed to open directory."); + return false; + } + + virtual void close() override + { + } + + virtual int32_t read(void* _data, int32_t _size, Error* _err) override + { + BX_UNUSED(_data, _size); + BX_CHECK(NULL != _err, "Reader/Writer interface calling functions must handle errors."); + BX_ERROR_SET(_err, BX_ERROR_READERWRITER_EOF, "DirectoryReader: EOF."); + return 0; + } + }; + +#endif // BX_CONFIG_CRT_DIRECTORY_READER + + DirectoryReader::DirectoryReader() + { + BX_STATIC_ASSERT(sizeof(DirectoryReaderImpl) <= sizeof(m_internal) ); + BX_PLACEMENT_NEW(m_internal, DirectoryReaderImpl); + } + + DirectoryReader::~DirectoryReader() + { + DirectoryReaderImpl* impl = reinterpret_cast<DirectoryReaderImpl*>(m_internal); + impl->~DirectoryReaderImpl(); + } + + bool DirectoryReader::open(const FilePath& _filePath, Error* _err) + { + DirectoryReaderImpl* impl = reinterpret_cast<DirectoryReaderImpl*>(m_internal); + return impl->open(_filePath, _err); + } + + void DirectoryReader::close() + { + DirectoryReaderImpl* impl = reinterpret_cast<DirectoryReaderImpl*>(m_internal); + impl->close(); + } + + int32_t DirectoryReader::read(void* _data, int32_t _size, Error* _err) + { + DirectoryReaderImpl* impl = reinterpret_cast<DirectoryReaderImpl*>(m_internal); + return impl->read(_data, _size, _err); + } + + bool stat(FileInfo& _outFileInfo, const FilePath& _filePath) { #if BX_CRT_NONE BX_UNUSED(_filePath, _outFileInfo); return false; #else - _outFileInfo.m_size = 0; - _outFileInfo.m_type = FileInfo::Count; + _outFileInfo.size = 0; + _outFileInfo.type = FileType::Count; # if BX_COMPILER_MSVC struct ::_stat64 st; - int32_t result = ::_stat64(_filePath.get(), &st); + int32_t result = ::_stat64(_filePath.getCPtr(), &st); if (0 != result) { @@ -573,15 +759,15 @@ namespace bx if (0 != (st.st_mode & _S_IFREG) ) { - _outFileInfo.m_type = FileInfo::Regular; + _outFileInfo.type = FileType::File; } else if (0 != (st.st_mode & _S_IFDIR) ) { - _outFileInfo.m_type = FileInfo::Directory; + _outFileInfo.type = FileType::Dir; } # else struct ::stat st; - int32_t result = ::stat(_filePath.get(), &st); + int32_t result = ::stat(_filePath.getCPtr(), &st); if (0 != result) { return false; @@ -589,18 +775,185 @@ namespace bx if (0 != (st.st_mode & S_IFREG) ) { - _outFileInfo.m_type = FileInfo::Regular; + _outFileInfo.type = FileType::File; } else if (0 != (st.st_mode & S_IFDIR) ) { - _outFileInfo.m_type = FileInfo::Directory; + _outFileInfo.type = FileType::Dir; } # endif // BX_COMPILER_MSVC - _outFileInfo.m_size = st.st_size; + _outFileInfo.size = st.st_size; return true; #endif // BX_CRT_NONE } + bool make(const FilePath& _filePath, Error* _err) + { + BX_ERROR_SCOPE(_err); + + if (!_err->isOk() ) + { + return false; + } + +#if BX_CRT_MSVC + int32_t result = ::_mkdir(_filePath.getCPtr() ); +#elif BX_CRT_MINGW + int32_t result = ::mkdir(_filePath.getCPtr()); +#elif BX_CRT_NONE + BX_UNUSED(_filePath); + int32_t result = -1; +#else + int32_t result = ::mkdir(_filePath.getCPtr(), 0700); +#endif // BX_CRT_MSVC + + if (0 != result) + { + BX_ERROR_SET(_err, BX_ERROR_ACCESS, "The parent directory does not allow write permission to the process."); + return false; + } + + return true; + } + + bool makeAll(const FilePath& _filePath, Error* _err) + { + BX_ERROR_SCOPE(_err); + + if (!_err->isOk() ) + { + return false; + } + + FileInfo fi; + + if (stat(fi, _filePath) ) + { + if (FileType::Dir == fi.type) + { + return true; + } + + BX_ERROR_SET(_err, BX_ERROR_NOT_DIRECTORY, "File already exist, and is not directory."); + return false; + } + + const StringView dir = strRTrim(_filePath, "/"); + const StringView slash = strRFind(dir, '/'); + + if (!slash.isEmpty() + && slash.getPtr() - dir.getPtr() > 1) + { + if (!makeAll(StringView(dir.getPtr(), slash.getPtr() ), _err) ) + { + return false; + } + } + + FilePath path(dir); + return make(path, _err); + } + + bool remove(const FilePath& _filePath, Error* _err) + { + BX_ERROR_SCOPE(_err); + + if (!_err->isOk() ) + { + return false; + } + +#if BX_CRT_MSVC + int32_t result = -1; + FileInfo fi; + if (stat(fi, _filePath) ) + { + if (FileType::Dir == fi.type) + { + result = ::_rmdir(_filePath.getCPtr() ); + } + else + { + result = ::remove(_filePath.getCPtr() ); + } + } +#elif BX_CRT_NONE + BX_UNUSED(_filePath); + int32_t result = -1; +#else + int32_t result = ::remove(_filePath.getCPtr() ); +#endif // BX_CRT_MSVC + + if (0 != result) + { + BX_ERROR_SET(_err, BX_ERROR_ACCESS, "The parent directory does not allow write permission to the process."); + return false; + } + + return true; + } + + bool removeAll(const FilePath& _filePath, Error* _err) + { + BX_ERROR_SCOPE(_err); + + if (remove(_filePath, _err) ) + { + return true; + } + + _err->reset(); + + FileInfo fi; + + if (!stat(fi, _filePath) ) + { + BX_ERROR_SET(_err, BX_ERROR_ACCESS, "The parent directory does not allow write permission to the process."); + return false; + } + + if (FileType::Dir != fi.type) + { + BX_ERROR_SET(_err, BX_ERROR_NOT_DIRECTORY, "File already exist, and is not directory."); + return false; + } + + Error err; + DirectoryReader dr; + + if (!bx::open(&dr, _filePath) ) + { + BX_ERROR_SET(_err, BX_ERROR_NOT_DIRECTORY, "File already exist, and is not directory."); + return false; + } + + while (err.isOk() ) + { + bx::read(&dr, fi, &err); + + if (err.isOk() ) + { + if (0 == strCmp(fi.filePath, ".") + || 0 == strCmp(fi.filePath, "..") ) + { + continue; + } + + FilePath path(_filePath); + path.join(fi.filePath); + if (!removeAll(path, _err) ) + { + _err->reset(); + break; + } + } + } + + bx::close(&dr); + + return remove(_filePath, _err); + } + } // namespace bx diff --git a/3rdparty/bx/src/filepath.cpp b/3rdparty/bx/src/filepath.cpp index 0ee2fc171ad..28149403aba 100644 --- a/3rdparty/bx/src/filepath.cpp +++ b/3rdparty/bx/src/filepath.cpp @@ -1,5 +1,5 @@ /* - * Copyright 2010-2018 Branimir Karadzic. All rights reserved. + * Copyright 2010-2019 Branimir Karadzic. All rights reserved. * License: https://github.com/bkaradzic/bx#license-bsd-2-clause */ @@ -9,16 +9,12 @@ #include <bx/readerwriter.h> #if !BX_CRT_NONE -# include <stdio.h> // remove -# include <dirent.h> // opendir - # if BX_CRT_MSVC # include <direct.h> // _getcwd # else -# include <sys/stat.h> // mkdir # include <unistd.h> // getcwd # endif // BX_CRT_MSVC -#endif // 0 +#endif // !BX_CRT_NONE #if BX_PLATFORM_WINDOWS extern "C" __declspec(dllimport) unsigned long __stdcall GetTempPathA(unsigned long _max, char* _ptr); @@ -154,7 +150,7 @@ namespace bx return size; } - static bool getEnv(char* _out, uint32_t* _inOutSize, const StringView& _name, FileInfo::Enum _type) + static bool getEnv(char* _out, uint32_t* _inOutSize, const StringView& _name, FileType::Enum _type) { uint32_t len = *_inOutSize; *_out = '\0'; @@ -162,8 +158,8 @@ namespace bx if (getEnv(_out, &len, _name) ) { FileInfo fi; - if (stat(_out, fi) - && _type == fi.m_type) + if (stat(fi, _out) + && _type == fi.type) { *_inOutSize = len; return true; @@ -204,9 +200,9 @@ namespace bx { return false #if BX_PLATFORM_WINDOWS - || getEnv(_out, _inOutSize, "USERPROFILE", FileInfo::Directory) + || getEnv(_out, _inOutSize, "USERPROFILE", FileType::Dir) #endif // BX_PLATFORM_WINDOWS - || getEnv(_out, _inOutSize, "HOME", FileInfo::Directory) + || getEnv(_out, _inOutSize, "HOME", FileType::Dir) ; } @@ -232,7 +228,7 @@ namespace bx { uint32_t len = *_inOutSize; *_out = '\0'; - bool ok = getEnv(_out, &len, *tmp, FileInfo::Directory); + bool ok = getEnv(_out, &len, *tmp, FileType::Dir); if (ok && len != 0 @@ -244,8 +240,8 @@ namespace bx } FileInfo fi; - if (stat("/tmp", fi) - && FileInfo::Directory == fi.m_type) + if (stat(fi, "/tmp") + && FileType::Dir == fi.type) { strCopy(_out, *_inOutSize, "/tmp"); *_inOutSize = 4; @@ -336,7 +332,12 @@ namespace bx set(tmp); } - const char* FilePath::get() const + FilePath::operator StringView() const + { + return StringView(m_filePath, strLen(m_filePath) ); + } + + const char* FilePath::getCPtr() const { return m_filePath; } @@ -360,7 +361,7 @@ namespace bx return StringView(fileName.getPtr()+1); } - return get(); + return getCPtr(); } StringView FilePath::getBaseName() const @@ -385,7 +386,8 @@ namespace bx const StringView fileName = getFileName(); if (!fileName.isEmpty() ) { - return strFind(fileName, '.'); + const StringView dot = strFind(fileName, '.'); + return StringView(dot.getPtr(), fileName.getTerm() ); } return StringView(); @@ -403,171 +405,4 @@ namespace bx return 0 == strCmp(m_filePath, "."); } - bool make(const FilePath& _filePath, Error* _err) - { - BX_ERROR_SCOPE(_err); - - if (!_err->isOk() ) - { - return false; - } - -#if BX_CRT_MSVC - int32_t result = ::_mkdir(_filePath.get() ); -#elif BX_CRT_MINGW - int32_t result = ::mkdir(_filePath.get()); -#elif BX_CRT_NONE - BX_UNUSED(_filePath); - int32_t result = -1; -#else - int32_t result = ::mkdir(_filePath.get(), 0700); -#endif // BX_CRT_MSVC - - if (0 != result) - { - BX_ERROR_SET(_err, BX_ERROR_ACCESS, "The parent directory does not allow write permission to the process."); - return false; - } - - return true; - } - - bool makeAll(const FilePath& _filePath, Error* _err) - { - BX_ERROR_SCOPE(_err); - - if (!_err->isOk() ) - { - return false; - } - - FileInfo fi; - - if (stat(_filePath, fi) ) - { - if (FileInfo::Directory == fi.m_type) - { - return true; - } - - BX_ERROR_SET(_err, BX_ERROR_NOT_DIRECTORY, "File already exist, and is not directory."); - return false; - } - - const StringView dir = strRTrim(_filePath.get(), "/"); - const StringView slash = strRFind(dir, '/'); - - if (!slash.isEmpty() - && slash.getPtr() - dir.getPtr() > 1) - { - if (!makeAll(StringView(dir.getPtr(), slash.getPtr() ), _err) ) - { - return false; - } - } - - FilePath path(dir); - return make(path, _err); - } - - bool remove(const FilePath& _filePath, Error* _err) - { - BX_ERROR_SCOPE(_err); - - if (!_err->isOk() ) - { - return false; - } - -#if BX_CRT_MSVC - int32_t result = -1; - FileInfo fi; - if (stat(_filePath, fi) ) - { - if (FileInfo::Directory == fi.m_type) - { - result = ::_rmdir(_filePath.get() ); - } - else - { - result = ::remove(_filePath.get() ); - } - } -#elif BX_CRT_NONE - BX_UNUSED(_filePath); - int32_t result = -1; -#else - int32_t result = ::remove(_filePath.get() ); -#endif // BX_CRT_MSVC - - if (0 != result) - { - BX_ERROR_SET(_err, BX_ERROR_ACCESS, "The parent directory does not allow write permission to the process."); - return false; - } - - return true; - } - - bool removeAll(const FilePath& _filePath, Error* _err) - { - BX_ERROR_SCOPE(_err); - - if (remove(_filePath, _err) ) - { - return true; - } - - _err->reset(); - - FileInfo fi; - - if (!stat(_filePath, fi) ) - { - BX_ERROR_SET(_err, BX_ERROR_ACCESS, "The parent directory does not allow write permission to the process."); - return false; - } - - if (FileInfo::Directory != fi.m_type) - { - BX_ERROR_SET(_err, BX_ERROR_NOT_DIRECTORY, "File already exist, and is not directory."); - return false; - } - -#if BX_CRT_NONE - BX_UNUSED(_filePath); - return false; -#elif BX_PLATFORM_WINDOWS \ - || BX_PLATFORM_LINUX \ - || BX_PLATFORM_OSX - DIR* dir = opendir(_filePath.get() ); - if (NULL == dir) - { - BX_ERROR_SET(_err, BX_ERROR_NOT_DIRECTORY, "File already exist, and is not directory."); - return false; - } - - for (dirent* item = readdir(dir); NULL != item; item = readdir(dir) ) - { - if (0 == strCmp(item->d_name, ".") - || 0 == strCmp(item->d_name, "..") ) - { - continue; - } - - FilePath path(_filePath); - path.join(item->d_name); - if (!removeAll(path, _err) ) - { - _err->reset(); - break; - } - } - - closedir(dir); -#endif // !BX_CRT_NONE - - return remove(_filePath, _err); - } - } // namespace bx diff --git a/3rdparty/bx/src/hash.cpp b/3rdparty/bx/src/hash.cpp index 7ae5e39cc80..d48a1e22f28 100644 --- a/3rdparty/bx/src/hash.cpp +++ b/3rdparty/bx/src/hash.cpp @@ -1,5 +1,5 @@ /* - * Copyright 2011-2018 Branimir Karadzic. All rights reserved. + * Copyright 2011-2019 Branimir Karadzic. All rights reserved. * License: https://github.com/bkaradzic/bx#license-bsd-2-clause */ diff --git a/3rdparty/bx/src/math.cpp b/3rdparty/bx/src/math.cpp index ba0676e96bf..4e3380a3774 100644 --- a/3rdparty/bx/src/math.cpp +++ b/3rdparty/bx/src/math.cpp @@ -1,5 +1,5 @@ /* - * Copyright 2011-2018 Branimir Karadzic. All rights reserved. + * Copyright 2011-2019 Branimir Karadzic. All rights reserved. * License: https://github.com/bkaradzic/bx#license-bsd-2-clause */ @@ -239,87 +239,53 @@ namespace bx return result; } - BX_CONST_FUNC float floor(float _a) + void mtxLookAt(float* _result, const Vec3& _eye, const Vec3& _at, const Vec3& _up, Handness::Enum _handness) { - if (_a < 0.0f) - { - const float fr = fract(-_a); - const float result = -_a - fr; - - return -(0.0f != fr - ? result + 1.0f - : result) - ; - } - - return _a - fract(_a); - } - - static void mtxLookAtImpl(float* _result, const Vec3& _eye, const Vec3& _view, const Vec3& _up) - { - const Vec3 uxv = cross(_up, _view); + const Vec3 view = normalize( + Handness::Right == _handness + ? sub(_eye, _at) + : sub(_at, _eye) + ); + const Vec3 uxv = cross(_up, view); const Vec3 right = normalize(uxv); - const Vec3 up = cross(_view, right); + const Vec3 up = cross(view, right); memSet(_result, 0, sizeof(float)*16); _result[ 0] = right.x; _result[ 1] = up.x; - _result[ 2] = _view.x; + _result[ 2] = view.x; _result[ 4] = right.y; _result[ 5] = up.y; - _result[ 6] = _view.y; + _result[ 6] = view.y; _result[ 8] = right.z; _result[ 9] = up.z; - _result[10] = _view.z; + _result[10] = view.z; _result[12] = -dot(right, _eye); - _result[13] = -dot(up, _eye); - _result[14] = -dot(_view, _eye); + _result[13] = -dot(up, _eye); + _result[14] = -dot(view, _eye); _result[15] = 1.0f; } - void mtxLookAtLh(float* _result, const Vec3& _eye, const Vec3& _at, const Vec3& _up) - { - const Vec3 tmp = sub(_at, _eye); - const Vec3 view = normalize(tmp); - - mtxLookAtImpl(_result, _eye, view, _up); - } - - void mtxLookAtRh(float* _result, const Vec3& _eye, const Vec3& _at, const Vec3& _up) - { - const Vec3 tmp = sub(_eye, _at); - const Vec3 view = normalize(tmp); - - mtxLookAtImpl(_result, _eye, view, _up); - } - - void mtxLookAt(float* _result, const Vec3& _eye, const Vec3& _at, const Vec3& _up) - { - mtxLookAtLh(_result, _eye, _at, _up); - } - - template<Handness::Enum HandnessT> - void mtxProjXYWH(float* _result, float _x, float _y, float _width, float _height, float _near, float _far, bool _oglNdc) + static void mtxProjXYWH(float* _result, float _x, float _y, float _width, float _height, float _near, float _far, bool _homogeneousNdc, Handness::Enum _handness) { const float diff = _far-_near; - const float aa = _oglNdc ? ( _far+_near)/diff : _far/diff; - const float bb = _oglNdc ? (2.0f*_far*_near)/diff : _near*aa; + const float aa = _homogeneousNdc ? ( _far+_near)/diff : _far/diff; + const float bb = _homogeneousNdc ? (2.0f*_far*_near)/diff : _near*aa; memSet(_result, 0, sizeof(float)*16); _result[ 0] = _width; _result[ 5] = _height; - _result[ 8] = (Handness::Right == HandnessT) ? _x : -_x; - _result[ 9] = (Handness::Right == HandnessT) ? _y : -_y; - _result[10] = (Handness::Right == HandnessT) ? -aa : aa; - _result[11] = (Handness::Right == HandnessT) ? -1.0f : 1.0f; + _result[ 8] = (Handness::Right == _handness) ? _x : -_x; + _result[ 9] = (Handness::Right == _handness) ? _y : -_y; + _result[10] = (Handness::Right == _handness) ? -aa : aa; + _result[11] = (Handness::Right == _handness) ? -1.0f : 1.0f; _result[14] = -bb; } - template<Handness::Enum HandnessT> - void mtxProjImpl(float* _result, float _ut, float _dt, float _lt, float _rt, float _near, float _far, bool _oglNdc) + void mtxProj(float* _result, float _ut, float _dt, float _lt, float _rt, float _near, float _far, bool _homogeneousNdc, Handness::Enum _handness) { const float invDiffRl = 1.0f/(_rt - _lt); const float invDiffUd = 1.0f/(_ut - _dt); @@ -327,96 +293,47 @@ namespace bx const float height = 2.0f*_near * invDiffUd; const float xx = (_rt + _lt) * invDiffRl; const float yy = (_ut + _dt) * invDiffUd; - mtxProjXYWH<HandnessT>(_result, xx, yy, width, height, _near, _far, _oglNdc); + mtxProjXYWH(_result, xx, yy, width, height, _near, _far, _homogeneousNdc, _handness); } - template<Handness::Enum HandnessT> - void mtxProjImpl(float* _result, const float _fov[4], float _near, float _far, bool _oglNdc) + void mtxProj(float* _result, const float _fov[4], float _near, float _far, bool _homogeneousNdc, Handness::Enum _handness) { - mtxProjImpl<HandnessT>(_result, _fov[0], _fov[1], _fov[2], _fov[3], _near, _far, _oglNdc); + mtxProj(_result, _fov[0], _fov[1], _fov[2], _fov[3], _near, _far, _homogeneousNdc, _handness); } - template<Handness::Enum HandnessT> - void mtxProjImpl(float* _result, float _fovy, float _aspect, float _near, float _far, bool _oglNdc) + void mtxProj(float* _result, float _fovy, float _aspect, float _near, float _far, bool _homogeneousNdc, Handness::Enum _handness) { const float height = 1.0f/tan(toRad(_fovy)*0.5f); const float width = height * 1.0f/_aspect; - mtxProjXYWH<HandnessT>(_result, 0.0f, 0.0f, width, height, _near, _far, _oglNdc); + mtxProjXYWH(_result, 0.0f, 0.0f, width, height, _near, _far, _homogeneousNdc, _handness); } - void mtxProj(float* _result, float _ut, float _dt, float _lt, float _rt, float _near, float _far, bool _oglNdc) - { - mtxProjImpl<Handness::Left>(_result, _ut, _dt, _lt, _rt, _near, _far, _oglNdc); - } - - void mtxProj(float* _result, const float _fov[4], float _near, float _far, bool _oglNdc) - { - mtxProjImpl<Handness::Left>(_result, _fov, _near, _far, _oglNdc); - } - - void mtxProj(float* _result, float _fovy, float _aspect, float _near, float _far, bool _oglNdc) - { - mtxProjImpl<Handness::Left>(_result, _fovy, _aspect, _near, _far, _oglNdc); - } - - void mtxProjLh(float* _result, float _ut, float _dt, float _lt, float _rt, float _near, float _far, bool _oglNdc) - { - mtxProjImpl<Handness::Left>(_result, _ut, _dt, _lt, _rt, _near, _far, _oglNdc); - } - - void mtxProjLh(float* _result, const float _fov[4], float _near, float _far, bool _oglNdc) - { - mtxProjImpl<Handness::Left>(_result, _fov, _near, _far, _oglNdc); - } - - void mtxProjLh(float* _result, float _fovy, float _aspect, float _near, float _far, bool _oglNdc) - { - mtxProjImpl<Handness::Left>(_result, _fovy, _aspect, _near, _far, _oglNdc); - } - - void mtxProjRh(float* _result, float _ut, float _dt, float _lt, float _rt, float _near, float _far, bool _oglNdc) - { - mtxProjImpl<Handness::Right>(_result, _ut, _dt, _lt, _rt, _near, _far, _oglNdc); - } - - void mtxProjRh(float* _result, const float _fov[4], float _near, float _far, bool _oglNdc) - { - mtxProjImpl<Handness::Right>(_result, _fov, _near, _far, _oglNdc); - } - - void mtxProjRh(float* _result, float _fovy, float _aspect, float _near, float _far, bool _oglNdc) - { - mtxProjImpl<Handness::Right>(_result, _fovy, _aspect, _near, _far, _oglNdc); - } - - template<NearFar::Enum NearFarT, Handness::Enum HandnessT> - void mtxProjInfXYWH(float* _result, float _x, float _y, float _width, float _height, float _near, bool _oglNdc) + static void mtxProjInfXYWH(float* _result, float _x, float _y, float _width, float _height, float _near, bool _homogeneousNdc, Handness::Enum _handness, NearFar::Enum _nearFar) { float aa; float bb; - if (BX_ENABLED(NearFar::Reverse == NearFarT) ) + if (NearFar::Reverse == _nearFar) { - aa = _oglNdc ? -1.0f : 0.0f; - bb = _oglNdc ? -2.0f*_near : -_near; + aa = _homogeneousNdc ? -1.0f : 0.0f; + bb = _homogeneousNdc ? -2.0f*_near : -_near; } else { aa = 1.0f; - bb = _oglNdc ? 2.0f*_near : _near; + bb = _homogeneousNdc ? 2.0f*_near : _near; } memSet(_result, 0, sizeof(float)*16); _result[ 0] = _width; _result[ 5] = _height; - _result[ 8] = (Handness::Right == HandnessT) ? _x : -_x; - _result[ 9] = (Handness::Right == HandnessT) ? _y : -_y; - _result[10] = (Handness::Right == HandnessT) ? -aa : aa; - _result[11] = (Handness::Right == HandnessT) ? -1.0f : 1.0f; + _result[ 8] = (Handness::Right == _handness) ? _x : -_x; + _result[ 9] = (Handness::Right == _handness) ? _y : -_y; + _result[10] = (Handness::Right == _handness) ? -aa : aa; + _result[11] = (Handness::Right == _handness) ? -1.0f : 1.0f; _result[14] = -bb; } - template<NearFar::Enum NearFarT, Handness::Enum HandnessT> - void mtxProjInfImpl(float* _result, float _ut, float _dt, float _lt, float _rt, float _near, bool _oglNdc) + void mtxProjInf(float* _result, float _ut, float _dt, float _lt, float _rt, float _near, bool _homogeneousNdc, Handness::Enum _handness, NearFar::Enum _nearFar) { const float invDiffRl = 1.0f/(_rt - _lt); const float invDiffUd = 1.0f/(_ut - _dt); @@ -424,107 +341,29 @@ namespace bx const float height = 2.0f*_near * invDiffUd; const float xx = (_rt + _lt) * invDiffRl; const float yy = (_ut + _dt) * invDiffUd; - mtxProjInfXYWH<NearFarT,HandnessT>(_result, xx, yy, width, height, _near, _oglNdc); + mtxProjInfXYWH(_result, xx, yy, width, height, _near, _homogeneousNdc, _handness, _nearFar); } - template<NearFar::Enum NearFarT, Handness::Enum HandnessT> - void mtxProjInfImpl(float* _result, const float _fov[4], float _near, bool _oglNdc) + void mtxProjInf(float* _result, const float _fov[4], float _near, bool _homogeneousNdc, Handness::Enum _handness, NearFar::Enum _nearFar) { - mtxProjInfImpl<NearFarT,HandnessT>(_result, _fov[0], _fov[1], _fov[2], _fov[3], _near, _oglNdc); + mtxProjInf(_result, _fov[0], _fov[1], _fov[2], _fov[3], _near, _homogeneousNdc, _handness, _nearFar); } - template<NearFar::Enum NearFarT, Handness::Enum HandnessT> - void mtxProjInfImpl(float* _result, float _fovy, float _aspect, float _near, bool _oglNdc) + void mtxProjInf(float* _result, float _fovy, float _aspect, float _near, bool _homogeneousNdc, Handness::Enum _handness, NearFar::Enum _nearFar) { const float height = 1.0f/tan(toRad(_fovy)*0.5f); const float width = height * 1.0f/_aspect; - mtxProjInfXYWH<NearFarT,HandnessT>(_result, 0.0f, 0.0f, width, height, _near, _oglNdc); - } - - void mtxProjInf(float* _result, const float _fov[4], float _near, bool _oglNdc) - { - mtxProjInfImpl<NearFar::Default,Handness::Left>(_result, _fov, _near, _oglNdc); - } - - void mtxProjInf(float* _result, float _ut, float _dt, float _lt, float _rt, float _near, bool _oglNdc) - { - mtxProjInfImpl<NearFar::Default,Handness::Left>(_result, _ut, _dt, _lt, _rt, _near, _oglNdc); + mtxProjInfXYWH(_result, 0.0f, 0.0f, width, height, _near, _homogeneousNdc, _handness, _nearFar); } - void mtxProjInf(float* _result, float _fovy, float _aspect, float _near, bool _oglNdc) - { - mtxProjInfImpl<NearFar::Default,Handness::Left>(_result, _fovy, _aspect, _near, _oglNdc); - } - - void mtxProjInfLh(float* _result, float _ut, float _dt, float _lt, float _rt, float _near, bool _oglNdc) - { - mtxProjInfImpl<NearFar::Default,Handness::Left>(_result, _ut, _dt, _lt, _rt, _near, _oglNdc); - } - - void mtxProjInfLh(float* _result, const float _fov[4], float _near, bool _oglNdc) - { - mtxProjInfImpl<NearFar::Default,Handness::Left>(_result, _fov, _near, _oglNdc); - } - - void mtxProjInfLh(float* _result, float _fovy, float _aspect, float _near, bool _oglNdc) - { - mtxProjInfImpl<NearFar::Default,Handness::Left>(_result, _fovy, _aspect, _near, _oglNdc); - } - - void mtxProjInfRh(float* _result, float _ut, float _dt, float _lt, float _rt, float _near, bool _oglNdc) - { - mtxProjInfImpl<NearFar::Default,Handness::Right>(_result, _ut, _dt, _lt, _rt, _near, _oglNdc); - } - - void mtxProjInfRh(float* _result, const float _fov[4], float _near, bool _oglNdc) - { - mtxProjInfImpl<NearFar::Default,Handness::Right>(_result, _fov, _near, _oglNdc); - } - - void mtxProjInfRh(float* _result, float _fovy, float _aspect, float _near, bool _oglNdc) - { - mtxProjInfImpl<NearFar::Default,Handness::Right>(_result, _fovy, _aspect, _near, _oglNdc); - } - - void mtxProjRevInfLh(float* _result, float _ut, float _dt, float _lt, float _rt, float _near, bool _oglNdc) - { - mtxProjInfImpl<NearFar::Reverse,Handness::Left>(_result, _ut, _dt, _lt, _rt, _near, _oglNdc); - } - - void mtxProjRevInfLh(float* _result, const float _fov[4], float _near, bool _oglNdc) - { - mtxProjInfImpl<NearFar::Reverse,Handness::Left>(_result, _fov, _near, _oglNdc); - } - - void mtxProjRevInfLh(float* _result, float _fovy, float _aspect, float _near, bool _oglNdc) - { - mtxProjInfImpl<NearFar::Reverse,Handness::Left>(_result, _fovy, _aspect, _near, _oglNdc); - } - - void mtxProjRevInfRh(float* _result, float _ut, float _dt, float _lt, float _rt, float _near, bool _oglNdc) - { - mtxProjInfImpl<NearFar::Reverse,Handness::Right>(_result, _ut, _dt, _lt, _rt, _near, _oglNdc); - } - - void mtxProjRevInfRh(float* _result, const float _fov[4], float _near, bool _oglNdc) - { - mtxProjInfImpl<NearFar::Reverse,Handness::Right>(_result, _fov, _near, _oglNdc); - } - - void mtxProjRevInfRh(float* _result, float _fovy, float _aspect, float _near, bool _oglNdc) - { - mtxProjInfImpl<NearFar::Reverse,Handness::Right>(_result, _fovy, _aspect, _near, _oglNdc); - } - - template<Handness::Enum HandnessT> - void mtxOrthoImpl(float* _result, float _left, float _right, float _bottom, float _top, float _near, float _far, float _offset, bool _oglNdc) + void mtxOrtho(float* _result, float _left, float _right, float _bottom, float _top, float _near, float _far, float _offset, bool _homogeneousNdc, Handness::Enum _handness) { const float aa = 2.0f/(_right - _left); const float bb = 2.0f/(_top - _bottom); - const float cc = (_oglNdc ? 2.0f : 1.0f) / (_far - _near); + const float cc = (_homogeneousNdc ? 2.0f : 1.0f) / (_far - _near); const float dd = (_left + _right )/(_left - _right); const float ee = (_top + _bottom)/(_bottom - _top ); - const float ff = _oglNdc + const float ff = _homogeneousNdc ? (_near + _far)/(_near - _far) : _near /(_near - _far) ; @@ -532,28 +371,13 @@ namespace bx memSet(_result, 0, sizeof(float)*16); _result[ 0] = aa; _result[ 5] = bb; - _result[10] = (Handness::Right == HandnessT) ? -cc : cc; + _result[10] = Handness::Right == _handness ? -cc : cc; _result[12] = dd + _offset; _result[13] = ee; _result[14] = ff; _result[15] = 1.0f; } - void mtxOrtho(float* _result, float _left, float _right, float _bottom, float _top, float _near, float _far, float _offset, bool _oglNdc) - { - mtxOrthoImpl<Handness::Left>(_result, _left, _right, _bottom, _top, _near, _far, _offset, _oglNdc); - } - - void mtxOrthoLh(float* _result, float _left, float _right, float _bottom, float _top, float _near, float _far, float _offset, bool _oglNdc) - { - mtxOrthoImpl<Handness::Left>(_result, _left, _right, _bottom, _top, _near, _far, _offset, _oglNdc); - } - - void mtxOrthoRh(float* _result, float _left, float _right, float _bottom, float _top, float _near, float _far, float _offset, bool _oglNdc) - { - mtxOrthoImpl<Handness::Right>(_result, _left, _right, _bottom, _top, _near, _far, _offset, _oglNdc); - } - void mtxRotateX(float* _result, float _ax) { const float sx = sin(_ax); @@ -694,15 +518,15 @@ namespace bx void mtx3Inverse(float* _result, const float* _a) { - float xx = _a[0]; - float xy = _a[1]; - float xz = _a[2]; - float yx = _a[3]; - float yy = _a[4]; - float yz = _a[5]; - float zx = _a[6]; - float zy = _a[7]; - float zz = _a[8]; + const float xx = _a[0]; + const float xy = _a[1]; + const float xz = _a[2]; + const float yx = _a[3]; + const float yy = _a[4]; + const float yz = _a[5]; + const float zx = _a[6]; + const float zy = _a[7]; + const float zz = _a[8]; float det = 0.0f; det += xx * (yy*zz - yz*zy); @@ -726,22 +550,22 @@ namespace bx void mtxInverse(float* _result, const float* _a) { - float xx = _a[ 0]; - float xy = _a[ 1]; - float xz = _a[ 2]; - float xw = _a[ 3]; - float yx = _a[ 4]; - float yy = _a[ 5]; - float yz = _a[ 6]; - float yw = _a[ 7]; - float zx = _a[ 8]; - float zy = _a[ 9]; - float zz = _a[10]; - float zw = _a[11]; - float wx = _a[12]; - float wy = _a[13]; - float wz = _a[14]; - float ww = _a[15]; + const float xx = _a[ 0]; + const float xy = _a[ 1]; + const float xz = _a[ 2]; + const float xw = _a[ 3]; + const float yx = _a[ 4]; + const float yy = _a[ 5]; + const float yz = _a[ 6]; + const float yw = _a[ 7]; + const float zx = _a[ 8]; + const float zy = _a[ 9]; + const float zz = _a[10]; + const float zw = _a[11]; + const float wx = _a[12]; + const float wy = _a[13]; + const float wz = _a[14]; + const float ww = _a[15]; float det = 0.0f; det += xx * (yy*(zz*ww - zw*wz) - yz*(zy*ww - zw*wy) + yw*(zy*wz - zz*wy) ); @@ -772,6 +596,71 @@ namespace bx _result[15] = +(xx*(yy*zz - zy*yz) - xy*(yx*zz - zx*yz) + xz*(yx*zy - zx*yy) ) * invDet; } + void mtx3Cofactor(float* _result, const float* _a) + { + const float xx = _a[0]; + const float xy = _a[1]; + const float xz = _a[2]; + const float yx = _a[3]; + const float yy = _a[4]; + const float yz = _a[5]; + const float zx = _a[6]; + const float zy = _a[7]; + const float zz = _a[8]; + + _result[0] = +(yy*zz - yz * zy); + _result[1] = -(yx*zz - yz * zx); + _result[2] = +(yx*zy - yy * zx); + + _result[3] = -(xy*zz - xz * zy); + _result[4] = +(xx*zz - xz * zx); + _result[5] = -(xx*zy - xy * zx); + + _result[6] = +(xy*yz - xz * yy); + _result[7] = -(xx*yz - xz * yx); + _result[8] = +(xx*yy - xy * yx); + } + + void mtxCofactor(float* _result, const float* _a) + { + const float xx = _a[0]; + const float xy = _a[1]; + const float xz = _a[2]; + const float xw = _a[3]; + const float yx = _a[4]; + const float yy = _a[5]; + const float yz = _a[6]; + const float yw = _a[7]; + const float zx = _a[8]; + const float zy = _a[9]; + const float zz = _a[10]; + const float zw = _a[11]; + const float wx = _a[12]; + const float wy = _a[13]; + const float wz = _a[14]; + const float ww = _a[15]; + + _result[ 0] = +(yy*(zz*ww - wz * zw) - yz * (zy*ww - wy * zw) + yw * (zy*wz - wy * zz) ); + _result[ 1] = -(yx*(zz*ww - wz * zw) - yz * (zx*ww - wx * zw) + yw * (zx*wz - wx * zz) ); + _result[ 2] = +(yx*(zy*ww - wy * zw) - yy * (zx*ww - wx * zw) + yw * (zx*wy - wx * zy) ); + _result[ 3] = -(yx*(zy*wz - wy * zz) - yy * (zx*wz - wx * zz) + yz * (zx*wy - wx * zy) ); + + _result[ 4] = -(xy*(zz*ww - wz * zw) - xz * (zy*ww - wy * zw) + xw * (zy*wz - wy * zz) ); + _result[ 5] = +(xx*(zz*ww - wz * zw) - xz * (zx*ww - wx * zw) + xw * (zx*wz - wx * zz) ); + _result[ 6] = -(xx*(zy*ww - wy * zw) - xy * (zx*ww - wx * zw) + xw * (zx*wy - wx * zy) ); + _result[ 7] = +(xx*(zy*wz - wy * zz) - xy * (zx*wz - wx * zz) + xz * (zx*wy - wx * zy) ); + + _result[ 8] = +(xy*(yz*ww - wz * yw) - xz * (yy*ww - wy * yw) + xw * (yy*wz - wy * yz) ); + _result[ 9] = -(xx*(yz*ww - wz * yw) - xz * (yx*ww - wx * yw) + xw * (yx*wz - wx * yz) ); + _result[10] = +(xx*(yy*ww - wy * yw) - xy * (yx*ww - wx * yw) + xw * (yx*wy - wx * yy) ); + _result[11] = -(xx*(yy*wz - wy * yz) - xy * (yx*wz - wx * yz) + xz * (yx*wy - wx * yy) ); + + _result[12] = -(xy*(yz*zw - zz * yw) - xz * (yy*zw - zy * yw) + xw * (yy*zz - zy * yz) ); + _result[13] = +(xx*(yz*zw - zz * yw) - xz * (yx*zw - zx * yw) + xw * (yx*zz - zx * yz) ); + _result[14] = -(xx*(yy*zw - zy * yw) - xy * (yx*zw - zx * yw) + xw * (yx*zy - zx * yy) ); + _result[15] = +(xx*(yy*zz - zy * yz) - xy * (yx*zz - zx * yz) + xz * (yx*zy - zx * yy) ); + } + void calcLinearFit2D(float _result[2], const void* _points, uint32_t _stride, uint32_t _numPoints) { float sumX = 0.0f; diff --git a/3rdparty/bx/src/mutex.cpp b/3rdparty/bx/src/mutex.cpp index 27c6b63b709..48f09d0be3e 100644 --- a/3rdparty/bx/src/mutex.cpp +++ b/3rdparty/bx/src/mutex.cpp @@ -1,5 +1,5 @@ /* - * Copyright 2010-2018 Branimir Karadzic. All rights reserved. + * Copyright 2010-2019 Branimir Karadzic. All rights reserved. * License: https://github.com/bkaradzic/bx#license-bsd-2-clause */ @@ -12,6 +12,7 @@ # include <bx/cpu.h> # include "crt0.h" #elif BX_PLATFORM_ANDROID \ + || BX_PLATFORM_HAIKU \ || BX_PLATFORM_LINUX \ || BX_PLATFORM_IOS \ || BX_PLATFORM_OSX \ diff --git a/3rdparty/bx/src/os.cpp b/3rdparty/bx/src/os.cpp index 0e925bb2f90..ab5d6bd0935 100644 --- a/3rdparty/bx/src/os.cpp +++ b/3rdparty/bx/src/os.cpp @@ -1,5 +1,5 @@ /* - * Copyright 2010-2018 Branimir Karadzic. All rights reserved. + * Copyright 2010-2019 Branimir Karadzic. All rights reserved. * License: https://github.com/bkaradzic/bx#license-bsd-2-clause */ @@ -18,21 +18,23 @@ # include <windows.h> # include <psapi.h> #elif BX_PLATFORM_ANDROID \ - || BX_PLATFORM_EMSCRIPTEN \ || BX_PLATFORM_BSD \ + || BX_PLATFORM_EMSCRIPTEN \ + || BX_PLATFORM_HAIKU \ || BX_PLATFORM_HURD \ || BX_PLATFORM_IOS \ || BX_PLATFORM_LINUX \ + || BX_PLATFORM_NX \ || BX_PLATFORM_OSX \ || BX_PLATFORM_PS4 \ || BX_PLATFORM_RPI \ - || BX_PLATFORM_STEAMLINK \ - || BX_PLATFORM_NX + || BX_PLATFORM_STEAMLINK # include <sched.h> // sched_yield -# if BX_PLATFORM_BSD \ - || BX_PLATFORM_IOS \ - || BX_PLATFORM_OSX \ - || BX_PLATFORM_PS4 \ +# if BX_PLATFORM_BSD \ + || BX_PLATFORM_HAIKU \ + || BX_PLATFORM_IOS \ + || BX_PLATFORM_OSX \ + || BX_PLATFORM_PS4 \ || BX_PLATFORM_STEAMLINK # include <pthread.h> // mach_port_t # endif // BX_PLATFORM_* @@ -50,6 +52,9 @@ # include <stdio.h> // fopen # include <unistd.h> // syscall # include <sys/syscall.h> +# elif BX_PLATFORM_HAIKU +# include <stdio.h> // fopen +# include <unistd.h> // syscall # elif BX_PLATFORM_OSX # include <mach/mach.h> // mach_task_basic_info # elif BX_PLATFORM_HURD @@ -62,7 +67,6 @@ namespace bx { - void sleep(uint32_t _ms) { #if BX_PLATFORM_WINDOWS @@ -174,7 +178,7 @@ namespace bx void* dlopen(const FilePath& _filePath) { #if BX_PLATFORM_WINDOWS - return (void*)::LoadLibraryA(_filePath.get() ); + return (void*)::LoadLibraryA(_filePath.getCPtr() ); #elif BX_PLATFORM_EMSCRIPTEN \ || BX_PLATFORM_PS4 \ || BX_PLATFORM_XBOXONE \ @@ -183,7 +187,7 @@ namespace bx BX_UNUSED(_filePath); return NULL; #else - return ::dlopen(_filePath.get(), RTLD_LOCAL|RTLD_LAZY); + return ::dlopen(_filePath.getCPtr(), RTLD_LOCAL|RTLD_LAZY); #endif // BX_PLATFORM_ } @@ -233,9 +237,10 @@ namespace bx bool result = len != 0 && len < *_inOutSize; *_inOutSize = len; return result; -#elif BX_PLATFORM_PS4 \ - || BX_PLATFORM_XBOXONE \ - || BX_PLATFORM_WINRT \ +#elif BX_PLATFORM_EMSCRIPTEN \ + || BX_PLATFORM_PS4 \ + || BX_PLATFORM_XBOXONE \ + || BX_PLATFORM_WINRT \ || BX_CRT_NONE BX_UNUSED(name, _out, _inOutSize); return false; @@ -275,9 +280,10 @@ namespace bx #if BX_PLATFORM_WINDOWS ::SetEnvironmentVariableA(name, value); -#elif BX_PLATFORM_PS4 \ - || BX_PLATFORM_XBOXONE \ - || BX_PLATFORM_WINRT \ +#elif BX_PLATFORM_EMSCRIPTEN \ + || BX_PLATFORM_PS4 \ + || BX_PLATFORM_XBOXONE \ + || BX_PLATFORM_WINRT \ || BX_CRT_NONE BX_UNUSED(name, value); #else diff --git a/3rdparty/bx/src/process.cpp b/3rdparty/bx/src/process.cpp index 90d663b3f5a..b079bbc77e5 100644 --- a/3rdparty/bx/src/process.cpp +++ b/3rdparty/bx/src/process.cpp @@ -1,5 +1,5 @@ /* - * Copyright 2010-2018 Branimir Karadzic. All rights reserved. + * Copyright 2010-2019 Branimir Karadzic. All rights reserved. * License: https://github.com/bkaradzic/bx#license-bsd-2-clause */ @@ -48,7 +48,7 @@ namespace bx } char tmp[kMaxFilePath*2] = "\""; - strCat(tmp, BX_COUNTOF(tmp), _filePath.get() ); + strCat(tmp, BX_COUNTOF(tmp), _filePath); strCat(tmp, BX_COUNTOF(tmp), "\" "); strCat(tmp, BX_COUNTOF(tmp), _args); @@ -119,7 +119,7 @@ namespace bx } char tmp[kMaxFilePath*2] = "\""; - strCat(tmp, BX_COUNTOF(tmp), _filePath.get() ); + strCat(tmp, BX_COUNTOF(tmp), _filePath); strCat(tmp, BX_COUNTOF(tmp), "\" "); strCat(tmp, BX_COUNTOF(tmp), _args); diff --git a/3rdparty/bx/src/semaphore.cpp b/3rdparty/bx/src/semaphore.cpp index 3663fe767b5..f4e99c66637 100644 --- a/3rdparty/bx/src/semaphore.cpp +++ b/3rdparty/bx/src/semaphore.cpp @@ -1,5 +1,5 @@ /* - * Copyright 2010-2018 Branimir Karadzic. All rights reserved. + * Copyright 2010-2019 Branimir Karadzic. All rights reserved. * License: https://github.com/bkaradzic/bx#license-bsd-2-clause */ diff --git a/3rdparty/bx/src/settings.cpp b/3rdparty/bx/src/settings.cpp index e09e11f687f..49bcc596817 100644 --- a/3rdparty/bx/src/settings.cpp +++ b/3rdparty/bx/src/settings.cpp @@ -1,5 +1,5 @@ /* - * Copyright 2011-2018 Branimir Karadzic. All rights reserved. + * Copyright 2011-2019 Branimir Karadzic. All rights reserved. * License: https://github.com/bkaradzic/bx#license-bsd-2-clause */ diff --git a/3rdparty/bx/src/sort.cpp b/3rdparty/bx/src/sort.cpp index 515b33f24f1..56cb7d48a98 100644 --- a/3rdparty/bx/src/sort.cpp +++ b/3rdparty/bx/src/sort.cpp @@ -1,5 +1,5 @@ /* - * Copyright 2010-2018 Branimir Karadzic. All rights reserved. + * Copyright 2010-2019 Branimir Karadzic. All rights reserved. * License: https://github.com/bkaradzic/bx#license-bsd-2-clause */ diff --git a/3rdparty/bx/src/string.cpp b/3rdparty/bx/src/string.cpp index 7710028ae6d..678af6664ce 100644 --- a/3rdparty/bx/src/string.cpp +++ b/3rdparty/bx/src/string.cpp @@ -1,12 +1,12 @@ /* - * Copyright 2010-2018 Branimir Karadzic. All rights reserved. + * Copyright 2010-2019 Branimir Karadzic. All rights reserved. * License: https://github.com/bkaradzic/bx#license-bsd-2-clause */ #include "bx_p.h" #include <bx/allocator.h> +#include <bx/file.h> #include <bx/hash.h> -#include <bx/readerwriter.h> #include <bx/string.h> namespace bx @@ -188,7 +188,12 @@ namespace bx } } - return 0 == max && _lhsMax == _rhsMax ? 0 : fn(*_lhs) - fn(*_rhs); + if (0 == max) + { + return _lhsMax == _rhsMax ? 0 : _lhsMax > _rhsMax ? 1 : -1; + } + + return fn(*_lhs) - fn(*_rhs); } int32_t strCmp(const StringView& _lhs, const StringView& _rhs, int32_t _max) @@ -370,7 +375,7 @@ namespace bx inline const char* strRFindUnsafe(const char* _str, int32_t _len, char _ch) { - for (int32_t ii = _len; 0 <= ii; --ii) + for (int32_t ii = _len-1; 0 <= ii; --ii) { if (_str[ii] == _ch) { @@ -687,9 +692,10 @@ namespace bx return StringView(_str.getTerm(), _str.getTerm() ); } - StringView findIdentifierMatch(const StringView& _str, const char** _words) + StringView findIdentifierMatch(const StringView& _str, const char** _words, int32_t _num) { - for (StringView word = *_words; !word.isEmpty(); ++_words, word = *_words) + int32_t ii = 0; + for (StringView word = *_words; ii < _num && !word.isEmpty(); ++ii, ++_words, word = *_words) { StringView match = findIdentifierMatch(_str, word); if (!match.isEmpty() ) @@ -1147,10 +1153,10 @@ namespace bx SizerWriter sizer; va_list argListCopy; va_copy(argListCopy, _argList); - int32_t size = write(&sizer, _format, argListCopy, &err); + int32_t total = write(&sizer, _format, argListCopy, &err); va_end(argListCopy); - return size; + return total; } int32_t snprintf(char* _out, int32_t _max, const char* _format, ...) @@ -1159,6 +1165,28 @@ namespace bx va_start(argList, _format); int32_t total = vsnprintf(_out, _max, _format, argList); va_end(argList); + + return total; + } + + int32_t vprintf(const char* _format, va_list _argList) + { + Error err; + va_list argListCopy; + va_copy(argListCopy, _argList); + int32_t total = write(getStdOut(), _format, argListCopy, &err); + va_end(argListCopy); + + return total; + } + + int32_t printf(const char* _format, ...) + { + va_list argList; + va_start(argList, _format); + int32_t total = vprintf(_format, argList); + va_end(argList); + return total; } diff --git a/3rdparty/bx/src/thread.cpp b/3rdparty/bx/src/thread.cpp index f52e0bdcbf4..7969d7852ae 100644 --- a/3rdparty/bx/src/thread.cpp +++ b/3rdparty/bx/src/thread.cpp @@ -1,5 +1,5 @@ /* - * Copyright 2010-2018 Branimir Karadzic. All rights reserved. + * Copyright 2010-2019 Branimir Karadzic. All rights reserved. * License: https://github.com/bkaradzic/bx#license-bsd-2-clause */ @@ -8,9 +8,14 @@ #if BX_CONFIG_SUPPORTS_THREADING +#if BX_PLATFORM_WINDOWS && !BX_CRT_NONE +# include <bx/string.h> +#endif + #if BX_CRT_NONE # include "crt0.h" #elif BX_PLATFORM_ANDROID \ + || BX_PLATFORM_HAIKU \ || BX_PLATFORM_LINUX \ || BX_PLATFORM_IOS \ || BX_PLATFORM_OSX \ @@ -240,33 +245,46 @@ namespace bx # else pthread_set_name_np(ti->m_handle, _name); # endif // defined(__NetBSD__) -#elif BX_PLATFORM_WINDOWS && BX_COMPILER_MSVC -# pragma pack(push, 8) - struct ThreadName - { - DWORD type; - LPCSTR name; - DWORD id; - DWORD flags; - }; -# pragma pack(pop) - ThreadName tn; - tn.type = 0x1000; - tn.name = _name; - tn.id = ti->m_threadId; - tn.flags = 0; - - __try - { - RaiseException(0x406d1388 - , 0 - , sizeof(tn)/4 - , reinterpret_cast<ULONG_PTR*>(&tn) - ); - } - __except(EXCEPTION_EXECUTE_HANDLER) +#elif BX_PLATFORM_WINDOWS + // Try to use the new thread naming API from Win10 Creators update onwards if we have it + typedef HRESULT (WINAPI *SetThreadDescriptionProc)(HANDLE, PCWSTR); + SetThreadDescriptionProc SetThreadDescription = (SetThreadDescriptionProc)(GetProcAddress(GetModuleHandleA("Kernel32.dll"), "SetThreadDescription")); + if (SetThreadDescription) { + uint32_t length = (uint32_t)bx::strLen(_name)+1; + uint32_t size = length*sizeof(wchar_t); + wchar_t* name = (wchar_t*)alloca(size); + mbstowcs(name, _name, size-2); + SetThreadDescription(ti->m_handle, name); } +# if BX_COMPILER_MSVC +# pragma pack(push, 8) + struct ThreadName + { + DWORD type; + LPCSTR name; + DWORD id; + DWORD flags; + }; +# pragma pack(pop) + ThreadName tn; + tn.type = 0x1000; + tn.name = _name; + tn.id = ti->m_threadId; + tn.flags = 0; + + __try + { + RaiseException(0x406d1388 + , 0 + , sizeof(tn)/4 + , reinterpret_cast<ULONG_PTR*>(&tn) + ); + } + __except(EXCEPTION_EXECUTE_HANDLER) + { + } +# endif // BX_COMPILER_MSVC #else BX_UNUSED(_name); #endif // BX_PLATFORM_ diff --git a/3rdparty/bx/src/timer.cpp b/3rdparty/bx/src/timer.cpp index 8cfebdda7f5..80bc28a8448 100644 --- a/3rdparty/bx/src/timer.cpp +++ b/3rdparty/bx/src/timer.cpp @@ -1,5 +1,5 @@ /* - * Copyright 2010-2018 Branimir Karadzic. All rights reserved. + * Copyright 2010-2019 Branimir Karadzic. All rights reserved. * License: https://github.com/bkaradzic/bx#license-bsd-2-clause */ diff --git a/3rdparty/bx/src/url.cpp b/3rdparty/bx/src/url.cpp index f58ecf5a9c3..b64a50b43b3 100644 --- a/3rdparty/bx/src/url.cpp +++ b/3rdparty/bx/src/url.cpp @@ -1,5 +1,5 @@ /* - * Copyright 2011-2018 Branimir Karadzic. All rights reserved. + * Copyright 2011-2019 Branimir Karadzic. All rights reserved. * License: https://github.com/bkaradzic/bnet#license-bsd-2-clause */ diff --git a/3rdparty/bx/tests/atomic_test.cpp b/3rdparty/bx/tests/atomic_test.cpp index 04a623686e7..219b0959d27 100644 --- a/3rdparty/bx/tests/atomic_test.cpp +++ b/3rdparty/bx/tests/atomic_test.cpp @@ -1,5 +1,5 @@ /* - * Copyright 2010-2018 Branimir Karadzic. All rights reserved. + * Copyright 2010-2019 Branimir Karadzic. All rights reserved. * License: https://github.com/bkaradzic/bx#license-bsd-2-clause */ diff --git a/3rdparty/bx/tests/crt_test.cpp b/3rdparty/bx/tests/crt_test.cpp index bda079036ee..bddcb2b2d76 100644 --- a/3rdparty/bx/tests/crt_test.cpp +++ b/3rdparty/bx/tests/crt_test.cpp @@ -1,5 +1,5 @@ /* - * Copyright 2010-2018 Branimir Karadzic. All rights reserved. + * Copyright 2010-2019 Branimir Karadzic. All rights reserved. * License: https://github.com/bkaradzic/bx#license-bsd-2-clause */ diff --git a/3rdparty/bx/tests/dbg.h b/3rdparty/bx/tests/dbg.h index 243dc234afc..2fb211862d1 100644 --- a/3rdparty/bx/tests/dbg.h +++ b/3rdparty/bx/tests/dbg.h @@ -1,5 +1,5 @@ /* - * Copyright 2011-2018 Branimir Karadzic. All rights reserved. + * Copyright 2011-2019 Branimir Karadzic. All rights reserved. * License: https://github.com/bkaradzic/bx#license-bsd-2-clause */ diff --git a/3rdparty/bx/tests/easing_test.cpp b/3rdparty/bx/tests/easing_test.cpp index 06944547f4a..4556c3ad19c 100644 --- a/3rdparty/bx/tests/easing_test.cpp +++ b/3rdparty/bx/tests/easing_test.cpp @@ -1,5 +1,5 @@ /* - * Copyright 2010-2018 Branimir Karadzic. All rights reserved. + * Copyright 2010-2019 Branimir Karadzic. All rights reserved. * License: https://github.com/bkaradzic/bx#license-bsd-2-clause */ diff --git a/3rdparty/bx/tests/filepath_test.cpp b/3rdparty/bx/tests/filepath_test.cpp index 2662adeddf1..fa912105569 100644 --- a/3rdparty/bx/tests/filepath_test.cpp +++ b/3rdparty/bx/tests/filepath_test.cpp @@ -1,10 +1,10 @@ /* - * Copyright 2010-2018 Branimir Karadzic. All rights reserved. + * Copyright 2010-2019 Branimir Karadzic. All rights reserved. * License: https://github.com/bkaradzic/bx#license-bsd-2-clause */ #include "test.h" -#include <bx/filepath.h> +#include <bx/file.h> struct FilePathTest { @@ -96,9 +96,8 @@ TEST_CASE("FilePath", "") const FilePathTest& test = s_filePathTest[ii]; fp.set(test.filePath); - const bx::StringView result = fp.get(); - REQUIRE(0 == bx::strCmp(test.expected, result) ); + REQUIRE(0 == bx::strCmp(test.expected, fp) ); } for (uint32_t ii = 0; ii < BX_COUNTOF(s_filePathSplit); ++ii) diff --git a/3rdparty/bx/tests/handle_bench.cpp b/3rdparty/bx/tests/handle_bench.cpp index 4effb59758e..082bee0e182 100644 --- a/3rdparty/bx/tests/handle_bench.cpp +++ b/3rdparty/bx/tests/handle_bench.cpp @@ -1,5 +1,5 @@ /* - * Copyright 2010-2018 Branimir Karadzic. All rights reserved. + * Copyright 2010-2019 Branimir Karadzic. All rights reserved. * License: https://github.com/bkaradzic/bx#license-bsd-2-clause */ diff --git a/3rdparty/bx/tests/handle_test.cpp b/3rdparty/bx/tests/handle_test.cpp index eb89b1f67a9..5c367f34531 100644 --- a/3rdparty/bx/tests/handle_test.cpp +++ b/3rdparty/bx/tests/handle_test.cpp @@ -1,5 +1,5 @@ /* - * Copyright 2010-2018 Branimir Karadzic. All rights reserved. + * Copyright 2010-2019 Branimir Karadzic. All rights reserved. * License: https://github.com/bkaradzic/bx#license-bsd-2-clause */ diff --git a/3rdparty/bx/tests/hash_test.cpp b/3rdparty/bx/tests/hash_test.cpp index 1ec6702080e..5c2d258dcc9 100644 --- a/3rdparty/bx/tests/hash_test.cpp +++ b/3rdparty/bx/tests/hash_test.cpp @@ -1,5 +1,5 @@ /* - * Copyright 2010-2018 Branimir Karadzic. All rights reserved. + * Copyright 2010-2019 Branimir Karadzic. All rights reserved. * License: https://github.com/bkaradzic/bx#license-bsd-2-clause */ diff --git a/3rdparty/bx/tests/macros_test.cpp b/3rdparty/bx/tests/macros_test.cpp index e0d5ffae8b4..3576b1cf2a9 100644 --- a/3rdparty/bx/tests/macros_test.cpp +++ b/3rdparty/bx/tests/macros_test.cpp @@ -1,5 +1,5 @@ /* - * Copyright 2010-2018 Branimir Karadzic. All rights reserved. + * Copyright 2010-2019 Branimir Karadzic. All rights reserved. * License: https://github.com/bkaradzic/bx#license-bsd-2-clause */ diff --git a/3rdparty/bx/tests/main_test.cpp b/3rdparty/bx/tests/main_test.cpp index b9e9074a8ea..2e0fbe2b431 100644 --- a/3rdparty/bx/tests/main_test.cpp +++ b/3rdparty/bx/tests/main_test.cpp @@ -1,17 +1,17 @@ /* - * Copyright 2010-2018 Branimir Karadzic. All rights reserved. + * Copyright 2010-2019 Branimir Karadzic. All rights reserved. * License: https://github.com/bkaradzic/bx#license-bsd-2-clause */ #include "test.h" -static const char* s_argv[] = { "bx.test" }; - int runAllTests(int _argc, const char* _argv[]); #if BX_PLATFORM_ANDROID # include <android/native_activity.h> +static const char* s_argv[] = { "bx.test" }; + void ANativeActivity_onCreate(ANativeActivity*, void*, size_t) { exit(runAllTests(BX_COUNTOF(s_argv), s_argv) ); diff --git a/3rdparty/bx/tests/math_bench.cpp b/3rdparty/bx/tests/math_bench.cpp index 50a8b3d2301..e9e083e385c 100644 --- a/3rdparty/bx/tests/math_bench.cpp +++ b/3rdparty/bx/tests/math_bench.cpp @@ -1,5 +1,5 @@ /* - * Copyright 2010-2018 Branimir Karadzic. All rights reserved. + * Copyright 2010-2019 Branimir Karadzic. All rights reserved. * License: https://github.com/bkaradzic/bx#license-bsd-2-clause */ @@ -60,6 +60,10 @@ void math_bench() mathTest<bx::sin >("bx::sin"); bx::write(writer, &err, "\n"); + mathTest< ::sinhf>(" ::sinhf"); + mathTest<bx::sinh >("bx::sinh"); + + bx::write(writer, &err, "\n"); mathTest< ::asinf>(" ::asinf"); mathTest<bx::asin >("bx::asin"); @@ -68,6 +72,10 @@ void math_bench() mathTest<bx::cos >("bx::cos"); bx::write(writer, &err, "\n"); + mathTest< ::coshf>(" ::coshf"); + mathTest<bx::cosh >("bx::cosh"); + + bx::write(writer, &err, "\n"); mathTest< ::acosf>(" ::acosf"); mathTest<bx::acos >("bx::acos"); @@ -76,6 +84,26 @@ void math_bench() mathTest<bx::tan >("bx::tan"); bx::write(writer, &err, "\n"); + mathTest< ::tanhf>(" ::tanhf"); + mathTest<bx::tanh >("bx::tanh"); + + bx::write(writer, &err, "\n"); mathTest< ::atanf>(" ::atanf"); mathTest<bx::atan >("bx::atan"); + + bx::write(writer, &err, "\n"); + mathTest< ::expf>(" ::expf"); + mathTest<bx::exp >("bx::exp"); + + bx::write(writer, &err, "\n"); + mathTest< ::exp2f>(" ::exp2f"); + mathTest<bx::exp2 >("bx::exp2"); + + bx::write(writer, &err, "\n"); + mathTest< ::logf >(" ::logf"); + mathTest<bx::log >("bx::log"); + + bx::write(writer, &err, "\n"); + mathTest< ::log2f>(" ::log2f"); + mathTest<bx::log2 >("bx::log2"); } diff --git a/3rdparty/bx/tests/math_test.cpp b/3rdparty/bx/tests/math_test.cpp index 2eeac286282..84bec2c1b10 100644 --- a/3rdparty/bx/tests/math_test.cpp +++ b/3rdparty/bx/tests/math_test.cpp @@ -1,5 +1,5 @@ /* - * Copyright 2010-2018 Branimir Karadzic. All rights reserved. + * Copyright 2010-2019 Branimir Karadzic. All rights reserved. * License: https://github.com/bkaradzic/bx#license-bsd-2-clause */ @@ -203,7 +203,7 @@ TEST_CASE("quaternion", "") float mtxQ[16]; float mtx[16]; - float quat[4] = { 0.0f, 0.0f, 0.0f, 1.0f }; + bx::Quaternion quat = { 0.0f, 0.0f, 0.0f, 1.0f }; bx::mtxQuat(mtxQ, quat); bx::mtxIdentity(mtx); mtxCheck(mtxQ, mtx); @@ -212,28 +212,27 @@ TEST_CASE("quaternion", "") float ay = bx::kPi/13.0f; float az = bx::kPi/7.0f; - bx::quatRotateX(quat, ax); + quat = bx::rotateX(ax); bx::mtxQuat(mtxQ, quat); bx::mtxRotateX(mtx, ax); mtxCheck(mtxQ, mtx); - float euler[3]; - bx::quatToEuler(euler, quat); - CHECK(bx::equal(euler[0], ax, 0.001f) ); + bx::Vec3 euler = bx::toEuler(quat); + CHECK(bx::equal(euler.x, ax, 0.001f) ); - bx::quatRotateY(quat, ay); + quat = bx::rotateY(ay); bx::mtxQuat(mtxQ, quat); bx::mtxRotateY(mtx, ay); mtxCheck(mtxQ, mtx); - bx::quatToEuler(euler, quat); - CHECK(bx::equal(euler[1], ay, 0.001f) ); + euler = bx::toEuler(quat); + CHECK(bx::equal(euler.y, ay, 0.001f) ); - bx::quatRotateZ(quat, az); + quat = bx::rotateZ(az); bx::mtxQuat(mtxQ, quat); bx::mtxRotateZ(mtx, az); mtxCheck(mtxQ, mtx); - bx::quatToEuler(euler, quat); - CHECK(bx::equal(euler[2], az, 0.001f) ); + euler = bx::toEuler(quat); + CHECK(bx::equal(euler.z, az, 0.001f) ); } diff --git a/3rdparty/bx/tests/nlalloc_test.cpp b/3rdparty/bx/tests/nlalloc_test.cpp index 91b0354926f..14c44e5f23f 100644 --- a/3rdparty/bx/tests/nlalloc_test.cpp +++ b/3rdparty/bx/tests/nlalloc_test.cpp @@ -1,5 +1,5 @@ /* - * Copyright 2010-2018 Branimir Karadzic. All rights reserved. + * Copyright 2010-2019 Branimir Karadzic. All rights reserved. * License: https://github.com/bkaradzic/bx#license-bsd-2-clause */ @@ -34,7 +34,7 @@ namespace bx { struct Blk { - static const uint64_t kInvalid = UINT64_MAX; + static constexpr uint64_t kInvalid = UINT64_MAX; Blk() : ptr(kInvalid) @@ -66,6 +66,8 @@ namespace bx class NonLocalAllocator { public: + static constexpr uint32_t kMinBlockSize = 16u; + NonLocalAllocator() { reset(); @@ -102,13 +104,13 @@ namespace bx Blk alloc(uint32_t _size) { - _size = max(_size, 16u); + _size = max(_size, kMinBlockSize); for (FreeList::iterator it = m_free.begin(), itEnd = m_free.end(); it != itEnd; ++it) { if (it->size >= _size) { - uint64_t ptr = it->ptr; + const uint64_t ptr = it->ptr; if (it->size != _size) { @@ -121,6 +123,7 @@ namespace bx } m_used += _size; + return Blk{ ptr, _size }; } } @@ -131,38 +134,32 @@ namespace bx void free(const Blk& _blk) { + BX_CHECK(isValid(_blk), "Freeing invalid block!"); + m_used -= _blk.size; - m_free.push_back(_blk); - compact(); - } - bool compact() - { - bx::quickSort( - m_free.begin() - , uint32_t(m_free.end() - m_free.begin() ) - , sizeof(Blk) - , [](const void* _a, const void* _b) -> int32_t { - const Blk& lhs = *(const Blk*)(_a); - const Blk& rhs = *(const Blk*)(_b); - return lhs < rhs ? -1 : 1; - }); - - for (FreeList::iterator it = m_free.begin(), next = it, itEnd = m_free.end(); next != itEnd;) + FreeList::iterator it = m_free.begin(); + FreeList::iterator itEnd = m_free.end(); + for (; it != itEnd; ++it) { - if ( (it->ptr + it->size) == next->ptr) + if ( (_blk.ptr + _blk.size) == it->ptr) { - it->size += next->size; - next = m_free.erase(next); + it->ptr = _blk.ptr; + it->size += _blk.size; + break; } - else + + if (_blk.ptr > it->ptr) { - it = next; - ++next; + m_free.insert(it, _blk); + break; } } - return 0 == m_used; + if (it == itEnd) + { + m_free.push_back(_blk); + } } uint32_t getUsed() const @@ -175,6 +172,9 @@ namespace bx FreeList m_free; uint32_t m_used; }; + + constexpr uint32_t NonLocalAllocator::kMinBlockSize; + } // namespace bx TEST_CASE("nlalloc") @@ -185,11 +185,45 @@ TEST_CASE("nlalloc") blk = nla.alloc(100); REQUIRE(!isValid(blk) ); - nla.add(bx::Blk{0x1000, 100}); + nla.add(bx::Blk{0x1000, 1024}); - blk = nla.alloc(100); + blk = nla.alloc(1024); REQUIRE(isValid(blk) ); + bx::Blk blk2 = nla.alloc(1); + REQUIRE(!isValid(blk2) ); + nla.free(blk); REQUIRE(0 == nla.getUsed() ); + + { + bx::Blk blk3 = nla.alloc(123); + REQUIRE(isValid(blk3) ); + + bx::Blk blk4 = nla.alloc(134); + REQUIRE(isValid(blk4) ); + + bx::Blk blk5 = nla.alloc(145); + REQUIRE(isValid(blk5) ); + + bx::Blk blk6 = nla.alloc(156); + REQUIRE(isValid(blk6) ); + + bx::Blk blk7 = nla.alloc(167); + REQUIRE(isValid(blk7) ); + + nla.free(blk3); + nla.free(blk5); + nla.free(blk7); + nla.free(blk4); + nla.free(blk6); + REQUIRE(0 == nla.getUsed() ); + } + + blk2 = nla.alloc(1); + REQUIRE(isValid(blk2) ); + REQUIRE(bx::NonLocalAllocator::kMinBlockSize == nla.getUsed() ); + + nla.free(blk2); + REQUIRE(0 == nla.getUsed() ); } diff --git a/3rdparty/bx/tests/os_test.cpp b/3rdparty/bx/tests/os_test.cpp index bf51c725e05..bd28bf51d2d 100644 --- a/3rdparty/bx/tests/os_test.cpp +++ b/3rdparty/bx/tests/os_test.cpp @@ -1,5 +1,5 @@ /* - * Copyright 2010-2018 Branimir Karadzic. All rights reserved. + * Copyright 2010-2019 Branimir Karadzic. All rights reserved. * License: https://github.com/bkaradzic/bx#license-bsd-2-clause */ diff --git a/3rdparty/bx/tests/queue_test.cpp b/3rdparty/bx/tests/queue_test.cpp index 4a4d5c9a325..c035dcc8db1 100644 --- a/3rdparty/bx/tests/queue_test.cpp +++ b/3rdparty/bx/tests/queue_test.cpp @@ -1,5 +1,5 @@ /* - * Copyright 2010-2018 Branimir Karadzic. All rights reserved. + * Copyright 2010-2019 Branimir Karadzic. All rights reserved. * License: https://github.com/bkaradzic/bx#license-bsd-2-clause */ diff --git a/3rdparty/bx/tests/readerwriter_test.cpp b/3rdparty/bx/tests/readerwriter_test.cpp new file mode 100644 index 00000000000..53fa3f80cd3 --- /dev/null +++ b/3rdparty/bx/tests/readerwriter_test.cpp @@ -0,0 +1,31 @@ +/* + * Copyright 2010-2019 Branimir Karadzic. All rights reserved. + * License: https://github.com/bkaradzic/bx#license-bsd-2-clause + */ + +#include "test.h" +#include <bx/readerwriter.h> + +TEST_CASE("writeLE", "") +{ + bx::SizerWriter writer; + + bx::Error err; + + int32_t total = bx::writeLE(&writer, 1.0f, &err); + + REQUIRE(err.isOk() ); + REQUIRE(total == 4); +} + +TEST_CASE("writeBE", "") +{ + bx::SizerWriter writer; + + bx::Error err; + + int32_t total = bx::writeBE(&writer, 1.0f, &err); + + REQUIRE(err.isOk() ); + REQUIRE(total == 4); +} diff --git a/3rdparty/bx/tests/ringbuffer_test.cpp b/3rdparty/bx/tests/ringbuffer_test.cpp index d0d2a426572..fb78b0862d7 100644 --- a/3rdparty/bx/tests/ringbuffer_test.cpp +++ b/3rdparty/bx/tests/ringbuffer_test.cpp @@ -1,5 +1,5 @@ /* - * Copyright 2010-2018 Branimir Karadzic. All rights reserved. + * Copyright 2010-2019 Branimir Karadzic. All rights reserved. * License: https://github.com/bkaradzic/bx#license-bsd-2-clause */ diff --git a/3rdparty/bx/tests/rng_test.cpp b/3rdparty/bx/tests/rng_test.cpp index b5161f7167e..c6586275e47 100644 --- a/3rdparty/bx/tests/rng_test.cpp +++ b/3rdparty/bx/tests/rng_test.cpp @@ -1,5 +1,5 @@ /* - * Copyright 2010-2018 Branimir Karadzic. All rights reserved. + * Copyright 2010-2019 Branimir Karadzic. All rights reserved. * License: https://github.com/bkaradzic/bx#license-bsd-2-clause */ diff --git a/3rdparty/bx/tests/run_test.cpp b/3rdparty/bx/tests/run_test.cpp index 090c1186c0c..1f7e0509273 100644 --- a/3rdparty/bx/tests/run_test.cpp +++ b/3rdparty/bx/tests/run_test.cpp @@ -1,5 +1,5 @@ /* - * Copyright 2010-2018 Branimir Karadzic. All rights reserved. + * Copyright 2010-2019 Branimir Karadzic. All rights reserved. * License: https://github.com/bkaradzic/bx#license-bsd-2-clause */ @@ -15,6 +15,7 @@ int runAllTests(int _argc, const char* _argv[]) ", CRT: " BX_CRT_NAME ", Date: " __DATE__ ", Time: " __TIME__ + ", C++: " BX_CPP_NAME ); return Catch::Session().run(_argc, _argv); } diff --git a/3rdparty/bx/tests/settings_test.cpp b/3rdparty/bx/tests/settings_test.cpp index 584397ad143..fbf6c9a1112 100644 --- a/3rdparty/bx/tests/settings_test.cpp +++ b/3rdparty/bx/tests/settings_test.cpp @@ -1,5 +1,5 @@ /* - * Copyright 2010-2018 Branimir Karadzic. All rights reserved. + * Copyright 2010-2019 Branimir Karadzic. All rights reserved. * License: https://github.com/bkaradzic/bx#license-bsd-2-clause */ diff --git a/3rdparty/bx/tests/simd_bench.cpp b/3rdparty/bx/tests/simd_bench.cpp index 66e26297aaf..5e77d4190d3 100644 --- a/3rdparty/bx/tests/simd_bench.cpp +++ b/3rdparty/bx/tests/simd_bench.cpp @@ -1,5 +1,5 @@ /* - * Copyright 2010-2018 Branimir Karadzic. All rights reserved. + * Copyright 2010-2019 Branimir Karadzic. All rights reserved. * License: https://github.com/bkaradzic/bx#license-bsd-2-clause */ diff --git a/3rdparty/bx/tests/simd_test.cpp b/3rdparty/bx/tests/simd_test.cpp index 3a211ca1054..8b959c8dffb 100644 --- a/3rdparty/bx/tests/simd_test.cpp +++ b/3rdparty/bx/tests/simd_test.cpp @@ -1,5 +1,5 @@ /* - * Copyright 2010-2018 Branimir Karadzic. All rights reserved. + * Copyright 2010-2019 Branimir Karadzic. All rights reserved. * License: https://github.com/bkaradzic/bx#license-bsd-2-clause */ diff --git a/3rdparty/bx/tests/sort_test.cpp b/3rdparty/bx/tests/sort_test.cpp index e2a5a25449d..a46a80b64f8 100644 --- a/3rdparty/bx/tests/sort_test.cpp +++ b/3rdparty/bx/tests/sort_test.cpp @@ -1,5 +1,5 @@ /* - * Copyright 2010-2018 Branimir Karadzic. All rights reserved. + * Copyright 2010-2019 Branimir Karadzic. All rights reserved. * License: https://github.com/bkaradzic/bx#license-bsd-2-clause */ diff --git a/3rdparty/bx/tests/string_test.cpp b/3rdparty/bx/tests/string_test.cpp index b7c4ed8ab52..c1c4b85f546 100644 --- a/3rdparty/bx/tests/string_test.cpp +++ b/3rdparty/bx/tests/string_test.cpp @@ -1,5 +1,5 @@ /* - * Copyright 2010-2018 Branimir Karadzic. All rights reserved. + * Copyright 2010-2019 Branimir Karadzic. All rights reserved. * License: https://github.com/bkaradzic/bx#license-bsd-2-clause */ @@ -91,6 +91,10 @@ TEST_CASE("strCat", "") TEST_CASE("strCmp", "") { + REQUIRE(0 < bx::strCmp("abvgd", "abv") ); + REQUIRE(0 < bx::strCmp("abvgd", "") ); + REQUIRE(0 > bx::strCmp("", "abvgd") ); + REQUIRE(0 != bx::strCmp(".tar.gz", ".") ); REQUIRE(0 != bx::strCmp("meh", "meh/") ); } @@ -184,6 +188,7 @@ TEST_CASE("strRFind", "") REQUIRE(bx::strRFind(bx::StringView(test, 0), 's').isEmpty() ); REQUIRE(bx::strRFind(bx::StringView(test, 1), 's').isEmpty() ); REQUIRE(&test[2] == bx::strRFind(test, 's').getPtr() ); + REQUIRE(&test[3] == bx::strRFind(test, 't').getPtr() ); } TEST_CASE("strFindI", "") diff --git a/3rdparty/bx/tests/test.h b/3rdparty/bx/tests/test.h index 43da95a56d2..b7654aba1e2 100644 --- a/3rdparty/bx/tests/test.h +++ b/3rdparty/bx/tests/test.h @@ -1,5 +1,5 @@ /* - * Copyright 2010-2018 Branimir Karadzic. All rights reserved. + * Copyright 2010-2019 Branimir Karadzic. All rights reserved. * License: https://github.com/bkaradzic/bx#license-bsd-2-clause */ diff --git a/3rdparty/bx/tests/thread_test.cpp b/3rdparty/bx/tests/thread_test.cpp index 80db9b5b969..a174e9f86d6 100644 --- a/3rdparty/bx/tests/thread_test.cpp +++ b/3rdparty/bx/tests/thread_test.cpp @@ -1,5 +1,5 @@ /* - * Copyright 2010-2018 Branimir Karadzic. All rights reserved. + * Copyright 2010-2019 Branimir Karadzic. All rights reserved. * License: https://github.com/bkaradzic/bx#license-bsd-2-clause */ diff --git a/3rdparty/bx/tests/tokenizecmd_test.cpp b/3rdparty/bx/tests/tokenizecmd_test.cpp index e0910fef087..c453ddaf1d9 100644 --- a/3rdparty/bx/tests/tokenizecmd_test.cpp +++ b/3rdparty/bx/tests/tokenizecmd_test.cpp @@ -1,5 +1,5 @@ /* - * Copyright 2012-2018 Branimir Karadzic. All rights reserved. + * Copyright 2012-2019 Branimir Karadzic. All rights reserved. * License: https://github.com/bkaradzic/bx#license-bsd-2-clause */ diff --git a/3rdparty/bx/tests/uint32_test.cpp b/3rdparty/bx/tests/uint32_test.cpp index bb7e0dccacd..b209219cd56 100644 --- a/3rdparty/bx/tests/uint32_test.cpp +++ b/3rdparty/bx/tests/uint32_test.cpp @@ -1,5 +1,5 @@ /* - * Copyright 2010-2018 Branimir Karadzic. All rights reserved. + * Copyright 2010-2019 Branimir Karadzic. All rights reserved. * License: https://github.com/bkaradzic/bx#license-bsd-2-clause */ @@ -14,28 +14,57 @@ TEST_CASE("StrideAlign") REQUIRE(12 == bx::strideAlign(ii+1, 12) ); } - REQUIRE(0 == bx::strideAlign16(0, 12) ); + REQUIRE(0 == bx::strideAlign<16>(0, 12) ); for (uint32_t ii = 0; ii < 12; ++ii) { - REQUIRE(48 == bx::strideAlign16(ii+1, 12) ); + REQUIRE(48 == bx::strideAlign<16>(ii+1, 12) ); } + + uint32_t offset = 11; + offset = bx::strideAlign(offset, 32); + REQUIRE(offset == 32); + + offset = bx::strideAlign(offset, 24); + REQUIRE(offset == 48); } TEST_CASE("uint32_cnt") { - REQUIRE( 0 == bx::uint32_cnttz(UINT32_C(1) ) ); - - REQUIRE(31 == bx::uint32_cntlz(UINT32_C(1) ) ); - - REQUIRE( 0 == bx::uint64_cnttz(UINT64_C(1) ) ); - - REQUIRE(63 == bx::uint64_cntlz(UINT64_C(1) ) ); - + REQUIRE( 0 == bx::uint32_cnttz<uint8_t >(1) ); + REQUIRE( 7 == bx::uint32_cnttz<uint8_t >(1<<7) ); + REQUIRE( 8 == bx::uint32_cnttz<uint8_t >(0) ); + REQUIRE( 1 == bx::uint32_cnttz<uint8_t >(0x3e) ); + REQUIRE( 0 == bx::uint32_cnttz<uint16_t>(1) ); + REQUIRE(15 == bx::uint32_cnttz<uint16_t>(1<<15) ); + REQUIRE(16 == bx::uint32_cnttz<uint16_t>(0) ); + REQUIRE( 0 == bx::uint32_cnttz<uint32_t>(1) ); + REQUIRE(32 == bx::uint32_cnttz<uint32_t>(0) ); + REQUIRE(31 == bx::uint32_cnttz<uint32_t>(1<<31) ); + REQUIRE( 0 == bx::uint32_cnttz<uint64_t>(1) ); + REQUIRE(64 == bx::uint32_cnttz<uint64_t>(0) ); + + REQUIRE( 7 == bx::uint32_cntlz<uint8_t >(1) ); + REQUIRE( 8 == bx::uint32_cntlz<uint8_t >(0) ); + REQUIRE( 2 == bx::uint32_cntlz<uint8_t >(0x3e) ); + REQUIRE(15 == bx::uint32_cntlz<uint16_t>(1) ); + REQUIRE(16 == bx::uint32_cntlz<uint16_t>(0) ); + REQUIRE(31 == bx::uint32_cntlz<uint32_t>(1) ); + REQUIRE(32 == bx::uint32_cntlz<uint32_t>(0) ); + REQUIRE(63 == bx::uint32_cntlz<uint64_t>(1) ); + REQUIRE(64 == bx::uint32_cntlz<uint64_t>(0) ); + + REQUIRE( 0 == bx::uint32_cntbits(0) ); REQUIRE( 1 == bx::uint32_cntbits(1) ); - REQUIRE(16 == bx::uint32_cntbits(UINT16_MAX) ); + REQUIRE( 4 == bx::uint32_cntbits<uint8_t>(0x55) ); + REQUIRE( 8 == bx::uint32_cntbits<uint16_t>(0x5555) ); + REQUIRE(16 == bx::uint32_cntbits<uint32_t>(0x55555555) ); + REQUIRE(32 == bx::uint32_cntbits<uint64_t>(0x5555555555555555) ); + REQUIRE( 8 == bx::uint32_cntbits(UINT8_MAX) ); + REQUIRE(16 == bx::uint32_cntbits(UINT16_MAX) ); REQUIRE(32 == bx::uint32_cntbits(UINT32_MAX) ); + REQUIRE(64 == bx::uint32_cntbits(UINT64_MAX) ); } TEST_CASE("uint32_part") diff --git a/3rdparty/bx/tests/url_test.cpp b/3rdparty/bx/tests/url_test.cpp index 50d2c3924d5..b43488c157e 100644 --- a/3rdparty/bx/tests/url_test.cpp +++ b/3rdparty/bx/tests/url_test.cpp @@ -1,5 +1,5 @@ /* - * Copyright 2010-2018 Branimir Karadzic. All rights reserved. + * Copyright 2010-2019 Branimir Karadzic. All rights reserved. * License: https://github.com/bkaradzic/bx#license-bsd-2-clause */ diff --git a/3rdparty/bx/tests/vsnprintf_test.cpp b/3rdparty/bx/tests/vsnprintf_test.cpp index 844f6363ce2..50bf5449ff7 100644 --- a/3rdparty/bx/tests/vsnprintf_test.cpp +++ b/3rdparty/bx/tests/vsnprintf_test.cpp @@ -1,5 +1,5 @@ /* - * Copyright 2010-2018 Branimir Karadzic. All rights reserved. + * Copyright 2010-2019 Branimir Karadzic. All rights reserved. * License: https://github.com/bkaradzic/bx#license-bsd-2-clause */ diff --git a/3rdparty/bx/tools/bin/darwin/bin2c b/3rdparty/bx/tools/bin/darwin/bin2c Binary files differindex cb1acca8021..7aa45d00061 100644 --- a/3rdparty/bx/tools/bin/darwin/bin2c +++ b/3rdparty/bx/tools/bin/darwin/bin2c diff --git a/3rdparty/bx/tools/bin/darwin/genie b/3rdparty/bx/tools/bin/darwin/genie Binary files differindex fd3624037ce..7ebe0a3cf11 100755 --- a/3rdparty/bx/tools/bin/darwin/genie +++ b/3rdparty/bx/tools/bin/darwin/genie diff --git a/3rdparty/bx/tools/bin/darwin/lemon b/3rdparty/bx/tools/bin/darwin/lemon Binary files differindex f8cfcebbc1b..c00a276b405 100644 --- a/3rdparty/bx/tools/bin/darwin/lemon +++ b/3rdparty/bx/tools/bin/darwin/lemon diff --git a/3rdparty/bx/tools/bin/linux/bin2c b/3rdparty/bx/tools/bin/linux/bin2c Binary files differindex ca595d9f82a..d825698d97a 100644 --- a/3rdparty/bx/tools/bin/linux/bin2c +++ b/3rdparty/bx/tools/bin/linux/bin2c diff --git a/3rdparty/bx/tools/bin/linux/genie b/3rdparty/bx/tools/bin/linux/genie Binary files differindex 6c662eada50..f61c3ee0039 100755 --- a/3rdparty/bx/tools/bin/linux/genie +++ b/3rdparty/bx/tools/bin/linux/genie diff --git a/3rdparty/bx/tools/bin/linux/lemon b/3rdparty/bx/tools/bin/linux/lemon Binary files differindex 64d24ce47ab..79e1aa9213d 100644 --- a/3rdparty/bx/tools/bin/linux/lemon +++ b/3rdparty/bx/tools/bin/linux/lemon diff --git a/3rdparty/bx/tools/bin/windows/bin2c.exe b/3rdparty/bx/tools/bin/windows/bin2c.exe Binary files differindex 2c01cdd8065..d93eff18e60 100644 --- a/3rdparty/bx/tools/bin/windows/bin2c.exe +++ b/3rdparty/bx/tools/bin/windows/bin2c.exe diff --git a/3rdparty/bx/tools/bin/windows/genie.exe b/3rdparty/bx/tools/bin/windows/genie.exe Binary files differindex 4b0426c3ddb..c8cecc1559c 100644 --- a/3rdparty/bx/tools/bin/windows/genie.exe +++ b/3rdparty/bx/tools/bin/windows/genie.exe diff --git a/3rdparty/bx/tools/bin/windows/lemon.exe b/3rdparty/bx/tools/bin/windows/lemon.exe Binary files differindex 5de0c8d6fda..ea46fecf996 100644 --- a/3rdparty/bx/tools/bin/windows/lemon.exe +++ b/3rdparty/bx/tools/bin/windows/lemon.exe diff --git a/3rdparty/bx/tools/bin2c/bin2c.cpp b/3rdparty/bx/tools/bin2c/bin2c.cpp index 2c7f48d1de0..afc64385be9 100644 --- a/3rdparty/bx/tools/bin2c/bin2c.cpp +++ b/3rdparty/bx/tools/bin2c/bin2c.cpp @@ -1,5 +1,5 @@ /* - * Copyright 2011-2018 Branimir Karadzic. All rights reserved. + * Copyright 2011-2019 Branimir Karadzic. All rights reserved. * License: https://github.com/bkaradzic/bx#license-bsd-2-clause */ @@ -8,6 +8,8 @@ #include <bx/file.h> #include <bx/string.h> +#include <bx/debug.h> + class Bin2cWriter : public bx::WriterI { public: @@ -15,6 +17,7 @@ public: : m_mb(_allocator) , m_mw(&m_mb) , m_name(_name) + , m_outputAsCStr(false) { } @@ -24,10 +27,88 @@ public: virtual int32_t write(const void* _data, int32_t _size, bx::Error* _err) override { + m_outputAsCStr = true; + + const char* data = (const char*)_data; + for (int32_t ii = 0; ii < _size; ++ii) + { + char ch = data[ii]; + if (!bx::isPrint(ch) + && !bx::isSpace(ch) ) + { + m_outputAsCStr = false; + break; + } + } + return bx::write(&m_mw, _data, _size, _err); } - void output(bx::WriterI* m_writer) + void output(bx::WriterI* _writer) + { + if (m_outputAsCStr) + { + outputString(_writer); + } + else + { + outputHex(_writer); + } + } + + void outputString(bx::WriterI* _writer) + { + const char* data = (const char*)m_mb.more(0); + uint32_t size = uint32_t(bx::seek(&m_mw) ); + + bx::Error err; + + bx::write( + _writer + , &err + , "static const char* %.*s = /* Generated with bin2c. */\n\t\"" + , m_name.getLength() + , m_name.getPtr() + ); + + if (NULL != data) + { + bool escaped = false; + + for (uint32_t ii = 0; ii < size; ++ii) + { + char ch = data[ii]; + + if (!escaped) + { + switch (ch) + { + case '\"': bx::write(_writer, "\\\"", &err); break; + case '\n': bx::write(_writer, "\\n\"\n\t\"", &err); break; + case '\r': bx::write(_writer, "\\r", &err); break; + case '\\': escaped = true; BX_FALLTHROUGH; + default: bx::write(_writer, ch, &err); break; + } + } + else + { + switch (ch) + { + case '\n': bx::write(_writer, "\\\"\n\t\"", &err); break; + case '\r': BX_FALLTHROUGH; + case '\t': bx::write(_writer, "\\", &err); BX_FALLTHROUGH; + default : bx::write(_writer, ch, &err); break; + } + + escaped = false; + } + } + } + + bx::write(_writer, &err, "\"\n\t;\n"); + } + + void outputHex(bx::WriterI* _writer) { #define HEX_DUMP_WIDTH 16 #define HEX_DUMP_SPACE_WIDTH 96 @@ -38,9 +119,9 @@ public: bx::Error err; bx::write( - m_writer + _writer , &err - , "static const uint8_t %.*s[%d] =\n{\n" + , "static const uint8_t %.*s[%d] = /* Generated with bin2c. */\n{\n" , m_name.getLength() , m_name.getPtr() , size @@ -63,7 +144,7 @@ public: if (HEX_DUMP_WIDTH == asciiPos) { ascii[asciiPos] = '\0'; - bx::write(m_writer, &err, "\t" HEX_DUMP_FORMAT "// %s\n", hex, ascii); + bx::write(_writer, &err, "\t" HEX_DUMP_FORMAT "// %s\n", hex, ascii); data += asciiPos; hexPos = 0; asciiPos = 0; @@ -73,11 +154,11 @@ public: if (0 != asciiPos) { ascii[asciiPos] = '\0'; - bx::write(m_writer, &err, "\t" HEX_DUMP_FORMAT "// %s\n", hex, ascii); + bx::write(_writer, &err, "\t" HEX_DUMP_FORMAT "// %s\n", hex, ascii); } } - bx::write(m_writer, &err, "};\n"); + bx::write(_writer, &err, "};\n"); #undef HEX_DUMP_WIDTH #undef HEX_DUMP_SPACE_WIDTH #undef HEX_DUMP_FORMAT @@ -86,6 +167,7 @@ public: bx::MemoryBlock m_mb; bx::MemoryWriter m_mw; bx::StringView m_name; + bool m_outputAsCStr; }; void help(const char* _error = NULL) @@ -100,7 +182,7 @@ void help(const char* _error = NULL) bx::write(stdOut, &err , "bin2c, binary to C\n" - "Copyright 2011-2018 Branimir Karadzic. All rights reserved.\n" + "Copyright 2011-2019 Branimir Karadzic. All rights reserved.\n" "License: https://github.com/bkaradzic/bx#license-bsd-2-clause\n\n" ); |