mirror of
https://github.com/catchorg/Catch2.git
synced 2025-09-12 16:35:40 +02:00
Compare commits
141 Commits
Author | SHA1 | Date | |
---|---|---|---|
![]() |
f9dce28e7d | ||
![]() |
b87caafd91 | ||
![]() |
bbbd5c4e08 | ||
![]() |
f41051f22a | ||
![]() |
e90d5a86e4 | ||
![]() |
dbc1295354 | ||
![]() |
f2cfc2b852 | ||
![]() |
c365ac392b | ||
![]() |
e640c3837a | ||
![]() |
b468d7cbff | ||
![]() |
7142d5a8c9 | ||
![]() |
1967feac49 | ||
![]() |
f0b7b0ca11 | ||
![]() |
4b1252547c | ||
![]() |
10067a47da | ||
![]() |
e340ab8db6 | ||
![]() |
ce2560ca95 | ||
![]() |
00347f1e79 | ||
![]() |
a5a2d08fbb | ||
![]() |
97602b248b | ||
![]() |
e28e162795 | ||
![]() |
90378f4a59 | ||
![]() |
84f8e806b8 | ||
![]() |
732e4b06db | ||
![]() |
0c43f98fa2 | ||
![]() |
bd703dd74b | ||
![]() |
99602787cd | ||
![]() |
bfb4ee1597 | ||
![]() |
31537c43d9 | ||
![]() |
96355da34e | ||
![]() |
71fce429af | ||
![]() |
d13e094598 | ||
![]() |
d30f1dda02 | ||
![]() |
3bce8ba14b | ||
![]() |
e680c4b9fb | ||
![]() |
f1e14a1168 | ||
![]() |
92ad9ee355 | ||
![]() |
e2862a8d71 | ||
![]() |
1161011dd0 | ||
![]() |
53a83e855e | ||
![]() |
9c741fe960 | ||
![]() |
979bbf03bb | ||
![]() |
33ce3f3953 | ||
![]() |
87a9424c9d | ||
![]() |
00cb0035c9 | ||
![]() |
6267b06089 | ||
![]() |
9837c35df1 | ||
![]() |
46066ede17 | ||
![]() |
6981783178 | ||
![]() |
08c8df1e3b | ||
![]() |
daeb5a87e6 | ||
![]() |
f2ee4f17ad | ||
![]() |
182fc3e46e | ||
![]() |
6b5b72651d | ||
![]() |
f45bb00351 | ||
![]() |
7c37501b07 | ||
![]() |
4a1ca1ab55 | ||
![]() |
e02d9e788f | ||
![]() |
541f1ed1b3 | ||
![]() |
346723c9b6 | ||
![]() |
5a74fcc9c9 | ||
![]() |
9d5d719868 | ||
![]() |
91b617c462 | ||
![]() |
45e552528d | ||
![]() |
3978e9653b | ||
![]() |
d6fce7bf34 | ||
![]() |
c3c82f539c | ||
![]() |
c7653811a6 | ||
![]() |
79417b9afc | ||
![]() |
11cdd72db9 | ||
![]() |
0c39409da7 | ||
![]() |
edfac75347 | ||
![]() |
ac94bd0520 | ||
![]() |
d4eec016a9 | ||
![]() |
36fb856163 | ||
![]() |
4e32e0a563 | ||
![]() |
1e2270b370 | ||
![]() |
5096e39297 | ||
![]() |
15ccced6da | ||
![]() |
682617b5b7 | ||
![]() |
15150c7b46 | ||
![]() |
5ce355a38c | ||
![]() |
edde6f4736 | ||
![]() |
6bc5d172ee | ||
![]() |
3079b514d4 | ||
![]() |
e99f1efd28 | ||
![]() |
b9dd1936e5 | ||
![]() |
293d617c49 | ||
![]() |
7be35af167 | ||
![]() |
02f13cf95a | ||
![]() |
43428c6093 | ||
![]() |
08147a23f9 | ||
![]() |
8af8704089 | ||
![]() |
3816e99d0c | ||
![]() |
b77cec05c0 | ||
![]() |
54089c4c8c | ||
![]() |
296d447452 | ||
![]() |
0531965349 | ||
![]() |
a1cdff4f18 | ||
![]() |
4611125801 | ||
![]() |
e509012e64 | ||
![]() |
448825db03 | ||
![]() |
0fff8e7791 | ||
![]() |
68a3c129ac | ||
![]() |
1ce5ec9b74 | ||
![]() |
37a4e32319 | ||
![]() |
0424c9a62c | ||
![]() |
d633072794 | ||
![]() |
51ed08be22 | ||
![]() |
1701325caa | ||
![]() |
7aee973a4a | ||
![]() |
99575b45db | ||
![]() |
1a03918455 | ||
![]() |
bd667f4d69 | ||
![]() |
28db5ed4c9 | ||
![]() |
7d2451f119 | ||
![]() |
5bf6e47381 | ||
![]() |
29b3b7ae6b | ||
![]() |
ef5fd8d42f | ||
![]() |
693647c43f | ||
![]() |
288387fa10 | ||
![]() |
165de9b072 | ||
![]() |
bf4771a7ed | ||
![]() |
7012a31a39 | ||
![]() |
269303d9d9 | ||
![]() |
e8bfd882e8 | ||
![]() |
2bd0722470 | ||
![]() |
45ebf17ec7 | ||
![]() |
093b72416d | ||
![]() |
c99a346490 | ||
![]() |
359a54b6bd | ||
![]() |
711d750ca7 | ||
![]() |
95f7712808 | ||
![]() |
dbbab8727c | ||
![]() |
5d4061af12 | ||
![]() |
9ccea82d7f | ||
![]() |
dd3d27de57 | ||
![]() |
7f229b4ff1 | ||
![]() |
c03b23c84b | ||
![]() |
17686ba571 | ||
![]() |
5347ff9e5f |
@@ -47,7 +47,7 @@ class BuilderSettings(object):
|
||||
|
||||
@property
|
||||
def reference(self):
|
||||
""" Read project version from branch create Conan referece
|
||||
""" Read project version from branch create Conan reference
|
||||
"""
|
||||
return os.getenv("CONAN_REFERENCE", "Catch2/{}".format(self._version))
|
||||
|
||||
|
1
.github/FUNDING.yml
vendored
Normal file
1
.github/FUNDING.yml
vendored
Normal file
@@ -0,0 +1 @@
|
||||
patreon: horenmar
|
2
.github/pull_request_template.md
vendored
2
.github/pull_request_template.md
vendored
@@ -12,7 +12,7 @@ at docs/contributing.md. It will tell you how to properly test your changes.
|
||||
<!--
|
||||
Describe the what and the why of your pull request. Remember that these two
|
||||
are usually a bit different. As an example, if you have made various changes
|
||||
to decrease the number of new strings allocated, thats what. The why probably
|
||||
to decrease the number of new strings allocated, that's what. The why probably
|
||||
was that you have a large set of tests and found that this speeds them up.
|
||||
-->
|
||||
|
||||
|
13
.travis.yml
13
.travis.yml
@@ -301,10 +301,19 @@ before_script:
|
||||
# Regenerate single header file, so it is tested in the examples...
|
||||
- python scripts/generateSingleHeader.py
|
||||
|
||||
- |
|
||||
if [[ ${CPP17} -eq 1 ]]; then
|
||||
export CPP_STANDARD=17
|
||||
elif [[ ${CPP14} -eq 1 ]]; then
|
||||
export CPP_STANDARD=14
|
||||
else
|
||||
export CPP_STANDARD=11
|
||||
fi
|
||||
|
||||
# Use Debug builds for running Valgrind and building examples
|
||||
- cmake -H. -BBuild-Debug -DCMAKE_BUILD_TYPE=Debug -Wdev -DUSE_CPP14=${CPP14} -DUSE_CPP17=${CPP17} -DCATCH_USE_VALGRIND=${VALGRIND} -DCATCH_BUILD_EXAMPLES=${EXAMPLES} -DCATCH_ENABLE_COVERAGE=${COVERAGE} -DCATCH_BUILD_EXTRA_TESTS=${EXTRAS}
|
||||
- cmake -H. -BBuild-Debug -DCMAKE_BUILD_TYPE=Debug -Wdev -DCATCH_USE_VALGRIND=${VALGRIND} -DCATCH_BUILD_EXAMPLES=${EXAMPLES} -DCATCH_ENABLE_COVERAGE=${COVERAGE} -DCATCH_BUILD_EXTRA_TESTS=${EXTRAS} -DCMAKE_CXX_STANDARD=${CPP_STANDARD} -DCMAKE_CXX_STANDARD_REQUIRED=On -DCMAKE_CXX_EXTENSIONS=OFF
|
||||
# Don't bother with release build for coverage build
|
||||
- cmake -H. -BBuild-Release -DCMAKE_BUILD_TYPE=Release -Wdev -DUSE_CPP14=${CPP14} -DUSE_CPP17=${CPP17}
|
||||
- cmake -H. -BBuild-Release -DCMAKE_BUILD_TYPE=Release -Wdev -DCMAKE_CXX_STANDARD=${CPP_STANDARD} -DCMAKE_CXX_STANDARD_REQUIRED=On -DCMAKE_CXX_EXTENSIONS=OFF
|
||||
|
||||
|
||||
script:
|
||||
|
@@ -19,7 +19,7 @@ set(CMAKE_REQUIRED_QUIET ${codecov_FIND_QUIETLY})
|
||||
|
||||
get_property(ENABLED_LANGUAGES GLOBAL PROPERTY ENABLED_LANGUAGES)
|
||||
foreach (LANG ${ENABLED_LANGUAGES})
|
||||
# Gcov evaluation is dependend on the used compiler. Check gcov support for
|
||||
# Gcov evaluation is dependent on the used compiler. Check gcov support for
|
||||
# each compiler that is used. If gcov binary was already found for this
|
||||
# compiler, do not try to find it again.
|
||||
if (NOT GCOV_${CMAKE_${LANG}_COMPILER_ID}_BIN)
|
||||
|
@@ -74,7 +74,7 @@ set(CMAKE_REQUIRED_QUIET ${codecov_FIND_QUIETLY})
|
||||
|
||||
get_property(ENABLED_LANGUAGES GLOBAL PROPERTY ENABLED_LANGUAGES)
|
||||
foreach (LANG ${ENABLED_LANGUAGES})
|
||||
# Coverage flags are not dependend on language, but the used compiler. So
|
||||
# Coverage flags are not dependent on language, but the used compiler. So
|
||||
# instead of searching flags foreach language, search flags foreach compiler
|
||||
# used.
|
||||
set(COMPILER ${CMAKE_${LANG}_COMPILER_ID})
|
||||
|
@@ -6,7 +6,11 @@ if(NOT DEFINED PROJECT_NAME)
|
||||
set(NOT_SUBPROJECT ON)
|
||||
endif()
|
||||
|
||||
project(Catch2 LANGUAGES CXX VERSION 2.6.0)
|
||||
project(Catch2 LANGUAGES CXX VERSION 2.9.0)
|
||||
|
||||
if (CMAKE_BINARY_DIR STREQUAL CMAKE_SOURCE_DIR)
|
||||
message(FATAL_ERROR "Building in-source is not supported! Create a build dir and remove ${CMAKE_SOURCE_DIR}/CMakeCache.txt")
|
||||
endif()
|
||||
|
||||
# Provide path for scripts
|
||||
list(APPEND CMAKE_MODULE_PATH "${CMAKE_CURRENT_LIST_DIR}/CMake")
|
||||
@@ -205,4 +209,15 @@ if (NOT_SUBPROJECT)
|
||||
${PKGCONFIG_INSTALL_DIR}
|
||||
)
|
||||
|
||||
# CPack/CMake started taking the package version from project version 3.12
|
||||
# So we need to set the version manually for older CMake versions
|
||||
if(${CMAKE_VERSION} VERSION_LESS "3.12.0")
|
||||
set(CPACK_PACKAGE_VERSION ${PROJECT_VERSION})
|
||||
endif()
|
||||
|
||||
set(CPACK_PACKAGE_CONTACT "https://github.com/catchorg/Catch2/")
|
||||
|
||||
|
||||
include( CPack )
|
||||
|
||||
endif(NOT_SUBPROJECT)
|
||||
|
@@ -5,17 +5,17 @@
|
||||
[](https://travis-ci.org/catchorg/Catch2)
|
||||
[](https://ci.appveyor.com/project/catchorg/catch2)
|
||||
[](https://codecov.io/gh/catchorg/Catch2)
|
||||
[](https://wandbox.org/permlink/85qcYBOdUKzlnpl5)
|
||||
[](https://wandbox.org/permlink/HbqvzsGh9s9aubN3)
|
||||
[](https://discord.gg/4CWS9zD)
|
||||
|
||||
|
||||
<a href="https://github.com/catchorg/Catch2/releases/download/v2.6.0/catch.hpp">The latest version of the single header can be downloaded directly using this link</a>
|
||||
<a href="https://github.com/catchorg/Catch2/releases/download/v2.9.0/catch.hpp">The latest version of the single header can be downloaded directly using this link</a>
|
||||
|
||||
## Catch2 is released!
|
||||
|
||||
If you've been using an earlier version of Catch, please see the
|
||||
Breaking Changes section of [the release notes](https://github.com/catchorg/Catch2/releases/tag/v2.0.1)
|
||||
before moving to Catch2. You might also like to read [this blog post](http://www.levelofindirection.com/journal/2017/11/3/catch2-released.html) for more details.
|
||||
before moving to Catch2. You might also like to read [this blog post](https://levelofindirection.com/blog/catch2-released.html) for more details.
|
||||
|
||||
## What's the Catch?
|
||||
|
||||
|
@@ -18,7 +18,7 @@ class CatchConan(ConanFile):
|
||||
cmake.definitions["BUILD_TESTING"] = "OFF"
|
||||
cmake.definitions["CATCH_INSTALL_DOCS"] = "OFF"
|
||||
cmake.definitions["CATCH_INSTALL_HELPERS"] = "ON"
|
||||
cmake.configure()
|
||||
cmake.configure(build_folder='build')
|
||||
cmake.install()
|
||||
|
||||
self.copy(pattern="LICENSE.txt", dst="licenses")
|
||||
|
@@ -22,6 +22,39 @@ function(add_command NAME)
|
||||
set(script "${script}${NAME}(${_args})\n" PARENT_SCOPE)
|
||||
endfunction()
|
||||
|
||||
macro(_add_catch_test_labels LINE)
|
||||
# convert to list of tags
|
||||
string(REPLACE "][" "]\\;[" tags ${line})
|
||||
|
||||
add_command(
|
||||
set_tests_properties "${prefix}${test}${suffix}"
|
||||
PROPERTIES
|
||||
LABELS "${tags}"
|
||||
)
|
||||
endmacro()
|
||||
|
||||
macro(_add_catch_test LINE)
|
||||
set(test ${line})
|
||||
# use escape commas to handle properly test cases with commans inside the name
|
||||
string(REPLACE "," "\\," test_name ${test})
|
||||
# ...and add to script
|
||||
add_command(
|
||||
add_test "${prefix}${test}${suffix}"
|
||||
${TEST_EXECUTOR}
|
||||
"${TEST_EXECUTABLE}"
|
||||
"${test_name}"
|
||||
${extra_args}
|
||||
)
|
||||
|
||||
add_command(
|
||||
set_tests_properties "${prefix}${test}${suffix}"
|
||||
PROPERTIES
|
||||
WORKING_DIRECTORY "${TEST_WORKING_DIR}"
|
||||
${properties}
|
||||
)
|
||||
list(APPEND tests "${prefix}${test}${suffix}")
|
||||
endmacro()
|
||||
|
||||
# Run test executable to get list of available tests
|
||||
if(NOT EXISTS "${TEST_EXECUTABLE}")
|
||||
message(FATAL_ERROR
|
||||
@@ -29,7 +62,7 @@ if(NOT EXISTS "${TEST_EXECUTABLE}")
|
||||
)
|
||||
endif()
|
||||
execute_process(
|
||||
COMMAND ${TEST_EXECUTOR} "${TEST_EXECUTABLE}" ${spec} --list-test-names-only
|
||||
COMMAND ${TEST_EXECUTOR} "${TEST_EXECUTABLE}" ${spec} --list-tests
|
||||
OUTPUT_VARIABLE output
|
||||
RESULT_VARIABLE result
|
||||
)
|
||||
@@ -47,27 +80,22 @@ elseif(${result} LESS 0)
|
||||
endif()
|
||||
|
||||
string(REPLACE "\n" ";" output "${output}")
|
||||
set(test)
|
||||
set(tags_regex "(\\[([^\\[]*)\\])+$")
|
||||
|
||||
# Parse output
|
||||
foreach(line ${output})
|
||||
set(test ${line})
|
||||
# use escape commas to handle properly test cases with commans inside the name
|
||||
string(REPLACE "," "\\," test_name ${test})
|
||||
# ...and add to script
|
||||
add_command(add_test
|
||||
"${prefix}${test}${suffix}"
|
||||
${TEST_EXECUTOR}
|
||||
"${TEST_EXECUTABLE}"
|
||||
"${test_name}"
|
||||
${extra_args}
|
||||
)
|
||||
add_command(set_tests_properties
|
||||
"${prefix}${test}${suffix}"
|
||||
PROPERTIES
|
||||
WORKING_DIRECTORY "${TEST_WORKING_DIR}"
|
||||
${properties}
|
||||
)
|
||||
list(APPEND tests "${prefix}${test}${suffix}")
|
||||
# lines without leading whitespaces are catch output not tests
|
||||
if(${line} MATCHES "^[ \t]+")
|
||||
# strip leading spaces and tabs
|
||||
string(REGEX REPLACE "^[ \t]+" "" line ${line})
|
||||
|
||||
if(${line} MATCHES "${tags_regex}")
|
||||
_add_catch_test_labels(${line})
|
||||
else()
|
||||
_add_catch_test(${line})
|
||||
endif()
|
||||
endif()
|
||||
endforeach()
|
||||
|
||||
# Create a list of all discovered tests, which users may use to e.g. set
|
||||
|
@@ -44,9 +44,19 @@
|
||||
# set(OptionalCatchTestLauncher ${MPIEXEC} ${MPIEXEC_NUMPROC_FLAG} ${NUMPROC}) #
|
||||
# just before calling this ParseAndAddCatchTests function #
|
||||
# #
|
||||
# The AdditionalCatchParameters optional variable can be used to pass extra argument to the test #
|
||||
# command. For example, to include successful tests in the output, one can write #
|
||||
# set(AdditionalCatchParameters --success) #
|
||||
# #
|
||||
# After the script, the ParseAndAddCatchTests_TESTS property for the target, and for each source #
|
||||
# file in the target is set, and contains the list of the tests extracted from that target, or #
|
||||
# from that file. This is useful, for example to add further labels or properties to the tests. #
|
||||
# #
|
||||
#==================================================================================================#
|
||||
|
||||
cmake_minimum_required(VERSION 2.8.8)
|
||||
if (CMAKE_MINIMUM_REQUIRED_VERSION VERSION_LESS 2.8.8)
|
||||
message(FATAL_ERROR "ParseAndAddCatchTests requires CMake 2.8.8 or newer")
|
||||
endif()
|
||||
|
||||
option(PARSE_CATCH_TESTS_VERBOSE "Print Catch to CTest parser debug messages" OFF)
|
||||
option(PARSE_CATCH_TESTS_NO_HIDDEN_TESTS "Exclude tests with [!hide], [.] or [.foo] tags" OFF)
|
||||
@@ -54,7 +64,7 @@ option(PARSE_CATCH_TESTS_ADD_FIXTURE_IN_TEST_NAME "Add fixture class name to the
|
||||
option(PARSE_CATCH_TESTS_ADD_TARGET_IN_TEST_NAME "Add target name to the test name" ON)
|
||||
option(PARSE_CATCH_TESTS_ADD_TO_CONFIGURE_DEPENDS "Add test file to CMAKE_CONFIGURE_DEPENDS property" OFF)
|
||||
|
||||
function(PrintDebugMessage)
|
||||
function(ParseAndAddCatchTests_PrintDebugMessage)
|
||||
if(PARSE_CATCH_TESTS_VERBOSE)
|
||||
message(STATUS "ParseAndAddCatchTests: ${ARGV}")
|
||||
endif()
|
||||
@@ -65,7 +75,7 @@ endfunction()
|
||||
# - full line comments (i.e. // ... )
|
||||
# contents have been read into '${CppCode}'.
|
||||
# !keep partial line comments
|
||||
function(RemoveComments CppCode)
|
||||
function(ParseAndAddCatchTests_RemoveComments CppCode)
|
||||
string(ASCII 2 CMakeBeginBlockComment)
|
||||
string(ASCII 3 CMakeEndBlockComment)
|
||||
string(REGEX REPLACE "/\\*" "${CMakeBeginBlockComment}" ${CppCode} "${${CppCode}}")
|
||||
@@ -77,24 +87,29 @@ function(RemoveComments CppCode)
|
||||
endfunction()
|
||||
|
||||
# Worker function
|
||||
function(ParseFile SourceFile TestTarget)
|
||||
function(ParseAndAddCatchTests_ParseFile SourceFile TestTarget)
|
||||
# If SourceFile is an object library, do not scan it (as it is not a file). Exit without giving a warning about a missing file.
|
||||
if(SourceFile MATCHES "\\\$<TARGET_OBJECTS:.+>")
|
||||
ParseAndAddCatchTests_PrintDebugMessage("Detected OBJECT library: ${SourceFile} this will not be scanned for tests.")
|
||||
return()
|
||||
endif()
|
||||
# According to CMake docs EXISTS behavior is well-defined only for full paths.
|
||||
get_filename_component(SourceFile ${SourceFile} ABSOLUTE)
|
||||
if(NOT EXISTS ${SourceFile})
|
||||
message(WARNING "Cannot find source file: ${SourceFile}")
|
||||
return()
|
||||
endif()
|
||||
PrintDebugMessage("parsing ${SourceFile}")
|
||||
ParseAndAddCatchTests_PrintDebugMessage("parsing ${SourceFile}")
|
||||
file(STRINGS ${SourceFile} Contents NEWLINE_CONSUME)
|
||||
|
||||
# Remove block and fullline comments
|
||||
RemoveComments(Contents)
|
||||
ParseAndAddCatchTests_RemoveComments(Contents)
|
||||
|
||||
# Find definition of test names
|
||||
string(REGEX MATCHALL "[ \t]*(CATCH_)?(TEST_CASE_METHOD|SCENARIO|TEST_CASE)[ \t]*\\([^\)]+\\)+[ \t\n]*{+[ \t]*(//[^\n]*[Tt][Ii][Mm][Ee][Oo][Uu][Tt][ \t]*[0-9]+)*" Tests "${Contents}")
|
||||
|
||||
if(PARSE_CATCH_TESTS_ADD_TO_CONFIGURE_DEPENDS AND Tests)
|
||||
PrintDebugMessage("Adding ${SourceFile} to CMAKE_CONFIGURE_DEPENDS property")
|
||||
ParseAndAddCatchTests_PrintDebugMessage("Adding ${SourceFile} to CMAKE_CONFIGURE_DEPENDS property")
|
||||
set_property(
|
||||
DIRECTORY
|
||||
APPEND
|
||||
@@ -155,7 +170,6 @@ function(ParseFile SourceFile TestTarget)
|
||||
|
||||
list(APPEND Labels ${Tags})
|
||||
|
||||
list(FIND Labels "!hide" IndexOfHideLabel)
|
||||
set(HiddenTagFound OFF)
|
||||
foreach(label ${Labels})
|
||||
string(REGEX MATCH "^!hide|^\\." result ${label})
|
||||
@@ -165,23 +179,34 @@ function(ParseFile SourceFile TestTarget)
|
||||
endif(result)
|
||||
endforeach(label)
|
||||
if(PARSE_CATCH_TESTS_NO_HIDDEN_TESTS AND ${HiddenTagFound} AND ${CMAKE_VERSION} VERSION_LESS "3.9")
|
||||
PrintDebugMessage("Skipping test \"${CTestName}\" as it has [!hide], [.] or [.foo] label")
|
||||
ParseAndAddCatchTests_PrintDebugMessage("Skipping test \"${CTestName}\" as it has [!hide], [.] or [.foo] label")
|
||||
else()
|
||||
PrintDebugMessage("Adding test \"${CTestName}\"")
|
||||
ParseAndAddCatchTests_PrintDebugMessage("Adding test \"${CTestName}\"")
|
||||
if(Labels)
|
||||
PrintDebugMessage("Setting labels to ${Labels}")
|
||||
ParseAndAddCatchTests_PrintDebugMessage("Setting labels to ${Labels}")
|
||||
endif()
|
||||
|
||||
# Escape commas in the test spec
|
||||
string(REPLACE "," "\\," Name ${Name})
|
||||
|
||||
# Add the test and set its properties
|
||||
add_test(NAME "\"${CTestName}\"" COMMAND ${OptionalCatchTestLauncher} ${TestTarget} ${Name} ${AdditionalCatchParameters})
|
||||
add_test(NAME "\"${CTestName}\"" COMMAND ${OptionalCatchTestLauncher} $<TARGET_FILE:${TestTarget}> ${Name} ${AdditionalCatchParameters})
|
||||
# Old CMake versions do not document VERSION_GREATER_EQUAL, so we use VERSION_GREATER with 3.8 instead
|
||||
if(PARSE_CATCH_TESTS_NO_HIDDEN_TESTS AND ${HiddenTagFound} AND ${CMAKE_VERSION} VERSION_GREATER "3.8")
|
||||
PrintDebugMessage("Setting DISABLED test property")
|
||||
ParseAndAddCatchTests_PrintDebugMessage("Setting DISABLED test property")
|
||||
set_tests_properties("\"${CTestName}\"" PROPERTIES DISABLED ON)
|
||||
else()
|
||||
set_tests_properties("\"${CTestName}\"" PROPERTIES FAIL_REGULAR_EXPRESSION "No tests ran"
|
||||
LABELS "${Labels}")
|
||||
endif()
|
||||
set_property(
|
||||
TARGET ${TestTarget}
|
||||
APPEND
|
||||
PROPERTY ParseAndAddCatchTests_TESTS "\"${CTestName}\"")
|
||||
set_property(
|
||||
SOURCE ${SourceFile}
|
||||
APPEND
|
||||
PROPERTY ParseAndAddCatchTests_TESTS "\"${CTestName}\"")
|
||||
endif()
|
||||
|
||||
|
||||
@@ -190,11 +215,11 @@ endfunction()
|
||||
|
||||
# entry point
|
||||
function(ParseAndAddCatchTests TestTarget)
|
||||
PrintDebugMessage("Started parsing ${TestTarget}")
|
||||
ParseAndAddCatchTests_PrintDebugMessage("Started parsing ${TestTarget}")
|
||||
get_target_property(SourceFiles ${TestTarget} SOURCES)
|
||||
PrintDebugMessage("Found the following sources: ${SourceFiles}")
|
||||
ParseAndAddCatchTests_PrintDebugMessage("Found the following sources: ${SourceFiles}")
|
||||
foreach(SourceFile ${SourceFiles})
|
||||
ParseFile(${SourceFile} ${TestTarget})
|
||||
ParseAndAddCatchTests_ParseFile(${SourceFile} ${TestTarget})
|
||||
endforeach()
|
||||
PrintDebugMessage("Finished parsing ${TestTarget}")
|
||||
ParseAndAddCatchTests_PrintDebugMessage("Finished parsing ${TestTarget}")
|
||||
endfunction()
|
||||
|
@@ -14,6 +14,7 @@ Writing tests:
|
||||
* [Event Listeners](event-listeners.md#top)
|
||||
* [Data Generators](generators.md#top)
|
||||
* [Other macros](other-macros.md#top)
|
||||
* [Micro benchmarking](benchmarks.md#top)
|
||||
|
||||
Fine tuning:
|
||||
* [Supplying your own main()](own-main.md#top)
|
||||
|
@@ -10,7 +10,7 @@
|
||||
|
||||
Most test frameworks have a large collection of assertion macros to capture all possible conditional forms (```_EQUALS```, ```_NOTEQUALS```, ```_GREATER_THAN``` etc).
|
||||
|
||||
Catch is different. Because it decomposes natural C-style conditional expressions most of these forms are reduced to one or two that you will use all the time. That said there are a rich set of auxiliary macros as well. We'll describe all of these here.
|
||||
Catch is different. Because it decomposes natural C-style conditional expressions most of these forms are reduced to one or two that you will use all the time. That said there is a rich set of auxiliary macros as well. We'll describe all of these here.
|
||||
|
||||
Most of these macros come in two forms:
|
||||
|
||||
@@ -61,7 +61,7 @@ Catch provides a way to perform tolerant comparisons of floating point values th
|
||||
REQUIRE( performComputation() == Approx( 2.1 ) );
|
||||
```
|
||||
|
||||
Catch also provides a UDL for `Approx`; `_a`. It resides in
|
||||
Catch also provides a user-defined literal for `Approx`; `_a`. It resides in
|
||||
the `Catch::literals` namespace and can be used like so:
|
||||
```cpp
|
||||
using namespace Catch::literals;
|
||||
|
250
docs/benchmarks.md
Normal file
250
docs/benchmarks.md
Normal file
@@ -0,0 +1,250 @@
|
||||
<a id="top"></a>
|
||||
# Authoring benchmarks
|
||||
|
||||
Writing benchmarks is not easy. Catch simplifies certain aspects but you'll
|
||||
always need to take care about various aspects. Understanding a few things about
|
||||
the way Catch runs your code will be very helpful when writing your benchmarks.
|
||||
|
||||
First off, let's go over some terminology that will be used throughout this
|
||||
guide.
|
||||
|
||||
- *User code*: user code is the code that the user provides to be measured.
|
||||
- *Run*: one run is one execution of the user code.
|
||||
- *Sample*: one sample is one data point obtained by measuring the time it takes
|
||||
to perform a certain number of runs. One sample can consist of more than one
|
||||
run if the clock available does not have enough resolution to accurately
|
||||
measure a single run. All samples for a given benchmark execution are obtained
|
||||
with the same number of runs.
|
||||
|
||||
## Execution procedure
|
||||
|
||||
Now I can explain how a benchmark is executed in Catch. There are three main
|
||||
steps, though the first does not need to be repeated for every benchmark.
|
||||
|
||||
1. *Environmental probe*: before any benchmarks can be executed, the clock's
|
||||
resolution is estimated. A few other environmental artifacts are also estimated
|
||||
at this point, like the cost of calling the clock function, but they almost
|
||||
never have any impact in the results.
|
||||
|
||||
2. *Estimation*: the user code is executed a few times to obtain an estimate of
|
||||
the amount of runs that should be in each sample. This also has the potential
|
||||
effect of bringing relevant code and data into the caches before the actual
|
||||
measurement starts.
|
||||
|
||||
3. *Measurement*: all the samples are collected sequentially by performing the
|
||||
number of runs estimated in the previous step for each sample.
|
||||
|
||||
This already gives us one important rule for writing benchmarks for Catch: the
|
||||
benchmarks must be repeatable. The user code will be executed several times, and
|
||||
the number of times it will be executed during the estimation step cannot be
|
||||
known beforehand since it depends on the time it takes to execute the code.
|
||||
User code that cannot be executed repeatedly will lead to bogus results or
|
||||
crashes.
|
||||
|
||||
## Benchmark specification
|
||||
|
||||
Benchmarks can be specified anywhere inside a Catch test case.
|
||||
There is a simple and a slightly more advanced version of the `BENCHMARK` macro.
|
||||
|
||||
Let's have a look how a naive Fibonacci implementation could be benchmarked:
|
||||
```c++
|
||||
std::uint64_t Fibonacci(std::uint64_t number) {
|
||||
return number < 2 ? 1 : Fibonacci(number - 1) + Fibonacci(number - 2);
|
||||
}
|
||||
```
|
||||
Now the most straight forward way to benchmark this function, is just adding a `BENCHMARK` macro to our test case:
|
||||
```c++
|
||||
TEST_CASE("Fibonacci") {
|
||||
CHECK(Fibonacci(0) == 1);
|
||||
// some more asserts..
|
||||
CHECK(Fibonacci(5) == 8);
|
||||
// some more asserts..
|
||||
|
||||
// now let's benchmark:
|
||||
BENCHMARK("Fibonacci 20") {
|
||||
return Fibonacci(20);
|
||||
};
|
||||
|
||||
BENCHMARK("Fibonacci 25") {
|
||||
return Fibonacci(25);
|
||||
};
|
||||
|
||||
BENCHMARK("Fibonacci 30") {
|
||||
return Fibonacci(30);
|
||||
};
|
||||
|
||||
BENCHMARK("Fibonacci 35") {
|
||||
return Fibonacci(35);
|
||||
};
|
||||
}
|
||||
```
|
||||
There's a few things to note:
|
||||
- As `BENCHMARK` expands to a lambda expression it is necessary to add a semicolon after
|
||||
the closing brace (as opposed to the first experimental version).
|
||||
- The `return` is a handy way to avoid the compiler optimizing away the benchmark code.
|
||||
|
||||
Running this already runs the benchmarks and outputs something similar to:
|
||||
```
|
||||
-------------------------------------------------------------------------------
|
||||
Fibonacci
|
||||
-------------------------------------------------------------------------------
|
||||
C:\path\to\Catch2\Benchmark.tests.cpp(10)
|
||||
...............................................................................
|
||||
benchmark name samples iterations estimated
|
||||
mean low mean high mean
|
||||
std dev low std dev high std dev
|
||||
-------------------------------------------------------------------------------
|
||||
Fibonacci 20 100 416439 83.2878 ms
|
||||
2 ns 2 ns 2 ns
|
||||
0 ns 0 ns 0 ns
|
||||
|
||||
Fibonacci 25 100 400776 80.1552 ms
|
||||
3 ns 3 ns 3 ns
|
||||
0 ns 0 ns 0 ns
|
||||
|
||||
Fibonacci 30 100 396873 79.3746 ms
|
||||
17 ns 17 ns 17 ns
|
||||
0 ns 0 ns 0 ns
|
||||
|
||||
Fibonacci 35 100 145169 87.1014 ms
|
||||
468 ns 464 ns 473 ns
|
||||
21 ns 15 ns 34 ns
|
||||
```
|
||||
|
||||
### Advanced benchmarking
|
||||
The simplest use case shown above, takes no arguments and just runs the user code that needs to be measured.
|
||||
However, if using the `BENCHMARK_ADVANCED` macro and adding a `Catch::Benchmark::Chronometer` argument after
|
||||
the macro, some advanced features are available. The contents of the simple benchmarks are invoked once per run,
|
||||
while the blocks of the advanced benchmarks are invoked exactly twice:
|
||||
once during the estimation phase, and another time during the execution phase.
|
||||
|
||||
```c++
|
||||
BENCHMARK("simple"){ return long_computation(); };
|
||||
|
||||
BENCHMARK_ADVANCED("advanced")(Catch::Benchmark::Chronometer meter) {
|
||||
set_up();
|
||||
meter.measure([] { return long_computation(); });
|
||||
};
|
||||
```
|
||||
|
||||
These advanced benchmarks no longer consist entirely of user code to be measured.
|
||||
In these cases, the code to be measured is provided via the
|
||||
`Catch::Benchmark::Chronometer::measure` member function. This allows you to set up any
|
||||
kind of state that might be required for the benchmark but is not to be included
|
||||
in the measurements, like making a vector of random integers to feed to a
|
||||
sorting algorithm.
|
||||
|
||||
A single call to `Catch::Benchmark::Chronometer::measure` performs the actual measurements
|
||||
by invoking the callable object passed in as many times as necessary. Anything
|
||||
that needs to be done outside the measurement can be done outside the call to
|
||||
`measure`.
|
||||
|
||||
The callable object passed in to `measure` can optionally accept an `int`
|
||||
parameter.
|
||||
|
||||
```c++
|
||||
meter.measure([](int i) { return long_computation(i); });
|
||||
```
|
||||
|
||||
If it accepts an `int` parameter, the sequence number of each run will be passed
|
||||
in, starting with 0. This is useful if you want to measure some mutating code,
|
||||
for example. The number of runs can be known beforehand by calling
|
||||
`Catch::Benchmark::Chronometer::runs`; with this one can set up a different instance to be
|
||||
mutated by each run.
|
||||
|
||||
```c++
|
||||
std::vector<std::string> v(meter.runs());
|
||||
std::fill(v.begin(), v.end(), test_string());
|
||||
meter.measure([&v](int i) { in_place_escape(v[i]); });
|
||||
```
|
||||
|
||||
Note that it is not possible to simply use the same instance for different runs
|
||||
and resetting it between each run since that would pollute the measurements with
|
||||
the resetting code.
|
||||
|
||||
It is also possible to just provide an argument name to the simple `BENCHMARK` macro to get
|
||||
the same semantics as providing a callable to `meter.measure` with `int` argument:
|
||||
|
||||
```c++
|
||||
BENCHMARK("indexed", i){ return long_computation(i); };
|
||||
```
|
||||
|
||||
### Constructors and destructors
|
||||
|
||||
All of these tools give you a lot mileage, but there are two things that still
|
||||
need special handling: constructors and destructors. The problem is that if you
|
||||
use automatic objects they get destroyed by the end of the scope, so you end up
|
||||
measuring the time for construction and destruction together. And if you use
|
||||
dynamic allocation instead, you end up including the time to allocate memory in
|
||||
the measurements.
|
||||
|
||||
To solve this conundrum, Catch provides class templates that let you manually
|
||||
construct and destroy objects without dynamic allocation and in a way that lets
|
||||
you measure construction and destruction separately.
|
||||
|
||||
```c++
|
||||
BENCHMARK_ADVANCED("construct")(Catch::Benchmark::Chronometer meter)
|
||||
{
|
||||
std::vector<Catch::Benchmark::storage_for<std::string>> storage(meter.runs());
|
||||
meter.measure([&](int i) { storage[i].construct("thing"); });
|
||||
})
|
||||
|
||||
BENCHMARK_ADVANCED("destroy", [](Catch::Benchmark::Chronometer meter)
|
||||
{
|
||||
std::vector<Catch::Benchmark::destructable_object<std::string>> storage(meter.runs());
|
||||
for(auto&& o : storage)
|
||||
o.construct("thing");
|
||||
meter.measure([&](int i) { storage[i].destruct(); });
|
||||
})
|
||||
```
|
||||
|
||||
`Catch::Benchmark::storage_for<T>` objects are just pieces of raw storage suitable for `T`
|
||||
objects. You can use the `Catch::Benchmark::storage_for::construct` member function to call a constructor and
|
||||
create an object in that storage. So if you want to measure the time it takes
|
||||
for a certain constructor to run, you can just measure the time it takes to run
|
||||
this function.
|
||||
|
||||
When the lifetime of a `Catch::Benchmark::storage_for<T>` object ends, if an actual object was
|
||||
constructed there it will be automatically destroyed, so nothing leaks.
|
||||
|
||||
If you want to measure a destructor, though, we need to use
|
||||
`Catch::Benchmark::destructable_object<T>`. These objects are similar to
|
||||
`Catch::Benchmark::storage_for<T>` in that construction of the `T` object is manual, but
|
||||
it does not destroy anything automatically. Instead, you are required to call
|
||||
the `Catch::Benchmark::destructable_object::destruct` member function, which is what you
|
||||
can use to measure the destruction time.
|
||||
|
||||
### The optimizer
|
||||
|
||||
Sometimes the optimizer will optimize away the very code that you want to
|
||||
measure. There are several ways to use results that will prevent the optimiser
|
||||
from removing them. You can use the `volatile` keyword, or you can output the
|
||||
value to standard output or to a file, both of which force the program to
|
||||
actually generate the value somehow.
|
||||
|
||||
Catch adds a third option. The values returned by any function provided as user
|
||||
code are guaranteed to be evaluated and not optimised out. This means that if
|
||||
your user code consists of computing a certain value, you don't need to bother
|
||||
with using `volatile` or forcing output. Just `return` it from the function.
|
||||
That helps with keeping the code in a natural fashion.
|
||||
|
||||
Here's an example:
|
||||
|
||||
```c++
|
||||
// may measure nothing at all by skipping the long calculation since its
|
||||
// result is not used
|
||||
BENCHMARK("no return"){ long_calculation(); };
|
||||
|
||||
// the result of long_calculation() is guaranteed to be computed somehow
|
||||
BENCHMARK("with return"){ return long_calculation(); };
|
||||
```
|
||||
|
||||
However, there's no other form of control over the optimizer whatsoever. It is
|
||||
up to you to write a benchmark that actually measures what you want and doesn't
|
||||
just measure the time to do a whole bunch of nothing.
|
||||
|
||||
To sum up, there are two simple rules: whatever you would do in handwritten code
|
||||
to control optimization still works in Catch; and Catch makes return values
|
||||
from user code into observable effects that can't be optimized away.
|
||||
|
||||
<i>Adapted from nonius' documentation.</i>
|
@@ -48,7 +48,7 @@ If Catch2 has been installed in system, both of these can be used after
|
||||
doing `find_package(Catch2 REQUIRED)`. Otherwise you need to add them
|
||||
to your CMake module path.
|
||||
|
||||
### `Catch.cmake` and `AddCatchTests.cmake`
|
||||
### `Catch.cmake` and `CatchAddTests.cmake`
|
||||
|
||||
`Catch.cmake` provides function `catch_discover_tests` to get tests from
|
||||
a target. This function works by running the resulting executable with
|
||||
|
@@ -20,14 +20,17 @@
|
||||
[Specify a seed for the Random Number Generator](#specify-a-seed-for-the-random-number-generator)<br>
|
||||
[Identify framework and version according to the libIdentify standard](#identify-framework-and-version-according-to-the-libidentify-standard)<br>
|
||||
[Wait for key before continuing](#wait-for-key-before-continuing)<br>
|
||||
[Specify multiples of clock resolution to run benchmarks for](#specify-multiples-of-clock-resolution-to-run-benchmarks-for)<br>
|
||||
[Specify the number of benchmark samples to collect](#specify-the-number-of-benchmark-samples-to-collect)<br>
|
||||
[Specify the number of benchmark resamples for bootstrapping](#specify-the-number-of-resamples-for-bootstrapping)<br>
|
||||
[Specify the confidence interval for bootstrapping](#specify-the-confidence-interval-for-bootstrapping)<br>
|
||||
[Disable statistical analysis of collected benchmark samples](#disable-statistical-analysis-of-collected-benchmark-samples)<br>
|
||||
[Usage](#usage)<br>
|
||||
[Specify the section to run](#specify-the-section-to-run)<br>
|
||||
[Filenames as tags](#filenames-as-tags)<br>
|
||||
[Override output colouring](#override-output-colouring)<br>
|
||||
|
||||
Catch works quite nicely without any command line options at all - but for those times when you want greater control the following options are available.
|
||||
Click one of the followings links to take you straight to that option - or scroll on to browse the available options.
|
||||
Click one of the following links to take you straight to that option - or scroll on to browse the available options.
|
||||
|
||||
<a href="#specifying-which-tests-to-run"> ` <test-spec> ...`</a><br />
|
||||
<a href="#usage"> ` -h, -?, --help`</a><br />
|
||||
@@ -57,7 +60,10 @@ Click one of the followings links to take you straight to that option - or scrol
|
||||
<a href="#rng-seed"> ` --rng-seed`</a><br />
|
||||
<a href="#libidentify"> ` --libidentify`</a><br />
|
||||
<a href="#wait-for-keypress"> ` --wait-for-keypress`</a><br />
|
||||
<a href="#benchmark-resolution-multiple"> ` --benchmark-resolution-multiple`</a><br />
|
||||
<a href="#benchmark-samples"> ` --benchmark-samples`</a><br />
|
||||
<a href="#benchmark-resamples"> ` --benchmark-resamples`</a><br />
|
||||
<a href="#benchmark-confidence-interval"> ` --benchmark-confidence-interval`</a><br />
|
||||
<a href="#benchmark-no-analysis"> ` --benchmark-no-analysis`</a><br />
|
||||
<a href="#use-colour"> ` --use-colour`</a><br />
|
||||
|
||||
</br>
|
||||
@@ -267,13 +273,40 @@ See [The LibIdentify repo for more information and examples](https://github.com/
|
||||
Will cause the executable to print a message and wait until the return/ enter key is pressed before continuing -
|
||||
either before running any tests, after running all tests - or both, depending on the argument.
|
||||
|
||||
<a id="benchmark-resolution-multiple"></a>
|
||||
## Specify multiples of clock resolution to run benchmarks for
|
||||
<pre>--benchmark-resolution-multiple <multiplier></pre>
|
||||
<a id="benchmark-samples"></a>
|
||||
## Specify the number of benchmark samples to collect
|
||||
<pre>--benchmark-samples <# of samples></pre>
|
||||
|
||||
When running benchmarks the clock resolution is estimated. Benchmarks are then run for exponentially increasing
|
||||
numbers of iterations until some multiple of the estimated resolution is exceed. By default that multiple is 100, but
|
||||
it can be overridden here.
|
||||
When running benchmarks a number of "samples" is collected. This is the base data for later statistical analysis.
|
||||
Per sample a clock resolution dependent number of iterations of the user code is run, which is independent of the number of samples. Defaults to 100.
|
||||
|
||||
<a id="benchmark-resamples"></a>
|
||||
## Specify the number of resamples for bootstrapping
|
||||
<pre>--benchmark-resamples <# of resamples></pre>
|
||||
|
||||
After the measurements are performed, statistical [bootstrapping] is performed
|
||||
on the samples. The number of resamples for that bootstrapping is configurable
|
||||
but defaults to 100000. Due to the bootstrapping it is possible to give
|
||||
estimates for the mean and standard deviation. The estimates come with a lower
|
||||
bound and an upper bound, and the confidence interval (which is configurable but
|
||||
defaults to 95%).
|
||||
|
||||
[bootstrapping]: http://en.wikipedia.org/wiki/Bootstrapping_%28statistics%29
|
||||
|
||||
<a id="benchmark-confidence-interval"></a>
|
||||
## Specify the confidence-interval for bootstrapping
|
||||
<pre>--benchmark-confidence-interval <confidence-interval></pre>
|
||||
|
||||
The confidence-interval is used for statistical bootstrapping on the samples to
|
||||
calculate the upper and lower bounds of mean and standard deviation.
|
||||
Must be between 0 and 1 and defaults to 0.95.
|
||||
|
||||
<a id="benchmark-no-analysis"></a>
|
||||
## Disable statistical analysis of collected benchmark samples
|
||||
<pre>--benchmark-no-analysis</pre>
|
||||
|
||||
When this flag is specified no bootstrapping or any other statistical analysis is performed.
|
||||
Instead the user code is only measured and the plain mean from the samples is reported.
|
||||
|
||||
<a id="usage"></a>
|
||||
## Usage
|
||||
|
@@ -16,3 +16,4 @@ fact then please let us know - either directly, via a PR or
|
||||
- [Bloomlife](https://bloomlife.com)
|
||||
- NASA
|
||||
- [Inscopix Inc.](https://www.inscopix.com/)
|
||||
- [Makimo](https://makimo.pl/)
|
||||
|
@@ -89,7 +89,7 @@ them yourself, their signatures are:
|
||||
By default, when Catch's stringification machinery has to stringify
|
||||
a type that does not specialize `StringMaker`, does not overload `operator<<`,
|
||||
is not an enumeration and is not a range, it uses `"{?}"`. This can be
|
||||
overriden by defining `CATCH_CONFIG_FALLBACK_STRINGIFIER` to name of a
|
||||
overridden by defining `CATCH_CONFIG_FALLBACK_STRINGIFIER` to name of a
|
||||
function that should perform the stringification instead.
|
||||
|
||||
All types that do not provide `StringMaker` specialization or `operator<<`
|
||||
@@ -149,6 +149,8 @@ by using `_NO_` in the macro, e.g. `CATCH_CONFIG_NO_CPP17_UNCAUGHT_EXCEPTIONS`.
|
||||
CATCH_CONFIG_DISABLE // Disables assertions and test case registration
|
||||
CATCH_CONFIG_WCHAR // Enables use of wchart_t
|
||||
CATCH_CONFIG_EXPERIMENTAL_REDIRECT // Enables the new (experimental) way of capturing stdout/stderr
|
||||
CATCH_CONFIG_ENABLE_BENCHMARKING // Enables the integrated benchmarking features (has a significant effect on compilation speed)
|
||||
CATCH_CONFIG_USE_ASYNC // Force parallel statistical processing of samples during benchmarking
|
||||
|
||||
Currently Catch enables `CATCH_CONFIG_WINDOWS_SEH` only when compiled with MSVC, because some versions of MinGW do not have the necessary Win32 API support.
|
||||
|
||||
|
@@ -75,8 +75,34 @@ before you do so, you need to check that the introduced changes are indeed
|
||||
intentional.
|
||||
|
||||
|
||||
## Code constructs to watch out for
|
||||
|
||||
*this document is still in-progress...*
|
||||
This section is a (sadly incomplete) listing of various constructs that
|
||||
are problematic and are not always caught by our CI infrastructure.
|
||||
|
||||
### Naked exceptions and exceptions-related function
|
||||
|
||||
If you are throwing an exception, it should be done via `CATCH_ERROR`
|
||||
or `CATCH_RUNTIME_ERROR` in `catch_enforce.h`. These macros will handle
|
||||
the differences between compilation with or without exceptions for you.
|
||||
However, some platforms (IAR) also have problems with exceptions-related
|
||||
functions, such as `std::current_exceptions`. We do not have IAR in our
|
||||
CI, but luckily there should not be too many reasons to use these.
|
||||
However, if you do, they should be kept behind a
|
||||
`CATCH_CONFIG_DISABLE_EXCEPTIONS` macro.
|
||||
|
||||
### Unqualified usage of functions from C's stdlib
|
||||
|
||||
If you are using a function from C's stdlib, please include the header
|
||||
as `<cfoo>` and call the function qualified. The common knowledge that
|
||||
there is no difference is wrong, QNX and VxWorks won't compile if you
|
||||
include the header as `<cfoo>` and call the function unqualified.
|
||||
|
||||
|
||||
----
|
||||
|
||||
_This documentation will always be in-progress as new information comes
|
||||
up, but we are trying to keep it as up to date as possible._
|
||||
|
||||
---
|
||||
|
||||
|
@@ -29,13 +29,13 @@ struct MyListener : Catch::TestEventListenerBase {
|
||||
|
||||
using TestEventListenerBase::TestEventListenerBase; // inherit constructor
|
||||
|
||||
virtual void testCaseStarting( Catch::TestCaseInfo const& testInfo ) override {
|
||||
void testCaseStarting( Catch::TestCaseInfo const& testInfo ) override {
|
||||
// Perform some setup before a test case is run
|
||||
}
|
||||
|
||||
virtual void testCaseEnded( Catch::TestCaseStats const& testCaseStats ) override {
|
||||
void testCaseEnded( Catch::TestCaseStats const& testCaseStats ) override {
|
||||
// Tear-down after a test case is run
|
||||
}
|
||||
}
|
||||
};
|
||||
CATCH_REGISTER_LISTENER( MyListener )
|
||||
```
|
||||
|
@@ -36,13 +36,18 @@ a test case,
|
||||
* 2 fundamental generators
|
||||
* `ValueGenerator<T>` -- contains only single element
|
||||
* `ValuesGenerator<T>` -- contains multiple elements
|
||||
* 4 generic generators that modify other generators
|
||||
* 5 generic generators that modify other generators
|
||||
* `FilterGenerator<T, Predicate>` -- filters out elements from a generator
|
||||
for which the predicate returns "false"
|
||||
* `TakeGenerator<T>` -- takes first `n` elements from a generator
|
||||
* `RepeatGenerator<T>` -- repeats output from a generator `n` times
|
||||
* `MapGenerator<T, U, Func>` -- returns the result of applying `Func`
|
||||
on elements from a different generator
|
||||
* `ChunkGenerator<T>` -- returns chunks (inside `std::vector`) of n elements from a generator
|
||||
* 3 specific purpose generators
|
||||
* `RandomIntegerGenerator<Integral>` -- generates random Integrals from range
|
||||
* `RandomFloatGenerator<Float>` -- generates random Floats from range
|
||||
* `RangeGenerator<T>` -- generates all values inside a specific range
|
||||
|
||||
The generators also have associated helper functions that infer their
|
||||
type, making their usage much nicer. These are
|
||||
@@ -52,8 +57,13 @@ type, making their usage much nicer. These are
|
||||
* `filter(predicate, GeneratorWrapper<T>&&)` for `FilterGenerator<T, Predicate>`
|
||||
* `take(count, GeneratorWrapper<T>&&)` for `TakeGenerator<T>`
|
||||
* `repeat(repeats, GeneratorWrapper<T>&&)` for `RepeatGenerator<T>`
|
||||
* `map(func, GeneratorWrapper<T>&&)` for `MapGenerator<T, T, Func>` (map `T` to `T`)
|
||||
* `map(func, GeneratorWrapper<T>&&)` for `MapGenerator<T, U, Func>` (map `U` to `T`, deduced from `Func`)
|
||||
* `map<T>(func, GeneratorWrapper<U>&&)` for `MapGenerator<T, U, Func>` (map `U` to `T`)
|
||||
* `chunk(chunk-size, GeneratorWrapper<T>&&)` for `ChunkGenerator<T>`
|
||||
* `random(IntegerOrFloat a, IntegerOrFloat b)` for `RandomIntegerGenerator` or `RandomFloatGenerator`
|
||||
* `range(start, end)` for `RangeGenerator<T>` with a step size of `1`
|
||||
* `range(start, end, step)` for `RangeGenerator<T>` with a custom step size
|
||||
|
||||
|
||||
And can be used as shown in the example below to create a generator
|
||||
that returns 100 odd random number:
|
||||
@@ -69,8 +79,6 @@ TEST_CASE("Generating random ints", "[example][generator]") {
|
||||
}
|
||||
```
|
||||
|
||||
_Note that `random` is currently not a part of the first-party generators_.
|
||||
|
||||
|
||||
Apart from registering generators with Catch2, the `GENERATE` macro has
|
||||
one more purpose, and that is to provide simple way of generating trivial
|
||||
@@ -82,7 +90,11 @@ used with other generators as arguments, such as `auto i = GENERATE(0, 2,
|
||||
take(100, random(300, 3000)));`. This is useful e.g. if you know that
|
||||
specific inputs are problematic and want to test them separately/first.
|
||||
|
||||
**For safety reasons, you cannot use variables inside the `GENERATE` macro.**
|
||||
**For safety reasons, you cannot use variables inside the `GENERATE` macro.
|
||||
This is done because the generator expression _will_ outlive the outside
|
||||
scope and thus capturing references is dangerous. If you need to use
|
||||
variables inside the generator expression, make sure you thought through
|
||||
the lifetime implications and use `GENERATE_COPY` or `GENERATE_REF`.**
|
||||
|
||||
You can also override the inferred type by using `as<type>` as the first
|
||||
argument to the macro. This can be useful when dealing with string literals,
|
||||
@@ -90,7 +102,7 @@ if you want them to come out as `std::string`:
|
||||
|
||||
```cpp
|
||||
TEST_CASE("type conversion", "[generators]") {
|
||||
auto str = GENERATE(as<std::string>{}, "a", "bb", "ccc");`
|
||||
auto str = GENERATE(as<std::string>{}, "a", "bb", "ccc");
|
||||
REQUIRE(str.size() > 0);
|
||||
}
|
||||
```
|
||||
|
@@ -15,7 +15,9 @@
|
||||
- Listener: [Listeners](../examples/210-Evt-EventListeners.cpp)
|
||||
- Configuration: [Provide your own output streams](../examples/231-Cfg-OutputStreams.cpp)
|
||||
- Generators: [Create your own generator](../examples/300-Gen-OwnGenerator.cpp)
|
||||
- Generators: [Use map to convert types in GENERATE expression](../examples/301-Gen-MapTypeConversion.cpp)
|
||||
- Generators: [Use variables in generator expressions](../examples/310-Gen-VariablesInGenerators.cpp)
|
||||
- Generators: [Use custom variable capture in generator expressions](../examples/311-Gen-CustomCapture.cpp)
|
||||
|
||||
|
||||
## Planned
|
||||
|
@@ -1,7 +1,7 @@
|
||||
<a id="top"></a>
|
||||
# Logging macros
|
||||
|
||||
Additional messages can be logged during a test case. Note that the messages are scoped and thus will not be reported if failure occurs in scope preceding the message declaration. An example:
|
||||
Additional messages can be logged during a test case. Note that the messages logged with `INFO` are scoped and thus will not be reported if failure occurs in scope preceding the message declaration. An example:
|
||||
|
||||
```cpp
|
||||
TEST_CASE("Foo") {
|
||||
@@ -28,10 +28,64 @@ The number is 1
|
||||
```
|
||||
When the last `CHECK` fails in the "Bar" test case, then only one message will be printed: `Test case start`.
|
||||
|
||||
## Logging without local scope
|
||||
|
||||
`UNSCOPED_INFO` is similar to `INFO` with two key differences:
|
||||
|
||||
- Lifetime of an unscoped message is not tied to its own scope.
|
||||
- An unscoped message can be reported by the first following assertion only, regardless of the result of that assertion.
|
||||
|
||||
In other words, lifetime of `UNSCOPED_INFO` is limited by the following assertion (or by the end of test case/section, whichever comes first) whereas lifetime of `INFO` is limited by its own scope.
|
||||
|
||||
These differences make this macro useful for reporting information from helper functions or inner scopes. An example:
|
||||
|
||||
```cpp
|
||||
void print_some_info() {
|
||||
UNSCOPED_INFO("Info from helper");
|
||||
}
|
||||
|
||||
TEST_CASE("Baz") {
|
||||
print_some_info();
|
||||
for (int i = 0; i < 2; ++i) {
|
||||
UNSCOPED_INFO("The number is " << i);
|
||||
}
|
||||
CHECK(false);
|
||||
}
|
||||
|
||||
TEST_CASE("Qux") {
|
||||
INFO("First info");
|
||||
UNSCOPED_INFO("First unscoped info");
|
||||
CHECK(false);
|
||||
|
||||
INFO("Second info");
|
||||
UNSCOPED_INFO("Second unscoped info");
|
||||
CHECK(false);
|
||||
}
|
||||
```
|
||||
|
||||
"Baz" test case prints:
|
||||
```
|
||||
Info from helper
|
||||
The number is 0
|
||||
The number is 1
|
||||
```
|
||||
|
||||
With "Qux" test case, two messages will be printed when the first `CHECK` fails:
|
||||
```
|
||||
First info
|
||||
First unscoped info
|
||||
```
|
||||
|
||||
"First unscoped info" message will be cleared after the first `CHECK`, while "First info" message will persist until the end of the test case. Therefore, when the second `CHECK` fails, three messages will be printed:
|
||||
```
|
||||
First info
|
||||
Second info
|
||||
Second unscoped info
|
||||
```
|
||||
|
||||
## Streaming macros
|
||||
|
||||
All these macros allow heterogenous sequences of values to be streaming using the insertion operator (```<<```) in the same way that std::ostream, std::cout, etc support it.
|
||||
All these macros allow heterogeneous sequences of values to be streaming using the insertion operator (```<<```) in the same way that std::ostream, std::cout, etc support it.
|
||||
|
||||
E.g.:
|
||||
```c++
|
||||
@@ -43,11 +97,15 @@ These macros come in three forms:
|
||||
|
||||
**INFO(** _message expression_ **)**
|
||||
|
||||
The message is logged to a buffer, but only reported with the next assertion that is logged. This allows you to log contextual information in case of failures which is not shown during a successful test run (for the console reporter, without -s). Messages are removed from the buffer at the end of their scope, so may be used, for example, in loops.
|
||||
The message is logged to a buffer, but only reported with next assertions that are logged. This allows you to log contextual information in case of failures which is not shown during a successful test run (for the console reporter, without -s). Messages are removed from the buffer at the end of their scope, so may be used, for example, in loops.
|
||||
|
||||
_Note that in Catch2 2.x.x `INFO` can be used without a trailing semicolon as there is a trailing semicolon inside macro.
|
||||
This semicolon will be removed with next major version. It is highly advised to use a trailing semicolon after `INFO` macro._
|
||||
|
||||
**UNSCOPED_INFO(** _message expression_ **)**
|
||||
|
||||
Similar to `INFO`, but messages are not limited to their own scope: They are removed from the buffer after each assertion, section or test case, whichever comes first.
|
||||
|
||||
**WARN(** _message expression_ **)**
|
||||
|
||||
The message is always reported but does not fail the test.
|
||||
|
@@ -92,7 +92,7 @@ public:
|
||||
IntRange( int begin, int end ) : m_begin( begin ), m_end( end ) {}
|
||||
|
||||
// Performs the test for this matcher
|
||||
virtual bool match( int const& i ) const override {
|
||||
bool match( int const& i ) const override {
|
||||
return i >= m_begin && i <= m_end;
|
||||
}
|
||||
|
||||
@@ -100,7 +100,7 @@ public:
|
||||
// include any provided data (the begin/ end in this case) and
|
||||
// be written as if it were stating a fact (in the output it will be
|
||||
// preceded by the value under test).
|
||||
virtual std::string describe() const {
|
||||
virtual std::string describe() const override {
|
||||
std::ostringstream ss;
|
||||
ss << "is between " << m_begin << " and " << m_end;
|
||||
return ss.str();
|
||||
|
@@ -17,29 +17,32 @@ Listing a project here does not imply endorsement and the plan is to keep these
|
||||
|
||||
## Libraries & Frameworks
|
||||
|
||||
### [ApprovalTests.cpp](https://github.com/approvals/ApprovalTests.cpp)
|
||||
C++11 implementation of Approval Tests, for quick, convenient testing of legacy code.
|
||||
|
||||
### [Azmq](https://github.com/zeromq/azmq)
|
||||
Boost Asio style bindings for ZeroMQ
|
||||
Boost Asio style bindings for ZeroMQ.
|
||||
|
||||
### [ChakraCore](https://github.com/Microsoft/ChakraCore)
|
||||
The core part of the Chakra JavaScript engine that powers Microsoft Edge
|
||||
The core part of the Chakra JavaScript engine that powers Microsoft Edge.
|
||||
|
||||
### [ChaiScript](https://github.com/ChaiScript/ChaiScript)
|
||||
A, header-only, embedded scripting language designed from the ground up to directly target C++ and take advantage of modern C++ development techniques
|
||||
A, header-only, embedded scripting language designed from the ground up to directly target C++ and take advantage of modern C++ development techniques.
|
||||
|
||||
### [Clara](https://github.com/philsquared/Clara)
|
||||
A, single-header-only, type-safe, command line parser - which also prints formatted usage strings.
|
||||
|
||||
### [Couchbase-lite-core](https://github.com/couchbase/couchbase-lite-core)
|
||||
The next-generation core storage and query engine for Couchbase Lite
|
||||
The next-generation core storage and query engine for Couchbase Lite.
|
||||
|
||||
### [cppcodec](https://github.com/tplgy/cppcodec)
|
||||
Header-only C++11 library to encode/decode base64, base64url, base32, base32hex and hex (a.k.a. base16) as specified in RFC 4648, plus Crockford's base32.
|
||||
|
||||
### [DtCraft](https://github.com/twhuang-uiuc/DtCraft)
|
||||
A High-performance Cluster Computing Engine
|
||||
A High-performance Cluster Computing Engine.
|
||||
|
||||
### [forest](https://github.com/xorz57/forest)
|
||||
Template Library of Tree Data Structures
|
||||
Template Library of Tree Data Structures.
|
||||
|
||||
### [Fuxedo](https://github.com/fuxedo/fuxedo)
|
||||
Open source Oracle Tuxedo-like XATMI middleware for C and C++.
|
||||
@@ -63,25 +66,25 @@ A small C++ library wrapper for the native C ODBC API.
|
||||
A header-only framework for benchmarking small snippets of C++ code.
|
||||
|
||||
### [SOCI](https://github.com/SOCI/soci)
|
||||
The C++ Database Access Library
|
||||
The C++ Database Access Library.
|
||||
|
||||
### [polymorphic_value](https://github.com/jbcoe/polymorphic_value)
|
||||
A polymorphic value-type for C++
|
||||
A polymorphic value-type for C++.
|
||||
|
||||
### [Ppconsul](https://github.com/oliora/ppconsul)
|
||||
A C++ client library for Consul. Consul is a distributed tool for discovering and configuring services in your infrastructure
|
||||
A C++ client library for Consul. Consul is a distributed tool for discovering and configuring services in your infrastructure.
|
||||
|
||||
### [Reactive-Extensions/ RxCpp](https://github.com/Reactive-Extensions/RxCpp)
|
||||
A library of algorithms for values-distributed-in-time
|
||||
A library of algorithms for values-distributed-in-time.
|
||||
|
||||
### [thor](https://github.com/xorz57/thor)
|
||||
Wrapper Library for CUDA
|
||||
Wrapper Library for CUDA.
|
||||
|
||||
### [TextFlowCpp](https://github.com/philsquared/textflowcpp)
|
||||
A small, single-header-only, library for wrapping and composing columns of text
|
||||
A small, single-header-only, library for wrapping and composing columns of text.
|
||||
|
||||
### [Trompeloeil](https://github.com/rollbear/trompeloeil)
|
||||
A thread safe header only mocking framework for C++14
|
||||
A thread-safe header-only mocking framework for C++14.
|
||||
|
||||
### [args](https://github.com/Taywee/args)
|
||||
A simple header-only C++ argument parser library.
|
||||
@@ -95,16 +98,19 @@ ArangoDB is a native multi-model database with flexible data models for document
|
||||
Minimal, open-source and cross-platform audio tool for live music production.
|
||||
|
||||
### [MAME](https://github.com/mamedev/mame)
|
||||
MAME originally stood for Multiple Arcade Machine Emulator
|
||||
MAME originally stood for Multiple Arcade Machine Emulator.
|
||||
|
||||
### [Newsbeuter](https://github.com/akrennmair/newsbeuter)
|
||||
Newsbeuter is an open-source RSS/Atom feed reader for text terminals.
|
||||
|
||||
### [raspigcd](https://github.com/pantadeusz/raspigcd)
|
||||
Low level CLI app and library for execution of GCODE on Raspberry Pi without any additional microcontrolers (just RPi + Stepsticks).
|
||||
|
||||
### [SpECTRE](https://github.com/sxs-collaboration/spectre)
|
||||
SpECTRE is a code for multi-scale, multi-physics problems in astrophysics and gravitational physics.
|
||||
|
||||
### [Standardese](https://github.com/foonathan/standardese)
|
||||
Standardese aims to be a nextgen Doxygen
|
||||
Standardese aims to be a nextgen Doxygen.
|
||||
|
||||
---
|
||||
|
||||
|
@@ -30,7 +30,7 @@ CHECKED_IF( a == b ) {
|
||||
|
||||
`CHECK_NOFAIL( expr )` is a variant of `CHECK` that does not fail the test
|
||||
case if _expr_ evaluates to `false`. This can be useful for checking some
|
||||
assumption, that might be violated without the test neccessarily failing.
|
||||
assumption, that might be violated without the test necessarily failing.
|
||||
|
||||
Example output:
|
||||
```
|
||||
@@ -120,7 +120,7 @@ constructor, or before Catch2's session is created in user's own main._
|
||||
`ANON_TEST_CASE` is a `TEST_CASE` replacement that will autogenerate
|
||||
unique name. The advantage of this is that you do not have to think
|
||||
of a name for the test case,`the disadvantage is that the name doesn't
|
||||
neccessarily remain stable across different links, and thus it might be
|
||||
necessarily remain stable across different links, and thus it might be
|
||||
hard to run directly.
|
||||
|
||||
Example:
|
||||
|
@@ -2,6 +2,12 @@
|
||||
|
||||
# Release notes
|
||||
**Contents**<br>
|
||||
[2.9.0](#290)<br>
|
||||
[2.8.0](#280)<br>
|
||||
[2.7.2](#272)<br>
|
||||
[2.7.1](#271)<br>
|
||||
[2.7.0](#270)<br>
|
||||
[2.6.1](#261)<br>
|
||||
[2.6.0](#260)<br>
|
||||
[2.5.0](#250)<br>
|
||||
[2.4.2](#242)<br>
|
||||
@@ -19,6 +25,117 @@
|
||||
[Older versions](#older-versions)<br>
|
||||
[Even Older versions](#even-older-versions)<br>
|
||||
|
||||
## 2.9.0
|
||||
|
||||
### Improvements
|
||||
* The experimental benchmarking support has been replaced by integrating Nonius code (#1616)
|
||||
* This provides a much more featurefull micro-benchmarking support.
|
||||
* Due to the compilation cost, it is disabled by default. See the documentation for details.
|
||||
* As far as backwards compatibility is concerned, this feature is still considered experimental in that we might change the interface based on user feedback.
|
||||
* `WithinULP` matcher now shows the acceptable range (#1581)
|
||||
* Template test cases now support type lists (#1627)
|
||||
|
||||
|
||||
## 2.8.0
|
||||
|
||||
### Improvements
|
||||
* Templated test cases no longer check whether the provided types are unique (#1628)
|
||||
* This allows you to e.g. test over `uint32_t`, `uint64_t`, and `size_t` without compilation failing
|
||||
* The precision of floating point stringification can be modified by user (#1612, #1614)
|
||||
* We now provide `REGISTER_ENUM` convenience macro for generating `StringMaker` specializations for enums
|
||||
* See the "String conversion" documentation for details
|
||||
* Added new set of macros for template test cases that enables the use of NTTPs (#1531, #1609)
|
||||
* See "Test cases and sections" documentation for details
|
||||
|
||||
### Fixes
|
||||
* `UNSCOPED_INFO` macro now has a prefixed/disabled/prefixed+disabled versions (#1611)
|
||||
* Reporting errors at startup should no longer cause a segfault under certain circumstances (#1626)
|
||||
|
||||
|
||||
### Miscellaneous
|
||||
* CMake will now prevent you from attempting in-tree build (#1636, #1638)
|
||||
* Previously it would break with an obscure error message during the build step
|
||||
|
||||
|
||||
## 2.7.2
|
||||
|
||||
### Improvements
|
||||
* Added an approximate vector matcher (#1499)
|
||||
|
||||
### Fixes
|
||||
* Filters will no longer be shown if there were none
|
||||
* Fixed compilation error when using Homebrew GCC on OS X (#1588, #1589)
|
||||
* Fixed the console reporter not showing messages that start with a newline (#1455, #1470)
|
||||
* Modified JUnit reporter's output so that rng seed and filters are reported according to the JUnit schema (#1598)
|
||||
* Fixed some obscure warnings and static analysis passes
|
||||
|
||||
### Miscellaneous
|
||||
* Various improvements to `ParseAndAddCatchTests` (#1559, #1601)
|
||||
* When a target is parsed, it receives `ParseAndAddCatchTests_TESTS` property which summarizes found tests
|
||||
* Fixed problem with tests not being found if the `OptionalCatchTestLauncher` variables is used
|
||||
* Including the script will no longer forcefully modify `CMAKE_MINIMUM_REQUIRED_VERSION`
|
||||
* CMake object libraries are ignored when parsing to avoid needless warnings
|
||||
* `CatchAddTests` now adds test's tags to their CTest labels (#1600)
|
||||
* Added basic CPack support to our build
|
||||
|
||||
## 2.7.1
|
||||
|
||||
### Improvements
|
||||
* Reporters now print out the filters applied to test cases (#1550, #1585)
|
||||
* Added `GENERATE_COPY` and `GENERATE_VAR` macros that can use variables inside the generator expression
|
||||
* Because of the significant danger of lifetime issues, the default `GENERATE` macro still does not allow variables
|
||||
* The `map` generator helper now deduces the mapped return type (#1576)
|
||||
|
||||
### Fixes
|
||||
* Fixed ObjC++ compilation (#1571)
|
||||
* Fixed test tag parsing so that `[.foo]` is now parsed as `[.][foo]`.
|
||||
* Suppressed warning caused by the Windows headers defining SE codes in different manners (#1575)
|
||||
|
||||
## 2.7.0
|
||||
|
||||
### Improvements
|
||||
* `TEMPLATE_PRODUCT_TEST_CASE` now uses the resulting type in the name, instead of the serial number (#1544)
|
||||
* Catch2's single header is now strictly ASCII (#1542)
|
||||
* Added generator for random integral/floating point types
|
||||
* The types are inferred within the `random` helper
|
||||
* Added back RangeGenerator (#1526)
|
||||
* RangeGenerator returns elements within a certain range
|
||||
* Added ChunkGenerator generic transform (#1538)
|
||||
* A ChunkGenerator returns the elements from different generator in chunks of n elements
|
||||
* Added `UNSCOPED_INFO` (#415, #983, #1522)
|
||||
* This is a variant of `INFO` that lives until next assertion/end of the test case.
|
||||
|
||||
|
||||
### Fixes
|
||||
* All calls to C stdlib functions are now `std::` qualified (#1541)
|
||||
* Code brought in from Clara was also updated.
|
||||
* Running tests will no longer open the specified output file twice (#1545)
|
||||
* This would cause trouble when the file was not a file, but rather a named pipe
|
||||
* Fixes the CLion/Resharper integration with Catch
|
||||
* Fixed `-Wunreachable-code` occurring with (old) ccache+cmake+clang combination (#1540)
|
||||
* Fixed `-Wdefaulted-function-deleted` warning with Clang 8 (#1537)
|
||||
* Catch2's type traits and helpers are now properly namespaced inside `Catch::` (#1548)
|
||||
* Fixed std{out,err} redirection for failing test (#1514, #1525)
|
||||
* Somehow, this bug has been present for well over a year before it was reported
|
||||
|
||||
|
||||
### Contrib
|
||||
* `ParseAndAddCatchTests` now properly escapes commas in the test name
|
||||
|
||||
|
||||
|
||||
## 2.6.1
|
||||
|
||||
### Improvements
|
||||
* The JUnit reporter now also reports random seed (#1520, #1521)
|
||||
|
||||
### Fixes
|
||||
* The TAP reporter now formats comments with test name properly (#1529)
|
||||
* `CATCH_REQUIRE_THROWS`'s internals were unified with `REQUIRE_THROWS` (#1536)
|
||||
* This fixes a potential `-Wunused-value` warning when used
|
||||
* Fixed a potential segfault when using any of the `--list-*` options (#1533, #1534)
|
||||
|
||||
|
||||
## 2.6.0
|
||||
|
||||
**With this release the data generator feature is now fully supported.**
|
||||
@@ -677,7 +794,7 @@ Cygwin issue with `gettimeofday` - `#define` was not early enough
|
||||
* Usage of `gettimeofday` inside Catch should no longer cause compilation errors.
|
||||
* Improved `-Wparentheses` suppression for gcc (#674)
|
||||
* When compiled with gcc 4.8 or newer, the suppression is localized to assertions only
|
||||
* Otherwise it is supressed for the whole TU
|
||||
* Otherwise it is suppressed for the whole TU
|
||||
* Fixed test spec parser issue (with escapes in multiple names)
|
||||
|
||||
##### Other
|
||||
@@ -760,7 +877,7 @@ Other:
|
||||
|
||||
##### Other:
|
||||
* Types with overloaded `&&` operator are no longer evaluated twice when used in an assertion macro.
|
||||
* The use of `__COUNTER__` is supressed when Catch is parsed by CLion
|
||||
* The use of `__COUNTER__` is suppressed when Catch is parsed by CLion
|
||||
* This change is not active when compiling a binary
|
||||
* Approval tests can now be run on Windows
|
||||
* CMake will now warn if a file is present in the `include` folder but not is not enumerated as part of the project
|
||||
|
@@ -32,7 +32,7 @@ Once a release is ready, release notes need to be written. They should summarize
|
||||
|
||||
### Commit and push update to GitHub
|
||||
|
||||
After version number is incremented, single-include header is regenerated and release notes are updated, changes should be commited and pushed to GitHub.
|
||||
After version number is incremented, single-include header is regenerated and release notes are updated, changes should be committed and pushed to GitHub.
|
||||
|
||||
|
||||
### Release on GitHub
|
||||
@@ -48,3 +48,25 @@ dependent on a specific version of the single-include header.
|
||||
|
||||
Since 2.5.0, the release tag and the "binaries" (headers) should be PGP
|
||||
signed.
|
||||
|
||||
#### Signing a tag
|
||||
|
||||
To create a signed tag, use `git tag -s <VERSION>`, where `<VERSION>`
|
||||
is the version being released, e.g. `git tag -s v2.6.0`.
|
||||
|
||||
Use the version name as the short message and the release notes as
|
||||
the body (long) message.
|
||||
|
||||
#### Signing the headers
|
||||
|
||||
This will create ASCII-armored signatures for the headers that are
|
||||
uploaded to the GitHub release:
|
||||
|
||||
```
|
||||
$ gpg2 --armor --output catch.hpp.asc --detach-sig catch.hpp
|
||||
$ gpg2 --armor --output catch_reporter_automake.hpp.asc --detach-sig catch_reporter_automake.hpp
|
||||
$ gpg2 --armor --output catch_reporter_teamcity.hpp.asc --detach-sig catch_reporter_teamcity.hpp
|
||||
$ gpg2 --armor --output catch_reporter_tap.hpp.asc --detach-sig catch_reporter_tap.hpp
|
||||
```
|
||||
|
||||
_GPG does not support signing multiple files in single invocation._
|
||||
|
@@ -51,14 +51,15 @@ After compiling `tests-main.cpp` once, it is enough to link it with separately c
|
||||
|
||||
```
|
||||
$ g++ tests-main.cpp -c
|
||||
$ g++ tests-main.o tests-factorial.cpp -o tests && ./tests -r compact
|
||||
$ g++ factorial.cpp -c
|
||||
$ g++ tests-main.o factorial.o tests-factorial.cpp -o tests && ./tests -r compact
|
||||
Passed 1 test case with 4 assertions.
|
||||
```
|
||||
|
||||
Now, the next time we change the file `tests-factorial.cpp` (say we add `REQUIRE( Factorial(0) == 1)`), it is enough to recompile the tests instead of recompiling main as well:
|
||||
|
||||
```
|
||||
$ g++ tests-main.o tests-factorial.cpp -o tests && ./tests -r compact
|
||||
$ g++ tests-main.o factorial.o tests-factorial.cpp -o tests && ./tests -r compact
|
||||
tests-factorial.cpp:11: failed: Factorial(0) == 1 for: 0 == 1
|
||||
Failed 1 test case, failed 1 assertion.
|
||||
```
|
||||
|
@@ -6,6 +6,7 @@
|
||||
[Tag aliases](#tag-aliases)<br>
|
||||
[BDD-style test cases](#bdd-style-test-cases)<br>
|
||||
[Type parametrised test cases](#type-parametrised-test-cases)<br>
|
||||
[Signature based parametrised test cases](#signature-based-parametrised-test-cases)<br>
|
||||
|
||||
While Catch fully supports the traditional, xUnit, style of class-based fixtures containing test case methods this is not the preferred style.
|
||||
|
||||
@@ -95,8 +96,8 @@ Other than the additional prefixes and the formatting in the console reporter th
|
||||
## Type parametrised test cases
|
||||
|
||||
In addition to `TEST_CASE`s, Catch2 also supports test cases parametrised
|
||||
by types, in the form of `TEMPLATE_TEST_CASE` and
|
||||
`TEMPLATE_PRODUCT_TEST_CASE`.
|
||||
by types, in the form of `TEMPLATE_TEST_CASE`,
|
||||
`TEMPLATE_PRODUCT_TEST_CASE` and `TEMPLATE_LIST_TEST_CASE`.
|
||||
|
||||
* **TEMPLATE_TEST_CASE(** _test name_ , _tags_, _type1_, _type2_, ..., _typen_ **)**
|
||||
|
||||
@@ -191,6 +192,73 @@ _While there is an upper limit on the number of types you can specify
|
||||
in single `TEMPLATE_TEST_CASE` or `TEMPLATE_PRODUCT_TEST_CASE`, the limit
|
||||
is very high and should not be encountered in practice._
|
||||
|
||||
* **TEMPLATE_LIST_TEST_CASE(** _test name_, _tags_, _type list_ **)**
|
||||
|
||||
_type list_ is a generic list of types on which test case should be instantiated.
|
||||
List can be `std::tuple`, `boost::mpl::list`, `boost::mp11::mp_list` or anything with
|
||||
`template <typename...>` signature.
|
||||
|
||||
This allows you to reuse the _type list_ in multiple test cases.
|
||||
|
||||
Example:
|
||||
```cpp
|
||||
using MyTypes = std::tuple<int, char, float>;
|
||||
TEMPLATE_LIST_TEST_CASE("Template test case with test types specified inside std::tuple", "[template][list]", MyTypes)
|
||||
{
|
||||
REQUIRE(sizeof(TestType) > 0);
|
||||
}
|
||||
```
|
||||
|
||||
|
||||
## Signature based parametrised test cases
|
||||
|
||||
In addition to [type parametrised test cases](#type-parametrised-test-cases) Catch2 also supports
|
||||
signature base parametrised test cases, in form of `TEMPLATE_TEST_CASE_SIG` and `TEMPLATE_PRODUCT_TEST_CASE_SIG`.
|
||||
These test cases have similar syntax like [type parametrised test cases](#type-parametrised-test-cases), with one
|
||||
additional positional argument which specifies the signature.
|
||||
|
||||
### Signature
|
||||
Signature has some strict rules for these tests cases to work properly:
|
||||
* signature with multiple template parameters e.g. `typename T, size_t S` must have this format in test case declaration
|
||||
`((typename T, size_t S), T, S)`
|
||||
* signature with variadic template arguments e.g. `typename T, size_t S, typename...Ts` must have this format in test case declaration
|
||||
`((typename T, size_t S, typename...Ts), T, S, Ts...)`
|
||||
* signature with single non type template parameter e.g. `int V` must have this format in test case declaration `((int V), V)`
|
||||
* signature with single type template parameter e.g. `typename T` should not be used as it is in fact `TEMPLATE_TEST_CASE`
|
||||
|
||||
Currently Catch2 support up to 11 template parameters in signature
|
||||
|
||||
### Examples
|
||||
|
||||
* **TEMPLATE_TEST_CASE_SIG(** _test name_ , _tags_, _signature_, _type1_, _type2_, ..., _typen_ **)**
|
||||
|
||||
Inside `TEMPLATE_TEST_CASE_SIG` test case you can use the names of template parameters as defined in _signature_.
|
||||
|
||||
```cpp
|
||||
TEMPLATE_TEST_CASE_SIG("TemplateTestSig: arrays can be created from NTTP arguments", "[vector][template][nttp]",
|
||||
((typename T, int V), T, V), (int,5), (float,4), (std::string,15), ((std::tuple<int, float>), 6)) {
|
||||
|
||||
std::array<T, V> v;
|
||||
REQUIRE(v.size() > 1);
|
||||
}
|
||||
```
|
||||
|
||||
* **TEMPLATE_PRODUCT_TEST_CASE_SIG(** _test name_ , _tags_, _signature_, (_template-type1_, _template-type2_, ..., _template-typen_), (_template-arg1_, _template-arg2_, ..., _template-argm_) **)**
|
||||
|
||||
```cpp
|
||||
|
||||
template<typename T, size_t S>
|
||||
struct Bar {
|
||||
size_t size() { return S; }
|
||||
};
|
||||
|
||||
TEMPLATE_PRODUCT_TEST_CASE_SIG("A Template product test case with array signature", "[template][product][nttp]", ((typename T, size_t S), T, S), (std::array, Bar), ((int, 9), (float, 42))) {
|
||||
TestType x;
|
||||
REQUIRE(x.size() > 0);
|
||||
}
|
||||
```
|
||||
|
||||
|
||||
---
|
||||
|
||||
[Home](Readme.md#top)
|
||||
|
@@ -84,6 +84,46 @@ _While there is an upper limit on the number of types you can specify
|
||||
in single `TEMPLATE_TEST_CASE_METHOD` or `TEMPLATE_PRODUCT_TEST_CASE_METHOD`,
|
||||
the limit is very high and should not be encountered in practice._
|
||||
|
||||
|
||||
Catch2 also provides `TEMPLATE_TEST_CASE_METHOD_SIG` and `TEMPLATE_PRODUCT_TEST_CASE_METHOD_SIG` to support
|
||||
fixtures using non-type template parameters. These test cases work similar to `TEMPLATE_TEST_CASE_METHOD` and `TEMPLATE_PRODUCT_TEST_CASE_METHOD`,
|
||||
with additional positional argument for [signature](test-cases-and-sections.md#signature-based-parametrised-test-cases).
|
||||
|
||||
Example:
|
||||
```cpp
|
||||
template <int V>
|
||||
struct Nttp_Fixture{
|
||||
int value = V;
|
||||
};
|
||||
|
||||
TEMPLATE_TEST_CASE_METHOD_SIG(Nttp_Fixture, "A TEMPLATE_TEST_CASE_METHOD_SIG based test run that succeeds", "[class][template][nttp]",((int V), V), 1, 3, 6) {
|
||||
REQUIRE(Nttp_Fixture<V>::value > 0);
|
||||
}
|
||||
|
||||
template< typename T, size_t V>
|
||||
struct Template_Foo_2 {
|
||||
size_t size() { return V; }
|
||||
};
|
||||
|
||||
TEMPLATE_PRODUCT_TEST_CASE_METHOD_SIG(Template_Fixture_2, "A TEMPLATE_PRODUCT_TEST_CASE_METHOD_SIG based test run that succeeds", "[class][template][product][nttp]", ((typename T, size_t S), T, S),(std::array, Template_Foo_2), ((int,2), (float,6)))
|
||||
{
|
||||
REQUIRE(Template_Fixture_2<TestType>{}.m_a.size() >= 2);
|
||||
}
|
||||
```
|
||||
|
||||
Catch2 also provides `TEMPLATE_LIST_TEST_CASE_METHOD` to support template fixtures with types specified in
|
||||
template type lists like `std::tuple`, `boost::mpl::list` or `boost::mp11::mp_list`. This test case works the same as `TEMPLATE_TEST_CASE_METHOD`,
|
||||
only difference is the source of types. This allows you to reuse the template type list in multiple test cases.
|
||||
|
||||
Example:
|
||||
```cpp
|
||||
using MyTypes = std::tuple<int, char, double>;
|
||||
TEMPLATE_LIST_TEST_CASE_METHOD(Template_Fixture, "Template test case method with test types specified inside std::tuple", "[class][template][list]", MyTypes)
|
||||
{
|
||||
REQUIRE( Template_Fixture<TestType>::m_a == 1 );
|
||||
}
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
[Home](Readme.md#top)
|
||||
|
@@ -6,6 +6,9 @@
|
||||
[Catch::StringMaker specialisation](#catchstringmaker-specialisation)<br>
|
||||
[Catch::is_range specialisation](#catchis_range-specialisation)<br>
|
||||
[Exceptions](#exceptions)<br>
|
||||
[Enums](#enums)<br>
|
||||
[Floating point precision](#floating-point-precision)<br>
|
||||
|
||||
|
||||
Catch needs to be able to convert types you use in assertions and logging expressions into strings (for logging and reporting purposes).
|
||||
Most built-in or std types are supported out of the box but there are two ways that you can tell Catch how to convert your own types (or other, third-party types) into strings.
|
||||
@@ -14,7 +17,7 @@ Most built-in or std types are supported out of the box but there are two ways t
|
||||
|
||||
This is the standard way of providing string conversions in C++ - and the chances are you may already provide this for your own purposes. If you're not familiar with this idiom it involves writing a free function of the form:
|
||||
|
||||
```
|
||||
```cpp
|
||||
std::ostream& operator << ( std::ostream& os, T const& value ) {
|
||||
os << convertMyTypeToString( value );
|
||||
return os;
|
||||
@@ -28,7 +31,7 @@ You should put this function in the same namespace as your type, or the global n
|
||||
## Catch::StringMaker specialisation
|
||||
If you don't want to provide an ```operator <<``` overload, or you want to convert your type differently for testing purposes, you can provide a specialization for `Catch::StringMaker<T>`:
|
||||
|
||||
```
|
||||
```cpp
|
||||
namespace Catch {
|
||||
template<>
|
||||
struct StringMaker<T> {
|
||||
@@ -60,12 +63,66 @@ namespace Catch {
|
||||
|
||||
By default all exceptions deriving from `std::exception` will be translated to strings by calling the `what()` method. For exception types that do not derive from `std::exception` - or if `what()` does not return a suitable string - use `CATCH_TRANSLATE_EXCEPTION`. This defines a function that takes your exception type, by reference, and returns a string. It can appear anywhere in the code - it doesn't have to be in the same translation unit. For example:
|
||||
|
||||
```
|
||||
```cpp
|
||||
CATCH_TRANSLATE_EXCEPTION( MyType& ex ) {
|
||||
return ex.message();
|
||||
}
|
||||
```
|
||||
|
||||
## Enums
|
||||
|
||||
Enums that already have a `<<` overload for `std::ostream` will convert to strings as expected.
|
||||
If you only need to convert enums to strings for test reporting purposes you can provide a `StringMaker` specialisations as any other type.
|
||||
However, as a convenience, Catch provides the `REGISTER_ENUM` helper macro that will generate the `StringMaker` specialiation for you with minimal code.
|
||||
Simply provide it the (qualified) enum name, followed by all the enum values, and you're done!
|
||||
|
||||
E.g.
|
||||
|
||||
```cpp
|
||||
enum class Fruits { Banana, Apple, Mango };
|
||||
|
||||
CATCH_REGISTER_ENUM( Fruits, Fruits::Banana, Fruits::Apple, Fruits::Mango )
|
||||
|
||||
TEST_CASE() {
|
||||
REQUIRE( Fruits::Mango == Fruits::Apple );
|
||||
}
|
||||
```
|
||||
|
||||
... or if the enum is in a namespace:
|
||||
```cpp
|
||||
namespace Bikeshed {
|
||||
enum class Colours { Red, Green, Blue };
|
||||
}
|
||||
|
||||
// Important!: This macro must appear at top level scope - not inside a namespace
|
||||
// You can fully qualify the names, or use a using if you prefer
|
||||
CATCH_REGISTER_ENUM( Bikeshed::Colours,
|
||||
Bikeshed::Colours::Red,
|
||||
Bikeshed::Colours::Green,
|
||||
Bikeshed::Colours::Blue )
|
||||
|
||||
TEST_CASE() {
|
||||
REQUIRE( Bikeshed::Colours::Red == Bikeshed::Colours::Blue );
|
||||
}
|
||||
```
|
||||
|
||||
## Floating point precision
|
||||
|
||||
Catch provides a built-in `StringMaker` specialization for both `float`
|
||||
and `double`. By default, it uses what we think is a reasonable precision,
|
||||
but you can customize it by modifying the `precision` static variable
|
||||
inside the `StringMaker` specialization, like so:
|
||||
|
||||
```cpp
|
||||
Catch::StringMaker<float>::precision = 15;
|
||||
const float testFloat1 = 1.12345678901234567899f;
|
||||
const float testFloat2 = 1.12345678991234567899f;
|
||||
REQUIRE(testFloat1 == testFloat2);
|
||||
```
|
||||
|
||||
This assertion will fail and print out the `testFloat1` and `testFloat2`
|
||||
to 15 decimal places.
|
||||
|
||||
---
|
||||
|
||||
[Home](Readme.md#top)
|
||||
|
@@ -23,7 +23,7 @@ The full source for Catch2, including test projects, documentation, and other th
|
||||
|
||||
## Where to put it?
|
||||
|
||||
Catch2 is header only. All you need to do is drop the file somewhere reachable from your project - either in some central location you can set your header search path to find, or directly into your project tree itself! This is a particularly good option for other Open-Source projects that want to use Catch for their test suite. See [this blog entry for more on that](http://www.levelofindirection.com/journal/2011/5/27/unit-testing-in-c-and-objective-c-just-got-ridiculously-easi.html).
|
||||
Catch2 is header only. All you need to do is drop the file somewhere reachable from your project - either in some central location you can set your header search path to find, or directly into your project tree itself! This is a particularly good option for other Open-Source projects that want to use Catch for their test suite. See [this blog entry for more on that](https://levelofindirection.com/blog/unit-testing-in-cpp-and-objective-c-just-got-ridiculously-easier-still.html).
|
||||
|
||||
The rest of this tutorial will assume that the Catch2 single-include header (or the include folder) is available unqualified - but you may need to prefix it with a folder name if necessary.
|
||||
|
||||
@@ -103,7 +103,7 @@ Of course there are still more issues to deal with. For example we'll hit proble
|
||||
|
||||
### What did we do here?
|
||||
|
||||
Although this was a simple test it's been enough to demonstrate a few things about how Catch is used. Let's take moment to consider those before we move on.
|
||||
Although this was a simple test it's been enough to demonstrate a few things about how Catch is used. Let's take a moment to consider those before we move on.
|
||||
|
||||
1. All we did was ```#define``` one identifier and ```#include``` one header and we got everything - even an implementation of ```main()``` that will [respond to command line arguments](command-line.md#top). You can only use that ```#define``` in one implementation file, for (hopefully) obvious reasons. Once you have more than one file with unit tests in you'll just ```#include "catch.hpp"``` and go. Usually it's a good idea to have a dedicated implementation file that just has ```#define CATCH_CONFIG_MAIN``` and ```#include "catch.hpp"```. You can also provide your own implementation of main and drive Catch yourself (see [Supplying-your-own-main()](own-main.md#top)).
|
||||
2. We introduce test cases with the ```TEST_CASE``` macro. This macro takes one or two arguments - a free form test name and, optionally, one or more tags (for more see <a href="#test-cases-and-sections">Test cases and Sections</a>, ). The test name must be unique. You can run sets of tests by specifying a wildcarded test name or a tag expression. See the [command line docs](command-line.md#top) for more information on running tests.
|
||||
@@ -159,7 +159,7 @@ This works because the ```SECTION``` macro contains an if statement that calls b
|
||||
|
||||
So far so good - this is already an improvement on the setup/teardown approach because now we see our setup code inline and use the stack.
|
||||
|
||||
The power of sections really shows, however, when we need to execute a sequence of, checked, operations. Continuing the vector example, we might want to verify that attempting to reserve a capacity smaller than the current capacity of the vector changes nothing. We can do that, naturally, like so:
|
||||
The power of sections really shows, however, when we need to execute a sequence of checked operations. Continuing the vector example, we might want to verify that attempting to reserve a capacity smaller than the current capacity of the vector changes nothing. We can do that, naturally, like so:
|
||||
|
||||
```c++
|
||||
SECTION( "reserving bigger changes capacity but not size" ) {
|
||||
|
@@ -305,7 +305,7 @@ struct MyListener : Catch::TestEventListenerBase {
|
||||
~MyListener();
|
||||
|
||||
// The whole test run starting
|
||||
virtual void testRunStarting( Catch::TestRunInfo const& testRunInfo ) override {
|
||||
void testRunStarting( Catch::TestRunInfo const& testRunInfo ) override {
|
||||
std::cout
|
||||
<< std::boolalpha
|
||||
<< "\nEvent: testRunStarting:\n";
|
||||
@@ -313,7 +313,7 @@ struct MyListener : Catch::TestEventListenerBase {
|
||||
}
|
||||
|
||||
// The whole test run ending
|
||||
virtual void testRunEnded( Catch::TestRunStats const& testRunStats ) override {
|
||||
void testRunEnded( Catch::TestRunStats const& testRunStats ) override {
|
||||
std::cout
|
||||
<< dashed_line
|
||||
<< "\nEvent: testRunEnded:\n";
|
||||
@@ -321,7 +321,7 @@ struct MyListener : Catch::TestEventListenerBase {
|
||||
}
|
||||
|
||||
// A test is being skipped (because it is "hidden")
|
||||
virtual void skipTest( Catch::TestCaseInfo const& testInfo ) override {
|
||||
void skipTest( Catch::TestCaseInfo const& testInfo ) override {
|
||||
std::cout
|
||||
<< dashed_line
|
||||
<< "\nEvent: skipTest:\n";
|
||||
@@ -329,7 +329,7 @@ struct MyListener : Catch::TestEventListenerBase {
|
||||
}
|
||||
|
||||
// Test cases starting
|
||||
virtual void testCaseStarting( Catch::TestCaseInfo const& testInfo ) override {
|
||||
void testCaseStarting( Catch::TestCaseInfo const& testInfo ) override {
|
||||
std::cout
|
||||
<< dashed_line
|
||||
<< "\nEvent: testCaseStarting:\n";
|
||||
@@ -337,30 +337,30 @@ struct MyListener : Catch::TestEventListenerBase {
|
||||
}
|
||||
|
||||
// Test cases ending
|
||||
virtual void testCaseEnded( Catch::TestCaseStats const& testCaseStats ) override {
|
||||
void testCaseEnded( Catch::TestCaseStats const& testCaseStats ) override {
|
||||
std::cout << "\nEvent: testCaseEnded:\n";
|
||||
print( std::cout, 1, "testCaseStats", testCaseStats );
|
||||
}
|
||||
|
||||
// Sections starting
|
||||
virtual void sectionStarting( Catch::SectionInfo const& sectionInfo ) override {
|
||||
void sectionStarting( Catch::SectionInfo const& sectionInfo ) override {
|
||||
std::cout << "\nEvent: sectionStarting:\n";
|
||||
print( std::cout, 1, "- sectionInfo", sectionInfo );
|
||||
}
|
||||
|
||||
// Sections ending
|
||||
virtual void sectionEnded( Catch::SectionStats const& sectionStats ) override {
|
||||
void sectionEnded( Catch::SectionStats const& sectionStats ) override {
|
||||
std::cout << "\nEvent: sectionEnded:\n";
|
||||
print( std::cout, 1, "- sectionStats", sectionStats );
|
||||
}
|
||||
|
||||
// Assertions before/ after
|
||||
virtual void assertionStarting( Catch::AssertionInfo const& assertionInfo ) override {
|
||||
void assertionStarting( Catch::AssertionInfo const& assertionInfo ) override {
|
||||
std::cout << "\nEvent: assertionStarting:\n";
|
||||
print( std::cout, 1, "- assertionInfo", assertionInfo );
|
||||
}
|
||||
|
||||
virtual bool assertionEnded( Catch::AssertionStats const& assertionStats ) override {
|
||||
bool assertionEnded( Catch::AssertionStats const& assertionStats ) override {
|
||||
std::cout << "\nEvent: assertionEnded:\n";
|
||||
print( std::cout, 1, "- assertionStats", assertionStats );
|
||||
return true;
|
||||
|
54
examples/301-Gen-MapTypeConversion.cpp
Normal file
54
examples/301-Gen-MapTypeConversion.cpp
Normal file
@@ -0,0 +1,54 @@
|
||||
// 301-Gen-MapTypeConversion.cpp
|
||||
// Shows how to use map to modify generator's return type.
|
||||
|
||||
// TODO
|
||||
|
||||
#include <catch2/catch.hpp>
|
||||
|
||||
#include <string>
|
||||
#include <sstream>
|
||||
|
||||
// Returns a line from a stream. You could have it e.g. read lines from
|
||||
// a file, but to avoid problems with paths in examples, we will use
|
||||
// a fixed stringstream.
|
||||
class LineGenerator : public Catch::Generators::IGenerator<std::string> {
|
||||
std::string m_line;
|
||||
std::stringstream m_stream;
|
||||
public:
|
||||
LineGenerator() {
|
||||
m_stream.str("1\n2\n3\n4\n");
|
||||
if (!next()) {
|
||||
throw Catch::GeneratorException("Couldn't read a single line");
|
||||
}
|
||||
}
|
||||
|
||||
std::string const& get() const override {
|
||||
return m_line;
|
||||
}
|
||||
|
||||
bool next() override {
|
||||
return !!std::getline(m_stream, m_line);
|
||||
}
|
||||
};
|
||||
|
||||
// This helper function provides a nicer UX when instantiating the generator
|
||||
// Notice that it returns an instance of GeneratorWrapper<std::string>, which
|
||||
// is a value-wrapper around std::unique_ptr<IGenerator<std::string>>.
|
||||
Catch::Generators::GeneratorWrapper<std::string> lines(std::string /* ignored for example */) {
|
||||
return Catch::Generators::GeneratorWrapper<std::string>(
|
||||
std::unique_ptr<Catch::Generators::IGenerator<std::string>>(
|
||||
new LineGenerator()
|
||||
)
|
||||
);
|
||||
}
|
||||
|
||||
|
||||
|
||||
TEST_CASE("filter can convert types inside the generator expression", "[example][generator]") {
|
||||
auto num = GENERATE(map<int>([](std::string const& line) { return std::stoi(line); },
|
||||
lines("fake-file")));
|
||||
|
||||
REQUIRE(num > 0);
|
||||
}
|
||||
|
||||
// Compiling and running this file will result in 4 successful assertions
|
@@ -8,41 +8,6 @@
|
||||
|
||||
#include <catch2/catch.hpp>
|
||||
|
||||
#include <random>
|
||||
|
||||
// Lets start by implementing a parametrizable double generator
|
||||
class RandomDoubleGenerator : public Catch::Generators::IGenerator<double> {
|
||||
std::minstd_rand m_rand;
|
||||
std::uniform_real_distribution<> m_dist;
|
||||
double current_number;
|
||||
public:
|
||||
|
||||
RandomDoubleGenerator(double low, double high):
|
||||
m_rand(std::random_device{}()),
|
||||
m_dist(low, high)
|
||||
{
|
||||
static_cast<void>(next());
|
||||
}
|
||||
|
||||
double const& get() const override;
|
||||
bool next() override {
|
||||
current_number = m_dist(m_rand);
|
||||
return true;
|
||||
}
|
||||
};
|
||||
|
||||
// Avoids -Wweak-vtables
|
||||
double const& RandomDoubleGenerator::get() const {
|
||||
return current_number;
|
||||
}
|
||||
|
||||
|
||||
// Also provide a nice shortcut for creating the generator
|
||||
Catch::Generators::GeneratorWrapper<double> random(double low, double high) {
|
||||
return Catch::Generators::GeneratorWrapper<double>(std::unique_ptr<Catch::Generators::IGenerator<double>>(new RandomDoubleGenerator(low, high)));
|
||||
}
|
||||
|
||||
|
||||
TEST_CASE("Generate random doubles across different ranges",
|
||||
"[generator][example][advanced]") {
|
||||
// Workaround for old libstdc++
|
||||
@@ -55,16 +20,12 @@ TEST_CASE("Generate random doubles across different ranges",
|
||||
}));
|
||||
|
||||
// This will not compile (intentionally), because it accesses a variable
|
||||
// auto number = GENERATE(take(50, random(r.first, r.second)));
|
||||
|
||||
// We have to manually register the generators instead
|
||||
// Notice that we are using value capture in the lambda, to avoid lifetime issues
|
||||
auto number = Catch::Generators::generate( CATCH_INTERNAL_LINEINFO,
|
||||
[=]{
|
||||
using namespace Catch::Generators;
|
||||
return makeGenerators(take(50, random(std::get<0>(r), std::get<1>(r))));
|
||||
}
|
||||
);
|
||||
// auto number = GENERATE(take(50, random(std::get<0>(r), std::get<1>(r))));
|
||||
|
||||
// GENERATE_COPY copies all variables mentioned inside the expression
|
||||
// thus this will work.
|
||||
auto number = GENERATE_COPY(take(50, random(std::get<0>(r), std::get<1>(r))));
|
||||
|
||||
REQUIRE(std::abs(number) > 0);
|
||||
}
|
||||
|
||||
|
41
examples/311-Gen-CustomCapture.cpp
Normal file
41
examples/311-Gen-CustomCapture.cpp
Normal file
@@ -0,0 +1,41 @@
|
||||
// 311-Gen-CustomCapture.cpp
|
||||
// Shows how to provide custom capture list to the generator expression
|
||||
|
||||
// Note that using variables inside generators is dangerous and should
|
||||
// be done only if you know what you are doing, because the generators
|
||||
// _WILL_ outlive the variables. Also, even if you know what you are
|
||||
// doing, you should probably use GENERATE_COPY or GENERATE_REF macros
|
||||
// instead. However, if your use case requires having a
|
||||
// per-variable custom capture list, this example shows how to achieve
|
||||
// that.
|
||||
|
||||
#include <catch2/catch.hpp>
|
||||
|
||||
TEST_CASE("Generate random doubles across different ranges",
|
||||
"[generator][example][advanced]") {
|
||||
// Workaround for old libstdc++
|
||||
using record = std::tuple<double, double>;
|
||||
// Set up 3 ranges to generate numbers from
|
||||
auto r1 = GENERATE(table<double, double>({
|
||||
record{3, 4},
|
||||
record{-4, -3},
|
||||
record{10, 1000}
|
||||
}));
|
||||
|
||||
auto r2(r1);
|
||||
|
||||
// This will take r1 by reference and r2 by value.
|
||||
// Note that there are no advantages for doing so in this example,
|
||||
// it is done only for expository purposes.
|
||||
auto number = Catch::Generators::generate( CATCH_INTERNAL_LINEINFO,
|
||||
[&r1, r2]{
|
||||
using namespace Catch::Generators;
|
||||
return makeGenerators(take(50, random(std::get<0>(r1), std::get<1>(r2))));
|
||||
}
|
||||
);
|
||||
|
||||
REQUIRE(std::abs(number) > 0);
|
||||
}
|
||||
|
||||
// Compiling and running this file will result in 150 successful assertions
|
||||
|
@@ -45,7 +45,9 @@ set( SOURCES_IDIOMATIC_TESTS
|
||||
120-Bdd-ScenarioGivenWhenThen.cpp
|
||||
210-Evt-EventListeners.cpp
|
||||
300-Gen-OwnGenerator.cpp
|
||||
301-Gen-MapTypeConversion.cpp
|
||||
310-Gen-VariablesInGenerators.cpp
|
||||
311-Gen-CustomCapture.cpp
|
||||
)
|
||||
|
||||
# main-s for reporter-specific test sources:
|
||||
|
@@ -10,7 +10,7 @@
|
||||
#define TWOBLUECUBES_CATCH_HPP_INCLUDED
|
||||
|
||||
#define CATCH_VERSION_MAJOR 2
|
||||
#define CATCH_VERSION_MINOR 6
|
||||
#define CATCH_VERSION_MINOR 9
|
||||
#define CATCH_VERSION_PATCH 0
|
||||
|
||||
#ifdef __clang__
|
||||
@@ -53,7 +53,6 @@
|
||||
#include "internal/catch_test_registry.h"
|
||||
#include "internal/catch_capture.hpp"
|
||||
#include "internal/catch_section.h"
|
||||
#include "internal/catch_benchmark.h"
|
||||
#include "internal/catch_interfaces_exception.h"
|
||||
#include "internal/catch_approx.h"
|
||||
#include "internal/catch_compiler_capabilities.h"
|
||||
@@ -63,6 +62,8 @@
|
||||
#include "internal/catch_capture_matchers.h"
|
||||
#endif
|
||||
#include "internal/catch_generators.hpp"
|
||||
#include "internal/catch_generators_generic.hpp"
|
||||
#include "internal/catch_generators_specific.hpp"
|
||||
|
||||
// These files are included here so the single_include script doesn't put them
|
||||
// in the conditionally compiled sections
|
||||
@@ -77,6 +78,10 @@
|
||||
#include "internal/catch_external_interfaces.h"
|
||||
#endif
|
||||
|
||||
#if defined(CATCH_CONFIG_ENABLE_BENCHMARKING)
|
||||
#include "internal/benchmark/catch_benchmark.hpp"
|
||||
#endif
|
||||
|
||||
#endif // ! CATCH_CONFIG_IMPL_ONLY
|
||||
|
||||
#ifdef CATCH_IMPL
|
||||
@@ -87,6 +92,7 @@
|
||||
#include "internal/catch_default_main.hpp"
|
||||
#endif
|
||||
|
||||
|
||||
#if !defined(CATCH_CONFIG_IMPL_ONLY)
|
||||
|
||||
#ifdef CLARA_CONFIG_MAIN_NOT_DEFINED
|
||||
@@ -101,7 +107,7 @@
|
||||
#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( ... ) 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)
|
||||
@@ -115,7 +121,7 @@
|
||||
#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( ... ) 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)
|
||||
@@ -130,6 +136,7 @@
|
||||
#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__ )
|
||||
|
||||
@@ -147,14 +154,22 @@
|
||||
|
||||
#ifndef CATCH_CONFIG_TRADITIONAL_MSVC_PREPROCESSOR
|
||||
#define CATCH_TEMPLATE_TEST_CASE( ... ) INTERNAL_CATCH_TEMPLATE_TEST_CASE( __VA_ARGS__ )
|
||||
#define CATCH_TEMPLATE_TEST_CASE_SIG( ... ) INTERNAL_CATCH_TEMPLATE_TEST_CASE_SIG( __VA_ARGS__ )
|
||||
#define CATCH_TEMPLATE_TEST_CASE_METHOD( className, ... ) INTERNAL_CATCH_TEMPLATE_TEST_CASE_METHOD( className, __VA_ARGS__ )
|
||||
#define CATCH_TEMPLATE_TEST_CASE_METHOD_SIG( className, ... ) INTERNAL_CATCH_TEMPLATE_TEST_CASE_METHOD_SIG( className, __VA_ARGS__ )
|
||||
#define CATCH_TEMPLATE_PRODUCT_TEST_CASE( ... ) INTERNAL_CATCH_TEMPLATE_PRODUCT_TEST_CASE( __VA_ARGS__ )
|
||||
#define CATCH_TEMPLATE_PRODUCT_TEST_CASE_SIG( ... ) INTERNAL_CATCH_TEMPLATE_PRODUCT_TEST_CASE_SIG( __VA_ARGS__ )
|
||||
#define CATCH_TEMPLATE_PRODUCT_TEST_CASE_METHOD( className, ... ) INTERNAL_CATCH_TEMPLATE_PRODUCT_TEST_CASE_METHOD( className, __VA_ARGS__ )
|
||||
#define CATCH_TEMPLATE_PRODUCT_TEST_CASE_METHOD_SIG( className, ... ) INTERNAL_CATCH_TEMPLATE_PRODUCT_TEST_CASE_METHOD_SIG( className, __VA_ARGS__ )
|
||||
#else
|
||||
#define CATCH_TEMPLATE_TEST_CASE( ... ) INTERNAL_CATCH_EXPAND_VARGS( INTERNAL_CATCH_TEMPLATE_TEST_CASE( __VA_ARGS__ ) )
|
||||
#define CATCH_TEMPLATE_TEST_CASE_SIG( ... ) INTERNAL_CATCH_EXPAND_VARGS( INTERNAL_CATCH_TEMPLATE_TEST_CASE_SIG( __VA_ARGS__ ) )
|
||||
#define CATCH_TEMPLATE_TEST_CASE_METHOD( className, ... ) INTERNAL_CATCH_EXPAND_VARGS( INTERNAL_CATCH_TEMPLATE_TEST_CASE_METHOD( className, __VA_ARGS__ ) )
|
||||
#define CATCH_TEMPLATE_TEST_CASE_METHOD_SIG( className, ... ) INTERNAL_CATCH_EXPAND_VARGS( INTERNAL_CATCH_TEMPLATE_TEST_CASE_METHOD_SIG( className, __VA_ARGS__ ) )
|
||||
#define CATCH_TEMPLATE_PRODUCT_TEST_CASE( ... ) INTERNAL_CATCH_EXPAND_VARGS( INTERNAL_CATCH_TEMPLATE_PRODUCT_TEST_CASE( __VA_ARGS__ ) )
|
||||
#define CATCH_TEMPLATE_PRODUCT_TEST_CASE_SIG( ... ) INTERNAL_CATCH_EXPAND_VARGS( INTERNAL_CATCH_TEMPLATE_PRODUCT_TEST_CASE_SIG( __VA_ARGS__ ) )
|
||||
#define CATCH_TEMPLATE_PRODUCT_TEST_CASE_METHOD( className, ... ) INTERNAL_CATCH_EXPAND_VARGS( INTERNAL_CATCH_TEMPLATE_PRODUCT_TEST_CASE_METHOD( className, __VA_ARGS__ ) )
|
||||
#define CATCH_TEMPLATE_PRODUCT_TEST_CASE_METHOD_SIG( className, ... ) INTERNAL_CATCH_EXPAND_VARGS( INTERNAL_CATCH_TEMPLATE_PRODUCT_TEST_CASE_METHOD_SIG( className, __VA_ARGS__ ) )
|
||||
#endif
|
||||
|
||||
|
||||
@@ -177,6 +192,13 @@
|
||||
#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
|
||||
|
||||
@@ -213,6 +235,7 @@
|
||||
#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__ )
|
||||
|
||||
@@ -229,14 +252,26 @@
|
||||
|
||||
#ifndef CATCH_CONFIG_TRADITIONAL_MSVC_PREPROCESSOR
|
||||
#define TEMPLATE_TEST_CASE( ... ) INTERNAL_CATCH_TEMPLATE_TEST_CASE( __VA_ARGS__ )
|
||||
#define TEMPLATE_TEST_CASE_SIG( ... ) INTERNAL_CATCH_TEMPLATE_TEST_CASE_SIG( __VA_ARGS__ )
|
||||
#define TEMPLATE_TEST_CASE_METHOD( className, ... ) INTERNAL_CATCH_TEMPLATE_TEST_CASE_METHOD( className, __VA_ARGS__ )
|
||||
#define TEMPLATE_TEST_CASE_METHOD_SIG( className, ... ) INTERNAL_CATCH_TEMPLATE_TEST_CASE_METHOD_SIG( className, __VA_ARGS__ )
|
||||
#define TEMPLATE_PRODUCT_TEST_CASE( ... ) INTERNAL_CATCH_TEMPLATE_PRODUCT_TEST_CASE( __VA_ARGS__ )
|
||||
#define TEMPLATE_PRODUCT_TEST_CASE_SIG( ... ) INTERNAL_CATCH_TEMPLATE_PRODUCT_TEST_CASE_SIG( __VA_ARGS__ )
|
||||
#define TEMPLATE_PRODUCT_TEST_CASE_METHOD( className, ... ) INTERNAL_CATCH_TEMPLATE_PRODUCT_TEST_CASE_METHOD( className, __VA_ARGS__ )
|
||||
#define TEMPLATE_PRODUCT_TEST_CASE_METHOD_SIG( className, ... ) INTERNAL_CATCH_TEMPLATE_PRODUCT_TEST_CASE_METHOD_SIG( className, __VA_ARGS__ )
|
||||
#define TEMPLATE_LIST_TEST_CASE( ... ) INTERNAL_CATCH_TEMPLATE_LIST_TEST_CASE(__VA_ARGS__)
|
||||
#define TEMPLATE_LIST_TEST_CASE_METHOD( className, ... ) INTERNAL_CATCH_TEMPLATE_LIST_TEST_CASE_METHOD( className, __VA_ARGS__ )
|
||||
#else
|
||||
#define TEMPLATE_TEST_CASE( ... ) INTERNAL_CATCH_EXPAND_VARGS( INTERNAL_CATCH_TEMPLATE_TEST_CASE( __VA_ARGS__ ) )
|
||||
#define TEMPLATE_TEST_CASE_SIG( ... ) INTERNAL_CATCH_EXPAND_VARGS( INTERNAL_CATCH_TEMPLATE_TEST_CASE_SIG( __VA_ARGS__ ) )
|
||||
#define TEMPLATE_TEST_CASE_METHOD( className, ... ) INTERNAL_CATCH_EXPAND_VARGS( INTERNAL_CATCH_TEMPLATE_TEST_CASE_METHOD( className, __VA_ARGS__ ) )
|
||||
#define TEMPLATE_TEST_CASE_METHOD_SIG( className, ... ) INTERNAL_CATCH_EXPAND_VARGS( INTERNAL_CATCH_TEMPLATE_TEST_CASE_METHOD_SIG( className, __VA_ARGS__ ) )
|
||||
#define TEMPLATE_PRODUCT_TEST_CASE( ... ) INTERNAL_CATCH_EXPAND_VARGS( INTERNAL_CATCH_TEMPLATE_PRODUCT_TEST_CASE( __VA_ARGS__ ) )
|
||||
#define TEMPLATE_PRODUCT_TEST_CASE_SIG( ... ) INTERNAL_CATCH_EXPAND_VARGS( INTERNAL_CATCH_TEMPLATE_PRODUCT_TEST_CASE_SIG( __VA_ARGS__ ) )
|
||||
#define TEMPLATE_PRODUCT_TEST_CASE_METHOD( className, ... ) INTERNAL_CATCH_EXPAND_VARGS( INTERNAL_CATCH_TEMPLATE_PRODUCT_TEST_CASE_METHOD( className, __VA_ARGS__ ) )
|
||||
#define TEMPLATE_PRODUCT_TEST_CASE_METHOD_SIG( className, ... ) INTERNAL_CATCH_EXPAND_VARGS( INTERNAL_CATCH_TEMPLATE_PRODUCT_TEST_CASE_METHOD_SIG( className, __VA_ARGS__ ) )
|
||||
#define TEMPLATE_LIST_TEST_CASE( ... ) INTERNAL_CATCH_EXPAND_VARGS( INTERNAL_CATCH_TEMPLATE_LIST_TEST_CASE( __VA_ARGS__ ) )
|
||||
#define TEMPLATE_LIST_TEST_CASE_METHOD( className, ... ) INTERNAL_CATCH_EXPAND_VARGS( INTERNAL_CATCH_TEMPLATE_LIST_TEST_CASE_METHOD( className, __VA_ARGS__ ) )
|
||||
#endif
|
||||
|
||||
|
||||
@@ -263,6 +298,13 @@
|
||||
#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
|
||||
@@ -302,9 +344,10 @@ using Catch::Detail::Approx;
|
||||
#define CATCH_REQUIRE_THAT( arg, matcher ) (void)(0)
|
||||
#endif // CATCH_CONFIG_DISABLE_MATCHERS
|
||||
|
||||
#define CATCH_INFO( msg ) (void)(0)
|
||||
#define CATCH_WARN( msg ) (void)(0)
|
||||
#define CATCH_CAPTURE( msg ) (void)(0)
|
||||
#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____ ))
|
||||
@@ -319,15 +362,23 @@ using Catch::Detail::Approx;
|
||||
#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(INTERNAL_CATCH_UNIQUE_NAME( ____C_A_T_C_H____T_E_M_P_L_A_T_E____T_E_S_T____ ) )
|
||||
#define CATCH_TEMPLATE_TEST_CASE_METHOD( className, ... ) INTERNAL_CATCH_TEMPLATE_TEST_CASE_METHOD_NO_REGISTRATION(INTERNAL_CATCH_UNIQUE_NAME( ____C_A_T_C_H____T_E_M_P_L_A_T_E____T_E_S_T____ ), className )
|
||||
#define CATCH_TEMPLATE_TEST_CASE( ... ) INTERNAL_CATCH_TEMPLATE_TEST_CASE_NO_REGISTRATION(__VA_ARGS__)
|
||||
#define CATCH_TEMPLATE_TEST_CASE_SIG( ... ) INTERNAL_CATCH_TEMPLATE_TEST_CASE_SIG_NO_REGISTRATION(__VA_ARGS__)
|
||||
#define CATCH_TEMPLATE_TEST_CASE_METHOD( className, ... ) INTERNAL_CATCH_TEMPLATE_TEST_CASE_METHOD_NO_REGISTRATION(className, __VA_ARGS__)
|
||||
#define CATCH_TEMPLATE_TEST_CASE_METHOD_SIG( className, ... ) INTERNAL_CATCH_TEMPLATE_TEST_CASE_METHOD_SIG_NO_REGISTRATION(className, __VA_ARGS__ )
|
||||
#define CATCH_TEMPLATE_PRODUCT_TEST_CASE( ... ) CATCH_TEMPLATE_TEST_CASE( __VA_ARGS__ )
|
||||
#define CATCH_TEMPLATE_PRODUCT_TEST_CASE_SIG( ... ) CATCH_TEMPLATE_TEST_CASE( __VA_ARGS__ )
|
||||
#define CATCH_TEMPLATE_PRODUCT_TEST_CASE_METHOD( className, ... ) CATCH_TEMPLATE_TEST_CASE_METHOD( className, __VA_ARGS__ )
|
||||
#define CATCH_TEMPLATE_PRODUCT_TEST_CASE_METHOD_SIG( className, ... ) CATCH_TEMPLATE_TEST_CASE_METHOD( className, __VA_ARGS__ )
|
||||
#else
|
||||
#define CATCH_TEMPLATE_TEST_CASE( ... ) INTERNAL_CATCH_EXPAND_VARGS( INTERNAL_CATCH_TEMPLATE_TEST_CASE_NO_REGISTRATION(INTERNAL_CATCH_UNIQUE_NAME( ____C_A_T_C_H____T_E_M_P_L_A_T_E____T_E_S_T____ ) ) )
|
||||
#define CATCH_TEMPLATE_TEST_CASE_METHOD( className, ... ) INTERNAL_CATCH_EXPAND_VARGS( INTERNAL_CATCH_TEMPLATE_TEST_CASE_METHOD_NO_REGISTRATION(INTERNAL_CATCH_UNIQUE_NAME( ____C_A_T_C_H____T_E_M_P_L_A_T_E____T_E_S_T____ ), className ) )
|
||||
#define CATCH_TEMPLATE_TEST_CASE( ... ) INTERNAL_CATCH_EXPAND_VARGS( INTERNAL_CATCH_TEMPLATE_TEST_CASE_NO_REGISTRATION(__VA_ARGS__) )
|
||||
#define CATCH_TEMPLATE_TEST_CASE_SIG( ... ) INTERNAL_CATCH_EXPAND_VARGS( INTERNAL_CATCH_TEMPLATE_TEST_CASE_SIG_NO_REGISTRATION(__VA_ARGS__) )
|
||||
#define CATCH_TEMPLATE_TEST_CASE_METHOD( className, ... ) INTERNAL_CATCH_EXPAND_VARGS( INTERNAL_CATCH_TEMPLATE_TEST_CASE_METHOD_NO_REGISTRATION(className, __VA_ARGS__ ) )
|
||||
#define CATCH_TEMPLATE_TEST_CASE_METHOD_SIG( className, ... ) INTERNAL_CATCH_EXPAND_VARGS( INTERNAL_CATCH_TEMPLATE_TEST_CASE_METHOD_SIG_NO_REGISTRATION(className, __VA_ARGS__ ) )
|
||||
#define CATCH_TEMPLATE_PRODUCT_TEST_CASE( ... ) CATCH_TEMPLATE_TEST_CASE( __VA_ARGS__ )
|
||||
#define CATCH_TEMPLATE_PRODUCT_TEST_CASE_SIG( ... ) CATCH_TEMPLATE_TEST_CASE( __VA_ARGS__ )
|
||||
#define CATCH_TEMPLATE_PRODUCT_TEST_CASE_METHOD( className, ... ) CATCH_TEMPLATE_TEST_CASE_METHOD( className, __VA_ARGS__ )
|
||||
#define CATCH_TEMPLATE_PRODUCT_TEST_CASE_METHOD_SIG( className, ... ) CATCH_TEMPLATE_TEST_CASE_METHOD( className, __VA_ARGS__ )
|
||||
#endif
|
||||
|
||||
// "BDD-style" convenience wrappers
|
||||
@@ -379,6 +430,7 @@ using Catch::Detail::Approx;
|
||||
#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)
|
||||
|
||||
@@ -394,15 +446,23 @@ using Catch::Detail::Approx;
|
||||
#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(INTERNAL_CATCH_UNIQUE_NAME( ____C_A_T_C_H____T_E_M_P_L_A_T_E____T_E_S_T____ ) )
|
||||
#define TEMPLATE_TEST_CASE_METHOD( className, ... ) INTERNAL_CATCH_TEMPLATE_TEST_CASE_METHOD_NO_REGISTRATION(INTERNAL_CATCH_UNIQUE_NAME( ____C_A_T_C_H____T_E_M_P_L_A_T_E____T_E_S_T____ ), className )
|
||||
#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(INTERNAL_CATCH_UNIQUE_NAME( ____C_A_T_C_H____T_E_M_P_L_A_T_E____T_E_S_T____ ) ) )
|
||||
#define TEMPLATE_TEST_CASE_METHOD( className, ... ) INTERNAL_CATCH_EXPAND_VARGS( INTERNAL_CATCH_TEMPLATE_TEST_CASE_METHOD_NO_REGISTRATION(INTERNAL_CATCH_UNIQUE_NAME( ____C_A_T_C_H____T_E_M_P_L_A_T_E____T_E_S_T____ ), className ) )
|
||||
#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)
|
||||
|
7
include/external/clara.hpp
vendored
7
include/external/clara.hpp
vendored
@@ -111,6 +111,9 @@ public:
|
||||
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;
|
||||
|
||||
@@ -370,7 +373,7 @@ inline auto Column::operator + (Column const& other) -> Columns {
|
||||
// ----------- end of #include from clara_textflow.hpp -----------
|
||||
// ........... back in clara.hpp
|
||||
|
||||
|
||||
#include <cctype>
|
||||
#include <string>
|
||||
#include <memory>
|
||||
#include <set>
|
||||
@@ -664,7 +667,7 @@ namespace detail {
|
||||
}
|
||||
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>( ::tolower(c) ); } );
|
||||
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")
|
||||
|
122
include/internal/benchmark/catch_benchmark.hpp
Normal file
122
include/internal/benchmark/catch_benchmark.hpp
Normal file
@@ -0,0 +1,122 @@
|
||||
/*
|
||||
* Created by Joachim on 16/04/2019.
|
||||
* Adapted from donated nonius code.
|
||||
*
|
||||
* 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)
|
||||
*/
|
||||
|
||||
// Benchmark
|
||||
#ifndef TWOBLUECUBES_CATCH_BENCHMARK_HPP_INCLUDED
|
||||
#define TWOBLUECUBES_CATCH_BENCHMARK_HPP_INCLUDED
|
||||
|
||||
#include "../catch_config.hpp"
|
||||
#include "../catch_context.h"
|
||||
#include "../catch_interfaces_reporter.h"
|
||||
#include "../catch_test_registry.h"
|
||||
|
||||
#include "catch_chronometer.hpp"
|
||||
#include "catch_clock.hpp"
|
||||
#include "catch_environment.hpp"
|
||||
#include "catch_execution_plan.hpp"
|
||||
#include "detail/catch_estimate_clock.hpp"
|
||||
#include "detail/catch_complete_invoke.hpp"
|
||||
#include "detail/catch_analyse.hpp"
|
||||
#include "detail/catch_benchmark_function.hpp"
|
||||
#include "detail/catch_run_for_at_least.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
|
||||
|
||||
#define INTERNAL_CATCH_GET_1_ARG(arg1, arg2, ...) arg1
|
||||
#define INTERNAL_CATCH_GET_2_ARG(arg1, arg2, ...) arg2
|
||||
|
||||
#define INTERNAL_CATCH_BENCHMARK(BenchmarkName, name, benchmarkIndex)\
|
||||
if( Catch::Benchmark::Benchmark BenchmarkName{name} ) \
|
||||
BenchmarkName = [&](int benchmarkIndex)
|
||||
|
||||
#define INTERNAL_CATCH_BENCHMARK_ADVANCED(BenchmarkName, name)\
|
||||
if( Catch::Benchmark::Benchmark BenchmarkName{name} ) \
|
||||
BenchmarkName = [&]
|
||||
|
||||
#endif // TWOBLUECUBES_CATCH_BENCHMARK_HPP_INCLUDED
|
71
include/internal/benchmark/catch_chronometer.hpp
Normal file
71
include/internal/benchmark/catch_chronometer.hpp
Normal file
@@ -0,0 +1,71 @@
|
||||
/*
|
||||
* Created by Joachim on 16/04/2019.
|
||||
* Adapted from donated nonius code.
|
||||
*
|
||||
* 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)
|
||||
*/
|
||||
|
||||
// User-facing chronometer
|
||||
|
||||
#ifndef TWOBLUECUBES_CATCH_CHRONOMETER_HPP_INCLUDED
|
||||
#define TWOBLUECUBES_CATCH_CHRONOMETER_HPP_INCLUDED
|
||||
|
||||
#include "catch_clock.hpp"
|
||||
#include "catch_optimizer.hpp"
|
||||
#include "detail/catch_complete_invoke.hpp"
|
||||
#include "../catch_meta.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());
|
||||
}
|
||||
|
||||
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();
|
||||
}
|
||||
|
||||
Detail::ChronometerConcept* impl;
|
||||
int k;
|
||||
};
|
||||
} // namespace Benchmark
|
||||
} // namespace Catch
|
||||
|
||||
#endif // TWOBLUECUBES_CATCH_CHRONOMETER_HPP_INCLUDED
|
40
include/internal/benchmark/catch_clock.hpp
Normal file
40
include/internal/benchmark/catch_clock.hpp
Normal file
@@ -0,0 +1,40 @@
|
||||
/*
|
||||
* Created by Joachim on 16/04/2019.
|
||||
* Adapted from donated nonius code.
|
||||
*
|
||||
* 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)
|
||||
*/
|
||||
|
||||
// Clocks
|
||||
|
||||
#ifndef TWOBLUECUBES_CATCH_CLOCK_HPP_INCLUDED
|
||||
#define TWOBLUECUBES_CATCH_CLOCK_HPP_INCLUDED
|
||||
|
||||
#include <chrono>
|
||||
#include <ratio>
|
||||
|
||||
namespace Catch {
|
||||
namespace Benchmark {
|
||||
template <typename Clock>
|
||||
using ClockDuration = typename Clock::duration;
|
||||
template <typename Clock>
|
||||
using FloatDuration = std::chrono::duration<double, typename Clock::period>;
|
||||
|
||||
template <typename Clock>
|
||||
using TimePoint = typename Clock::time_point;
|
||||
|
||||
using default_clock = std::chrono::steady_clock;
|
||||
|
||||
template <typename Clock>
|
||||
struct now {
|
||||
TimePoint<Clock> operator()() const {
|
||||
return Clock::now();
|
||||
}
|
||||
};
|
||||
|
||||
using fp_seconds = std::chrono::duration<double, std::ratio<1>>;
|
||||
} // namespace Benchmark
|
||||
} // namespace Catch
|
||||
|
||||
#endif // TWOBLUECUBES_CATCH_CLOCK_HPP_INCLUDED
|
73
include/internal/benchmark/catch_constructor.hpp
Normal file
73
include/internal/benchmark/catch_constructor.hpp
Normal file
@@ -0,0 +1,73 @@
|
||||
/*
|
||||
* Created by Joachim on 16/04/2019.
|
||||
* Adapted from donated nonius code.
|
||||
*
|
||||
* 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)
|
||||
*/
|
||||
|
||||
// Constructor and destructor helpers
|
||||
|
||||
#ifndef TWOBLUECUBES_CATCH_CONSTRUCTOR_HPP_INCLUDED
|
||||
#define TWOBLUECUBES_CATCH_CONSTRUCTOR_HPP_INCLUDED
|
||||
|
||||
#include <type_traits>
|
||||
|
||||
namespace Catch {
|
||||
namespace Detail {
|
||||
template <typename T, bool Destruct>
|
||||
struct ObjectStorage
|
||||
{
|
||||
using TStorage = typename std::aligned_storage<sizeof(T), std::alignment_of<T>::value>::type;
|
||||
|
||||
ObjectStorage() : data() {}
|
||||
|
||||
ObjectStorage(const ObjectStorage& other)
|
||||
{
|
||||
new(&data) T(other.stored_object());
|
||||
}
|
||||
|
||||
ObjectStorage(ObjectStorage&& other)
|
||||
{
|
||||
new(&data) T(std::move(other.stored_object()));
|
||||
}
|
||||
|
||||
~ObjectStorage() { destruct_on_exit<T>(); }
|
||||
|
||||
template <typename... Args>
|
||||
void construct(Args&&... args)
|
||||
{
|
||||
new (&data) T(std::forward<Args>(args)...);
|
||||
}
|
||||
|
||||
template <bool AllowManualDestruction = !Destruct>
|
||||
typename std::enable_if<AllowManualDestruction>::type destruct()
|
||||
{
|
||||
stored_object().~T();
|
||||
}
|
||||
|
||||
private:
|
||||
// If this is a constructor benchmark, destruct the underlying object
|
||||
template <typename U>
|
||||
void destruct_on_exit(typename std::enable_if<Destruct, U>::type* = 0) { destruct<true>(); }
|
||||
// Otherwise, don't
|
||||
template <typename U>
|
||||
void destruct_on_exit(typename std::enable_if<!Destruct, U>::type* = 0) { }
|
||||
|
||||
T& stored_object()
|
||||
{
|
||||
return *static_cast<T*>(static_cast<void*>(&data));
|
||||
}
|
||||
|
||||
TStorage data;
|
||||
};
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
using storage_for = Detail::ObjectStorage<T, true>;
|
||||
|
||||
template <typename T>
|
||||
using destructable_object = Detail::ObjectStorage<T, false>;
|
||||
}
|
||||
|
||||
#endif // TWOBLUECUBES_CATCH_CONSTRUCTOR_HPP_INCLUDED
|
38
include/internal/benchmark/catch_environment.hpp
Normal file
38
include/internal/benchmark/catch_environment.hpp
Normal file
@@ -0,0 +1,38 @@
|
||||
/*
|
||||
* Created by Joachim on 16/04/2019.
|
||||
* Adapted from donated nonius code.
|
||||
*
|
||||
* 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)
|
||||
*/
|
||||
|
||||
// Environment information
|
||||
|
||||
#ifndef TWOBLUECUBES_CATCH_ENVIRONMENT_HPP_INCLUDED
|
||||
#define TWOBLUECUBES_CATCH_ENVIRONMENT_HPP_INCLUDED
|
||||
|
||||
#include "catch_clock.hpp"
|
||||
#include "catch_outlier_classification.hpp"
|
||||
|
||||
namespace Catch {
|
||||
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
|
||||
|
||||
#endif // TWOBLUECUBES_CATCH_ENVIRONMENT_HPP_INCLUDED
|
31
include/internal/benchmark/catch_estimate.hpp
Normal file
31
include/internal/benchmark/catch_estimate.hpp
Normal file
@@ -0,0 +1,31 @@
|
||||
/*
|
||||
* Created by Joachim on 16/04/2019.
|
||||
* Adapted from donated nonius code.
|
||||
*
|
||||
* 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)
|
||||
*/
|
||||
|
||||
// Statistics estimates
|
||||
|
||||
#ifndef TWOBLUECUBES_CATCH_ESTIMATE_HPP_INCLUDED
|
||||
#define TWOBLUECUBES_CATCH_ESTIMATE_HPP_INCLUDED
|
||||
|
||||
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
|
||||
|
||||
#endif // TWOBLUECUBES_CATCH_ESTIMATE_HPP_INCLUDED
|
58
include/internal/benchmark/catch_execution_plan.hpp
Normal file
58
include/internal/benchmark/catch_execution_plan.hpp
Normal file
@@ -0,0 +1,58 @@
|
||||
/*
|
||||
* Created by Joachim on 16/04/2019.
|
||||
* Adapted from donated nonius code.
|
||||
*
|
||||
* 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)
|
||||
*/
|
||||
|
||||
// Execution plan
|
||||
|
||||
#ifndef TWOBLUECUBES_CATCH_EXECUTION_PLAN_HPP_INCLUDED
|
||||
#define TWOBLUECUBES_CATCH_EXECUTION_PLAN_HPP_INCLUDED
|
||||
|
||||
#include "../catch_config.hpp"
|
||||
#include "catch_clock.hpp"
|
||||
#include "catch_environment.hpp"
|
||||
#include "detail/catch_benchmark_function.hpp"
|
||||
#include "detail/catch_repeat.hpp"
|
||||
#include "detail/catch_run_for_at_least.hpp"
|
||||
|
||||
#include <algorithm>
|
||||
|
||||
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 };
|
||||
}
|
||||
|
||||
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
|
||||
|
||||
#endif // TWOBLUECUBES_CATCH_EXECUTION_PLAN_HPP_INCLUDED
|
68
include/internal/benchmark/catch_optimizer.hpp
Normal file
68
include/internal/benchmark/catch_optimizer.hpp
Normal file
@@ -0,0 +1,68 @@
|
||||
/*
|
||||
* Created by Joachim on 16/04/2019.
|
||||
* Adapted from donated nonius code.
|
||||
*
|
||||
* 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)
|
||||
*/
|
||||
|
||||
// Hinting the optimizer
|
||||
|
||||
#ifndef TWOBLUECUBES_CATCH_OPTIMIZER_HPP_INCLUDED
|
||||
#define TWOBLUECUBES_CATCH_OPTIMIZER_HPP_INCLUDED
|
||||
|
||||
#if defined(_MSC_VER)
|
||||
# include <atomic> // atomic_thread_fence
|
||||
#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 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
|
||||
|
||||
#endif
|
||||
|
||||
template <typename T>
|
||||
inline void deoptimize_value(T&& x) {
|
||||
keep_memory(&x);
|
||||
}
|
||||
|
||||
template <typename Fn, typename... Args>
|
||||
inline auto invoke_deoptimized(Fn&& fn, Args&&... args) -> typename std::enable_if<!std::is_same<void, decltype(fn(args...))>::value>::type {
|
||||
deoptimize_value(std::forward<Fn>(fn) (std::forward<Args...>(args...)));
|
||||
}
|
||||
|
||||
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
|
||||
|
||||
#endif // TWOBLUECUBES_CATCH_OPTIMIZER_HPP_INCLUDED
|
29
include/internal/benchmark/catch_outlier_classification.hpp
Normal file
29
include/internal/benchmark/catch_outlier_classification.hpp
Normal file
@@ -0,0 +1,29 @@
|
||||
/*
|
||||
* Created by Joachim on 16/04/2019.
|
||||
* Adapted from donated nonius code.
|
||||
*
|
||||
* 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)
|
||||
*/
|
||||
|
||||
// Outlier information
|
||||
#ifndef TWOBLUECUBES_CATCH_OUTLIERS_HPP_INCLUDED
|
||||
#define TWOBLUECUBES_CATCH_OUTLIERS_HPP_INCLUDED
|
||||
|
||||
namespace Catch {
|
||||
namespace Benchmark {
|
||||
struct OutlierClassification {
|
||||
int samples_seen = 0;
|
||||
int low_severe = 0; // more than 3 times IQR below Q1
|
||||
int low_mild = 0; // 1.5 to 3 times IQR below Q1
|
||||
int high_mild = 0; // 1.5 to 3 times IQR above Q3
|
||||
int high_severe = 0; // more than 3 times IQR above Q3
|
||||
|
||||
int total() const {
|
||||
return low_severe + low_mild + high_mild + high_severe;
|
||||
}
|
||||
};
|
||||
} // namespace Benchmark
|
||||
} // namespace Catch
|
||||
|
||||
#endif // TWOBLUECUBES_CATCH_OUTLIERS_HPP_INCLUDED
|
50
include/internal/benchmark/catch_sample_analysis.hpp
Normal file
50
include/internal/benchmark/catch_sample_analysis.hpp
Normal file
@@ -0,0 +1,50 @@
|
||||
/*
|
||||
* Created by Joachim on 16/04/2019.
|
||||
* Adapted from donated nonius code.
|
||||
*
|
||||
* 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)
|
||||
*/
|
||||
|
||||
// Benchmark results
|
||||
|
||||
#ifndef TWOBLUECUBES_CATCH_BENCHMARK_RESULTS_HPP_INCLUDED
|
||||
#define TWOBLUECUBES_CATCH_BENCHMARK_RESULTS_HPP_INCLUDED
|
||||
|
||||
#include "catch_clock.hpp"
|
||||
#include "catch_estimate.hpp"
|
||||
#include "catch_outlier_classification.hpp"
|
||||
|
||||
#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
|
||||
|
||||
#endif // TWOBLUECUBES_CATCH_BENCHMARK_RESULTS_HPP_INCLUDED
|
78
include/internal/benchmark/detail/catch_analyse.hpp
Normal file
78
include/internal/benchmark/detail/catch_analyse.hpp
Normal file
@@ -0,0 +1,78 @@
|
||||
/*
|
||||
* Created by Joachim on 16/04/2019.
|
||||
* Adapted from donated nonius code.
|
||||
*
|
||||
* 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)
|
||||
*/
|
||||
|
||||
// Run and analyse one benchmark
|
||||
|
||||
#ifndef TWOBLUECUBES_CATCH_DETAIL_ANALYSE_HPP_INCLUDED
|
||||
#define TWOBLUECUBES_CATCH_DETAIL_ANALYSE_HPP_INCLUDED
|
||||
|
||||
#include "../catch_clock.hpp"
|
||||
#include "../catch_sample_analysis.hpp"
|
||||
#include "catch_stats.hpp"
|
||||
|
||||
#include <algorithm>
|
||||
#include <iterator>
|
||||
#include <vector>
|
||||
|
||||
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
|
||||
|
||||
#endif // TWOBLUECUBES_CATCH_DETAIL_ANALYSE_HPP_INCLUDED
|
105
include/internal/benchmark/detail/catch_benchmark_function.hpp
Normal file
105
include/internal/benchmark/detail/catch_benchmark_function.hpp
Normal file
@@ -0,0 +1,105 @@
|
||||
/*
|
||||
* Created by Joachim on 16/04/2019.
|
||||
* Adapted from donated nonius code.
|
||||
*
|
||||
* 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)
|
||||
*/
|
||||
|
||||
// Dumb std::function implementation for consistent call overhead
|
||||
|
||||
#ifndef TWOBLUECUBES_CATCH_DETAIL_BENCHMARK_FUNCTION_HPP_INCLUDED
|
||||
#define TWOBLUECUBES_CATCH_DETAIL_BENCHMARK_FUNCTION_HPP_INCLUDED
|
||||
|
||||
#include "../catch_chronometer.hpp"
|
||||
#include "catch_complete_invoke.hpp"
|
||||
#include "../../catch_meta.hpp"
|
||||
|
||||
#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);
|
||||
}
|
||||
|
||||
Fun fun;
|
||||
};
|
||||
|
||||
struct do_nothing { void operator()() const {} };
|
||||
|
||||
template <typename T>
|
||||
BenchmarkFunction(model<T>* c) : f(c) {}
|
||||
|
||||
public:
|
||||
BenchmarkFunction()
|
||||
: f(new model<do_nothing>{ {} }) {}
|
||||
|
||||
template <typename Fun,
|
||||
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))) {}
|
||||
|
||||
BenchmarkFunction(BenchmarkFunction&& that)
|
||||
: f(std::move(that.f)) {}
|
||||
|
||||
BenchmarkFunction(BenchmarkFunction const& that)
|
||||
: f(that.f->clone()) {}
|
||||
|
||||
BenchmarkFunction& operator=(BenchmarkFunction&& that) {
|
||||
f = std::move(that.f);
|
||||
return *this;
|
||||
}
|
||||
|
||||
BenchmarkFunction& operator=(BenchmarkFunction const& that) {
|
||||
f.reset(that.f->clone());
|
||||
return *this;
|
||||
}
|
||||
|
||||
void operator()(Chronometer meter) const { f->call(meter); }
|
||||
|
||||
private:
|
||||
std::unique_ptr<callable> f;
|
||||
};
|
||||
} // namespace Detail
|
||||
} // namespace Benchmark
|
||||
} // namespace Catch
|
||||
|
||||
#endif // TWOBLUECUBES_CATCH_DETAIL_BENCHMARK_FUNCTION_HPP_INCLUDED
|
69
include/internal/benchmark/detail/catch_complete_invoke.hpp
Normal file
69
include/internal/benchmark/detail/catch_complete_invoke.hpp
Normal file
@@ -0,0 +1,69 @@
|
||||
/*
|
||||
* Created by Joachim on 16/04/2019.
|
||||
* Adapted from donated nonius code.
|
||||
*
|
||||
* 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)
|
||||
*/
|
||||
|
||||
// Invoke with a special case for void
|
||||
|
||||
#ifndef TWOBLUECUBES_CATCH_DETAIL_COMPLETE_INVOKE_HPP_INCLUDED
|
||||
#define TWOBLUECUBES_CATCH_DETAIL_COMPLETE_INVOKE_HPP_INCLUDED
|
||||
|
||||
#include "../../catch_enforce.h"
|
||||
|
||||
#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)...);
|
||||
}
|
||||
};
|
||||
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;
|
||||
|
||||
// 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)...);
|
||||
}
|
||||
|
||||
const std::string benchmarkErrorMsg = "a benchmark failed to run successfully";
|
||||
} // namespace Detail
|
||||
|
||||
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
|
||||
|
||||
#endif // TWOBLUECUBES_CATCH_DETAIL_COMPLETE_INVOKE_HPP_INCLUDED
|
113
include/internal/benchmark/detail/catch_estimate_clock.hpp
Normal file
113
include/internal/benchmark/detail/catch_estimate_clock.hpp
Normal file
@@ -0,0 +1,113 @@
|
||||
/*
|
||||
* Created by Joachim on 16/04/2019.
|
||||
* Adapted from donated nonius code.
|
||||
*
|
||||
* 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)
|
||||
*/
|
||||
|
||||
// Environment measurement
|
||||
|
||||
#ifndef TWOBLUECUBES_CATCH_DETAIL_ESTIMATE_CLOCK_HPP_INCLUDED
|
||||
#define TWOBLUECUBES_CATCH_DETAIL_ESTIMATE_CLOCK_HPP_INCLUDED
|
||||
|
||||
#include "../catch_clock.hpp"
|
||||
#include "../catch_environment.hpp"
|
||||
#include "catch_stats.hpp"
|
||||
#include "catch_measure.hpp"
|
||||
#include "catch_run_for_at_least.hpp"
|
||||
#include "../catch_clock.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;
|
||||
}
|
||||
|
||||
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()),
|
||||
};
|
||||
}
|
||||
|
||||
template <typename Clock>
|
||||
Environment<FloatDuration<Clock>> measure_environment() {
|
||||
static Environment<FloatDuration<Clock>>* env = nullptr;
|
||||
if (env) {
|
||||
return *env;
|
||||
}
|
||||
|
||||
auto iters = Detail::warmup<Clock>();
|
||||
auto resolution = Detail::estimate_clock_resolution<Clock>(iters);
|
||||
auto cost = Detail::estimate_clock_cost<Clock>(resolution.mean);
|
||||
|
||||
env = new Environment<FloatDuration<Clock>>{ resolution, cost };
|
||||
return *env;
|
||||
}
|
||||
} // namespace Detail
|
||||
} // namespace Benchmark
|
||||
} // namespace Catch
|
||||
|
||||
#endif // TWOBLUECUBES_CATCH_DETAIL_ESTIMATE_CLOCK_HPP_INCLUDED
|
35
include/internal/benchmark/detail/catch_measure.hpp
Normal file
35
include/internal/benchmark/detail/catch_measure.hpp
Normal file
@@ -0,0 +1,35 @@
|
||||
/*
|
||||
* Created by Joachim on 16/04/2019.
|
||||
* Adapted from donated nonius code.
|
||||
*
|
||||
* 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)
|
||||
*/
|
||||
|
||||
// Measure
|
||||
|
||||
#ifndef TWOBLUECUBES_CATCH_DETAIL_MEASURE_HPP_INCLUDED
|
||||
#define TWOBLUECUBES_CATCH_DETAIL_MEASURE_HPP_INCLUDED
|
||||
|
||||
#include "../catch_clock.hpp"
|
||||
#include "catch_complete_invoke.hpp"
|
||||
#include "catch_timing.hpp"
|
||||
|
||||
#include <utility>
|
||||
|
||||
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
|
||||
|
||||
#endif // TWOBLUECUBES_CATCH_DETAIL_MEASURE_HPP_INCLUDED
|
37
include/internal/benchmark/detail/catch_repeat.hpp
Normal file
37
include/internal/benchmark/detail/catch_repeat.hpp
Normal file
@@ -0,0 +1,37 @@
|
||||
/*
|
||||
* Created by Joachim on 16/04/2019.
|
||||
* Adapted from donated nonius code.
|
||||
*
|
||||
* 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)
|
||||
*/
|
||||
|
||||
// repeat algorithm
|
||||
|
||||
#ifndef TWOBLUECUBES_CATCH_DETAIL_REPEAT_HPP_INCLUDED
|
||||
#define TWOBLUECUBES_CATCH_DETAIL_REPEAT_HPP_INCLUDED
|
||||
|
||||
#include <type_traits>
|
||||
#include <utility>
|
||||
|
||||
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
|
||||
|
||||
#endif // TWOBLUECUBES_CATCH_DETAIL_REPEAT_HPP_INCLUDED
|
65
include/internal/benchmark/detail/catch_run_for_at_least.hpp
Normal file
65
include/internal/benchmark/detail/catch_run_for_at_least.hpp
Normal file
@@ -0,0 +1,65 @@
|
||||
/*
|
||||
* Created by Joachim on 16/04/2019.
|
||||
* Adapted from donated nonius code.
|
||||
*
|
||||
* 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)
|
||||
*/
|
||||
|
||||
// Run a function for a minimum amount of time
|
||||
|
||||
#ifndef TWOBLUECUBES_CATCH_RUN_FOR_AT_LEAST_HPP_INCLUDED
|
||||
#define TWOBLUECUBES_CATCH_RUN_FOR_AT_LEAST_HPP_INCLUDED
|
||||
|
||||
#include "../catch_clock.hpp"
|
||||
#include "../catch_chronometer.hpp"
|
||||
#include "catch_measure.hpp"
|
||||
#include "catch_complete_invoke.hpp"
|
||||
#include "catch_timing.hpp"
|
||||
#include "../../catch_meta.hpp"
|
||||
|
||||
#include <utility>
|
||||
#include <type_traits>
|
||||
|
||||
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));
|
||||
|
||||
return { meter.elapsed(), std::move(result), iters };
|
||||
}
|
||||
|
||||
template <typename Clock, typename Fun>
|
||||
using run_for_at_least_argument_t = typename std::conditional<is_callable<Fun(Chronometer)>::value, Chronometer, int>::type;
|
||||
|
||||
struct optimized_away_error : std::exception {
|
||||
const char* what() const noexcept override {
|
||||
return "could not measure benchmark, maybe it was optimized away";
|
||||
}
|
||||
};
|
||||
|
||||
template <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)>());
|
||||
|
||||
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
|
||||
|
||||
#endif // TWOBLUECUBES_CATCH_RUN_FOR_AT_LEAST_HPP_INCLUDED
|
223
include/internal/benchmark/detail/catch_stats.cpp
Normal file
223
include/internal/benchmark/detail/catch_stats.cpp
Normal file
@@ -0,0 +1,223 @@
|
||||
/*
|
||||
* Created by Martin on 15/06/2019.
|
||||
* Adapted from donated nonius code.
|
||||
*
|
||||
* 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)
|
||||
*/
|
||||
|
||||
// Statistical analysis tools
|
||||
|
||||
#if defined(CATCH_CONFIG_ENABLE_BENCHMARKING)
|
||||
|
||||
#include "catch_stats.hpp"
|
||||
|
||||
#include "../../catch_compiler_capabilities.h"
|
||||
|
||||
#include <cassert>
|
||||
#include <random>
|
||||
|
||||
|
||||
#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);
|
||||
}
|
||||
|
||||
|
||||
double erfc_inv(double x) {
|
||||
return erf_inv(1.0 - x);
|
||||
}
|
||||
|
||||
double normal_quantile(double p) {
|
||||
static const double ROOT_TWO = std::sqrt(2.0);
|
||||
|
||||
double result = 0.0;
|
||||
assert(p >= 0 && p <= 1);
|
||||
if (p < 0 || p > 1) {
|
||||
return result;
|
||||
}
|
||||
|
||||
result = -erfc_inv(2.0 * p);
|
||||
// result *= normal distribution standard deviation (1.0) * sqrt(2)
|
||||
result *= /*sd * */ ROOT_TWO;
|
||||
// result += normal disttribution mean (0)
|
||||
return result;
|
||||
}
|
||||
|
||||
|
||||
double outlier_variance(Estimate<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;
|
||||
}
|
||||
|
||||
|
||||
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
|
||||
|
||||
auto n = static_cast<int>(last - first); // seriously, one can't use integral types without hell in C++
|
||||
|
||||
auto mean = &Detail::mean<std::vector<double>::iterator>;
|
||||
auto stddev = &standard_deviation;
|
||||
|
||||
#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);
|
||||
});
|
||||
};
|
||||
|
||||
auto mean_future = Estimate(mean);
|
||||
auto stddev_future = Estimate(stddev);
|
||||
|
||||
auto mean_estimate = mean_future.get();
|
||||
auto stddev_estimate = stddev_future.get();
|
||||
#else
|
||||
auto Estimate = [=](double(*f)(std::vector<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
|
||||
|
||||
#endif // CATCH_CONFIG_ENABLE_BENCHMARKING
|
158
include/internal/benchmark/detail/catch_stats.hpp
Normal file
158
include/internal/benchmark/detail/catch_stats.hpp
Normal file
@@ -0,0 +1,158 @@
|
||||
/*
|
||||
* Created by Joachim on 16/04/2019.
|
||||
* Adapted from donated nonius code.
|
||||
*
|
||||
* 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)
|
||||
*/
|
||||
|
||||
// Statistical analysis tools
|
||||
|
||||
#ifndef TWOBLUECUBES_CATCH_DETAIL_ANALYSIS_HPP_INCLUDED
|
||||
#define TWOBLUECUBES_CATCH_DETAIL_ANALYSIS_HPP_INCLUDED
|
||||
|
||||
#include "../catch_clock.hpp"
|
||||
#include "../catch_estimate.hpp"
|
||||
#include "../catch_outlier_classification.hpp"
|
||||
|
||||
#include <algorithm>
|
||||
#include <functional>
|
||||
#include <vector>
|
||||
#include <numeric>
|
||||
#include <tuple>
|
||||
#include <cmath>
|
||||
#include <utility>
|
||||
#include <cstddef>
|
||||
|
||||
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;
|
||||
}
|
||||
|
||||
template <typename Iterator>
|
||||
double mean(Iterator first, Iterator last) {
|
||||
auto count = last - first;
|
||||
double sum = std::accumulate(first, last, 0.);
|
||||
return sum / count;
|
||||
}
|
||||
|
||||
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;
|
||||
}
|
||||
|
||||
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);
|
||||
|
||||
for (auto it = first; it != last; ++it) {
|
||||
std::iter_swap(it, first);
|
||||
results.push_back(estimator(second, last));
|
||||
}
|
||||
|
||||
return results;
|
||||
}
|
||||
|
||||
inline double normal_cdf(double x) {
|
||||
return std::erfc(-x / std::sqrt(2.0)) / 2.0;
|
||||
}
|
||||
|
||||
double erfc_inv(double x);
|
||||
|
||||
double normal_quantile(double p);
|
||||
|
||||
template <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 };
|
||||
}
|
||||
|
||||
double outlier_variance(Estimate<double> mean, Estimate<double> stddev, int n);
|
||||
|
||||
struct bootstrap_analysis {
|
||||
Estimate<double> mean;
|
||||
Estimate<double> standard_deviation;
|
||||
double outlier_variance;
|
||||
};
|
||||
|
||||
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
|
||||
|
||||
#endif // TWOBLUECUBES_CATCH_DETAIL_ANALYSIS_HPP_INCLUDED
|
33
include/internal/benchmark/detail/catch_timing.hpp
Normal file
33
include/internal/benchmark/detail/catch_timing.hpp
Normal file
@@ -0,0 +1,33 @@
|
||||
/*
|
||||
* Created by Joachim on 16/04/2019.
|
||||
* Adapted from donated nonius code.
|
||||
*
|
||||
* 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)
|
||||
*/
|
||||
|
||||
// Timing
|
||||
|
||||
#ifndef TWOBLUECUBES_CATCH_DETAIL_TIMING_HPP_INCLUDED
|
||||
#define TWOBLUECUBES_CATCH_DETAIL_TIMING_HPP_INCLUDED
|
||||
|
||||
#include "../catch_clock.hpp"
|
||||
#include "catch_complete_invoke.hpp"
|
||||
|
||||
#include <tuple>
|
||||
#include <type_traits>
|
||||
|
||||
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
|
||||
|
||||
#endif // TWOBLUECUBES_CATCH_DETAIL_TIMING_HPP_INCLUDED
|
@@ -55,18 +55,18 @@ namespace Detail {
|
||||
return marginComparison(m_value, other, m_margin) || marginComparison(m_value, other, m_epsilon * (m_scale + std::fabs(m_value)));
|
||||
}
|
||||
|
||||
void Approx::setMargin(double margin) {
|
||||
CATCH_ENFORCE(margin >= 0,
|
||||
"Invalid Approx::margin: " << margin << '.'
|
||||
void Approx::setMargin(double newMargin) {
|
||||
CATCH_ENFORCE(newMargin >= 0,
|
||||
"Invalid Approx::margin: " << newMargin << '.'
|
||||
<< " Approx::Margin has to be non-negative.");
|
||||
m_margin = margin;
|
||||
m_margin = newMargin;
|
||||
}
|
||||
|
||||
void Approx::setEpsilon(double epsilon) {
|
||||
CATCH_ENFORCE(epsilon >= 0 && epsilon <= 1.0,
|
||||
"Invalid Approx::epsilon: " << epsilon << '.'
|
||||
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 = epsilon;
|
||||
m_epsilon = newEpsilon;
|
||||
}
|
||||
|
||||
} // end namespace Detail
|
||||
|
@@ -1,36 +0,0 @@
|
||||
/*
|
||||
* Created by Phil on 04/07/2017.
|
||||
* 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)
|
||||
*/
|
||||
|
||||
#include "catch_benchmark.h"
|
||||
#include "catch_capture.hpp"
|
||||
#include "catch_interfaces_reporter.h"
|
||||
#include "catch_context.h"
|
||||
|
||||
namespace Catch {
|
||||
|
||||
auto BenchmarkLooper::getResolution() -> uint64_t {
|
||||
return getEstimatedClockResolution() * getCurrentContext().getConfig()->benchmarkResolutionMultiple();
|
||||
}
|
||||
|
||||
void BenchmarkLooper::reportStart() {
|
||||
getResultCapture().benchmarkStarting( { m_name } );
|
||||
}
|
||||
auto BenchmarkLooper::needsMoreIterations() -> bool {
|
||||
auto elapsed = m_timer.getElapsedNanoseconds();
|
||||
|
||||
// Exponentially increasing iterations until we're confident in our timer resolution
|
||||
if( elapsed < m_resolution ) {
|
||||
m_iterationsToRun *= 10;
|
||||
return true;
|
||||
}
|
||||
|
||||
getResultCapture().benchmarkEnded( { { m_name }, m_count, elapsed } );
|
||||
return false;
|
||||
}
|
||||
|
||||
} // end namespace Catch
|
@@ -1,57 +0,0 @@
|
||||
/*
|
||||
* Created by Phil on 04/07/2017.
|
||||
* 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)
|
||||
*/
|
||||
#ifndef TWOBLUECUBES_CATCH_BENCHMARK_H_INCLUDED
|
||||
#define TWOBLUECUBES_CATCH_BENCHMARK_H_INCLUDED
|
||||
|
||||
#include "catch_stringref.h"
|
||||
#include "catch_timer.h"
|
||||
|
||||
#include <cstdint>
|
||||
#include <string>
|
||||
|
||||
namespace Catch {
|
||||
|
||||
class BenchmarkLooper {
|
||||
|
||||
std::string m_name;
|
||||
std::size_t m_count = 0;
|
||||
std::size_t m_iterationsToRun = 1;
|
||||
uint64_t m_resolution;
|
||||
Timer m_timer;
|
||||
|
||||
static auto getResolution() -> uint64_t;
|
||||
public:
|
||||
// Keep most of this inline as it's on the code path that is being timed
|
||||
BenchmarkLooper( StringRef name )
|
||||
: m_name( name ),
|
||||
m_resolution( getResolution() )
|
||||
{
|
||||
reportStart();
|
||||
m_timer.start();
|
||||
}
|
||||
|
||||
explicit operator bool() {
|
||||
if( m_count < m_iterationsToRun )
|
||||
return true;
|
||||
return needsMoreIterations();
|
||||
}
|
||||
|
||||
void increment() {
|
||||
++m_count;
|
||||
}
|
||||
|
||||
void reportStart();
|
||||
auto needsMoreIterations() -> bool;
|
||||
};
|
||||
|
||||
} // end namespace Catch
|
||||
|
||||
#define BENCHMARK( name ) \
|
||||
for( Catch::BenchmarkLooper looper( name ); looper; looper.increment() )
|
||||
|
||||
#endif // TWOBLUECUBES_CATCH_BENCHMARK_H_INCLUDED
|
@@ -48,7 +48,7 @@
|
||||
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
|
||||
} 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 &&.
|
||||
|
||||
///////////////////////////////////////////////////////////////////////////////
|
||||
@@ -130,6 +130,10 @@
|
||||
#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, ... ) \
|
||||
|
@@ -196,11 +196,19 @@ namespace Catch {
|
||||
| Opt( setWaitForKeypress, "start|exit|both" )
|
||||
["--wait-for-keypress"]
|
||||
( "waits for a keypress before exiting" )
|
||||
| Opt( config.benchmarkResolutionMultiple, "multiplier" )
|
||||
["--benchmark-resolution-multiple"]
|
||||
( "multiple of clock resolution to run benchmarks" )
|
||||
|
||||
| Arg( config.testsOrTags, "test name|pattern|tags" )
|
||||
| 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" );
|
||||
|
||||
return cli;
|
||||
|
@@ -64,6 +64,12 @@
|
||||
# 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__
|
||||
|
||||
|
||||
@@ -112,9 +118,9 @@
|
||||
// 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))
|
||||
&& !defined(_GLIBCXX_HAVE_BROKEN_VSWPRINTF))
|
||||
|
||||
# define CATCH_INTERNAL_CONFIG_NO_CPP11_TO_STRING
|
||||
# define CATCH_INTERNAL_CONFIG_NO_CPP11_TO_STRING
|
||||
|
||||
# endif
|
||||
#endif // __CYGWIN__
|
||||
@@ -142,7 +148,11 @@
|
||||
# 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
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
@@ -262,6 +272,10 @@
|
||||
# define CATCH_CONFIG_POLYFILL_ISNAN
|
||||
#endif
|
||||
|
||||
#if defined(CATCH_INTERNAL_CONFIG_USE_ASYNC) && !defined(CATCH_CONFIG_NO_USE_ASYNC) && !defined(CATCH_CONFIG_USE_ASYNC)
|
||||
# define CATCH_CONFIG_USE_ASYNC
|
||||
#endif
|
||||
|
||||
#if !defined(CATCH_INTERNAL_SUPPRESS_PARENTHESES_WARNINGS)
|
||||
# define CATCH_INTERNAL_SUPPRESS_PARENTHESES_WARNINGS
|
||||
# define CATCH_INTERNAL_UNSUPPRESS_PARENTHESES_WARNINGS
|
||||
@@ -274,6 +288,10 @@
|
||||
# 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
|
||||
|
||||
#if defined(CATCH_CONFIG_DISABLE_EXCEPTIONS)
|
||||
#define CATCH_TRY if ((true))
|
||||
|
@@ -16,10 +16,7 @@ namespace Catch {
|
||||
m_stream( openStream() )
|
||||
{
|
||||
TestSpecParser parser(ITagAliasRegistry::get());
|
||||
if (data.testsOrTags.empty()) {
|
||||
parser.parse("~[.]"); // All not hidden tests
|
||||
}
|
||||
else {
|
||||
if (!data.testsOrTags.empty()) {
|
||||
m_hasTestFilters = true;
|
||||
for( auto const& testOrTags : data.testsOrTags )
|
||||
parser.parse( testOrTags );
|
||||
@@ -35,7 +32,7 @@ namespace Catch {
|
||||
bool Config::listTestNamesOnly() const { return m_data.listTestNamesOnly; }
|
||||
bool Config::listTags() const { return m_data.listTags; }
|
||||
bool Config::listReporters() const { return m_data.listReporters; }
|
||||
|
||||
|
||||
std::string Config::getProcessName() const { return m_data.processName; }
|
||||
std::string const& Config::getReporterName() const { return m_data.reporterName; }
|
||||
|
||||
@@ -57,13 +54,17 @@ namespace Catch {
|
||||
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; }
|
||||
int Config::benchmarkResolutionMultiple() const { return m_data.benchmarkResolutionMultiple; }
|
||||
UseColour::YesOrNo Config::useColour() const { return m_data.useColour; }
|
||||
bool Config::shouldDebugBreak() const { return m_data.shouldDebugBreak; }
|
||||
int Config::abortAfter() const { return m_data.abortAfter; }
|
||||
bool Config::showInvisibles() const { return m_data.showInvisibles; }
|
||||
Verbosity Config::verbosity() const { return m_data.verbosity; }
|
||||
|
||||
bool Config::benchmarkNoAnalysis() const { return m_data.benchmarkNoAnalysis; }
|
||||
int Config::benchmarkSamples() const { return m_data.benchmarkSamples; }
|
||||
double Config::benchmarkConfidenceInterval() const { return m_data.benchmarkConfidenceInterval; }
|
||||
unsigned int Config::benchmarkResamples() const { return m_data.benchmarkResamples; }
|
||||
|
||||
IStream const* Config::openStream() {
|
||||
return Catch::makeStream(m_data.outputFilename);
|
||||
}
|
||||
|
@@ -42,7 +42,11 @@ namespace Catch {
|
||||
|
||||
int abortAfter = -1;
|
||||
unsigned int rngSeed = 0;
|
||||
int benchmarkResolutionMultiple = 100;
|
||||
|
||||
bool benchmarkNoAnalysis = false;
|
||||
unsigned int benchmarkSamples = 100;
|
||||
double benchmarkConfidenceInterval = 0.95;
|
||||
unsigned int benchmarkResamples = 100000;
|
||||
|
||||
Verbosity verbosity = Verbosity::Normal;
|
||||
WarnAbout::What warnings = WarnAbout::Nothing;
|
||||
@@ -82,10 +86,10 @@ namespace Catch {
|
||||
std::string getProcessName() const;
|
||||
std::string const& getReporterName() const;
|
||||
|
||||
std::vector<std::string> const& getTestsOrTags() const;
|
||||
std::vector<std::string> const& getTestsOrTags() const override;
|
||||
std::vector<std::string> const& getSectionsToRun() const override;
|
||||
|
||||
virtual TestSpec const& testSpec() const override;
|
||||
TestSpec const& testSpec() const override;
|
||||
bool hasTestFilters() const override;
|
||||
|
||||
bool showHelp() const;
|
||||
@@ -100,12 +104,15 @@ namespace Catch {
|
||||
ShowDurations::OrNot showDurations() const override;
|
||||
RunTests::InWhatOrder runOrder() const override;
|
||||
unsigned int rngSeed() const override;
|
||||
int benchmarkResolutionMultiple() 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:
|
||||
|
||||
|
@@ -69,7 +69,7 @@ namespace {
|
||||
originalBackgroundAttributes = csbiInfo.wAttributes & ~( FOREGROUND_GREEN | FOREGROUND_RED | FOREGROUND_BLUE | FOREGROUND_INTENSITY );
|
||||
}
|
||||
|
||||
virtual void use( Colour::Code _colourCode ) override {
|
||||
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 );
|
||||
@@ -132,7 +132,7 @@ namespace {
|
||||
// https://github.com/philsquared/Catch/pull/131
|
||||
class PosixColourImpl : public IColourImpl {
|
||||
public:
|
||||
virtual void use( Colour::Code _colourCode ) override {
|
||||
void use( Colour::Code _colourCode ) override {
|
||||
switch( _colourCode ) {
|
||||
case Colour::None:
|
||||
case Colour::White: return setColour( "[0m" );
|
||||
@@ -222,7 +222,13 @@ namespace Catch {
|
||||
|
||||
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& ) {
|
||||
|
@@ -13,27 +13,27 @@ namespace Catch {
|
||||
class Context : public IMutableContext, NonCopyable {
|
||||
|
||||
public: // IContext
|
||||
virtual IResultCapture* getResultCapture() override {
|
||||
IResultCapture* getResultCapture() override {
|
||||
return m_resultCapture;
|
||||
}
|
||||
virtual IRunner* getRunner() override {
|
||||
IRunner* getRunner() override {
|
||||
return m_runner;
|
||||
}
|
||||
|
||||
virtual IConfigPtr const& getConfig() const override {
|
||||
IConfigPtr const& getConfig() const override {
|
||||
return m_config;
|
||||
}
|
||||
|
||||
virtual ~Context() override;
|
||||
~Context() override;
|
||||
|
||||
public: // IMutableContext
|
||||
virtual void setResultCapture( IResultCapture* resultCapture ) override {
|
||||
void setResultCapture( IResultCapture* resultCapture ) override {
|
||||
m_resultCapture = resultCapture;
|
||||
}
|
||||
virtual void setRunner( IRunner* runner ) override {
|
||||
void setRunner( IRunner* runner ) override {
|
||||
m_runner = runner;
|
||||
}
|
||||
virtual void setConfig( IConfigPtr const& config ) override {
|
||||
void setConfig( IConfigPtr const& config ) override {
|
||||
m_config = config;
|
||||
}
|
||||
|
||||
|
@@ -18,19 +18,23 @@
|
||||
# include <stdbool.h>
|
||||
# include <sys/types.h>
|
||||
# include <unistd.h>
|
||||
# include <sys/sysctl.h>
|
||||
# include <cstddef>
|
||||
# include <ostream>
|
||||
|
||||
namespace Catch {
|
||||
#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:
|
||||
// http://developer.apple.com/library/mac/#qa/qa2004/qa1361.html
|
||||
// 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;
|
||||
@@ -60,6 +64,12 @@ namespace Catch {
|
||||
|
||||
return ( (info.kp_proc.p_flag & P_TRACED) != 0 );
|
||||
}
|
||||
#else
|
||||
bool isDebuggerActive() {
|
||||
// We need to find another way to determine this for non-appleclang compilers on macOS
|
||||
return false;
|
||||
}
|
||||
#endif
|
||||
} // namespace Catch
|
||||
|
||||
#elif defined(CATCH_PLATFORM_LINUX)
|
||||
|
@@ -7,6 +7,9 @@
|
||||
|
||||
#include "catch_enforce.h"
|
||||
|
||||
#include <stdexcept>
|
||||
|
||||
|
||||
namespace Catch {
|
||||
#if defined(CATCH_CONFIG_DISABLE_EXCEPTIONS) && !defined(CATCH_CONFIG_DISABLE_EXCEPTIONS_CUSTOM_HANDLER)
|
||||
[[noreturn]]
|
||||
@@ -16,4 +19,22 @@ namespace Catch {
|
||||
std::terminate();
|
||||
}
|
||||
#endif
|
||||
|
||||
[[noreturn]]
|
||||
void throw_logic_error(std::string const& msg) {
|
||||
throw_exception(std::logic_error(msg));
|
||||
}
|
||||
|
||||
[[noreturn]]
|
||||
void throw_domain_error(std::string const& msg) {
|
||||
throw_exception(std::domain_error(msg));
|
||||
}
|
||||
|
||||
[[noreturn]]
|
||||
void throw_runtime_error(std::string const& msg) {
|
||||
throw_exception(std::runtime_error(msg));
|
||||
}
|
||||
|
||||
|
||||
|
||||
} // namespace Catch;
|
||||
|
@@ -11,8 +11,7 @@
|
||||
#include "catch_compiler_capabilities.h"
|
||||
#include "catch_stream.h"
|
||||
|
||||
|
||||
#include <stdexcept>
|
||||
#include <exception>
|
||||
|
||||
namespace Catch {
|
||||
#if !defined(CATCH_CONFIG_DISABLE_EXCEPTIONS)
|
||||
@@ -25,18 +24,30 @@ namespace Catch {
|
||||
[[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_PREPARE_EXCEPTION( type, msg ) \
|
||||
type( ( Catch::ReusableStringStream() << msg ).str() )
|
||||
#define CATCH_INTERNAL_ERROR( msg ) \
|
||||
Catch::throw_exception(CATCH_PREPARE_EXCEPTION( std::logic_error, CATCH_INTERNAL_LINEINFO << ": Internal Catch error: " << msg))
|
||||
#define CATCH_ERROR( msg ) \
|
||||
Catch::throw_exception(CATCH_PREPARE_EXCEPTION( std::domain_error, msg ))
|
||||
#define CATCH_RUNTIME_ERROR( msg ) \
|
||||
Catch::throw_exception(CATCH_PREPARE_EXCEPTION( std::runtime_error, msg ))
|
||||
#define CATCH_ENFORCE( condition, msg ) \
|
||||
do{ if( !(condition) ) CATCH_ERROR( msg ); } while(false)
|
||||
#define CATCH_MAKE_MSG(...) \
|
||||
(Catch::ReusableStringStream() << __VA_ARGS__).str()
|
||||
|
||||
#define CATCH_INTERNAL_ERROR(...) \
|
||||
Catch::throw_logic_error(CATCH_MAKE_MSG( CATCH_INTERNAL_LINEINFO << ": Internal Catch2 error: " << __VA_ARGS__));
|
||||
|
||||
#define CATCH_ERROR(...) \
|
||||
Catch::throw_domain_error(CATCH_MAKE_MSG( __VA_ARGS__ ));
|
||||
|
||||
#define CATCH_RUNTIME_ERROR(...) \
|
||||
Catch::throw_runtime_error(CATCH_MAKE_MSG( __VA_ARGS__ ));
|
||||
|
||||
#define CATCH_ENFORCE( condition, ... ) \
|
||||
do{ if( !(condition) ) CATCH_ERROR( __VA_ARGS__ ); } while(false)
|
||||
|
||||
|
||||
#endif // TWOBLUECUBES_CATCH_ENFORCE_H_INCLUDED
|
||||
|
65
include/internal/catch_enum_values_registry.cpp
Normal file
65
include/internal/catch_enum_values_registry.cpp
Normal file
@@ -0,0 +1,65 @@
|
||||
/*
|
||||
* Created by Phil on 4/4/2019.
|
||||
* Copyright 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)
|
||||
*/
|
||||
#include "catch_enum_values_registry.h"
|
||||
#include "catch_string_manip.h"
|
||||
#include "catch_stream.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
|
||||
|
35
include/internal/catch_enum_values_registry.h
Normal file
35
include/internal/catch_enum_values_registry.h
Normal file
@@ -0,0 +1,35 @@
|
||||
/*
|
||||
* Created by Phil on 4/4/2019.
|
||||
* Copyright 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_CATCH_ENUMVALUESREGISTRY_H_INCLUDED
|
||||
#define TWOBLUECUBES_CATCH_ENUMVALUESREGISTRY_H_INCLUDED
|
||||
|
||||
#include "catch_interfaces_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
|
||||
|
||||
#endif //TWOBLUECUBES_CATCH_ENUMVALUESREGISTRY_H_INCLUDED
|
@@ -19,7 +19,7 @@ namespace Catch {
|
||||
public:
|
||||
~ExceptionTranslatorRegistry();
|
||||
virtual void registerTranslator( const IExceptionTranslator* translator );
|
||||
virtual std::string translateActiveException() const override;
|
||||
std::string translateActiveException() const override;
|
||||
std::string tryTranslators() const;
|
||||
|
||||
private:
|
||||
|
@@ -37,10 +37,10 @@ namespace Catch {
|
||||
// Windows can easily distinguish between SO and SigSegV,
|
||||
// but SigInt, SigTerm, etc are handled differently.
|
||||
static 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" },
|
||||
{ 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) {
|
||||
|
@@ -179,182 +179,12 @@ namespace Generators {
|
||||
return makeGenerators( value( T( std::forward<U>( val ) ) ), std::forward<Gs>( moreGenerators )... );
|
||||
}
|
||||
|
||||
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)
|
||||
{
|
||||
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)));
|
||||
}
|
||||
|
||||
|
||||
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))
|
||||
{
|
||||
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"));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
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)
|
||||
{
|
||||
assert(m_target_repeats > 0 && "Repeat generator must repeat at least once");
|
||||
}
|
||||
|
||||
T const& get() const override {
|
||||
if (m_current_repeat == 0) {
|
||||
m_returned.push_back(m_generator.get());
|
||||
return m_returned.back();
|
||||
}
|
||||
return m_returned[m_repeat_index];
|
||||
}
|
||||
|
||||
bool next() override {
|
||||
// There are 2 basic cases:
|
||||
// 1) We are still reading the generator
|
||||
// 2) We are reading our own cache
|
||||
|
||||
// In the first case, we need to poke the underlying generator.
|
||||
// If it happily moves, we are left in that state, otherwise it is time to start reading from our cache
|
||||
if (m_current_repeat == 0) {
|
||||
const auto success = m_generator.next();
|
||||
if (!success) {
|
||||
++m_current_repeat;
|
||||
}
|
||||
return m_current_repeat < m_target_repeats;
|
||||
}
|
||||
|
||||
// In the second case, we need to move indices forward and check that we haven't run up against the end
|
||||
++m_repeat_index;
|
||||
if (m_repeat_index == m_returned.size()) {
|
||||
m_repeat_index = 0;
|
||||
++m_current_repeat;
|
||||
}
|
||||
return m_current_repeat < m_target_repeats;
|
||||
}
|
||||
};
|
||||
|
||||
template <typename T>
|
||||
GeneratorWrapper<T> repeat(size_t repeats, GeneratorWrapper<T>&& generator) {
|
||||
return GeneratorWrapper<T>(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;
|
||||
}
|
||||
};
|
||||
|
||||
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, typename Func>
|
||||
GeneratorWrapper<T> map(Func&& function, GeneratorWrapper<T>&& generator) {
|
||||
return GeneratorWrapper<T>(
|
||||
pf::make_unique<MapGenerator<T, T, Func>>(std::forward<Func>(function), std::move(generator))
|
||||
);
|
||||
}
|
||||
|
||||
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, ¯\_(ツ)_/¯
|
||||
// return type. Yeah.
|
||||
auto generate( SourceLineInfo const& lineInfo, L const& generatorExpression ) -> decltype(std::declval<decltype(generatorExpression())>().get()) {
|
||||
using UnderlyingType = typename decltype(generatorExpression())::type;
|
||||
|
||||
@@ -371,7 +201,10 @@ namespace Generators {
|
||||
} // namespace Catch
|
||||
|
||||
#define GENERATE( ... ) \
|
||||
Catch::Generators::generate( CATCH_INTERNAL_LINEINFO, []{ using namespace Catch::Generators; return makeGenerators( __VA_ARGS__ ); } )
|
||||
|
||||
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__ ); } )
|
||||
|
||||
#endif // TWOBLUECUBES_CATCH_GENERATORS_HPP_INCLUDED
|
||||
|
242
include/internal/catch_generators_generic.hpp
Normal file
242
include/internal/catch_generators_generic.hpp
Normal file
@@ -0,0 +1,242 @@
|
||||
/*
|
||||
* Created by Martin on 23/2/2019.
|
||||
*
|
||||
* 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_CATCH_GENERATORS_GENERIC_HPP_INCLUDED
|
||||
#define TWOBLUECUBES_CATCH_GENERATORS_GENERIC_HPP_INCLUDED
|
||||
|
||||
#include "catch_generators.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)
|
||||
{
|
||||
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)));
|
||||
}
|
||||
|
||||
|
||||
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))
|
||||
{
|
||||
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"));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
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)
|
||||
{
|
||||
assert(m_target_repeats > 0 && "Repeat generator must repeat at least once");
|
||||
}
|
||||
|
||||
T const& get() const override {
|
||||
if (m_current_repeat == 0) {
|
||||
m_returned.push_back(m_generator.get());
|
||||
return m_returned.back();
|
||||
}
|
||||
return m_returned[m_repeat_index];
|
||||
}
|
||||
|
||||
bool next() override {
|
||||
// There are 2 basic cases:
|
||||
// 1) We are still reading the generator
|
||||
// 2) We are reading our own cache
|
||||
|
||||
// In the first case, we need to poke the underlying generator.
|
||||
// If it happily moves, we are left in that state, otherwise it is time to start reading from our cache
|
||||
if (m_current_repeat == 0) {
|
||||
const auto success = m_generator.next();
|
||||
if (!success) {
|
||||
++m_current_repeat;
|
||||
}
|
||||
return m_current_repeat < m_target_repeats;
|
||||
}
|
||||
|
||||
// In the second case, we need to move indices forward and check that we haven't run up against the end
|
||||
++m_repeat_index;
|
||||
if (m_repeat_index == m_returned.size()) {
|
||||
m_repeat_index = 0;
|
||||
++m_current_repeat;
|
||||
}
|
||||
return m_current_repeat < m_target_repeats;
|
||||
}
|
||||
};
|
||||
|
||||
template <typename T>
|
||||
GeneratorWrapper<T> repeat(size_t repeats, GeneratorWrapper<T>&& generator) {
|
||||
return GeneratorWrapper<T>(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;
|
||||
}
|
||||
};
|
||||
|
||||
#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 <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))
|
||||
);
|
||||
}
|
||||
|
||||
} // namespace Generators
|
||||
} // namespace Catch
|
||||
|
||||
|
||||
#endif // TWOBLUECUBES_CATCH_GENERATORS_GENERIC_HPP_INCLUDED
|
135
include/internal/catch_generators_specific.hpp
Normal file
135
include/internal/catch_generators_specific.hpp
Normal file
@@ -0,0 +1,135 @@
|
||||
/*
|
||||
* Created by Martin on 15/6/2018.
|
||||
*
|
||||
* 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_CATCH_GENERATORS_SPECIFIC_HPP_INCLUDED
|
||||
#define TWOBLUECUBES_CATCH_GENERATORS_SPECIFIC_HPP_INCLUDED
|
||||
|
||||
#include "catch_context.h"
|
||||
#include "catch_generators.hpp"
|
||||
#include "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
|
||||
|
||||
|
||||
#endif // TWOBLUECUBES_CATCH_GENERATORS_SPECIFIC_HPP_INCLUDED
|
@@ -9,6 +9,7 @@
|
||||
#define TWOBLUECUBES_CATCH_INTERFACES_CAPTURE_H_INCLUDED
|
||||
|
||||
#include <string>
|
||||
#include <chrono>
|
||||
|
||||
#include "catch_stringref.h"
|
||||
#include "catch_result_type.h"
|
||||
@@ -20,15 +21,20 @@ namespace Catch {
|
||||
struct SectionInfo;
|
||||
struct SectionEndInfo;
|
||||
struct MessageInfo;
|
||||
struct MessageBuilder;
|
||||
struct Counts;
|
||||
struct BenchmarkInfo;
|
||||
struct BenchmarkStats;
|
||||
struct AssertionReaction;
|
||||
struct SourceLineInfo;
|
||||
|
||||
struct ITransientExpression;
|
||||
struct IGeneratorTracker;
|
||||
|
||||
#if defined(CATCH_CONFIG_ENABLE_BENCHMARKING)
|
||||
struct BenchmarkInfo;
|
||||
template <typename Duration = std::chrono::duration<double, std::nano>>
|
||||
struct BenchmarkStats;
|
||||
#endif // CATCH_CONFIG_ENABLE_BENCHMARKING
|
||||
|
||||
struct IResultCapture {
|
||||
|
||||
virtual ~IResultCapture();
|
||||
@@ -40,12 +46,18 @@ namespace Catch {
|
||||
|
||||
virtual auto acquireGeneratorTracker( SourceLineInfo const& lineInfo ) -> IGeneratorTracker& = 0;
|
||||
|
||||
#if defined(CATCH_CONFIG_ENABLE_BENCHMARKING)
|
||||
virtual void benchmarkPreparing( std::string const& name ) = 0;
|
||||
virtual void benchmarkStarting( BenchmarkInfo const& info ) = 0;
|
||||
virtual void benchmarkEnded( BenchmarkStats const& stats ) = 0;
|
||||
virtual void benchmarkEnded( BenchmarkStats<> const& stats ) = 0;
|
||||
virtual void benchmarkFailed( std::string const& error ) = 0;
|
||||
#endif // CATCH_CONFIG_ENABLE_BENCHMARKING
|
||||
|
||||
virtual void pushScopedMessage( MessageInfo const& message ) = 0;
|
||||
virtual void popScopedMessage( MessageInfo const& message ) = 0;
|
||||
|
||||
virtual void emplaceUnscopedMessage( MessageBuilder const& builder ) = 0;
|
||||
|
||||
virtual void handleFatalErrorCondition( StringRef message ) = 0;
|
||||
|
||||
virtual void handleExpr
|
||||
|
@@ -9,6 +9,7 @@
|
||||
#define TWOBLUECUBES_CATCH_INTERFACES_CONFIG_H_INCLUDED
|
||||
|
||||
#include "catch_common.h"
|
||||
#include "catch_option.hpp"
|
||||
|
||||
#include <iosfwd>
|
||||
#include <string>
|
||||
@@ -50,7 +51,7 @@ namespace Catch {
|
||||
BeforeExit = 2,
|
||||
BeforeStartAndExit = BeforeStart | BeforeExit
|
||||
}; };
|
||||
|
||||
|
||||
class TestSpec;
|
||||
|
||||
struct IConfig : NonCopyable {
|
||||
@@ -69,12 +70,17 @@ namespace Catch {
|
||||
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 int benchmarkResolutionMultiple() 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;
|
||||
};
|
||||
|
||||
using IConfigPtr = std::shared_ptr<IConfig const>;
|
||||
|
45
include/internal/catch_interfaces_enum_values_registry.h
Normal file
45
include/internal/catch_interfaces_enum_values_registry.h
Normal file
@@ -0,0 +1,45 @@
|
||||
/*
|
||||
* Created by Phil on 4/4/2019.
|
||||
* Copyright 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_CATCH_INTERFACESENUMVALUESREGISTRY_H_INCLUDED
|
||||
#define TWOBLUECUBES_CATCH_INTERFACESENUMVALUESREGISTRY_H_INCLUDED
|
||||
|
||||
#include "catch_stringref.h"
|
||||
|
||||
#include <vector>
|
||||
|
||||
namespace Catch {
|
||||
|
||||
namespace Detail {
|
||||
struct EnumInfo {
|
||||
StringRef m_name;
|
||||
std::vector<std::pair<int, std::string>> m_values;
|
||||
|
||||
~EnumInfo();
|
||||
|
||||
StringRef lookup( int value ) const;
|
||||
};
|
||||
} // namespace Detail
|
||||
|
||||
struct IMutableEnumValuesRegistry {
|
||||
virtual ~IMutableEnumValuesRegistry();
|
||||
|
||||
virtual Detail::EnumInfo const& registerEnum( StringRef enumName, StringRef allEnums, std::vector<int> const& values ) = 0;
|
||||
|
||||
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 );
|
||||
}
|
||||
};
|
||||
|
||||
} // Catch
|
||||
|
||||
#endif //TWOBLUECUBES_CATCH_INTERFACESENUMVALUESREGISTRY_H_INCLUDED
|
@@ -22,6 +22,8 @@ namespace Catch {
|
||||
struct IReporterRegistry;
|
||||
struct IReporterFactory;
|
||||
struct ITagAliasRegistry;
|
||||
struct IMutableEnumValuesRegistry;
|
||||
|
||||
class StartupExceptionRegistry;
|
||||
|
||||
using IReporterFactoryPtr = std::shared_ptr<IReporterFactory>;
|
||||
@@ -32,7 +34,6 @@ namespace Catch {
|
||||
virtual IReporterRegistry const& getReporterRegistry() const = 0;
|
||||
virtual ITestCaseRegistry const& getTestCaseRegistry() const = 0;
|
||||
virtual ITagAliasRegistry const& getTagAliasRegistry() const = 0;
|
||||
|
||||
virtual IExceptionTranslatorRegistry const& getExceptionTranslatorRegistry() const = 0;
|
||||
|
||||
|
||||
@@ -47,6 +48,7 @@ namespace Catch {
|
||||
virtual void registerTranslator( const IExceptionTranslator* translator ) = 0;
|
||||
virtual void registerTagAlias( std::string const& alias, std::string const& tag, SourceLineInfo const& lineInfo ) = 0;
|
||||
virtual void registerStartupException() noexcept = 0;
|
||||
virtual IMutableEnumValuesRegistry& getMutableEnumValuesRegistry() = 0;
|
||||
};
|
||||
|
||||
IRegistryHub const& getRegistryHub();
|
||||
|
@@ -18,12 +18,18 @@
|
||||
#include "catch_option.hpp"
|
||||
#include "catch_stringref.h"
|
||||
|
||||
#if defined(CATCH_CONFIG_ENABLE_BENCHMARKING)
|
||||
#include "benchmark/catch_estimate.hpp"
|
||||
#include "benchmark/catch_outlier_classification.hpp"
|
||||
#endif // CATCH_CONFIG_ENABLE_BENCHMARKING
|
||||
|
||||
|
||||
#include <string>
|
||||
#include <iosfwd>
|
||||
#include <map>
|
||||
#include <set>
|
||||
#include <memory>
|
||||
#include <algorithm>
|
||||
|
||||
namespace Catch {
|
||||
|
||||
@@ -80,8 +86,8 @@ namespace Catch {
|
||||
|
||||
AssertionStats( AssertionStats const& ) = default;
|
||||
AssertionStats( AssertionStats && ) = default;
|
||||
AssertionStats& operator = ( AssertionStats const& ) = default;
|
||||
AssertionStats& operator = ( AssertionStats && ) = default;
|
||||
AssertionStats& operator = ( AssertionStats const& ) = delete;
|
||||
AssertionStats& operator = ( AssertionStats && ) = delete;
|
||||
virtual ~AssertionStats();
|
||||
|
||||
AssertionResult assertionResult;
|
||||
@@ -159,14 +165,43 @@ namespace Catch {
|
||||
bool aborting;
|
||||
};
|
||||
|
||||
#if defined(CATCH_CONFIG_ENABLE_BENCHMARKING)
|
||||
struct BenchmarkInfo {
|
||||
std::string name;
|
||||
double estimatedDuration;
|
||||
int iterations;
|
||||
int samples;
|
||||
unsigned int resamples;
|
||||
double clockResolution;
|
||||
double clockCost;
|
||||
};
|
||||
|
||||
template <class Duration>
|
||||
struct BenchmarkStats {
|
||||
BenchmarkInfo info;
|
||||
std::size_t iterations;
|
||||
uint64_t elapsedTimeInNanoseconds;
|
||||
|
||||
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
|
||||
|
||||
struct IStreamingReporter {
|
||||
virtual ~IStreamingReporter() = default;
|
||||
@@ -185,17 +220,18 @@ namespace Catch {
|
||||
virtual void testCaseStarting( TestCaseInfo const& testInfo ) = 0;
|
||||
virtual void sectionStarting( SectionInfo const& sectionInfo ) = 0;
|
||||
|
||||
// *** experimental ***
|
||||
#if defined(CATCH_CONFIG_ENABLE_BENCHMARKING)
|
||||
virtual void benchmarkPreparing( std::string const& ) {}
|
||||
virtual void benchmarkStarting( BenchmarkInfo const& ) {}
|
||||
virtual void benchmarkEnded( BenchmarkStats<> const& ) {}
|
||||
virtual void benchmarkFailed( std::string const& ) {}
|
||||
#endif // CATCH_CONFIG_ENABLE_BENCHMARKING
|
||||
|
||||
virtual void assertionStarting( AssertionInfo const& assertionInfo ) = 0;
|
||||
|
||||
// The return value indicates if the messages buffer should be cleared:
|
||||
virtual bool assertionEnded( AssertionStats const& assertionStats ) = 0;
|
||||
|
||||
// *** experimental ***
|
||||
virtual void benchmarkEnded( BenchmarkStats const& ) {}
|
||||
|
||||
virtual void sectionEnded( SectionStats const& sectionStats ) = 0;
|
||||
virtual void testCaseEnded( TestCaseStats const& testCaseStats ) = 0;
|
||||
virtual void testGroupEnded( TestGroupStats const& testGroupStats ) = 0;
|
||||
|
@@ -12,6 +12,7 @@
|
||||
#include "catch_interfaces_reporter.h"
|
||||
#include "catch_interfaces_testcase.h"
|
||||
|
||||
#include "catch_context.h"
|
||||
#include "catch_stream.h"
|
||||
#include "catch_text.h"
|
||||
|
||||
@@ -146,15 +147,16 @@ namespace Catch {
|
||||
return factories.size();
|
||||
}
|
||||
|
||||
Option<std::size_t> list( Config const& config ) {
|
||||
Option<std::size_t> list( std::shared_ptr<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() )
|
||||
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;
|
||||
}
|
||||
|
@@ -31,7 +31,7 @@ namespace Catch {
|
||||
|
||||
std::size_t listReporters();
|
||||
|
||||
Option<std::size_t> list( Config const& config );
|
||||
Option<std::size_t> list( std::shared_ptr<Config> const& config );
|
||||
|
||||
} // end namespace Catch
|
||||
|
||||
|
@@ -14,6 +14,9 @@
|
||||
#include <cstdlib>
|
||||
#include <cstdint>
|
||||
#include <cstring>
|
||||
#include <sstream>
|
||||
#include <iomanip>
|
||||
#include <limits>
|
||||
|
||||
namespace Catch {
|
||||
namespace Matchers {
|
||||
@@ -74,8 +77,16 @@ bool almostEqualUlps(FP lhs, FP rhs, int maxUlpDiff) {
|
||||
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
|
||||
|
||||
|
||||
namespace Catch {
|
||||
namespace Matchers {
|
||||
@@ -125,7 +136,29 @@ namespace Floating {
|
||||
#endif
|
||||
|
||||
std::string WithinUlpsMatcher::describe() const {
|
||||
return "is within " + Catch::to_string(m_ulps) + " ULPs of " + ::Catch::Detail::stringify(m_target) + ((m_type == FloatingPointKind::Float)? "f" : "");
|
||||
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" : "");
|
||||
}
|
||||
|
||||
}// namespace Floating
|
||||
|
@@ -45,7 +45,7 @@ public:
|
||||
|
||||
// The following functions create the actual matcher objects.
|
||||
// The user has to explicitly specify type to the function, because
|
||||
// infering std::function<bool(T const&)> is hard (but possible) and
|
||||
// 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 = "") {
|
||||
|
@@ -9,6 +9,7 @@
|
||||
#define TWOBLUECUBES_CATCH_MATCHERS_VECTOR_H_INCLUDED
|
||||
|
||||
#include "catch_matchers.h"
|
||||
#include "catch_approx.h"
|
||||
|
||||
#include <algorithm>
|
||||
|
||||
@@ -16,28 +17,6 @@ namespace Catch {
|
||||
namespace Matchers {
|
||||
|
||||
namespace Vector {
|
||||
namespace Detail {
|
||||
template <typename InputIterator, typename T>
|
||||
size_t count(InputIterator first, InputIterator last, T const& item) {
|
||||
size_t cnt = 0;
|
||||
for (; first != last; ++first) {
|
||||
if (*first == item) {
|
||||
++cnt;
|
||||
}
|
||||
}
|
||||
return cnt;
|
||||
}
|
||||
template <typename InputIterator, typename T>
|
||||
bool contains(InputIterator first, InputIterator last, T const& item) {
|
||||
for (; first != last; ++first) {
|
||||
if (*first == item) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
template<typename T>
|
||||
struct ContainsElementMatcher : MatcherBase<std::vector<T>> {
|
||||
|
||||
@@ -112,6 +91,42 @@ namespace Matchers {
|
||||
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) {}
|
||||
@@ -121,28 +136,7 @@ namespace Matchers {
|
||||
if (m_target.size() != vec.size()) {
|
||||
return false;
|
||||
}
|
||||
auto lfirst = m_target.begin(), llast = m_target.end();
|
||||
auto rfirst = vec.begin(), rlast = vec.end();
|
||||
// Cut common prefix to optimize checking of permuted parts
|
||||
while (lfirst != llast && *lfirst == *rfirst) {
|
||||
++lfirst; ++rfirst;
|
||||
}
|
||||
if (lfirst == llast) {
|
||||
return true;
|
||||
}
|
||||
|
||||
for (auto mid = lfirst; mid != llast; ++mid) {
|
||||
// Skip already counted items
|
||||
if (Detail::contains(lfirst, mid, *mid)) {
|
||||
continue;
|
||||
}
|
||||
size_t num_vec = Detail::count(rfirst, rlast, *mid);
|
||||
if (num_vec == 0 || Detail::count(lfirst, llast, *mid) != num_vec) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
return std::is_permutation(m_target.begin(), m_target.end(), vec.begin());
|
||||
}
|
||||
|
||||
std::string describe() const override {
|
||||
@@ -172,6 +166,11 @@ namespace Matchers {
|
||||
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);
|
||||
|
@@ -9,6 +9,7 @@
|
||||
#include "catch_message.h"
|
||||
#include "catch_interfaces_capture.h"
|
||||
#include "catch_uncaught_exceptions.h"
|
||||
#include "catch_enforce.h"
|
||||
|
||||
#include <cassert>
|
||||
#include <stack>
|
||||
@@ -47,14 +48,20 @@ namespace Catch {
|
||||
|
||||
|
||||
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&& old )
|
||||
: m_info( old.m_info ), m_moved()
|
||||
{
|
||||
old.m_moved = true;
|
||||
}
|
||||
|
||||
ScopedMessage::~ScopedMessage() {
|
||||
if ( !uncaught_exceptions() ){
|
||||
if ( !uncaught_exceptions() && !m_moved ){
|
||||
getResultCapture().popScopedMessage(m_info);
|
||||
}
|
||||
}
|
||||
@@ -70,6 +77,15 @@ namespace Catch {
|
||||
}
|
||||
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;
|
||||
@@ -90,6 +106,10 @@ namespace Catch {
|
||||
// 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);
|
||||
|
@@ -64,9 +64,12 @@ namespace Catch {
|
||||
class ScopedMessage {
|
||||
public:
|
||||
explicit ScopedMessage( MessageBuilder const& builder );
|
||||
ScopedMessage( ScopedMessage& duplicate ) = delete;
|
||||
ScopedMessage( ScopedMessage&& old );
|
||||
~ScopedMessage();
|
||||
|
||||
MessageInfo m_info;
|
||||
bool m_moved;
|
||||
};
|
||||
|
||||
class Capturer {
|
||||
|
@@ -11,71 +11,28 @@
|
||||
|
||||
#include <type_traits>
|
||||
|
||||
template< typename... >
|
||||
struct TypeList{};
|
||||
|
||||
template< typename... >
|
||||
struct append;
|
||||
|
||||
template< template<typename...> class L1
|
||||
, typename...E1
|
||||
, template<typename...> class L2
|
||||
, typename...E2
|
||||
>
|
||||
struct append< L1<E1...>, L2<E2...> >
|
||||
{
|
||||
using type = L1<E1..., E2...>;
|
||||
};
|
||||
|
||||
template< template<typename...> class L1
|
||||
, typename...E1
|
||||
, template<typename...> class L2
|
||||
, typename...E2
|
||||
, typename...Rest
|
||||
>
|
||||
struct append< L1<E1...>, L2<E2...>, Rest...>
|
||||
{
|
||||
using type = typename append< L1<E1..., E2...>, Rest... >::type;
|
||||
};
|
||||
|
||||
template< template<typename...> class
|
||||
, typename...
|
||||
>
|
||||
struct rewrap;
|
||||
|
||||
template< template<typename...> class Container
|
||||
, template<typename...> class List
|
||||
, typename...elems
|
||||
>
|
||||
struct rewrap<Container, List<elems...>>
|
||||
{
|
||||
using type = TypeList< Container< elems... > >;
|
||||
};
|
||||
|
||||
template< template<typename...> class Container
|
||||
, template<typename...> class List
|
||||
, class...Elems
|
||||
, typename...Elements>
|
||||
struct rewrap<Container, List<Elems...>, Elements...>
|
||||
{
|
||||
using type = typename append<TypeList<Container<Elems...>>, typename rewrap<Container, Elements...>::type>::type;
|
||||
};
|
||||
|
||||
template< template<typename...> class...Containers >
|
||||
struct combine
|
||||
{
|
||||
template< typename...Types >
|
||||
struct with_types
|
||||
{
|
||||
template< template <typename...> class Final >
|
||||
struct into
|
||||
{
|
||||
using type = typename append<Final<>, typename rewrap<Containers, Types...>::type...>::type;
|
||||
};
|
||||
};
|
||||
};
|
||||
|
||||
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(...);
|
||||
};
|
||||
|
||||
template <typename T>
|
||||
struct is_callable;
|
||||
|
||||
template <typename Fun, typename... Args>
|
||||
struct is_callable<Fun(Args...)> : decltype(is_callable_tester::test<Fun, Args...>(0)) {};
|
||||
|
||||
} // namespace Catch
|
||||
|
||||
namespace mpl_{
|
||||
struct na;
|
||||
}
|
||||
|
||||
#endif // TWOBLUECUBES_CATCH_META_HPP_INCLUDED
|
||||
|
@@ -116,7 +116,7 @@ namespace Catch {
|
||||
arcSafeRelease( m_substr );
|
||||
}
|
||||
|
||||
bool match( NSString* arg ) const override {
|
||||
bool match( NSString* const& str ) const override {
|
||||
return false;
|
||||
}
|
||||
|
||||
@@ -126,7 +126,7 @@ namespace Catch {
|
||||
struct Equals : StringHolder {
|
||||
Equals( NSString* substr ) : StringHolder( substr ){}
|
||||
|
||||
bool match( NSString* str ) const override {
|
||||
bool match( NSString* const& str ) const override {
|
||||
return (str != nil || m_substr == nil ) &&
|
||||
[str isEqualToString:m_substr];
|
||||
}
|
||||
@@ -139,7 +139,7 @@ namespace Catch {
|
||||
struct Contains : StringHolder {
|
||||
Contains( NSString* substr ) : StringHolder( substr ){}
|
||||
|
||||
bool match( NSString* str ) const {
|
||||
bool match( NSString* const& str ) const override {
|
||||
return (str != nil || m_substr == nil ) &&
|
||||
[str rangeOfString:m_substr].location != NSNotFound;
|
||||
}
|
||||
@@ -152,7 +152,7 @@ namespace Catch {
|
||||
struct StartsWith : StringHolder {
|
||||
StartsWith( NSString* substr ) : StringHolder( substr ){}
|
||||
|
||||
bool match( NSString* str ) const override {
|
||||
bool match( NSString* const& str ) const override {
|
||||
return (str != nil || m_substr == nil ) &&
|
||||
[str rangeOfString:m_substr].location == 0;
|
||||
}
|
||||
@@ -164,7 +164,7 @@ namespace Catch {
|
||||
struct EndsWith : StringHolder {
|
||||
EndsWith( NSString* substr ) : StringHolder( substr ){}
|
||||
|
||||
bool match( NSString* str ) const override {
|
||||
bool match( NSString* const& str ) const override {
|
||||
return (str != nil || m_substr == nil ) &&
|
||||
[str rangeOfString:m_substr].location == [str length] - [m_substr length];
|
||||
}
|
||||
|
@@ -49,6 +49,15 @@ namespace Catch {
|
||||
{}
|
||||
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)
|
||||
|
||||
@@ -62,7 +71,7 @@ namespace Catch {
|
||||
if (strerror_s(buffer, errno)) {
|
||||
CATCH_RUNTIME_ERROR("Could not translate errno to a string");
|
||||
}
|
||||
CATCH_RUNTIME_ERROR("Coul dnot open the temp file: '" << m_buffer << "' because: " << buffer);
|
||||
CATCH_RUNTIME_ERROR("Could not open the temp file: '" << m_buffer << "' because: " << buffer);
|
||||
}
|
||||
}
|
||||
#else
|
||||
|
@@ -46,6 +46,21 @@ namespace Catch {
|
||||
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)
|
||||
|
||||
|
@@ -57,23 +57,166 @@
|
||||
#define INTERNAL_CATCH_EXPAND2(...) INTERNAL_CATCH_NO## __VA_ARGS__
|
||||
#define INTERNAL_CATCH_DEF(...) INTERNAL_CATCH_DEF __VA_ARGS__
|
||||
#define INTERNAL_CATCH_NOINTERNAL_CATCH_DEF
|
||||
#define INTERNAL_CATCH_STRINGIZE(...) INTERNAL_CATCH_STRINGIZE2(__VA_ARGS__)
|
||||
#ifndef CATCH_CONFIG_TRADITIONAL_MSVC_PREPROCESSOR
|
||||
#define INTERNAL_CATCH_STRINGIZE2(...) #__VA_ARGS__
|
||||
#define INTERNAL_CATCH_STRINGIZE_WITHOUT_PARENS(param) INTERNAL_CATCH_STRINGIZE(INTERNAL_CATCH_REMOVE_PARENS(param))
|
||||
#else
|
||||
// MSVC is adding extra space and needs another indirection to expand INTERNAL_CATCH_NOINTERNAL_CATCH_DEF
|
||||
#define INTERNAL_CATCH_STRINGIZE2(...) INTERNAL_CATCH_STRINGIZE3(__VA_ARGS__)
|
||||
#define INTERNAL_CATCH_STRINGIZE3(...) #__VA_ARGS__
|
||||
#define INTERNAL_CATCH_STRINGIZE_WITHOUT_PARENS(param) (INTERNAL_CATCH_STRINGIZE(INTERNAL_CATCH_REMOVE_PARENS(param)) + 1)
|
||||
#endif
|
||||
|
||||
#define INTERNAL_CATCH_MAKE_NAMESPACE2(...) ns_##__VA_ARGS__
|
||||
#define INTERNAL_CATCH_MAKE_NAMESPACE(name) INTERNAL_CATCH_MAKE_NAMESPACE2(name)
|
||||
|
||||
#define INTERNAL_CATCH_REMOVE_PARENS(...) INTERNAL_CATCH_EXPAND1(INTERNAL_CATCH_DEF __VA_ARGS__)
|
||||
|
||||
#define INTERNAL_CATCH_TEMPLATE_UNIQUE_NAME2(Name, ...) INTERNAL_CATCH_TEMPLATE_UNIQUE_NAME3(Name, __VA_ARGS__)
|
||||
#ifndef CATCH_CONFIG_TRADITIONAL_MSVC_PREPROCESSOR
|
||||
#define INTERNAL_CATCH_TEMPLATE_UNIQUE_NAME3(Name,...) Name " - " #__VA_ARGS__
|
||||
#define INTERNAL_CATCH_TEMPLATE_UNIQUE_NAME(Name,...) INTERNAL_CATCH_TEMPLATE_UNIQUE_NAME2(Name, INTERNAL_CATCH_REMOVE_PARENS(__VA_ARGS__))
|
||||
#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
|
||||
// MSVC is adding extra space and needs more calls to properly remove ()
|
||||
#define INTERNAL_CATCH_TEMPLATE_UNIQUE_NAME3(Name,...) Name " -" #__VA_ARGS__
|
||||
#define INTERNAL_CATCH_TEMPLATE_UNIQUE_NAME1(Name, ...) INTERNAL_CATCH_TEMPLATE_UNIQUE_NAME2(Name, __VA_ARGS__)
|
||||
#define INTERNAL_CATCH_TEMPLATE_UNIQUE_NAME(Name, ...) INTERNAL_CATCH_TEMPLATE_UNIQUE_NAME1(Name, INTERNAL_CATCH_EXPAND_VARGS(INTERNAL_CATCH_REMOVE_PARENS(__VA_ARGS__)))
|
||||
#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_LIST(types) TypeList<INTERNAL_CATCH_REMOVE_PARENS(types)>
|
||||
#define INTERNAL_CATCH_MAKE_TYPE_LISTS_FROM_TYPES(...)\
|
||||
CATCH_REC_LIST(INTERNAL_CATCH_MAKE_TYPE_LIST,__VA_ARGS__)
|
||||
|
||||
#define INTERNAL_CATCH_MAKE_TYPE_LISTS_FROM_TYPES(types)\
|
||||
CATCH_REC_LIST(INTERNAL_CATCH_MAKE_TYPE_LIST,INTERNAL_CATCH_REMOVE_PARENS(types))
|
||||
#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
|
||||
|
||||
#endif // TWOBLUECUBES_CATCH_PREPROCESSOR_HPP_INCLUDED
|
||||
|
@@ -15,6 +15,7 @@
|
||||
#include "catch_tag_alias_registry.h"
|
||||
#include "catch_startup_exception_registry.h"
|
||||
#include "catch_singletons.hpp"
|
||||
#include "catch_enum_values_registry.h"
|
||||
|
||||
namespace Catch {
|
||||
|
||||
@@ -60,6 +61,9 @@ namespace Catch {
|
||||
void registerStartupException() noexcept override {
|
||||
m_exceptionRegistry.add(std::current_exception());
|
||||
}
|
||||
IMutableEnumValuesRegistry& getMutableEnumValuesRegistry() override {
|
||||
return m_enumValuesRegistry;
|
||||
}
|
||||
|
||||
private:
|
||||
TestRegistry m_testCaseRegistry;
|
||||
@@ -67,6 +71,7 @@ namespace Catch {
|
||||
ExceptionTranslatorRegistry m_exceptionTranslatorRegistry;
|
||||
TagAliasRegistry m_tagAliasRegistry;
|
||||
StartupExceptionRegistry m_exceptionRegistry;
|
||||
Detail::EnumValuesRegistry m_enumValuesRegistry;
|
||||
};
|
||||
}
|
||||
|
||||
|
@@ -18,11 +18,11 @@ namespace Catch {
|
||||
|
||||
class ReporterFactory : public IReporterFactory {
|
||||
|
||||
virtual IStreamingReporterPtr create( ReporterConfig const& config ) const override {
|
||||
IStreamingReporterPtr create( ReporterConfig const& config ) const override {
|
||||
return std::unique_ptr<T>( new T( config ) );
|
||||
}
|
||||
|
||||
virtual std::string getDescription() const override {
|
||||
std::string getDescription() const override {
|
||||
return T::getDescription();
|
||||
}
|
||||
};
|
||||
@@ -39,10 +39,10 @@ namespace Catch {
|
||||
|
||||
class ListenerFactory : public IReporterFactory {
|
||||
|
||||
virtual IStreamingReporterPtr create( ReporterConfig const& config ) const override {
|
||||
IStreamingReporterPtr create( ReporterConfig const& config ) const override {
|
||||
return std::unique_ptr<T>( new T( config ) );
|
||||
}
|
||||
virtual std::string getDescription() const override {
|
||||
std::string getDescription() const override {
|
||||
return std::string();
|
||||
}
|
||||
};
|
||||
|
@@ -67,7 +67,6 @@ namespace Catch {
|
||||
GeneratorTracker::~GeneratorTracker() {}
|
||||
}
|
||||
|
||||
|
||||
RunContext::RunContext(IConfigPtr const& _config, IStreamingReporterPtr&& reporter)
|
||||
: m_runInfo(_config->name()),
|
||||
m_context(getCurrentMutableContext()),
|
||||
@@ -162,6 +161,9 @@ namespace Catch {
|
||||
// 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;
|
||||
@@ -216,6 +218,7 @@ namespace Catch {
|
||||
|
||||
m_reporter->sectionEnded(SectionStats(endInfo.sectionInfo, assertions, endInfo.durationInSeconds, missingAssertions));
|
||||
m_messages.clear();
|
||||
m_messageScopes.clear();
|
||||
}
|
||||
|
||||
void RunContext::sectionEndedEarly(SectionEndInfo const & endInfo) {
|
||||
@@ -227,12 +230,21 @@ namespace Catch {
|
||||
|
||||
m_unfinishedSections.push_back(endInfo);
|
||||
}
|
||||
|
||||
#if defined(CATCH_CONFIG_ENABLE_BENCHMARKING)
|
||||
void RunContext::benchmarkPreparing(std::string const& name) {
|
||||
m_reporter->benchmarkPreparing(name);
|
||||
}
|
||||
void RunContext::benchmarkStarting( BenchmarkInfo const& info ) {
|
||||
m_reporter->benchmarkStarting( info );
|
||||
}
|
||||
void RunContext::benchmarkEnded( BenchmarkStats const& stats ) {
|
||||
void RunContext::benchmarkEnded( BenchmarkStats<> const& stats ) {
|
||||
m_reporter->benchmarkEnded( stats );
|
||||
}
|
||||
void RunContext::benchmarkFailed(std::string const & error) {
|
||||
m_reporter->benchmarkFailed(error);
|
||||
}
|
||||
#endif // CATCH_CONFIG_ENABLE_BENCHMARKING
|
||||
|
||||
void RunContext::pushScopedMessage(MessageInfo const & message) {
|
||||
m_messages.push_back(message);
|
||||
@@ -242,6 +254,10 @@ namespace Catch {
|
||||
m_messages.erase(std::remove(m_messages.begin(), m_messages.end(), message), m_messages.end());
|
||||
}
|
||||
|
||||
void RunContext::emplaceUnscopedMessage( MessageBuilder const& builder ) {
|
||||
m_messageScopes.emplace_back( builder );
|
||||
}
|
||||
|
||||
std::string RunContext::getCurrentTestName() const {
|
||||
return m_activeTestCase
|
||||
? m_activeTestCase->getTestCaseInfo().name
|
||||
@@ -302,6 +318,7 @@ namespace Catch {
|
||||
m_lastAssertionPassed = true;
|
||||
++m_totals.assertions.passed;
|
||||
resetAssertionInfo();
|
||||
m_messageScopes.clear();
|
||||
}
|
||||
|
||||
bool RunContext::aborting() const {
|
||||
@@ -323,13 +340,10 @@ namespace Catch {
|
||||
CATCH_TRY {
|
||||
if (m_reporter->getPreferences().shouldRedirectStdOut) {
|
||||
#if !defined(CATCH_CONFIG_EXPERIMENTAL_REDIRECT)
|
||||
RedirectedStdOut redirectedStdOut;
|
||||
RedirectedStdErr redirectedStdErr;
|
||||
RedirectedStreams redirectedStreams(redirectedCout, redirectedCerr);
|
||||
|
||||
timer.start();
|
||||
invokeActiveTestCase();
|
||||
redirectedCout += redirectedStdOut.str();
|
||||
redirectedCerr += redirectedStdErr.str();
|
||||
#else
|
||||
OutputRedirect r(redirectedCout, redirectedCerr);
|
||||
timer.start();
|
||||
@@ -356,6 +370,7 @@ namespace Catch {
|
||||
m_testCaseTracker->close();
|
||||
handleUnfinishedSections();
|
||||
m_messages.clear();
|
||||
m_messageScopes.clear();
|
||||
|
||||
SectionStats testCaseSectionStats(testCaseSection, assertions, duration, missingAssertions);
|
||||
m_reporter->sectionEnded(testCaseSectionStats);
|
||||
|
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user