mirror of
https://github.com/catchorg/Catch2.git
synced 2024-11-17 19:22:25 +01:00
Merge remote-tracking branch 'upstream/master' into contrib
This commit is contained in:
commit
9a1791ab9f
68
.travis.yml
68
.travis.yml
@ -17,7 +17,7 @@ matrix:
|
||||
addons:
|
||||
apt:
|
||||
sources: *all_sources
|
||||
packages: ['valgrind', 'clang-3.5']
|
||||
packages: ['valgrind', 'lcov', 'clang-3.5']
|
||||
env: COMPILER='clang++-3.5' VALGRIND=1
|
||||
|
||||
- os: linux
|
||||
@ -25,7 +25,7 @@ matrix:
|
||||
addons:
|
||||
apt:
|
||||
sources: *all_sources
|
||||
packages: ['valgrind', 'clang-3.6']
|
||||
packages: ['valgrind', 'lcov', 'clang-3.6']
|
||||
env: COMPILER='clang++-3.6' VALGRIND=1
|
||||
|
||||
# Travis's containers do not seem to have Clang 3.7 in apt, no matter what sources I add.
|
||||
@ -42,7 +42,7 @@ matrix:
|
||||
addons:
|
||||
apt:
|
||||
sources: *all_sources
|
||||
packages: ['valgrind', 'clang-3.8']
|
||||
packages: ['valgrind', 'lcov', 'clang-3.8']
|
||||
env: COMPILER='clang++-3.8' VALGRIND=1
|
||||
|
||||
- os: linux
|
||||
@ -50,7 +50,7 @@ matrix:
|
||||
addons:
|
||||
apt:
|
||||
sources: *all_sources
|
||||
packages: ['clang-3.9', 'valgrind']
|
||||
packages: ['clang-3.9', 'valgrind', 'lcov']
|
||||
env: COMPILER='clang++-3.9' VALGRIND=1
|
||||
|
||||
- os: linux
|
||||
@ -58,7 +58,7 @@ matrix:
|
||||
addons:
|
||||
apt:
|
||||
sources: *all_sources
|
||||
packages: ['clang-4.0', 'valgrind']
|
||||
packages: ['clang-4.0', 'valgrind', 'lcov']
|
||||
env: COMPILER='clang++-4.0' VALGRIND=1
|
||||
|
||||
- os: linux
|
||||
@ -66,7 +66,7 @@ matrix:
|
||||
addons:
|
||||
apt:
|
||||
sources: *all_sources
|
||||
packages: ['clang-5.0', 'valgrind']
|
||||
packages: ['clang-5.0', 'valgrind', 'lcov']
|
||||
env: COMPILER='clang++-5.0' VALGRIND=1
|
||||
|
||||
# 2/ Linux GCC Builds
|
||||
@ -75,7 +75,7 @@ matrix:
|
||||
addons:
|
||||
apt:
|
||||
sources: ['ubuntu-toolchain-r-test']
|
||||
packages: ['valgrind', 'g++-4.8']
|
||||
packages: ['valgrind', 'lcov', 'g++-4.8']
|
||||
env: COMPILER='g++-4.8' VALGRIND=1
|
||||
|
||||
- os: linux
|
||||
@ -83,7 +83,7 @@ matrix:
|
||||
addons:
|
||||
apt:
|
||||
sources: *all_sources
|
||||
packages: ['valgrind', 'g++-4.9']
|
||||
packages: ['valgrind', 'lcov', 'g++-4.9']
|
||||
env: COMPILER='g++-4.9' VALGRIND=1
|
||||
|
||||
- os: linux
|
||||
@ -91,7 +91,7 @@ matrix:
|
||||
addons:
|
||||
apt:
|
||||
sources: *all_sources
|
||||
packages: ['valgrind', 'g++-5']
|
||||
packages: ['valgrind', 'lcov', 'g++-5']
|
||||
env: COMPILER='g++-5' VALGRIND=1
|
||||
|
||||
- os: linux
|
||||
@ -99,7 +99,7 @@ matrix:
|
||||
addons: &gcc6
|
||||
apt:
|
||||
sources: *all_sources
|
||||
packages: ['valgrind', 'g++-6']
|
||||
packages: ['valgrind', 'lcov', 'g++-6']
|
||||
env: COMPILER='g++-6' VALGRIND=1
|
||||
|
||||
- os: linux
|
||||
@ -107,20 +107,15 @@ matrix:
|
||||
addons: &gcc7
|
||||
apt:
|
||||
sources: *all_sources
|
||||
packages: ['valgrind', 'g++-7']
|
||||
packages: ['valgrind', 'lcov', 'g++-7']
|
||||
env: COMPILER='g++-7' VALGRIND=1
|
||||
|
||||
- os: linux
|
||||
compiler: gcc
|
||||
addons: *gcc7
|
||||
env: COMPILER='g++-7' ENV_NO_SELFTEST=1 ENV_BUILD_EXAMPLES=1
|
||||
|
||||
# 3b/ Linux C++14 Clang builds
|
||||
- os: linux
|
||||
compiler: clang
|
||||
addons:
|
||||
apt:
|
||||
packages: ['clang-3.8', 'valgrind', 'libstdc++-6-dev']
|
||||
packages: ['clang-3.8', 'valgrind', 'lcov', 'libstdc++-6-dev']
|
||||
sources:
|
||||
- ubuntu-toolchain-r-test
|
||||
- llvm-toolchain-trusty
|
||||
@ -131,7 +126,7 @@ matrix:
|
||||
addons:
|
||||
apt:
|
||||
sources: *all_sources
|
||||
packages: ['clang-3.9', 'valgrind', 'libstdc++-6-dev']
|
||||
packages: ['clang-3.9', 'valgrind', 'lcov', 'libstdc++-6-dev']
|
||||
env: COMPILER='clang++-3.9' CPP14=1 VALGRIND=1
|
||||
|
||||
- os: linux
|
||||
@ -139,7 +134,7 @@ matrix:
|
||||
addons:
|
||||
apt:
|
||||
sources: *all_sources
|
||||
packages: ['clang-4.0', 'valgrind', 'libstdc++-6-dev']
|
||||
packages: ['clang-4.0', 'valgrind', 'lcov', 'libstdc++-6-dev']
|
||||
env: COMPILER='clang++-4.0' CPP14=1 VALGRIND=1
|
||||
|
||||
- os: linux
|
||||
@ -147,7 +142,7 @@ matrix:
|
||||
addons:
|
||||
apt:
|
||||
sources: *all_sources
|
||||
packages: ['clang-5.0', 'valgrind', 'libstdc++-6-dev']
|
||||
packages: ['clang-5.0', 'valgrind', 'lcov', 'libstdc++-6-dev']
|
||||
env: COMPILER='clang++-5.0' CPP14=1 VALGRIND=1
|
||||
|
||||
|
||||
@ -188,6 +183,7 @@ matrix:
|
||||
compiler: clang
|
||||
env: COMPILER='clang++' USE_CPP14=1
|
||||
|
||||
|
||||
install:
|
||||
- DEPS_DIR="${TRAVIS_BUILD_DIR}/deps"
|
||||
- mkdir -p ${DEPS_DIR} && cd ${DEPS_DIR}
|
||||
@ -197,7 +193,7 @@ install:
|
||||
mkdir cmake && travis_retry wget --no-check-certificate --quiet -O - ${CMAKE_URL} | tar --strip-components=1 -xz -C cmake
|
||||
export PATH=${DEPS_DIR}/cmake/bin:${PATH}
|
||||
elif [[ "${TRAVIS_OS_NAME}" == "osx" ]]; then
|
||||
which cmake || brew install cmake
|
||||
which cmake || brew install cmake;
|
||||
fi
|
||||
|
||||
before_script:
|
||||
@ -206,15 +202,25 @@ before_script:
|
||||
# Regenerate single header file, so it is tested in the examples...
|
||||
- python scripts/generateSingleHeader.py
|
||||
|
||||
# Use Debug builds for running Valgrind and building examples
|
||||
- cmake -H. -BBuild-Debug -DCMAKE_BUILD_TYPE=Debug -Wdev -DUSE_CPP14=${CPP14} -DUSE_VALGRIND=${VALGRIND} -DBUILD_EXAMPLES=ON
|
||||
# Check that we don't miscompile with optimalizations...
|
||||
- cmake -H. -BBuild-Release -DCMAKE_BUILD_TYPE=Release -Wdev -DUSE_CPP14=${CPP14}
|
||||
- |
|
||||
# Use Debug builds for running Valgrind and building examples
|
||||
cmake -H. -BBuild-Debug -DCMAKE_BUILD_TYPE=Debug -Wdev -DUSE_CPP14=${CPP14} -DUSE_VALGRIND=${VALGRIND} -DBUILD_EXAMPLES=ON -DENABLE_COVERAGE=ON
|
||||
# Don't bother with release build for coverage build
|
||||
cmake -H. -BBuild-Release -DCMAKE_BUILD_TYPE=Release -Wdev -DUSE_CPP14=${CPP14}
|
||||
|
||||
|
||||
script:
|
||||
- cd Build-Debug
|
||||
- make -j 2
|
||||
- ctest -V -j 2
|
||||
- cd ../Build-Release
|
||||
- make -j 2
|
||||
- ctest -V -j 2
|
||||
- |
|
||||
cd Build-Debug
|
||||
make -j 2
|
||||
CTEST_OUTPUT_ON_FAILURE=1 ctest -j 2
|
||||
# Coverage collection does not work for OS X atm
|
||||
if [[ "${TRAVIS_OS_NAME}" == "linux" ]]; then
|
||||
make gcov
|
||||
make lcov
|
||||
bash <(curl -s https://codecov.io/bash) -X gcov || echo "Codecov did not collect coverage reports"
|
||||
fi
|
||||
# Go to release build
|
||||
cd ../Build-Release
|
||||
make -j 2
|
||||
CTEST_OUTPUT_ON_FAILURE=1 ctest -j 2
|
||||
|
157
CMake/FindGcov.cmake
Normal file
157
CMake/FindGcov.cmake
Normal file
@ -0,0 +1,157 @@
|
||||
# This file is part of CMake-codecov.
|
||||
#
|
||||
# Copyright (c)
|
||||
# 2015-2017 RWTH Aachen University, Federal Republic of Germany
|
||||
#
|
||||
# See the LICENSE file in the package base directory for details
|
||||
#
|
||||
# Written by Alexander Haase, alexander.haase@rwth-aachen.de
|
||||
#
|
||||
|
||||
|
||||
# include required Modules
|
||||
include(FindPackageHandleStandardArgs)
|
||||
|
||||
|
||||
# Search for gcov binary.
|
||||
set(CMAKE_REQUIRED_QUIET_SAVE ${CMAKE_REQUIRED_QUIET})
|
||||
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
|
||||
# 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)
|
||||
get_filename_component(COMPILER_PATH "${CMAKE_${LANG}_COMPILER}" PATH)
|
||||
|
||||
if ("${CMAKE_${LANG}_COMPILER_ID}" STREQUAL "GNU")
|
||||
# Some distributions like OSX (homebrew) ship gcov with the compiler
|
||||
# version appended as gcov-x. To find this binary we'll build the
|
||||
# suggested binary name with the compiler version.
|
||||
string(REGEX MATCH "^[0-9]+" GCC_VERSION
|
||||
"${CMAKE_${LANG}_COMPILER_VERSION}")
|
||||
|
||||
find_program(GCOV_BIN NAMES gcov-${GCC_VERSION} gcov
|
||||
HINTS ${COMPILER_PATH})
|
||||
|
||||
elseif ("${CMAKE_${LANG}_COMPILER_ID}" STREQUAL "Clang")
|
||||
# Some distributions like Debian ship llvm-cov with the compiler
|
||||
# version appended as llvm-cov-x.y. To find this binary we'll build
|
||||
# the suggested binary name with the compiler version.
|
||||
string(REGEX MATCH "^[0-9]+.[0-9]+" LLVM_VERSION
|
||||
"${CMAKE_${LANG}_COMPILER_VERSION}")
|
||||
|
||||
# llvm-cov prior version 3.5 seems to be not working with coverage
|
||||
# evaluation tools, but these versions are compatible with the gcc
|
||||
# gcov tool.
|
||||
if(LLVM_VERSION VERSION_GREATER 3.4)
|
||||
find_program(LLVM_COV_BIN NAMES "llvm-cov-${LLVM_VERSION}"
|
||||
"llvm-cov" HINTS ${COMPILER_PATH})
|
||||
mark_as_advanced(LLVM_COV_BIN)
|
||||
|
||||
if (LLVM_COV_BIN)
|
||||
find_program(LLVM_COV_WRAPPER "llvm-cov-wrapper" PATHS
|
||||
${CMAKE_MODULE_PATH})
|
||||
if (LLVM_COV_WRAPPER)
|
||||
set(GCOV_BIN "${LLVM_COV_WRAPPER}" CACHE FILEPATH "")
|
||||
|
||||
# set additional parameters
|
||||
set(GCOV_${CMAKE_${LANG}_COMPILER_ID}_ENV
|
||||
"LLVM_COV_BIN=${LLVM_COV_BIN}" CACHE STRING
|
||||
"Environment variables for llvm-cov-wrapper.")
|
||||
mark_as_advanced(GCOV_${CMAKE_${LANG}_COMPILER_ID}_ENV)
|
||||
endif ()
|
||||
endif ()
|
||||
endif ()
|
||||
|
||||
if (NOT GCOV_BIN)
|
||||
# Fall back to gcov binary if llvm-cov was not found or is
|
||||
# incompatible. This is the default on OSX, but may crash on
|
||||
# recent Linux versions.
|
||||
find_program(GCOV_BIN gcov HINTS ${COMPILER_PATH})
|
||||
endif ()
|
||||
endif ()
|
||||
|
||||
|
||||
if (GCOV_BIN)
|
||||
set(GCOV_${CMAKE_${LANG}_COMPILER_ID}_BIN "${GCOV_BIN}" CACHE STRING
|
||||
"${LANG} gcov binary.")
|
||||
|
||||
if (NOT CMAKE_REQUIRED_QUIET)
|
||||
message("-- Found gcov evaluation for "
|
||||
"${CMAKE_${LANG}_COMPILER_ID}: ${GCOV_BIN}")
|
||||
endif()
|
||||
|
||||
unset(GCOV_BIN CACHE)
|
||||
endif ()
|
||||
endif ()
|
||||
endforeach ()
|
||||
|
||||
|
||||
|
||||
|
||||
# Add a new global target for all gcov targets. This target could be used to
|
||||
# generate the gcov files for the whole project instead of calling <TARGET>-gcov
|
||||
# for each target.
|
||||
if (NOT TARGET gcov)
|
||||
add_custom_target(gcov)
|
||||
endif (NOT TARGET gcov)
|
||||
|
||||
|
||||
|
||||
# This function will add gcov evaluation for target <TNAME>. Only sources of
|
||||
# this target will be evaluated and no dependencies will be added. It will call
|
||||
# Gcov on any source file of <TNAME> once and store the gcov file in the same
|
||||
# directory.
|
||||
function (add_gcov_target TNAME)
|
||||
set(TDIR ${CMAKE_CURRENT_BINARY_DIR}/CMakeFiles/${TNAME}.dir)
|
||||
|
||||
# We don't have to check, if the target has support for coverage, thus this
|
||||
# will be checked by add_coverage_target in Findcoverage.cmake. Instead we
|
||||
# have to determine which gcov binary to use.
|
||||
get_target_property(TSOURCES ${TNAME} SOURCES)
|
||||
set(SOURCES "")
|
||||
set(TCOMPILER "")
|
||||
foreach (FILE ${TSOURCES})
|
||||
codecov_path_of_source(${FILE} FILE)
|
||||
if (NOT "${FILE}" STREQUAL "")
|
||||
codecov_lang_of_source(${FILE} LANG)
|
||||
if (NOT "${LANG}" STREQUAL "")
|
||||
list(APPEND SOURCES "${FILE}")
|
||||
set(TCOMPILER ${CMAKE_${LANG}_COMPILER_ID})
|
||||
endif ()
|
||||
endif ()
|
||||
endforeach ()
|
||||
|
||||
# If no gcov binary was found, coverage data can't be evaluated.
|
||||
if (NOT GCOV_${TCOMPILER}_BIN)
|
||||
message(WARNING "No coverage evaluation binary found for ${TCOMPILER}.")
|
||||
return()
|
||||
endif ()
|
||||
|
||||
set(GCOV_BIN "${GCOV_${TCOMPILER}_BIN}")
|
||||
set(GCOV_ENV "${GCOV_${TCOMPILER}_ENV}")
|
||||
|
||||
|
||||
set(BUFFER "")
|
||||
foreach(FILE ${SOURCES})
|
||||
get_filename_component(FILE_PATH "${TDIR}/${FILE}" PATH)
|
||||
|
||||
# call gcov
|
||||
add_custom_command(OUTPUT ${TDIR}/${FILE}.gcov
|
||||
COMMAND ${GCOV_ENV} ${GCOV_BIN} ${TDIR}/${FILE}.gcno > /dev/null
|
||||
DEPENDS ${TNAME} ${TDIR}/${FILE}.gcno
|
||||
WORKING_DIRECTORY ${FILE_PATH}
|
||||
)
|
||||
|
||||
list(APPEND BUFFER ${TDIR}/${FILE}.gcov)
|
||||
endforeach()
|
||||
|
||||
|
||||
# add target for gcov evaluation of <TNAME>
|
||||
add_custom_target(${TNAME}-gcov DEPENDS ${BUFFER})
|
||||
|
||||
# add evaluation target to the global gcov target.
|
||||
add_dependencies(gcov ${TNAME}-gcov)
|
||||
endfunction (add_gcov_target)
|
354
CMake/FindLcov.cmake
Normal file
354
CMake/FindLcov.cmake
Normal file
@ -0,0 +1,354 @@
|
||||
# This file is part of CMake-codecov.
|
||||
#
|
||||
# Copyright (c)
|
||||
# 2015-2017 RWTH Aachen University, Federal Republic of Germany
|
||||
#
|
||||
# See the LICENSE file in the package base directory for details
|
||||
#
|
||||
# Written by Alexander Haase, alexander.haase@rwth-aachen.de
|
||||
#
|
||||
|
||||
|
||||
# configuration
|
||||
set(LCOV_DATA_PATH "${CMAKE_BINARY_DIR}/lcov/data")
|
||||
set(LCOV_DATA_PATH_INIT "${LCOV_DATA_PATH}/init")
|
||||
set(LCOV_DATA_PATH_CAPTURE "${LCOV_DATA_PATH}/capture")
|
||||
set(LCOV_HTML_PATH "${CMAKE_BINARY_DIR}/lcov/html")
|
||||
|
||||
|
||||
|
||||
|
||||
# Search for Gcov which is used by Lcov.
|
||||
find_package(Gcov)
|
||||
|
||||
|
||||
|
||||
|
||||
# This function will add lcov evaluation for target <TNAME>. Only sources of
|
||||
# this target will be evaluated and no dependencies will be added. It will call
|
||||
# geninfo on any source file of <TNAME> once and store the info file in the same
|
||||
# directory.
|
||||
#
|
||||
# Note: This function is only a wrapper to define this function always, even if
|
||||
# coverage is not supported by the compiler or disabled. This function must
|
||||
# be defined here, because the module will be exited, if there is no coverage
|
||||
# support by the compiler or it is disabled by the user.
|
||||
function (add_lcov_target TNAME)
|
||||
if (LCOV_FOUND)
|
||||
# capture initial coverage data
|
||||
lcov_capture_initial_tgt(${TNAME})
|
||||
|
||||
# capture coverage data after execution
|
||||
lcov_capture_tgt(${TNAME})
|
||||
endif ()
|
||||
endfunction (add_lcov_target)
|
||||
|
||||
|
||||
|
||||
|
||||
# include required Modules
|
||||
include(FindPackageHandleStandardArgs)
|
||||
|
||||
# Search for required lcov binaries.
|
||||
find_program(LCOV_BIN lcov)
|
||||
find_program(GENINFO_BIN geninfo)
|
||||
find_program(GENHTML_BIN genhtml)
|
||||
find_package_handle_standard_args(lcov
|
||||
REQUIRED_VARS LCOV_BIN GENINFO_BIN GENHTML_BIN
|
||||
)
|
||||
|
||||
# enable genhtml C++ demangeling, if c++filt is found.
|
||||
set(GENHTML_CPPFILT_FLAG "")
|
||||
find_program(CPPFILT_BIN c++filt)
|
||||
if (NOT CPPFILT_BIN STREQUAL "")
|
||||
set(GENHTML_CPPFILT_FLAG "--demangle-cpp")
|
||||
endif (NOT CPPFILT_BIN STREQUAL "")
|
||||
|
||||
# enable no-external flag for lcov, if available.
|
||||
if (GENINFO_BIN AND NOT DEFINED GENINFO_EXTERN_FLAG)
|
||||
set(FLAG "")
|
||||
execute_process(COMMAND ${GENINFO_BIN} --help OUTPUT_VARIABLE GENINFO_HELP)
|
||||
string(REGEX MATCH "external" GENINFO_RES "${GENINFO_HELP}")
|
||||
if (GENINFO_RES)
|
||||
set(FLAG "--no-external")
|
||||
endif ()
|
||||
|
||||
set(GENINFO_EXTERN_FLAG "${FLAG}"
|
||||
CACHE STRING "Geninfo flag to exclude system sources.")
|
||||
endif ()
|
||||
|
||||
# If Lcov was not found, exit module now.
|
||||
if (NOT LCOV_FOUND)
|
||||
return()
|
||||
endif (NOT LCOV_FOUND)
|
||||
|
||||
|
||||
|
||||
|
||||
# Create directories to be used.
|
||||
file(MAKE_DIRECTORY ${LCOV_DATA_PATH_INIT})
|
||||
file(MAKE_DIRECTORY ${LCOV_DATA_PATH_CAPTURE})
|
||||
|
||||
set(LCOV_REMOVE_PATTERNS "")
|
||||
|
||||
# This function will merge lcov files to a single target file. Additional lcov
|
||||
# flags may be set with setting LCOV_EXTRA_FLAGS before calling this function.
|
||||
function (lcov_merge_files OUTFILE ...)
|
||||
# Remove ${OUTFILE} from ${ARGV} and generate lcov parameters with files.
|
||||
list(REMOVE_AT ARGV 0)
|
||||
|
||||
# Generate merged file.
|
||||
string(REPLACE "${CMAKE_BINARY_DIR}/" "" FILE_REL "${OUTFILE}")
|
||||
add_custom_command(OUTPUT "${OUTFILE}.raw"
|
||||
COMMAND cat ${ARGV} > ${OUTFILE}.raw
|
||||
DEPENDS ${ARGV}
|
||||
COMMENT "Generating ${FILE_REL}"
|
||||
)
|
||||
|
||||
add_custom_command(OUTPUT "${OUTFILE}"
|
||||
COMMAND ${LCOV_BIN} --quiet -a ${OUTFILE}.raw --output-file ${OUTFILE}
|
||||
--base-directory ${PROJECT_SOURCE_DIR} ${LCOV_EXTRA_FLAGS}
|
||||
COMMAND ${LCOV_BIN} --quiet -r ${OUTFILE} ${LCOV_REMOVE_PATTERNS}
|
||||
--output-file ${OUTFILE} ${LCOV_EXTRA_FLAGS}
|
||||
DEPENDS ${OUTFILE}.raw
|
||||
COMMENT "Post-processing ${FILE_REL}"
|
||||
)
|
||||
endfunction ()
|
||||
|
||||
|
||||
|
||||
|
||||
# Add a new global target to generate initial coverage reports for all targets.
|
||||
# This target will be used to generate the global initial info file, which is
|
||||
# used to gather even empty report data.
|
||||
if (NOT TARGET lcov-capture-init)
|
||||
add_custom_target(lcov-capture-init)
|
||||
set(LCOV_CAPTURE_INIT_FILES "" CACHE INTERNAL "")
|
||||
endif (NOT TARGET lcov-capture-init)
|
||||
|
||||
|
||||
# This function will add initial capture of coverage data for target <TNAME>,
|
||||
# which is needed to get also data for objects, which were not loaded at
|
||||
# execution time. It will call geninfo for every source file of <TNAME> once and
|
||||
# store the info file in the same directory.
|
||||
function (lcov_capture_initial_tgt TNAME)
|
||||
# We don't have to check, if the target has support for coverage, thus this
|
||||
# will be checked by add_coverage_target in Findcoverage.cmake. Instead we
|
||||
# have to determine which gcov binary to use.
|
||||
get_target_property(TSOURCES ${TNAME} SOURCES)
|
||||
set(SOURCES "")
|
||||
set(TCOMPILER "")
|
||||
foreach (FILE ${TSOURCES})
|
||||
codecov_path_of_source(${FILE} FILE)
|
||||
if (NOT "${FILE}" STREQUAL "")
|
||||
codecov_lang_of_source(${FILE} LANG)
|
||||
if (NOT "${LANG}" STREQUAL "")
|
||||
list(APPEND SOURCES "${FILE}")
|
||||
set(TCOMPILER ${CMAKE_${LANG}_COMPILER_ID})
|
||||
endif ()
|
||||
endif ()
|
||||
endforeach ()
|
||||
|
||||
# If no gcov binary was found, coverage data can't be evaluated.
|
||||
if (NOT GCOV_${TCOMPILER}_BIN)
|
||||
message(WARNING "No coverage evaluation binary found for ${TCOMPILER}.")
|
||||
return()
|
||||
endif ()
|
||||
|
||||
set(GCOV_BIN "${GCOV_${TCOMPILER}_BIN}")
|
||||
set(GCOV_ENV "${GCOV_${TCOMPILER}_ENV}")
|
||||
|
||||
|
||||
set(TDIR ${CMAKE_CURRENT_BINARY_DIR}/CMakeFiles/${TNAME}.dir)
|
||||
set(GENINFO_FILES "")
|
||||
foreach(FILE ${SOURCES})
|
||||
# generate empty coverage files
|
||||
set(OUTFILE "${TDIR}/${FILE}.info.init")
|
||||
list(APPEND GENINFO_FILES ${OUTFILE})
|
||||
|
||||
add_custom_command(OUTPUT ${OUTFILE} COMMAND ${GCOV_ENV} ${GENINFO_BIN}
|
||||
--quiet --base-directory ${PROJECT_SOURCE_DIR} --initial
|
||||
--gcov-tool ${GCOV_BIN} --output-filename ${OUTFILE}
|
||||
${GENINFO_EXTERN_FLAG} ${TDIR}/${FILE}.gcno
|
||||
DEPENDS ${TNAME}
|
||||
COMMENT "Capturing initial coverage data for ${FILE}"
|
||||
)
|
||||
endforeach()
|
||||
|
||||
# Concatenate all files generated by geninfo to a single file per target.
|
||||
set(OUTFILE "${LCOV_DATA_PATH_INIT}/${TNAME}.info")
|
||||
set(LCOV_EXTRA_FLAGS "--initial")
|
||||
lcov_merge_files("${OUTFILE}" ${GENINFO_FILES})
|
||||
add_custom_target(${TNAME}-capture-init ALL DEPENDS ${OUTFILE})
|
||||
|
||||
# add geninfo file generation to global lcov-geninfo target
|
||||
add_dependencies(lcov-capture-init ${TNAME}-capture-init)
|
||||
set(LCOV_CAPTURE_INIT_FILES "${LCOV_CAPTURE_INIT_FILES}"
|
||||
"${OUTFILE}" CACHE INTERNAL ""
|
||||
)
|
||||
endfunction (lcov_capture_initial_tgt)
|
||||
|
||||
|
||||
# This function will generate the global info file for all targets. It has to be
|
||||
# called after all other CMake functions in the root CMakeLists.txt file, to get
|
||||
# a full list of all targets that generate coverage data.
|
||||
function (lcov_capture_initial)
|
||||
# Skip this function (and do not create the following targets), if there are
|
||||
# no input files.
|
||||
if ("${LCOV_CAPTURE_INIT_FILES}" STREQUAL "")
|
||||
return()
|
||||
endif ()
|
||||
|
||||
# Add a new target to merge the files of all targets.
|
||||
set(OUTFILE "${LCOV_DATA_PATH_INIT}/all_targets.info")
|
||||
lcov_merge_files("${OUTFILE}" ${LCOV_CAPTURE_INIT_FILES})
|
||||
add_custom_target(lcov-geninfo-init ALL DEPENDS ${OUTFILE}
|
||||
lcov-capture-init
|
||||
)
|
||||
endfunction (lcov_capture_initial)
|
||||
|
||||
|
||||
|
||||
|
||||
# Add a new global target to generate coverage reports for all targets. This
|
||||
# target will be used to generate the global info file.
|
||||
if (NOT TARGET lcov-capture)
|
||||
add_custom_target(lcov-capture)
|
||||
set(LCOV_CAPTURE_FILES "" CACHE INTERNAL "")
|
||||
endif (NOT TARGET lcov-capture)
|
||||
|
||||
|
||||
# This function will add capture of coverage data for target <TNAME>, which is
|
||||
# needed to get also data for objects, which were not loaded at execution time.
|
||||
# It will call geninfo for every source file of <TNAME> once and store the info
|
||||
# file in the same directory.
|
||||
function (lcov_capture_tgt TNAME)
|
||||
# We don't have to check, if the target has support for coverage, thus this
|
||||
# will be checked by add_coverage_target in Findcoverage.cmake. Instead we
|
||||
# have to determine which gcov binary to use.
|
||||
get_target_property(TSOURCES ${TNAME} SOURCES)
|
||||
set(SOURCES "")
|
||||
set(TCOMPILER "")
|
||||
foreach (FILE ${TSOURCES})
|
||||
codecov_path_of_source(${FILE} FILE)
|
||||
if (NOT "${FILE}" STREQUAL "")
|
||||
codecov_lang_of_source(${FILE} LANG)
|
||||
if (NOT "${LANG}" STREQUAL "")
|
||||
list(APPEND SOURCES "${FILE}")
|
||||
set(TCOMPILER ${CMAKE_${LANG}_COMPILER_ID})
|
||||
endif ()
|
||||
endif ()
|
||||
endforeach ()
|
||||
|
||||
# If no gcov binary was found, coverage data can't be evaluated.
|
||||
if (NOT GCOV_${TCOMPILER}_BIN)
|
||||
message(WARNING "No coverage evaluation binary found for ${TCOMPILER}.")
|
||||
return()
|
||||
endif ()
|
||||
|
||||
set(GCOV_BIN "${GCOV_${TCOMPILER}_BIN}")
|
||||
set(GCOV_ENV "${GCOV_${TCOMPILER}_ENV}")
|
||||
|
||||
|
||||
set(TDIR ${CMAKE_CURRENT_BINARY_DIR}/CMakeFiles/${TNAME}.dir)
|
||||
set(GENINFO_FILES "")
|
||||
foreach(FILE ${SOURCES})
|
||||
# Generate coverage files. If no .gcda file was generated during
|
||||
# execution, the empty coverage file will be used instead.
|
||||
set(OUTFILE "${TDIR}/${FILE}.info")
|
||||
list(APPEND GENINFO_FILES ${OUTFILE})
|
||||
|
||||
add_custom_command(OUTPUT ${OUTFILE}
|
||||
COMMAND test -f "${TDIR}/${FILE}.gcda"
|
||||
&& ${GCOV_ENV} ${GENINFO_BIN} --quiet --base-directory
|
||||
${PROJECT_SOURCE_DIR} --gcov-tool ${GCOV_BIN}
|
||||
--output-filename ${OUTFILE} ${GENINFO_EXTERN_FLAG}
|
||||
${TDIR}/${FILE}.gcda
|
||||
|| cp ${OUTFILE}.init ${OUTFILE}
|
||||
DEPENDS ${TNAME} ${TNAME}-capture-init
|
||||
COMMENT "Capturing coverage data for ${FILE}"
|
||||
)
|
||||
endforeach()
|
||||
|
||||
# Concatenate all files generated by geninfo to a single file per target.
|
||||
set(OUTFILE "${LCOV_DATA_PATH_CAPTURE}/${TNAME}.info")
|
||||
lcov_merge_files("${OUTFILE}" ${GENINFO_FILES})
|
||||
add_custom_target(${TNAME}-geninfo DEPENDS ${OUTFILE})
|
||||
|
||||
# add geninfo file generation to global lcov-capture target
|
||||
add_dependencies(lcov-capture ${TNAME}-geninfo)
|
||||
set(LCOV_CAPTURE_FILES "${LCOV_CAPTURE_FILES}" "${OUTFILE}" CACHE INTERNAL
|
||||
""
|
||||
)
|
||||
|
||||
# Add target for generating html output for this target only.
|
||||
file(MAKE_DIRECTORY ${LCOV_HTML_PATH}/${TNAME})
|
||||
add_custom_target(${TNAME}-genhtml
|
||||
COMMAND ${GENHTML_BIN} --quiet --sort --prefix ${PROJECT_SOURCE_DIR}
|
||||
--baseline-file ${LCOV_DATA_PATH_INIT}/${TNAME}.info
|
||||
--output-directory ${LCOV_HTML_PATH}/${TNAME}
|
||||
--title "${CMAKE_PROJECT_NAME} - target ${TNAME}"
|
||||
${GENHTML_CPPFILT_FLAG} ${OUTFILE}
|
||||
DEPENDS ${TNAME}-geninfo ${TNAME}-capture-init
|
||||
)
|
||||
endfunction (lcov_capture_tgt)
|
||||
|
||||
|
||||
# This function will generate the global info file for all targets. It has to be
|
||||
# called after all other CMake functions in the root CMakeLists.txt file, to get
|
||||
# a full list of all targets that generate coverage data.
|
||||
function (lcov_capture)
|
||||
# Skip this function (and do not create the following targets), if there are
|
||||
# no input files.
|
||||
if ("${LCOV_CAPTURE_FILES}" STREQUAL "")
|
||||
return()
|
||||
endif ()
|
||||
|
||||
# Add a new target to merge the files of all targets.
|
||||
set(OUTFILE "${LCOV_DATA_PATH_CAPTURE}/all_targets.info")
|
||||
lcov_merge_files("${OUTFILE}" ${LCOV_CAPTURE_FILES})
|
||||
add_custom_target(lcov-geninfo DEPENDS ${OUTFILE} lcov-capture)
|
||||
|
||||
# Add a new global target for all lcov targets. This target could be used to
|
||||
# generate the lcov html output for the whole project instead of calling
|
||||
# <TARGET>-geninfo and <TARGET>-genhtml for each target. It will also be
|
||||
# used to generate a html site for all project data together instead of one
|
||||
# for each target.
|
||||
if (NOT TARGET lcov)
|
||||
file(MAKE_DIRECTORY ${LCOV_HTML_PATH}/all_targets)
|
||||
add_custom_target(lcov
|
||||
COMMAND ${GENHTML_BIN} --quiet --sort
|
||||
--baseline-file ${LCOV_DATA_PATH_INIT}/all_targets.info
|
||||
--output-directory ${LCOV_HTML_PATH}/all_targets
|
||||
--title "${CMAKE_PROJECT_NAME}" --prefix "${PROJECT_SOURCE_DIR}"
|
||||
${GENHTML_CPPFILT_FLAG} ${OUTFILE}
|
||||
DEPENDS lcov-geninfo-init lcov-geninfo
|
||||
)
|
||||
endif ()
|
||||
endfunction (lcov_capture)
|
||||
|
||||
|
||||
|
||||
|
||||
# Add a new global target to generate the lcov html report for the whole project
|
||||
# instead of calling <TARGET>-genhtml for each target (to create an own report
|
||||
# for each target). Instead of the lcov target it does not require geninfo for
|
||||
# all targets, so you have to call <TARGET>-geninfo to generate the info files
|
||||
# the targets you'd like to have in your report or lcov-geninfo for generating
|
||||
# info files for all targets before calling lcov-genhtml.
|
||||
file(MAKE_DIRECTORY ${LCOV_HTML_PATH}/selected_targets)
|
||||
if (NOT TARGET lcov-genhtml)
|
||||
add_custom_target(lcov-genhtml
|
||||
COMMAND ${GENHTML_BIN}
|
||||
--quiet
|
||||
--output-directory ${LCOV_HTML_PATH}/selected_targets
|
||||
--title \"${CMAKE_PROJECT_NAME} - targets `find
|
||||
${LCOV_DATA_PATH_CAPTURE} -name \"*.info\" ! -name
|
||||
\"all_targets.info\" -exec basename {} .info \\\;`\"
|
||||
--prefix ${PROJECT_SOURCE_DIR}
|
||||
--sort
|
||||
${GENHTML_CPPFILT_FLAG}
|
||||
`find ${LCOV_DATA_PATH_CAPTURE} -name \"*.info\" ! -name
|
||||
\"all_targets.info\"`
|
||||
)
|
||||
endif (NOT TARGET lcov-genhtml)
|
258
CMake/Findcodecov.cmake
Normal file
258
CMake/Findcodecov.cmake
Normal file
@ -0,0 +1,258 @@
|
||||
# This file is part of CMake-codecov.
|
||||
#
|
||||
# Copyright (c)
|
||||
# 2015-2017 RWTH Aachen University, Federal Republic of Germany
|
||||
#
|
||||
# See the LICENSE file in the package base directory for details
|
||||
#
|
||||
# Written by Alexander Haase, alexander.haase@rwth-aachen.de
|
||||
#
|
||||
|
||||
|
||||
# Add an option to choose, if coverage should be enabled or not. If enabled
|
||||
# marked targets will be build with coverage support and appropriate targets
|
||||
# will be added. If disabled coverage will be ignored for *ALL* targets.
|
||||
option(ENABLE_COVERAGE "Enable coverage build." OFF)
|
||||
|
||||
set(COVERAGE_FLAG_CANDIDATES
|
||||
# gcc and clang
|
||||
"-O0 -g -fprofile-arcs -ftest-coverage"
|
||||
|
||||
# gcc and clang fallback
|
||||
"-O0 -g --coverage"
|
||||
)
|
||||
|
||||
|
||||
# Add coverage support for target ${TNAME} and register target for coverage
|
||||
# evaluation. If coverage is disabled or not supported, this function will
|
||||
# simply do nothing.
|
||||
#
|
||||
# Note: This function is only a wrapper to define this function always, even if
|
||||
# coverage is not supported by the compiler or disabled. This function must
|
||||
# be defined here, because the module will be exited, if there is no coverage
|
||||
# support by the compiler or it is disabled by the user.
|
||||
function (add_coverage TNAME)
|
||||
# only add coverage for target, if coverage is support and enabled.
|
||||
if (ENABLE_COVERAGE)
|
||||
foreach (TNAME ${ARGV})
|
||||
add_coverage_target(${TNAME})
|
||||
endforeach ()
|
||||
endif ()
|
||||
endfunction (add_coverage)
|
||||
|
||||
|
||||
# Add global target to gather coverage information after all targets have been
|
||||
# added. Other evaluation functions could be added here, after checks for the
|
||||
# specific module have been passed.
|
||||
#
|
||||
# Note: This function is only a wrapper to define this function always, even if
|
||||
# coverage is not supported by the compiler or disabled. This function must
|
||||
# be defined here, because the module will be exited, if there is no coverage
|
||||
# support by the compiler or it is disabled by the user.
|
||||
function (coverage_evaluate)
|
||||
# add lcov evaluation
|
||||
if (LCOV_FOUND)
|
||||
lcov_capture_initial()
|
||||
lcov_capture()
|
||||
endif (LCOV_FOUND)
|
||||
endfunction ()
|
||||
|
||||
|
||||
# Exit this module, if coverage is disabled. add_coverage is defined before this
|
||||
# return, so this module can be exited now safely without breaking any build-
|
||||
# scripts.
|
||||
if (NOT ENABLE_COVERAGE)
|
||||
return()
|
||||
endif ()
|
||||
|
||||
|
||||
|
||||
|
||||
# Find the reuired flags foreach language.
|
||||
set(CMAKE_REQUIRED_QUIET_SAVE ${CMAKE_REQUIRED_QUIET})
|
||||
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
|
||||
# instead of searching flags foreach language, search flags foreach compiler
|
||||
# used.
|
||||
set(COMPILER ${CMAKE_${LANG}_COMPILER_ID})
|
||||
if (NOT COVERAGE_${COMPILER}_FLAGS)
|
||||
foreach (FLAG ${COVERAGE_FLAG_CANDIDATES})
|
||||
if(NOT CMAKE_REQUIRED_QUIET)
|
||||
message(STATUS "Try ${COMPILER} code coverage flag = [${FLAG}]")
|
||||
endif()
|
||||
|
||||
set(CMAKE_REQUIRED_FLAGS "${FLAG}")
|
||||
unset(COVERAGE_FLAG_DETECTED CACHE)
|
||||
|
||||
if (${LANG} STREQUAL "C")
|
||||
include(CheckCCompilerFlag)
|
||||
check_c_compiler_flag("${FLAG}" COVERAGE_FLAG_DETECTED)
|
||||
|
||||
elseif (${LANG} STREQUAL "CXX")
|
||||
include(CheckCXXCompilerFlag)
|
||||
check_cxx_compiler_flag("${FLAG}" COVERAGE_FLAG_DETECTED)
|
||||
|
||||
elseif (${LANG} STREQUAL "Fortran")
|
||||
# CheckFortranCompilerFlag was introduced in CMake 3.x. To be
|
||||
# compatible with older Cmake versions, we will check if this
|
||||
# module is present before we use it. Otherwise we will define
|
||||
# Fortran coverage support as not available.
|
||||
include(CheckFortranCompilerFlag OPTIONAL
|
||||
RESULT_VARIABLE INCLUDED)
|
||||
if (INCLUDED)
|
||||
check_fortran_compiler_flag("${FLAG}"
|
||||
COVERAGE_FLAG_DETECTED)
|
||||
elseif (NOT CMAKE_REQUIRED_QUIET)
|
||||
message("-- Performing Test COVERAGE_FLAG_DETECTED")
|
||||
message("-- Performing Test COVERAGE_FLAG_DETECTED - Failed"
|
||||
" (Check not supported)")
|
||||
endif ()
|
||||
endif()
|
||||
|
||||
if (COVERAGE_FLAG_DETECTED)
|
||||
set(COVERAGE_${COMPILER}_FLAGS "${FLAG}"
|
||||
CACHE STRING "${COMPILER} flags for code coverage.")
|
||||
mark_as_advanced(COVERAGE_${COMPILER}_FLAGS)
|
||||
break()
|
||||
else ()
|
||||
message(WARNING "Code coverage is not available for ${COMPILER}"
|
||||
" compiler. Targets using this compiler will be "
|
||||
"compiled without it.")
|
||||
endif ()
|
||||
endforeach ()
|
||||
endif ()
|
||||
endforeach ()
|
||||
|
||||
set(CMAKE_REQUIRED_QUIET ${CMAKE_REQUIRED_QUIET_SAVE})
|
||||
|
||||
|
||||
|
||||
|
||||
# Helper function to get the language of a source file.
|
||||
function (codecov_lang_of_source FILE RETURN_VAR)
|
||||
get_filename_component(FILE_EXT "${FILE}" EXT)
|
||||
string(TOLOWER "${FILE_EXT}" FILE_EXT)
|
||||
string(SUBSTRING "${FILE_EXT}" 1 -1 FILE_EXT)
|
||||
|
||||
get_property(ENABLED_LANGUAGES GLOBAL PROPERTY ENABLED_LANGUAGES)
|
||||
foreach (LANG ${ENABLED_LANGUAGES})
|
||||
list(FIND CMAKE_${LANG}_SOURCE_FILE_EXTENSIONS "${FILE_EXT}" TEMP)
|
||||
if (NOT ${TEMP} EQUAL -1)
|
||||
set(${RETURN_VAR} "${LANG}" PARENT_SCOPE)
|
||||
return()
|
||||
endif ()
|
||||
endforeach()
|
||||
|
||||
set(${RETURN_VAR} "" PARENT_SCOPE)
|
||||
endfunction ()
|
||||
|
||||
|
||||
# Helper function to get the relative path of the source file destination path.
|
||||
# This path is needed by FindGcov and FindLcov cmake files to locate the
|
||||
# captured data.
|
||||
function (codecov_path_of_source FILE RETURN_VAR)
|
||||
string(REGEX MATCH "TARGET_OBJECTS:([^ >]+)" _source ${FILE})
|
||||
|
||||
# If expression was found, SOURCEFILE is a generator-expression for an
|
||||
# object library. Currently we found no way to call this function automatic
|
||||
# for the referenced target, so it must be called in the directoryso of the
|
||||
# object library definition.
|
||||
if (NOT "${_source}" STREQUAL "")
|
||||
set(${RETURN_VAR} "" PARENT_SCOPE)
|
||||
return()
|
||||
endif ()
|
||||
|
||||
|
||||
string(REPLACE "${CMAKE_CURRENT_BINARY_DIR}/" "" FILE "${FILE}")
|
||||
if(IS_ABSOLUTE ${FILE})
|
||||
file(RELATIVE_PATH FILE ${CMAKE_CURRENT_SOURCE_DIR} ${FILE})
|
||||
endif()
|
||||
|
||||
# get the right path for file
|
||||
string(REPLACE ".." "__" PATH "${FILE}")
|
||||
|
||||
set(${RETURN_VAR} "${PATH}" PARENT_SCOPE)
|
||||
endfunction()
|
||||
|
||||
|
||||
|
||||
|
||||
# Add coverage support for target ${TNAME} and register target for coverage
|
||||
# evaluation.
|
||||
function(add_coverage_target TNAME)
|
||||
# Check if all sources for target use the same compiler. If a target uses
|
||||
# e.g. C and Fortran mixed and uses different compilers (e.g. clang and
|
||||
# gfortran) this can trigger huge problems, because different compilers may
|
||||
# use different implementations for code coverage.
|
||||
get_target_property(TSOURCES ${TNAME} SOURCES)
|
||||
set(TARGET_COMPILER "")
|
||||
set(ADDITIONAL_FILES "")
|
||||
foreach (FILE ${TSOURCES})
|
||||
# If expression was found, FILE is a generator-expression for an object
|
||||
# library. Object libraries will be ignored.
|
||||
string(REGEX MATCH "TARGET_OBJECTS:([^ >]+)" _file ${FILE})
|
||||
if ("${_file}" STREQUAL "")
|
||||
codecov_lang_of_source(${FILE} LANG)
|
||||
if (LANG)
|
||||
list(APPEND TARGET_COMPILER ${CMAKE_${LANG}_COMPILER_ID})
|
||||
|
||||
list(APPEND ADDITIONAL_FILES "${FILE}.gcno")
|
||||
list(APPEND ADDITIONAL_FILES "${FILE}.gcda")
|
||||
endif ()
|
||||
endif ()
|
||||
endforeach ()
|
||||
|
||||
list(REMOVE_DUPLICATES TARGET_COMPILER)
|
||||
list(LENGTH TARGET_COMPILER NUM_COMPILERS)
|
||||
|
||||
if (NUM_COMPILERS GREATER 1)
|
||||
message(WARNING "Can't use code coverage for target ${TNAME}, because "
|
||||
"it will be compiled by incompatible compilers. Target will be "
|
||||
"compiled without code coverage.")
|
||||
return()
|
||||
|
||||
elseif (NUM_COMPILERS EQUAL 0)
|
||||
message(WARNING "Can't use code coverage for target ${TNAME}, because "
|
||||
"it uses an unknown compiler. Target will be compiled without "
|
||||
"code coverage.")
|
||||
return()
|
||||
|
||||
elseif (NOT DEFINED "COVERAGE_${TARGET_COMPILER}_FLAGS")
|
||||
# A warning has been printed before, so just return if flags for this
|
||||
# compiler aren't available.
|
||||
return()
|
||||
endif()
|
||||
|
||||
|
||||
# enable coverage for target
|
||||
set_property(TARGET ${TNAME} APPEND_STRING
|
||||
PROPERTY COMPILE_FLAGS " ${COVERAGE_${TARGET_COMPILER}_FLAGS}")
|
||||
set_property(TARGET ${TNAME} APPEND_STRING
|
||||
PROPERTY LINK_FLAGS " ${COVERAGE_${TARGET_COMPILER}_FLAGS}")
|
||||
|
||||
|
||||
# Add gcov files generated by compiler to clean target.
|
||||
set(CLEAN_FILES "")
|
||||
foreach (FILE ${ADDITIONAL_FILES})
|
||||
codecov_path_of_source(${FILE} FILE)
|
||||
list(APPEND CLEAN_FILES "CMakeFiles/${TNAME}.dir/${FILE}")
|
||||
endforeach()
|
||||
|
||||
set_directory_properties(PROPERTIES ADDITIONAL_MAKE_CLEAN_FILES
|
||||
"${CLEAN_FILES}")
|
||||
|
||||
|
||||
add_gcov_target(${TNAME})
|
||||
add_lcov_target(${TNAME})
|
||||
endfunction(add_coverage_target)
|
||||
|
||||
|
||||
|
||||
|
||||
# Include modules for parsing the collected data and output it in a readable
|
||||
# format (like gcov and lcov).
|
||||
find_package(Gcov)
|
||||
find_package(Lcov)
|
56
CMake/llvm-cov-wrapper
Executable file
56
CMake/llvm-cov-wrapper
Executable file
@ -0,0 +1,56 @@
|
||||
#!/bin/sh
|
||||
|
||||
# This file is part of CMake-codecov.
|
||||
#
|
||||
# Copyright (c)
|
||||
# 2015-2017 RWTH Aachen University, Federal Republic of Germany
|
||||
#
|
||||
# See the LICENSE file in the package base directory for details
|
||||
#
|
||||
# Written by Alexander Haase, alexander.haase@rwth-aachen.de
|
||||
#
|
||||
|
||||
if [ -z "$LLVM_COV_BIN" ]
|
||||
then
|
||||
echo "LLVM_COV_BIN not set!" >& 2
|
||||
exit 1
|
||||
fi
|
||||
|
||||
|
||||
# Get LLVM version to find out.
|
||||
LLVM_VERSION=$($LLVM_COV_BIN -version | grep -i "LLVM version" \
|
||||
| sed "s/^\([A-Za-z ]*\)\([0-9]\).\([0-9]\).*$/\2.\3/g")
|
||||
|
||||
if [ "$1" = "-v" ]
|
||||
then
|
||||
echo "llvm-cov-wrapper $LLVM_VERSION"
|
||||
exit 0
|
||||
fi
|
||||
|
||||
|
||||
if [ -n "$LLVM_VERSION" ]
|
||||
then
|
||||
MAJOR=$(echo $LLVM_VERSION | cut -d'.' -f1)
|
||||
MINOR=$(echo $LLVM_VERSION | cut -d'.' -f2)
|
||||
|
||||
if [ $MAJOR -eq 3 ] && [ $MINOR -le 4 ]
|
||||
then
|
||||
if [ -f "$1" ]
|
||||
then
|
||||
filename=$(basename "$1")
|
||||
extension="${filename##*.}"
|
||||
|
||||
case "$extension" in
|
||||
"gcno") exec $LLVM_COV_BIN --gcno="$1" ;;
|
||||
"gcda") exec $LLVM_COV_BIN --gcda="$1" ;;
|
||||
esac
|
||||
fi
|
||||
fi
|
||||
|
||||
if [ $MAJOR -eq 3 ] && [ $MINOR -le 5 ]
|
||||
then
|
||||
exec $LLVM_COV_BIN $@
|
||||
fi
|
||||
fi
|
||||
|
||||
exec $LLVM_COV_BIN gcov $@
|
@ -4,6 +4,7 @@ project(CatchSelfTest)
|
||||
|
||||
option(USE_VALGRIND "Perform SelfTests with Valgrind" OFF)
|
||||
option(BUILD_EXAMPLES "Build documentation examples" OFF)
|
||||
option(ENABLE_COVERAGE "Generate coverage for codecov.io" OFF)
|
||||
|
||||
set_property(GLOBAL PROPERTY USE_FOLDERS ON)
|
||||
|
||||
@ -280,7 +281,6 @@ set(HEADERS
|
||||
SOURCE_GROUP("Tests" FILES ${TEST_SOURCES})
|
||||
SOURCE_GROUP("Surrogates" FILES ${SURROGATE_SOURCES})
|
||||
|
||||
# configure the executable
|
||||
|
||||
# Projects consuming Catch via ExternalProject_Add might want to use install step
|
||||
# without building all of our selftests.
|
||||
@ -299,10 +299,17 @@ if (NOT NO_SELFTEST)
|
||||
set_property(TARGET SelfTest PROPERTY CXX_STANDARD_REQUIRED ON)
|
||||
set_property(TARGET SelfTest PROPERTY CXX_EXTENSIONS OFF)
|
||||
|
||||
if (ENABLE_COVERAGE)
|
||||
list(APPEND CMAKE_MODULE_PATH "${CMAKE_CURRENT_LIST_DIR}/CMake")
|
||||
find_package(codecov)
|
||||
add_coverage(SelfTest)
|
||||
list(APPEND LCOV_REMOVE_PATTERNS "/usr/")
|
||||
coverage_evaluate()
|
||||
endif()
|
||||
|
||||
# Add desired warnings
|
||||
if ( CMAKE_CXX_COMPILER_ID MATCHES "Clang|AppleClang|GNU" )
|
||||
target_compile_options( SelfTest PRIVATE -Wall -Wextra -Wunreachable-code )
|
||||
target_compile_options( SelfTest PRIVATE -Wall -Wextra -Wunreachable-code -Werror )
|
||||
endif()
|
||||
# Clang specific warning go here
|
||||
if ( CMAKE_CXX_COMPILER_ID MATCHES "Clang" )
|
||||
@ -315,22 +322,30 @@ if (NOT NO_SELFTEST)
|
||||
|
||||
|
||||
# configure unit tests via CTest
|
||||
enable_testing()
|
||||
include(CTest)
|
||||
add_test(NAME RunTests COMMAND $<TARGET_FILE:SelfTest>)
|
||||
|
||||
add_test(NAME ListTests COMMAND $<TARGET_FILE:SelfTest> --list-tests)
|
||||
add_test(NAME ListTests COMMAND $<TARGET_FILE:SelfTest> --list-tests --verbosity high)
|
||||
set_tests_properties(ListTests PROPERTIES PASS_REGULAR_EXPRESSION "[0-9]+ test cases")
|
||||
|
||||
add_test(NAME ListTags COMMAND $<TARGET_FILE:SelfTest> --list-tags)
|
||||
set_tests_properties(ListTags PROPERTIES PASS_REGULAR_EXPRESSION "[0-9]+ tags")
|
||||
|
||||
add_test(NAME ListReporters COMMAND $<TARGET_FILE:SelfTest> --list-reporters)
|
||||
set_tests_properties(ListReporters PROPERTIES PASS_REGULAR_EXPRESSION "Available reporters:")
|
||||
|
||||
add_test(NAME ListTestNamesOnly COMMAND $<TARGET_FILE:SelfTest> --list-test-names-only)
|
||||
set_tests_properties(ListTestNamesOnly PROPERTIES PASS_REGULAR_EXPRESSION "Regex string matcher")
|
||||
|
||||
|
||||
|
||||
# AppVeyor has a Python 2.7 in path, but doesn't have .py files as autorunnable
|
||||
add_test(NAME ApprovalTests COMMAND python ${CMAKE_CURRENT_SOURCE_DIR}/scripts/approvalTests.py $<TARGET_FILE:SelfTest>)
|
||||
set_tests_properties(ApprovalTests PROPERTIES FAIL_REGULAR_EXPRESSION "Results differed")
|
||||
|
||||
if (USE_VALGRIND)
|
||||
add_test(NAME ValgrindRunTests COMMAND valgrind --leak-check=full --error-exitcode=1 $<TARGET_FILE:SelfTest>)
|
||||
add_test(NAME ValgrindListTests COMMAND valgrind --leak-check=full --error-exitcode=1 $<TARGET_FILE:SelfTest> --list-tests)
|
||||
add_test(NAME ValgrindListTests COMMAND valgrind --leak-check=full --error-exitcode=1 $<TARGET_FILE:SelfTest> --list-tests --verbosity high)
|
||||
set_tests_properties(ValgrindListTests PROPERTIES PASS_REGULAR_EXPRESSION "definitely lost: 0 bytes in 0 blocks")
|
||||
add_test(NAME ValgrindListTags COMMAND valgrind --leak-check=full --error-exitcode=1 $<TARGET_FILE:SelfTest> --list-tags)
|
||||
set_tests_properties(ValgrindListTags PROPERTIES PASS_REGULAR_EXPRESSION "definitely lost: 0 bytes in 0 blocks")
|
||||
@ -343,7 +358,6 @@ if(BUILD_EXAMPLES)
|
||||
add_subdirectory(examples)
|
||||
endif()
|
||||
|
||||
|
||||
install(DIRECTORY "single_include/" DESTINATION "include/catch")
|
||||
|
||||
## Provide some pkg-config integration
|
||||
|
@ -2,8 +2,8 @@
|
||||
![catch logo](artwork/catch2-logo-small.png)
|
||||
|
||||
[![Github Releases](https://img.shields.io/github/release/catchorg/catch2.svg)](https://github.com/catchorg/catch2/releases)
|
||||
[![Build Status](https://travis-ci.org/catchorg/Catch2.svg)](https://travis-ci.org/catchorg/Catch2)
|
||||
[![Build status](https://ci.appveyor.com/api/projects/status/hrtk60hv6tw6fght?svg=true)](https://ci.appveyor.com/project/catchorg/catch2)
|
||||
[![Build Status](https://travis-ci.org/catchorg/Catch2.svg?branch=master)](https://travis-ci.org/catchorg/Catch2)
|
||||
[![Build status](https://ci.appveyor.com/api/projects/status/github/catchorg/Catch2?svg=true)](https://ci.appveyor.com/project/catchorg/catch2)
|
||||
[![Try online](https://img.shields.io/badge/try-online-blue.svg)](https://wandbox.org/permlink/tB8z0G3kMAIZkIca)
|
||||
|
||||
<a href="https://github.com/catchorg/Catch2/releases/download/v2.0.1/catch.hpp">The latest version of the single header can be downloaded directly using this link</a>
|
||||
|
18
appveyor.yml
18
appveyor.yml
@ -27,11 +27,10 @@ matrix:
|
||||
|
||||
init:
|
||||
- git config --global core.autocrlf input
|
||||
# Set build version to git commit-hash
|
||||
- ps: Update-AppveyorBuild -Version "$($env:APPVEYOR_REPO_BRANCH) - $($env:APPVEYOR_REPO_COMMIT)"
|
||||
|
||||
# fetch repository as zip archive
|
||||
shallow_clone: true
|
||||
install:
|
||||
- ps: if (($env:CONFIGURATION) -eq "Debug" ) { python -m pip install codecov }
|
||||
- ps: if (($env:CONFIGURATION) -eq "Debug" ) { .\misc\installOpenCppCoverage.ps1 }
|
||||
|
||||
# Win32 and x64 are CMake-compatible solution platform names.
|
||||
# This allows us to pass %PLATFORM% to CMake -A.
|
||||
@ -46,9 +45,12 @@ configuration:
|
||||
|
||||
#Cmake will autodetect the compiler, but we set the arch
|
||||
before_build:
|
||||
- python scripts/generateSingleHeader.py
|
||||
- set CXXFLAGS=%additional_flags%
|
||||
- cmake -H. -BBuild -A%PLATFORM% -DUSE_WMAIN=%wmain% -DBUILD_EXAMPLES=ON
|
||||
# Indirection because appveyor doesn't handle multiline batch scripts properly
|
||||
# https://stackoverflow.com/questions/37627248/how-to-split-a-command-over-multiple-lines-in-appveyor-yml/37647169#37647169
|
||||
# https://help.appveyor.com/discussions/questions/3888-multi-line-cmd-or-powershell-warning-ignore
|
||||
- cmd: .\misc\appveyorBuildConfigurationScript.bat
|
||||
|
||||
|
||||
# build with MSBuild
|
||||
build:
|
||||
@ -57,5 +59,5 @@ build:
|
||||
verbosity: normal # MSBuild verbosity level {quiet|minimal|normal|detailed}
|
||||
|
||||
test_script:
|
||||
- cd Build
|
||||
- ctest -V -j 2 -C %CONFIGURATION%
|
||||
- set CTEST_OUTPUT_ON_FAILURE=1
|
||||
- cmd: .\misc\appveyorTestRunScript.bat
|
||||
|
10
codecov.yml
Normal file
10
codecov.yml
Normal file
@ -0,0 +1,10 @@
|
||||
codecov:
|
||||
branch: master
|
||||
|
||||
coverage:
|
||||
ignore:
|
||||
- "projects/SelfTest"
|
||||
- "**/catch_reporter_tap.hpp"
|
||||
- "**/catch_reporter_automake.hpp"
|
||||
- "**/catch_reporter_teamcity.hpp"
|
||||
- "**/external/clara.hpp"
|
@ -87,6 +87,41 @@
|
||||
|
||||
# Older versions
|
||||
|
||||
## 1.11.x
|
||||
|
||||
### 1.11.0
|
||||
|
||||
#### Fixes
|
||||
* The original expression in `REQUIRE_FALSE( expr )` is now reporter properly as `!( expr )` (#1051)
|
||||
* Previously the parentheses were missing and `x != y` would be expanded as `!x != x`
|
||||
* `Approx::Margin` is now inclusive (#952)
|
||||
* Previously it was meant and documented as inclusive, but the check itself wasn't
|
||||
* This means that `REQUIRE( 0.25f == Approx( 0.0f ).margin( 0.25f ) )` passes, instead of fails
|
||||
* `RandomNumberGenerator::result_type` is now unsigned (#1050)
|
||||
|
||||
#### Improvements
|
||||
* `__JETBRAINS_IDE__` macro handling is now CLion version specific (#1017)
|
||||
* When CLion 2017.3 or newer is detected, `__COUNTER__` is used instead of
|
||||
* TeamCity reporter now explicitly flushes output stream after each report (#1057)
|
||||
* On some platforms, output from redirected streams would show up only after the tests finished running
|
||||
* `ParseAndAddCatchTests` now can add test files as dependency to CMake configuration
|
||||
* This means you do not have to manually rerun CMake configuration step to detect new tests
|
||||
|
||||
## 1.10.x
|
||||
|
||||
### 1.10.0
|
||||
|
||||
#### Fixes
|
||||
* Evaluation layer has been rewritten (backported from Catch 2)
|
||||
* The new layer is much simpler and fixes some issues (#981)
|
||||
* Implemented workaround for VS 2017 raw string literal stringification bug (#995)
|
||||
* Fixed interaction between `[!shouldfail]` and `[!mayfail]` tags and sections
|
||||
* Previously sections with failing assertions would be marked as failed, not failed-but-ok
|
||||
|
||||
#### Improvements
|
||||
* Added [libidentify](https://github.com/janwilmans/LibIdentify) support
|
||||
* Added "wait-for-keypress" option
|
||||
|
||||
## 1.9.x
|
||||
|
||||
### 1.9.6
|
||||
|
@ -295,13 +295,16 @@ void print( std::ostream& os, int const level, std::string const& title, Catch::
|
||||
// 2. My listener and registration:
|
||||
//
|
||||
|
||||
const std::string dashed_line =
|
||||
char const * dashed_line =
|
||||
"--------------------------------------------------------------------------";
|
||||
|
||||
struct MyListener : Catch::TestEventListenerBase {
|
||||
|
||||
using TestEventListenerBase::TestEventListenerBase; // inherit constructor
|
||||
|
||||
// Get rid of Wweak-tables
|
||||
~MyListener();
|
||||
|
||||
// The whole test run starting
|
||||
virtual void testRunStarting( Catch::TestRunInfo const& testRunInfo ) override {
|
||||
std::cout
|
||||
@ -367,6 +370,10 @@ struct MyListener : Catch::TestEventListenerBase {
|
||||
|
||||
CATCH_REGISTER_LISTENER( MyListener )
|
||||
|
||||
// Get rid of Wweak-tables
|
||||
MyListener::~MyListener() {}
|
||||
|
||||
|
||||
// -----------------------------------------------------------------------
|
||||
// 3. Test cases:
|
||||
//
|
||||
|
@ -13,8 +13,7 @@
|
||||
#include "catch_debugger.h"
|
||||
#include "catch_interfaces_registry_hub.h"
|
||||
#include "catch_capture_matchers.h"
|
||||
|
||||
#include <cassert>
|
||||
#include "catch_run_context.h"
|
||||
|
||||
namespace Catch {
|
||||
|
||||
@ -54,87 +53,60 @@ namespace Catch {
|
||||
SourceLineInfo const& lineInfo,
|
||||
StringRef capturedExpression,
|
||||
ResultDisposition::Flags resultDisposition )
|
||||
: m_assertionInfo{ macroName, lineInfo, capturedExpression, resultDisposition }
|
||||
{
|
||||
getCurrentContext().getResultCapture()->assertionStarting( m_assertionInfo );
|
||||
: m_assertionInfo{ macroName, lineInfo, capturedExpression, resultDisposition },
|
||||
m_resultCapture( getResultCapture() )
|
||||
{}
|
||||
|
||||
AssertionHandler::~AssertionHandler() {
|
||||
if ( !m_completed )
|
||||
m_resultCapture.handleIncomplete( m_assertionInfo );
|
||||
}
|
||||
|
||||
void AssertionHandler::handle( ITransientExpression const& expr ) {
|
||||
|
||||
bool negated = isFalseTest( m_assertionInfo.resultDisposition );
|
||||
bool result = expr.getResult() != negated;
|
||||
|
||||
if(result && !getCurrentContext().getConfig()->includeSuccessfulResults())
|
||||
{
|
||||
getCurrentContext().getResultCapture()->assertionRun();
|
||||
getCurrentContext().getResultCapture()->assertionPassed();
|
||||
return;
|
||||
}
|
||||
|
||||
handle( result ? ResultWas::Ok : ResultWas::ExpressionFailed, &expr, negated );
|
||||
void AssertionHandler::handleExpr( ITransientExpression const& expr ) {
|
||||
m_resultCapture.handleExpr( m_assertionInfo, expr, m_reaction );
|
||||
}
|
||||
void AssertionHandler::handle( ResultWas::OfType resultType ) {
|
||||
handle( resultType, nullptr, false );
|
||||
}
|
||||
void AssertionHandler::handle( ResultWas::OfType resultType, StringRef const& message ) {
|
||||
AssertionResultData data( resultType, LazyExpression( false ) );
|
||||
data.message = message;
|
||||
handle( data, nullptr );
|
||||
}
|
||||
void AssertionHandler::handle( ResultWas::OfType resultType, ITransientExpression const* expr, bool negated ) {
|
||||
AssertionResultData data( resultType, LazyExpression( negated ) );
|
||||
handle( data, expr );
|
||||
}
|
||||
void AssertionHandler::handle( AssertionResultData const& resultData, ITransientExpression const* expr ) {
|
||||
|
||||
getResultCapture().assertionRun();
|
||||
|
||||
AssertionResult assertionResult{ m_assertionInfo, resultData };
|
||||
assertionResult.m_resultData.lazyExpression.m_transientExpression = expr;
|
||||
|
||||
getResultCapture().assertionEnded( assertionResult );
|
||||
|
||||
if( !assertionResult.isOk() ) {
|
||||
m_shouldDebugBreak = getCurrentContext().getConfig()->shouldDebugBreak();
|
||||
m_shouldThrow =
|
||||
getCurrentContext().getRunner()->aborting() ||
|
||||
(m_assertionInfo.resultDisposition & ResultDisposition::Normal);
|
||||
}
|
||||
void AssertionHandler::handleMessage(ResultWas::OfType resultType, StringRef const& message) {
|
||||
m_resultCapture.handleMessage( m_assertionInfo, resultType, message, m_reaction );
|
||||
}
|
||||
|
||||
auto AssertionHandler::allowThrows() const -> bool {
|
||||
return getCurrentContext().getConfig()->allowThrows();
|
||||
}
|
||||
|
||||
auto AssertionHandler::shouldDebugBreak() const -> bool {
|
||||
return m_shouldDebugBreak;
|
||||
}
|
||||
void AssertionHandler::reactWithDebugBreak() const {
|
||||
if (m_shouldDebugBreak) {
|
||||
///////////////////////////////////////////////////////////////////
|
||||
// To inspect the state during test, you need to go one level up the callstack
|
||||
// To go back to the test and change execution, jump over the reactWithoutDebugBreak() call
|
||||
///////////////////////////////////////////////////////////////////
|
||||
void AssertionHandler::complete() {
|
||||
setCompleted();
|
||||
if( m_reaction.shouldDebugBreak ) {
|
||||
|
||||
// If you find your debugger stopping you here then go one level up on the
|
||||
// call-stack for the code that caused it (typically a failed assertion)
|
||||
|
||||
// (To go back to the test and change execution, jump over the throw, next)
|
||||
CATCH_BREAK_INTO_DEBUGGER();
|
||||
}
|
||||
reactWithoutDebugBreak();
|
||||
}
|
||||
void AssertionHandler::reactWithoutDebugBreak() const {
|
||||
if( m_shouldThrow )
|
||||
if( m_reaction.shouldThrow )
|
||||
throw Catch::TestFailureException();
|
||||
}
|
||||
|
||||
void AssertionHandler::useActiveException() {
|
||||
handle( ResultWas::ThrewException, Catch::translateActiveException() );
|
||||
void AssertionHandler::setCompleted() {
|
||||
m_completed = true;
|
||||
}
|
||||
|
||||
void AssertionHandler::setExceptionGuard() {
|
||||
assert( m_inExceptionGuard == false );
|
||||
m_inExceptionGuard = true;
|
||||
void AssertionHandler::handleUnexpectedInflightException() {
|
||||
m_resultCapture.handleUnexpectedInflightException( m_assertionInfo, Catch::translateActiveException(), m_reaction );
|
||||
}
|
||||
void AssertionHandler::unsetExceptionGuard() {
|
||||
assert( m_inExceptionGuard == true );
|
||||
m_inExceptionGuard = false;
|
||||
|
||||
void AssertionHandler::handleExceptionThrownAsExpected() {
|
||||
m_resultCapture.handleNonExpr(m_assertionInfo, ResultWas::Ok, m_reaction);
|
||||
}
|
||||
void AssertionHandler::handleExceptionNotThrownAsExpected() {
|
||||
m_resultCapture.handleNonExpr(m_assertionInfo, ResultWas::Ok, m_reaction);
|
||||
}
|
||||
|
||||
void AssertionHandler::handleUnexpectedExceptionNotThrown() {
|
||||
m_resultCapture.handleUnexpectedExceptionNotThrown( m_assertionInfo, m_reaction );
|
||||
}
|
||||
|
||||
void AssertionHandler::handleThrowingCallSkipped() {
|
||||
m_resultCapture.handleNonExpr(m_assertionInfo, ResultWas::Ok, m_reaction);
|
||||
}
|
||||
|
||||
// This is the overload that takes a string and infers the Equals matcher from it
|
||||
|
@ -17,10 +17,13 @@ namespace Catch {
|
||||
|
||||
struct TestFailureException{};
|
||||
struct AssertionResultData;
|
||||
struct IResultCapture;
|
||||
class RunContext;
|
||||
|
||||
class LazyExpression {
|
||||
friend class AssertionHandler;
|
||||
friend struct AssertionStats;
|
||||
friend class RunContext;
|
||||
|
||||
ITransientExpression const* m_transientExpression = nullptr;
|
||||
bool m_isNegated;
|
||||
@ -34,11 +37,16 @@ namespace Catch {
|
||||
friend auto operator << ( std::ostream& os, LazyExpression const& lazyExpr ) -> std::ostream&;
|
||||
};
|
||||
|
||||
struct AssertionReaction {
|
||||
bool shouldDebugBreak = false;
|
||||
bool shouldThrow = false;
|
||||
};
|
||||
|
||||
class AssertionHandler {
|
||||
AssertionInfo m_assertionInfo;
|
||||
bool m_shouldDebugBreak = false;
|
||||
bool m_shouldThrow = false;
|
||||
bool m_inExceptionGuard = false;
|
||||
AssertionReaction m_reaction;
|
||||
bool m_completed = false;
|
||||
IResultCapture& m_resultCapture;
|
||||
|
||||
public:
|
||||
AssertionHandler
|
||||
@ -54,24 +62,25 @@ namespace Catch {
|
||||
}
|
||||
}
|
||||
|
||||
void handle( ITransientExpression const& expr );
|
||||
|
||||
template<typename T>
|
||||
void handle( ExprLhs<T> const& expr ) {
|
||||
handle( expr.makeUnaryExpr() );
|
||||
void handleExpr( ExprLhs<T> const& expr ) {
|
||||
handleExpr( expr.makeUnaryExpr() );
|
||||
}
|
||||
void handle( ResultWas::OfType resultType );
|
||||
void handle( ResultWas::OfType resultType, StringRef const& message );
|
||||
void handle( ResultWas::OfType resultType, ITransientExpression const* expr, bool negated );
|
||||
void handle( AssertionResultData const& resultData, ITransientExpression const* expr );
|
||||
void handleExpr( ITransientExpression const& expr );
|
||||
|
||||
auto shouldDebugBreak() const -> bool;
|
||||
void handleMessage(ResultWas::OfType resultType, StringRef const& message);
|
||||
|
||||
void handleExceptionThrownAsExpected();
|
||||
void handleUnexpectedExceptionNotThrown();
|
||||
void handleExceptionNotThrownAsExpected();
|
||||
void handleThrowingCallSkipped();
|
||||
void handleUnexpectedInflightException();
|
||||
|
||||
void complete();
|
||||
void setCompleted();
|
||||
|
||||
// query
|
||||
auto allowThrows() const -> bool;
|
||||
void reactWithDebugBreak() const;
|
||||
void reactWithoutDebugBreak() const;
|
||||
void useActiveException();
|
||||
void setExceptionGuard();
|
||||
void unsetExceptionGuard();
|
||||
};
|
||||
|
||||
void handleExceptionMatchExpr( AssertionHandler& handler, std::string const& str, StringRef matcherString );
|
||||
|
@ -11,7 +11,6 @@
|
||||
#include "catch_assertionhandler.h"
|
||||
#include "catch_message.h"
|
||||
#include "catch_interfaces_capture.h"
|
||||
#include "catch_debugger.h"
|
||||
|
||||
#if !defined(CATCH_CONFIG_DISABLE)
|
||||
|
||||
@ -22,48 +21,33 @@
|
||||
#endif
|
||||
|
||||
#if defined(CATCH_CONFIG_FAST_COMPILE)
|
||||
///////////////////////////////////////////////////////////////////////////////
|
||||
// We can speedup compilation significantly by breaking into debugger lower in
|
||||
// the callstack, because then we don't have to expand CATCH_BREAK_INTO_DEBUGGER
|
||||
// macro in each assertion
|
||||
#define INTERNAL_CATCH_REACT( handler ) \
|
||||
handler.reactWithDebugBreak();
|
||||
|
||||
///////////////////////////////////////////////////////////////////////////////
|
||||
// Another way to speed-up compilation is to omit local try-catch for REQUIRE*
|
||||
// macros.
|
||||
// This can potentially cause false negative, if the test code catches
|
||||
// the exception before it propagates back up to the runner.
|
||||
#define INTERNAL_CATCH_TRY( capturer ) capturer.setExceptionGuard();
|
||||
#define INTERNAL_CATCH_CATCH( capturer ) capturer.unsetExceptionGuard();
|
||||
#define INTERNAL_CATCH_TRY
|
||||
#define INTERNAL_CATCH_CATCH( capturer )
|
||||
|
||||
#else // CATCH_CONFIG_FAST_COMPILE
|
||||
|
||||
///////////////////////////////////////////////////////////////////////////////
|
||||
// In the event of a failure works out if the debugger needs to be invoked
|
||||
// and/or an exception thrown and takes appropriate action.
|
||||
// This needs to be done as a macro so the debugger will stop in the user
|
||||
// source code rather than in Catch library code
|
||||
#define INTERNAL_CATCH_REACT( handler ) \
|
||||
if( handler.shouldDebugBreak() ) CATCH_BREAK_INTO_DEBUGGER(); \
|
||||
handler.reactWithoutDebugBreak();
|
||||
|
||||
#define INTERNAL_CATCH_TRY( capturer ) try
|
||||
#define INTERNAL_CATCH_CATCH( capturer ) catch(...) { capturer.useActiveException(); }
|
||||
#define INTERNAL_CATCH_TRY try
|
||||
#define INTERNAL_CATCH_CATCH( handler ) catch(...) { handler.handleUnexpectedInflightException(); }
|
||||
|
||||
#endif
|
||||
|
||||
#define INTERNAL_CATCH_REACT( handler ) handler.complete();
|
||||
|
||||
///////////////////////////////////////////////////////////////////////////////
|
||||
#define INTERNAL_CATCH_TEST( macroName, resultDisposition, ... ) \
|
||||
do { \
|
||||
Catch::AssertionHandler catchAssertionHandler( macroName, CATCH_INTERNAL_LINEINFO, CATCH_INTERNAL_STRINGIFY(__VA_ARGS__), resultDisposition ); \
|
||||
INTERNAL_CATCH_TRY( catchAssertionHandler ) { \
|
||||
INTERNAL_CATCH_TRY { \
|
||||
CATCH_INTERNAL_SUPPRESS_PARENTHESES_WARNINGS \
|
||||
catchAssertionHandler.handle( Catch::Decomposer() <= __VA_ARGS__ ); \
|
||||
catchAssertionHandler.handleExpr( Catch::Decomposer() <= __VA_ARGS__ ); \
|
||||
CATCH_INTERNAL_UNSUPPRESS_PARENTHESES_WARNINGS \
|
||||
} INTERNAL_CATCH_CATCH( catchAssertionHandler ) \
|
||||
INTERNAL_CATCH_REACT( catchAssertionHandler ) \
|
||||
} while( Catch::isTrue(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 &&.
|
||||
|
||||
///////////////////////////////////////////////////////////////////////////////
|
||||
@ -82,13 +66,13 @@
|
||||
Catch::AssertionHandler catchAssertionHandler( macroName, CATCH_INTERNAL_LINEINFO, CATCH_INTERNAL_STRINGIFY(__VA_ARGS__), resultDisposition ); \
|
||||
try { \
|
||||
static_cast<void>(__VA_ARGS__); \
|
||||
catchAssertionHandler.handle( Catch::ResultWas::Ok ); \
|
||||
catchAssertionHandler.handleExceptionNotThrownAsExpected(); \
|
||||
} \
|
||||
catch( ... ) { \
|
||||
catchAssertionHandler.useActiveException(); \
|
||||
catchAssertionHandler.handleUnexpectedInflightException(); \
|
||||
} \
|
||||
INTERNAL_CATCH_REACT( catchAssertionHandler ) \
|
||||
} while( Catch::alwaysFalse() )
|
||||
} while( false )
|
||||
|
||||
///////////////////////////////////////////////////////////////////////////////
|
||||
#define INTERNAL_CATCH_THROWS( macroName, resultDisposition, ... ) \
|
||||
@ -97,15 +81,15 @@
|
||||
if( catchAssertionHandler.allowThrows() ) \
|
||||
try { \
|
||||
static_cast<void>(__VA_ARGS__); \
|
||||
catchAssertionHandler.handle( Catch::ResultWas::DidntThrowException ); \
|
||||
catchAssertionHandler.handleUnexpectedExceptionNotThrown(); \
|
||||
} \
|
||||
catch( ... ) { \
|
||||
catchAssertionHandler.handle( Catch::ResultWas::Ok ); \
|
||||
catchAssertionHandler.handleExceptionThrownAsExpected(); \
|
||||
} \
|
||||
else \
|
||||
catchAssertionHandler.handle( Catch::ResultWas::Ok ); \
|
||||
catchAssertionHandler.handleThrowingCallSkipped(); \
|
||||
INTERNAL_CATCH_REACT( catchAssertionHandler ) \
|
||||
} while( Catch::alwaysFalse() )
|
||||
} while( false )
|
||||
|
||||
///////////////////////////////////////////////////////////////////////////////
|
||||
#define INTERNAL_CATCH_THROWS_AS( macroName, exceptionType, resultDisposition, expr ) \
|
||||
@ -114,31 +98,31 @@
|
||||
if( catchAssertionHandler.allowThrows() ) \
|
||||
try { \
|
||||
static_cast<void>(expr); \
|
||||
catchAssertionHandler.handle( Catch::ResultWas::DidntThrowException ); \
|
||||
catchAssertionHandler.handleUnexpectedExceptionNotThrown(); \
|
||||
} \
|
||||
catch( exceptionType const& ) { \
|
||||
catchAssertionHandler.handle( Catch::ResultWas::Ok ); \
|
||||
catchAssertionHandler.handleExceptionThrownAsExpected(); \
|
||||
} \
|
||||
catch( ... ) { \
|
||||
catchAssertionHandler.useActiveException(); \
|
||||
catchAssertionHandler.handleUnexpectedInflightException(); \
|
||||
} \
|
||||
else \
|
||||
catchAssertionHandler.handle( Catch::ResultWas::Ok ); \
|
||||
catchAssertionHandler.handleThrowingCallSkipped(); \
|
||||
INTERNAL_CATCH_REACT( catchAssertionHandler ) \
|
||||
} while( Catch::alwaysFalse() )
|
||||
} while( false )
|
||||
|
||||
|
||||
///////////////////////////////////////////////////////////////////////////////
|
||||
#define INTERNAL_CATCH_MSG( macroName, messageType, resultDisposition, ... ) \
|
||||
do { \
|
||||
Catch::AssertionHandler catchAssertionHandler( macroName, CATCH_INTERNAL_LINEINFO, "", resultDisposition ); \
|
||||
catchAssertionHandler.handle( messageType, ( Catch::MessageStream() << __VA_ARGS__ + ::Catch::StreamEndStop() ).m_stream.str() ); \
|
||||
catchAssertionHandler.handleMessage( messageType, ( Catch::MessageStream() << __VA_ARGS__ + ::Catch::StreamEndStop() ).m_stream.str() ); \
|
||||
INTERNAL_CATCH_REACT( catchAssertionHandler ) \
|
||||
} while( Catch::alwaysFalse() )
|
||||
} while( false )
|
||||
|
||||
///////////////////////////////////////////////////////////////////////////////
|
||||
#define INTERNAL_CATCH_INFO( macroName, log ) \
|
||||
Catch::ScopedMessage INTERNAL_CATCH_UNIQUE_NAME( scopedMessage ) = Catch::MessageBuilder( macroName, CATCH_INTERNAL_LINEINFO, Catch::ResultWas::Info ) << log;
|
||||
Catch::ScopedMessage INTERNAL_CATCH_UNIQUE_NAME( scopedMessage )( Catch::MessageBuilder( macroName, CATCH_INTERNAL_LINEINFO, Catch::ResultWas::Info ) << log );
|
||||
|
||||
///////////////////////////////////////////////////////////////////////////////
|
||||
// Although this is matcher-based, it can be used with just a string
|
||||
@ -148,15 +132,15 @@
|
||||
if( catchAssertionHandler.allowThrows() ) \
|
||||
try { \
|
||||
static_cast<void>(__VA_ARGS__); \
|
||||
catchAssertionHandler.handle( Catch::ResultWas::DidntThrowException ); \
|
||||
catchAssertionHandler.handleUnexpectedExceptionNotThrown(); \
|
||||
} \
|
||||
catch( ... ) { \
|
||||
handleExceptionMatchExpr( catchAssertionHandler, matcher, #matcher ); \
|
||||
Catch::handleExceptionMatchExpr( catchAssertionHandler, matcher, #matcher ); \
|
||||
} \
|
||||
else \
|
||||
catchAssertionHandler.handle( Catch::ResultWas::Ok ); \
|
||||
catchAssertionHandler.handleThrowingCallSkipped(); \
|
||||
INTERNAL_CATCH_REACT( catchAssertionHandler ) \
|
||||
} while( Catch::alwaysFalse() )
|
||||
} while( false )
|
||||
|
||||
#endif // CATCH_CONFIG_DISABLE
|
||||
|
||||
|
@ -18,7 +18,7 @@ namespace Catch {
|
||||
void handleExceptionMatchExpr( AssertionHandler& handler, StringMatcher const& matcher, StringRef matcherString ) {
|
||||
std::string exceptionMessage = Catch::translateActiveException();
|
||||
MatchExpr<std::string, StringMatcher const&> expr( exceptionMessage, matcher, matcherString );
|
||||
handler.handle( expr );
|
||||
handler.handleExpr( expr );
|
||||
}
|
||||
|
||||
} // namespace Catch
|
||||
|
@ -21,18 +21,14 @@ namespace Catch {
|
||||
ArgT const& m_arg;
|
||||
MatcherT m_matcher;
|
||||
StringRef m_matcherString;
|
||||
bool m_result;
|
||||
public:
|
||||
MatchExpr( ArgT const& arg, MatcherT const& matcher, StringRef matcherString )
|
||||
: m_arg( arg ),
|
||||
: ITransientExpression{ true, matcher.match( arg ) },
|
||||
m_arg( arg ),
|
||||
m_matcher( matcher ),
|
||||
m_matcherString( matcherString ),
|
||||
m_result( matcher.match( arg ) )
|
||||
m_matcherString( matcherString )
|
||||
{}
|
||||
|
||||
auto isBinaryExpression() const -> bool override { return true; }
|
||||
auto getResult() const -> bool override { return m_result; }
|
||||
|
||||
void streamReconstructedExpression( std::ostream &os ) const override {
|
||||
auto matcherAsString = m_matcher.toString();
|
||||
os << Catch::Detail::stringify( m_arg ) << ' ';
|
||||
@ -59,11 +55,11 @@ namespace Catch {
|
||||
#define INTERNAL_CHECK_THAT( macroName, matcher, resultDisposition, arg ) \
|
||||
do { \
|
||||
Catch::AssertionHandler catchAssertionHandler( macroName, CATCH_INTERNAL_LINEINFO, CATCH_INTERNAL_STRINGIFY(arg) ", " CATCH_INTERNAL_STRINGIFY(matcher), resultDisposition ); \
|
||||
INTERNAL_CATCH_TRY( catchAssertionHandler ) { \
|
||||
catchAssertionHandler.handle( Catch::makeMatchExpr( arg, matcher, #matcher ) ); \
|
||||
INTERNAL_CATCH_TRY { \
|
||||
catchAssertionHandler.handleExpr( Catch::makeMatchExpr( arg, matcher, #matcher ) ); \
|
||||
} INTERNAL_CATCH_CATCH( catchAssertionHandler ) \
|
||||
INTERNAL_CATCH_REACT( catchAssertionHandler ) \
|
||||
} while( Catch::alwaysFalse() )
|
||||
} while( false )
|
||||
|
||||
|
||||
///////////////////////////////////////////////////////////////////////////////
|
||||
@ -73,17 +69,17 @@ namespace Catch {
|
||||
if( catchAssertionHandler.allowThrows() ) \
|
||||
try { \
|
||||
static_cast<void>(__VA_ARGS__ ); \
|
||||
catchAssertionHandler.handle( Catch::ResultWas::DidntThrowException ); \
|
||||
catchAssertionHandler.handleUnexpectedExceptionNotThrown(); \
|
||||
} \
|
||||
catch( exceptionType const& ex ) { \
|
||||
catchAssertionHandler.handle( Catch::makeMatchExpr( ex, matcher, #matcher ) ); \
|
||||
catchAssertionHandler.handleExpr( Catch::makeMatchExpr( ex, matcher, #matcher ) ); \
|
||||
} \
|
||||
catch( ... ) { \
|
||||
catchAssertionHandler.useActiveException(); \
|
||||
catchAssertionHandler.handleUnexpectedInflightException(); \
|
||||
} \
|
||||
else \
|
||||
catchAssertionHandler.handle( Catch::ResultWas::Ok ); \
|
||||
catchAssertionHandler.handleThrowingCallSkipped(); \
|
||||
INTERNAL_CATCH_REACT( catchAssertionHandler ) \
|
||||
} while( Catch::alwaysFalse() )
|
||||
} while( false )
|
||||
|
||||
#endif // TWOBLUECUBES_CATCH_CAPTURE_MATCHERS_HPP_INCLUDED
|
||||
|
@ -34,9 +34,6 @@ namespace Catch {
|
||||
return os;
|
||||
}
|
||||
|
||||
bool alwaysTrue() { return true; }
|
||||
bool alwaysFalse() { return false; }
|
||||
|
||||
std::string StreamEndStop::operator+() const {
|
||||
return std::string();
|
||||
}
|
||||
|
@ -61,12 +61,6 @@ namespace Catch {
|
||||
|
||||
std::ostream& operator << ( std::ostream& os, SourceLineInfo const& info );
|
||||
|
||||
// This is just here to avoid compiler warnings with macro constants and boolean literals
|
||||
inline bool isTrue( bool value ){ return value; }
|
||||
|
||||
bool alwaysTrue();
|
||||
bool alwaysFalse();
|
||||
|
||||
// Use this in variadic streaming macros to allow
|
||||
// >> +StreamEndStop
|
||||
// as well as
|
||||
|
@ -40,7 +40,7 @@ namespace Catch {
|
||||
#ifdef CATCH_TRAP
|
||||
#define CATCH_BREAK_INTO_DEBUGGER() if( Catch::isDebuggerActive() ) { CATCH_TRAP(); }
|
||||
#else
|
||||
#define CATCH_BREAK_INTO_DEBUGGER() Catch::alwaysTrue();
|
||||
#define CATCH_BREAK_INTO_DEBUGGER() (void)0, 0
|
||||
#endif
|
||||
|
||||
#endif // TWOBLUECUBES_CATCH_DEBUGGER_H_INCLUDED
|
||||
|
@ -24,27 +24,32 @@
|
||||
namespace Catch {
|
||||
|
||||
struct ITransientExpression {
|
||||
virtual auto isBinaryExpression() const -> bool = 0;
|
||||
virtual auto getResult() const -> bool = 0;
|
||||
auto isBinaryExpression() const -> bool { return m_isBinaryExpression; }
|
||||
auto getResult() const -> bool { return m_result; }
|
||||
virtual void streamReconstructedExpression( std::ostream &os ) const = 0;
|
||||
|
||||
// We don't actually need a virtual destructore, but many static analysers
|
||||
ITransientExpression( bool isBinaryExpression, bool result )
|
||||
: m_isBinaryExpression( isBinaryExpression ),
|
||||
m_result( result )
|
||||
{}
|
||||
|
||||
// We don't actually need a virtual destructor, but many static analysers
|
||||
// complain if it's not here :-(
|
||||
virtual ~ITransientExpression();
|
||||
|
||||
bool m_isBinaryExpression;
|
||||
bool m_result;
|
||||
|
||||
};
|
||||
|
||||
void formatReconstructedExpression( std::ostream &os, std::string const& lhs, StringRef op, std::string const& rhs );
|
||||
|
||||
template<typename LhsT, typename RhsT>
|
||||
class BinaryExpr : public ITransientExpression {
|
||||
bool m_result;
|
||||
LhsT m_lhs;
|
||||
StringRef m_op;
|
||||
RhsT m_rhs;
|
||||
|
||||
auto isBinaryExpression() const -> bool override { return true; }
|
||||
auto getResult() const -> bool override { return m_result; }
|
||||
|
||||
void streamReconstructedExpression( std::ostream &os ) const override {
|
||||
formatReconstructedExpression
|
||||
( os, Catch::Detail::stringify( m_lhs ), m_op, Catch::Detail::stringify( m_rhs ) );
|
||||
@ -52,7 +57,7 @@ namespace Catch {
|
||||
|
||||
public:
|
||||
BinaryExpr( bool comparisonResult, LhsT lhs, StringRef op, RhsT rhs )
|
||||
: m_result( comparisonResult ),
|
||||
: ITransientExpression{ true, comparisonResult },
|
||||
m_lhs( lhs ),
|
||||
m_op( op ),
|
||||
m_rhs( rhs )
|
||||
@ -63,15 +68,15 @@ namespace Catch {
|
||||
class UnaryExpr : public ITransientExpression {
|
||||
LhsT m_lhs;
|
||||
|
||||
auto isBinaryExpression() const -> bool override { return false; }
|
||||
auto getResult() const -> bool override { return m_lhs ? true : false; }
|
||||
|
||||
void streamReconstructedExpression( std::ostream &os ) const override {
|
||||
os << Catch::Detail::stringify( m_lhs );
|
||||
}
|
||||
|
||||
public:
|
||||
UnaryExpr( LhsT lhs ) : m_lhs( lhs ) {}
|
||||
explicit UnaryExpr( LhsT lhs )
|
||||
: ITransientExpression{ false, lhs ? true : false },
|
||||
m_lhs( lhs )
|
||||
{}
|
||||
};
|
||||
|
||||
|
||||
@ -103,43 +108,43 @@ namespace Catch {
|
||||
class ExprLhs {
|
||||
LhsT m_lhs;
|
||||
public:
|
||||
ExprLhs( LhsT lhs ) : m_lhs( lhs ) {}
|
||||
explicit ExprLhs( LhsT lhs ) : m_lhs( lhs ) {}
|
||||
|
||||
template<typename RhsT>
|
||||
auto operator == ( RhsT const& rhs ) -> BinaryExpr<LhsT, RhsT const&> const {
|
||||
return BinaryExpr<LhsT, RhsT const&>( compareEqual( m_lhs, rhs ), m_lhs, "==", rhs );
|
||||
return { compareEqual( m_lhs, rhs ), m_lhs, "==", rhs };
|
||||
}
|
||||
auto operator == ( bool rhs ) -> BinaryExpr<LhsT, bool> const {
|
||||
return BinaryExpr<LhsT, bool>( m_lhs == rhs, m_lhs, "==", rhs );
|
||||
return { m_lhs == rhs, m_lhs, "==", rhs };
|
||||
}
|
||||
|
||||
template<typename RhsT>
|
||||
auto operator != ( RhsT const& rhs ) -> BinaryExpr<LhsT, RhsT const&> const {
|
||||
return BinaryExpr<LhsT, RhsT const&>( compareNotEqual( m_lhs, rhs ), m_lhs, "!=", rhs );
|
||||
return { compareNotEqual( m_lhs, rhs ), m_lhs, "!=", rhs };
|
||||
}
|
||||
auto operator != ( bool rhs ) -> BinaryExpr<LhsT, bool> const {
|
||||
return BinaryExpr<LhsT, bool>( m_lhs != rhs, m_lhs, "!=", rhs );
|
||||
return { m_lhs != rhs, m_lhs, "!=", rhs };
|
||||
}
|
||||
|
||||
template<typename RhsT>
|
||||
auto operator > ( RhsT const& rhs ) -> BinaryExpr<LhsT, RhsT const&> const {
|
||||
return BinaryExpr<LhsT, RhsT const&>( m_lhs > rhs, m_lhs, ">", rhs );
|
||||
return { m_lhs > rhs, m_lhs, ">", rhs };
|
||||
}
|
||||
template<typename RhsT>
|
||||
auto operator < ( RhsT const& rhs ) -> BinaryExpr<LhsT, RhsT const&> const {
|
||||
return BinaryExpr<LhsT, RhsT const&>( m_lhs < rhs, m_lhs, "<", rhs );
|
||||
return { m_lhs < rhs, m_lhs, "<", rhs };
|
||||
}
|
||||
template<typename RhsT>
|
||||
auto operator >= ( RhsT const& rhs ) -> BinaryExpr<LhsT, RhsT const&> const {
|
||||
return BinaryExpr<LhsT, RhsT const&>( m_lhs >= rhs, m_lhs, ">=", rhs );
|
||||
return { m_lhs >= rhs, m_lhs, ">=", rhs };
|
||||
}
|
||||
template<typename RhsT>
|
||||
auto operator <= ( RhsT const& rhs ) -> BinaryExpr<LhsT, RhsT const&> const {
|
||||
return BinaryExpr<LhsT, RhsT const&>( m_lhs <= rhs, m_lhs, "<=", rhs );
|
||||
return { m_lhs <= rhs, m_lhs, "<=", rhs };
|
||||
}
|
||||
|
||||
auto makeUnaryExpr() const -> UnaryExpr<LhsT> {
|
||||
return UnaryExpr<LhsT>( m_lhs );
|
||||
return UnaryExpr<LhsT>{ m_lhs };
|
||||
}
|
||||
};
|
||||
|
||||
@ -153,10 +158,11 @@ namespace Catch {
|
||||
struct Decomposer {
|
||||
template<typename T>
|
||||
auto operator <= ( T const& lhs ) -> ExprLhs<T const&> {
|
||||
return ExprLhs<T const&>( lhs );
|
||||
return ExprLhs<T const&>{ lhs };
|
||||
}
|
||||
|
||||
auto operator <=( bool value ) -> ExprLhs<bool> {
|
||||
return ExprLhs<bool>( value );
|
||||
return ExprLhs<bool>{ value };
|
||||
}
|
||||
};
|
||||
|
||||
|
@ -12,6 +12,11 @@
|
||||
#include "catch_context.h"
|
||||
#include "catch_interfaces_capture.h"
|
||||
|
||||
#if defined(__GNUC__)
|
||||
# pragma GCC diagnostic push
|
||||
# pragma GCC diagnostic ignored "-Wmissing-field-initializers"
|
||||
#endif
|
||||
|
||||
namespace {
|
||||
// Report the error condition
|
||||
void reportFatal( char const * const message ) {
|
||||
@ -174,3 +179,7 @@ namespace Catch {
|
||||
# endif // CATCH_CONFIG_POSIX_SIGNALS
|
||||
|
||||
#endif // not Windows
|
||||
|
||||
#if defined(__GNUC__)
|
||||
# pragma GCC diagnostic pop
|
||||
#endif
|
||||
|
@ -11,6 +11,7 @@
|
||||
#include <string>
|
||||
|
||||
#include "catch_stringref.h"
|
||||
#include "catch_result_type.h"
|
||||
|
||||
namespace Catch {
|
||||
|
||||
@ -22,13 +23,14 @@ namespace Catch {
|
||||
struct Counts;
|
||||
struct BenchmarkInfo;
|
||||
struct BenchmarkStats;
|
||||
struct AssertionReaction;
|
||||
|
||||
struct ITransientExpression;
|
||||
|
||||
struct IResultCapture {
|
||||
|
||||
virtual ~IResultCapture();
|
||||
|
||||
virtual void assertionStarting( AssertionInfo const& info ) = 0;
|
||||
virtual void assertionEnded( AssertionResult const& result ) = 0;
|
||||
virtual bool sectionStarted( SectionInfo const& sectionInfo,
|
||||
Counts& assertions ) = 0;
|
||||
virtual void sectionEnded( SectionEndInfo const& endInfo ) = 0;
|
||||
@ -40,16 +42,40 @@ namespace Catch {
|
||||
virtual void pushScopedMessage( MessageInfo const& message ) = 0;
|
||||
virtual void popScopedMessage( MessageInfo const& message ) = 0;
|
||||
|
||||
virtual std::string getCurrentTestName() const = 0;
|
||||
virtual const AssertionResult* getLastResult() const = 0;
|
||||
|
||||
virtual void exceptionEarlyReported() = 0;
|
||||
|
||||
virtual void handleFatalErrorCondition( StringRef message ) = 0;
|
||||
|
||||
virtual void handleExpr
|
||||
( AssertionInfo const& info,
|
||||
ITransientExpression const& expr,
|
||||
AssertionReaction& reaction ) = 0;
|
||||
virtual void handleMessage
|
||||
( AssertionInfo const& info,
|
||||
ResultWas::OfType resultType,
|
||||
StringRef const& message,
|
||||
AssertionReaction& reaction ) = 0;
|
||||
virtual void handleUnexpectedExceptionNotThrown
|
||||
( AssertionInfo const& info,
|
||||
AssertionReaction& reaction ) = 0;
|
||||
virtual void handleUnexpectedInflightException
|
||||
( AssertionInfo const& info,
|
||||
std::string const& message,
|
||||
AssertionReaction& reaction ) = 0;
|
||||
virtual void handleIncomplete
|
||||
( AssertionInfo const& info ) = 0;
|
||||
virtual void handleNonExpr
|
||||
( AssertionInfo const &info,
|
||||
ResultWas::OfType resultType,
|
||||
AssertionReaction &reaction ) = 0;
|
||||
|
||||
|
||||
|
||||
virtual bool lastAssertionPassed() = 0;
|
||||
virtual void assertionPassed() = 0;
|
||||
virtual void assertionRun() = 0;
|
||||
|
||||
// Deprecated, do not use:
|
||||
virtual std::string getCurrentTestName() const = 0;
|
||||
virtual const AssertionResult* getLastResult() const = 0;
|
||||
virtual void exceptionEarlyReported() = 0;
|
||||
};
|
||||
|
||||
IResultCapture& getResultCapture();
|
||||
|
@ -73,7 +73,9 @@ namespace Catch {
|
||||
///////////////////////////////////////////////////////////////////////////////
|
||||
#define INTERNAL_CATCH_TRANSLATE_EXCEPTION2( translatorName, signature ) \
|
||||
static std::string translatorName( signature ); \
|
||||
namespace{ Catch::ExceptionTranslatorRegistrar INTERNAL_CATCH_UNIQUE_NAME( catch_internal_ExceptionRegistrar )( &translatorName ); }\
|
||||
CATCH_INTERNAL_SUPPRESS_GLOBALS_WARNINGS \
|
||||
namespace{ Catch::ExceptionTranslatorRegistrar INTERNAL_CATCH_UNIQUE_NAME( catch_internal_ExceptionRegistrar )( &translatorName ); } \
|
||||
CATCH_INTERNAL_UNSUPPRESS_GLOBALS_WARNINGS \
|
||||
static std::string translatorName( signature )
|
||||
|
||||
#define INTERNAL_CATCH_TRANSLATE_EXCEPTION( signature ) INTERNAL_CATCH_TRANSLATE_EXCEPTION2( INTERNAL_CATCH_UNIQUE_NAME( catch_internal_ExceptionTranslator ), signature )
|
||||
|
@ -8,11 +8,11 @@
|
||||
#include "catch_leak_detector.h"
|
||||
|
||||
|
||||
namespace Catch {
|
||||
|
||||
#ifdef CATCH_CONFIG_WINDOWS_CRTDBG
|
||||
#include <crtdbg.h>
|
||||
|
||||
namespace Catch {
|
||||
|
||||
LeakDetector::LeakDetector() {
|
||||
int flag = _CrtSetDbgFlag(_CRTDBG_REPORT_FLAG);
|
||||
flag |= _CRTDBG_LEAK_CHECK_DF;
|
||||
@ -23,11 +23,10 @@ namespace Catch {
|
||||
// Change this to leaking allocation's number to break there
|
||||
_CrtSetBreakAlloc(-1);
|
||||
}
|
||||
}
|
||||
|
||||
#else
|
||||
|
||||
LeakDetector::LeakDetector(){}
|
||||
Catch::LeakDetector::LeakDetector() {}
|
||||
|
||||
#endif
|
||||
|
||||
}
|
||||
|
@ -79,7 +79,11 @@ namespace Catch {
|
||||
namespace Matchers {
|
||||
namespace Floating {
|
||||
WithinAbsMatcher::WithinAbsMatcher(double target, double margin)
|
||||
:m_target{ target }, m_margin{ margin } {}
|
||||
:m_target{ target }, m_margin{ margin } {
|
||||
if (m_margin < 0) {
|
||||
throw std::domain_error("Allowed margin difference has to be >= 0");
|
||||
}
|
||||
}
|
||||
|
||||
// Performs equivalent check of std::fabs(lhs - rhs) <= margin
|
||||
// But without the subtraction to allow for INFINITY in comparison
|
||||
@ -95,7 +99,7 @@ namespace Floating {
|
||||
WithinUlpsMatcher::WithinUlpsMatcher(double target, int ulps, FloatingPointKind baseType)
|
||||
:m_target{ target }, m_ulps{ ulps }, m_type{ baseType } {
|
||||
if (m_ulps < 0) {
|
||||
throw std::domain_error("Expected ulp difference has to be >0");
|
||||
throw std::domain_error("Allowed ulp difference has to be >= 0");
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -10,10 +10,33 @@
|
||||
|
||||
#include "catch_matchers.h"
|
||||
|
||||
#include <algorithm>
|
||||
|
||||
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>> {
|
||||
@ -89,6 +112,46 @@ namespace Matchers {
|
||||
std::vector<T> const& m_comparator;
|
||||
};
|
||||
|
||||
template<typename T>
|
||||
struct UnorderedEqualsMatcher : MatcherBase<std::vector<T>> {
|
||||
UnorderedEqualsMatcher(std::vector<T> const& target) : m_target(target) {}
|
||||
bool match(std::vector<T> const& vec) const override {
|
||||
// Note: This is a reimplementation of std::is_permutation,
|
||||
// because I don't want to include <algorithm> inside the common path
|
||||
if (m_target.size() != vec.size()) {
|
||||
return false;
|
||||
}
|
||||
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;
|
||||
}
|
||||
|
||||
std::string describe() const override {
|
||||
return "UnorderedEquals: " + ::Catch::Detail::stringify(m_target);
|
||||
}
|
||||
private:
|
||||
std::vector<T> const& m_target;
|
||||
};
|
||||
|
||||
} // namespace Vector
|
||||
|
||||
// The following functions create the actual matcher objects.
|
||||
@ -109,6 +172,11 @@ namespace Matchers {
|
||||
return Vector::EqualsMatcher<T>( comparator );
|
||||
}
|
||||
|
||||
template<typename T>
|
||||
Vector::UnorderedEqualsMatcher<T> UnorderedEquals(std::vector<T> const& target) {
|
||||
return Vector::UnorderedEqualsMatcher<T>(target);
|
||||
}
|
||||
|
||||
} // namespace Matchers
|
||||
} // namespace Catch
|
||||
|
||||
|
@ -49,11 +49,18 @@ namespace Catch {
|
||||
getResultCapture().pushScopedMessage( m_info );
|
||||
}
|
||||
|
||||
#if defined(_MSC_VER)
|
||||
#pragma warning(push)
|
||||
#pragma warning(disable:4996) // std::uncaught_exception is deprecated in C++17
|
||||
#endif
|
||||
ScopedMessage::~ScopedMessage() {
|
||||
if ( !std::uncaught_exception() ){
|
||||
getResultCapture().popScopedMessage(m_info);
|
||||
}
|
||||
}
|
||||
#if defined(_MSC_VER)
|
||||
#pragma warning(pop)
|
||||
#endif
|
||||
|
||||
|
||||
} // end namespace Catch
|
||||
|
@ -59,7 +59,7 @@ namespace Catch {
|
||||
|
||||
class ScopedMessage {
|
||||
public:
|
||||
ScopedMessage( MessageBuilder const& builder );
|
||||
explicit ScopedMessage( MessageBuilder const& builder );
|
||||
~ScopedMessage();
|
||||
|
||||
MessageInfo m_info;
|
||||
|
@ -87,6 +87,7 @@ namespace Catch {
|
||||
delete getTheRegistryHub();
|
||||
getTheRegistryHub() = nullptr;
|
||||
cleanUpContext();
|
||||
ReusableStringStream::cleanup();
|
||||
}
|
||||
std::string translateActiveException() {
|
||||
return getRegistryHub().getExceptionTranslatorRegistry().translateActiveException();
|
||||
|
@ -29,7 +29,7 @@ namespace Catch {
|
||||
|
||||
public:
|
||||
|
||||
ReporterRegistrar( std::string const& name ) {
|
||||
explicit ReporterRegistrar( std::string const& name ) {
|
||||
getMutableRegistryHub().registerReporter( name, std::make_shared<ReporterFactory>() );
|
||||
}
|
||||
};
|
||||
|
@ -10,37 +10,55 @@
|
||||
|
||||
namespace Catch {
|
||||
|
||||
StreamRedirect::StreamRedirect(std::ostream& stream, std::string& targetString)
|
||||
: m_stream(stream),
|
||||
m_prevBuf(stream.rdbuf()),
|
||||
m_targetString(targetString) {
|
||||
stream.rdbuf(m_oss.get().rdbuf());
|
||||
}
|
||||
class RedirectedStream {
|
||||
std::ostream& m_originalStream;
|
||||
std::ostream& m_redirectionStream;
|
||||
std::streambuf* m_prevBuf;
|
||||
|
||||
StreamRedirect::~StreamRedirect() {
|
||||
m_targetString += m_oss.str();
|
||||
m_stream.rdbuf(m_prevBuf);
|
||||
}
|
||||
public:
|
||||
RedirectedStream( std::ostream& originalStream, std::ostream& redirectionStream )
|
||||
: m_originalStream( originalStream ),
|
||||
m_redirectionStream( redirectionStream ),
|
||||
m_prevBuf( m_originalStream.rdbuf() )
|
||||
{
|
||||
m_originalStream.rdbuf( m_redirectionStream.rdbuf() );
|
||||
}
|
||||
~RedirectedStream() {
|
||||
m_originalStream.rdbuf( m_prevBuf );
|
||||
}
|
||||
};
|
||||
|
||||
StdErrRedirect::StdErrRedirect(std::string & targetString)
|
||||
:m_cerrBuf(cerr().rdbuf()), m_clogBuf(clog().rdbuf()),
|
||||
m_targetString(targetString) {
|
||||
cerr().rdbuf(m_oss.get().rdbuf());
|
||||
clog().rdbuf(m_oss.get().rdbuf());
|
||||
}
|
||||
class RedirectedStdOut {
|
||||
ReusableStringStream m_rss;
|
||||
RedirectedStream m_cout;
|
||||
public:
|
||||
RedirectedStdOut() : m_cout( Catch::cout(), m_rss.get() ) {}
|
||||
auto str() const -> std::string { return m_rss.str(); }
|
||||
};
|
||||
|
||||
// StdErr has two constituent streams in C++, std::cerr and std::clog
|
||||
// This means that we need to redirect 2 streams into 1 to keep proper
|
||||
// order of writes
|
||||
class RedirectedStdErr {
|
||||
ReusableStringStream m_rss;
|
||||
RedirectedStream m_cerr;
|
||||
RedirectedStream m_clog;
|
||||
public:
|
||||
RedirectedStdErr()
|
||||
: m_cerr( Catch::cerr(), m_rss.get() ),
|
||||
m_clog( Catch::clog(), m_rss.get() )
|
||||
{}
|
||||
auto str() const -> std::string { return m_rss.str(); }
|
||||
};
|
||||
|
||||
StdErrRedirect::~StdErrRedirect() {
|
||||
m_targetString += m_oss.str();
|
||||
cerr().rdbuf(m_cerrBuf);
|
||||
clog().rdbuf(m_clogBuf);
|
||||
}
|
||||
|
||||
RunContext::RunContext(IConfigPtr const& _config, IStreamingReporterPtr&& reporter)
|
||||
: m_runInfo(_config->name()),
|
||||
m_context(getCurrentMutableContext()),
|
||||
m_config(_config),
|
||||
m_reporter(std::move(reporter)),
|
||||
m_lastAssertionInfo{ "", SourceLineInfo("",0), "", ResultDisposition::Normal }
|
||||
m_lastAssertionInfo{ "", SourceLineInfo("",0), "", ResultDisposition::Normal },
|
||||
m_includeSuccessfulResults( m_config->includeSuccessfulResults() )
|
||||
{
|
||||
m_context.setRunner(this);
|
||||
m_context.setConfig(m_config);
|
||||
@ -109,18 +127,20 @@ namespace Catch {
|
||||
return *m_reporter;
|
||||
}
|
||||
|
||||
void RunContext::assertionStarting(AssertionInfo const& info) {
|
||||
m_reporter->assertionStarting( info );
|
||||
}
|
||||
void RunContext::assertionEnded(AssertionResult const & result) {
|
||||
if (result.getResultType() == ResultWas::Ok) {
|
||||
m_totals.assertions.passed++;
|
||||
m_lastAssertionPassed = true;
|
||||
} else if (!result.isOk()) {
|
||||
m_lastAssertionPassed = false;
|
||||
if( m_activeTestCase->getTestCaseInfo().okToFail() )
|
||||
m_totals.assertions.failedButOk++;
|
||||
else
|
||||
m_totals.assertions.failed++;
|
||||
}
|
||||
else {
|
||||
m_lastAssertionPassed = true;
|
||||
}
|
||||
|
||||
// We have no use for the return value (whether messages should be cleared), because messages were made scoped
|
||||
// and should be let to clear themselves out.
|
||||
@ -223,7 +243,7 @@ namespace Catch {
|
||||
tempResult.message = message;
|
||||
AssertionResult result(m_lastAssertionInfo, tempResult);
|
||||
|
||||
getResultCapture().assertionEnded(result);
|
||||
assertionEnded(result);
|
||||
|
||||
handleUnfinishedSections();
|
||||
|
||||
@ -252,18 +272,15 @@ namespace Catch {
|
||||
}
|
||||
|
||||
bool RunContext::lastAssertionPassed() {
|
||||
return m_totals.assertions.passed == (m_prevPassed + 1);
|
||||
return m_lastAssertionPassed;
|
||||
}
|
||||
|
||||
void RunContext::assertionPassed() {
|
||||
m_lastAssertionPassed = true;
|
||||
++m_totals.assertions.passed;
|
||||
resetAssertionInfo();
|
||||
}
|
||||
|
||||
void RunContext::assertionRun() {
|
||||
m_prevPassed = m_totals.assertions.passed;
|
||||
}
|
||||
|
||||
bool RunContext::aborting() const {
|
||||
return m_totals.assertions.failed == static_cast<std::size_t>(m_config->abortAfter());
|
||||
}
|
||||
@ -282,10 +299,13 @@ namespace Catch {
|
||||
Timer timer;
|
||||
try {
|
||||
if (m_reporter->getPreferences().shouldRedirectStdOut) {
|
||||
StreamRedirect coutRedir(cout(), redirectedCout);
|
||||
StdErrRedirect errRedir(redirectedCerr);
|
||||
RedirectedStdOut redirectedStdOut;
|
||||
RedirectedStdErr redirectedStdErr;
|
||||
timer.start();
|
||||
invokeActiveTestCase();
|
||||
redirectedCout += redirectedStdOut.str();
|
||||
redirectedCerr += redirectedStdErr.str();
|
||||
|
||||
} else {
|
||||
timer.start();
|
||||
invokeActiveTestCase();
|
||||
@ -296,12 +316,9 @@ namespace Catch {
|
||||
} catch (...) {
|
||||
// Under CATCH_CONFIG_FAST_COMPILE, unexpected exceptions under REQUIRE assertions
|
||||
// are reported without translation at the point of origin.
|
||||
if (m_shouldReportUnexpected) {
|
||||
AssertionHandler
|
||||
( m_lastAssertionInfo.macroName,
|
||||
m_lastAssertionInfo.lineInfo,
|
||||
m_lastAssertionInfo.capturedExpression,
|
||||
m_lastAssertionInfo.resultDisposition ).useActiveException();
|
||||
if( m_shouldReportUnexpected ) {
|
||||
AssertionReaction dummyReaction;
|
||||
handleUnexpectedInflightException( m_lastAssertionInfo, translateActiveException(), dummyReaction );
|
||||
}
|
||||
}
|
||||
m_testCaseTracker->close();
|
||||
@ -331,6 +348,113 @@ namespace Catch {
|
||||
m_unfinishedSections.clear();
|
||||
}
|
||||
|
||||
void RunContext::handleExpr(
|
||||
AssertionInfo const& info,
|
||||
ITransientExpression const& expr,
|
||||
AssertionReaction& reaction
|
||||
) {
|
||||
m_reporter->assertionStarting( info );
|
||||
|
||||
bool negated = isFalseTest( info.resultDisposition );
|
||||
bool result = expr.getResult() != negated;
|
||||
|
||||
if( result ) {
|
||||
if (!m_includeSuccessfulResults) {
|
||||
assertionPassed();
|
||||
}
|
||||
else {
|
||||
reportExpr(info, ResultWas::Ok, &expr, negated);
|
||||
}
|
||||
}
|
||||
else {
|
||||
reportExpr(info, ResultWas::ExpressionFailed, &expr, negated );
|
||||
populateReaction( reaction );
|
||||
}
|
||||
}
|
||||
void RunContext::reportExpr(
|
||||
AssertionInfo const &info,
|
||||
ResultWas::OfType resultType,
|
||||
ITransientExpression const *expr,
|
||||
bool negated ) {
|
||||
|
||||
m_lastAssertionInfo = info;
|
||||
AssertionResultData data( resultType, LazyExpression( negated ) );
|
||||
|
||||
AssertionResult assertionResult{ info, data };
|
||||
assertionResult.m_resultData.lazyExpression.m_transientExpression = expr;
|
||||
|
||||
assertionEnded( assertionResult );
|
||||
}
|
||||
|
||||
void RunContext::handleMessage(
|
||||
AssertionInfo const& info,
|
||||
ResultWas::OfType resultType,
|
||||
StringRef const& message,
|
||||
AssertionReaction& reaction
|
||||
) {
|
||||
m_reporter->assertionStarting( info );
|
||||
|
||||
m_lastAssertionInfo = info;
|
||||
|
||||
AssertionResultData data( resultType, LazyExpression( false ) );
|
||||
data.message = message;
|
||||
AssertionResult assertionResult{ m_lastAssertionInfo, data };
|
||||
assertionEnded( assertionResult );
|
||||
if( !assertionResult.isOk() )
|
||||
populateReaction( reaction );
|
||||
}
|
||||
void RunContext::handleUnexpectedExceptionNotThrown(
|
||||
AssertionInfo const& info,
|
||||
AssertionReaction& reaction
|
||||
) {
|
||||
handleNonExpr(info, Catch::ResultWas::DidntThrowException, reaction);
|
||||
}
|
||||
|
||||
void RunContext::handleUnexpectedInflightException(
|
||||
AssertionInfo const& info,
|
||||
std::string const& message,
|
||||
AssertionReaction& reaction
|
||||
) {
|
||||
m_lastAssertionInfo = info;
|
||||
|
||||
AssertionResultData data( ResultWas::ThrewException, LazyExpression( false ) );
|
||||
data.message = message;
|
||||
AssertionResult assertionResult{ info, data };
|
||||
assertionEnded( assertionResult );
|
||||
populateReaction( reaction );
|
||||
}
|
||||
|
||||
void RunContext::populateReaction( AssertionReaction& reaction ) {
|
||||
reaction.shouldDebugBreak = m_config->shouldDebugBreak();
|
||||
reaction.shouldThrow = aborting() || (m_lastAssertionInfo.resultDisposition & ResultDisposition::Normal);
|
||||
}
|
||||
|
||||
void RunContext::handleIncomplete(
|
||||
AssertionInfo const& info
|
||||
) {
|
||||
m_lastAssertionInfo = info;
|
||||
|
||||
AssertionResultData data( ResultWas::ThrewException, LazyExpression( false ) );
|
||||
data.message = "Exception translation was disabled by CATCH_CONFIG_FAST_COMPILE";
|
||||
AssertionResult assertionResult{ info, data };
|
||||
assertionEnded( assertionResult );
|
||||
}
|
||||
void RunContext::handleNonExpr(
|
||||
AssertionInfo const &info,
|
||||
ResultWas::OfType resultType,
|
||||
AssertionReaction &reaction
|
||||
) {
|
||||
m_lastAssertionInfo = info;
|
||||
|
||||
AssertionResultData data( resultType, LazyExpression( false ) );
|
||||
AssertionResult assertionResult{ info, data };
|
||||
assertionEnded( assertionResult );
|
||||
|
||||
if( !assertionResult.isOk() )
|
||||
populateReaction( reaction );
|
||||
}
|
||||
|
||||
|
||||
IResultCapture& getResultCapture() {
|
||||
if (auto* capture = getCurrentContext().getResultCapture())
|
||||
return *capture;
|
||||
|
@ -28,33 +28,6 @@ namespace Catch {
|
||||
|
||||
struct IMutableContext;
|
||||
|
||||
class StreamRedirect {
|
||||
|
||||
public:
|
||||
StreamRedirect(std::ostream& stream, std::string& targetString);
|
||||
~StreamRedirect();
|
||||
|
||||
private:
|
||||
std::ostream& m_stream;
|
||||
std::streambuf* m_prevBuf;
|
||||
ReusableStringStream m_oss;
|
||||
std::string& m_targetString;
|
||||
};
|
||||
|
||||
// StdErr has two constituent streams in C++, std::cerr and std::clog
|
||||
// This means that we need to redirect 2 streams into 1 to keep proper
|
||||
// order of writes and cannot use StreamRedirect on its own
|
||||
class StdErrRedirect {
|
||||
public:
|
||||
StdErrRedirect(std::string& targetString);
|
||||
~StdErrRedirect();
|
||||
private:
|
||||
std::streambuf* m_cerrBuf;
|
||||
std::streambuf* m_clogBuf;
|
||||
ReusableStringStream m_oss;
|
||||
std::string& m_targetString;
|
||||
};
|
||||
|
||||
///////////////////////////////////////////////////////////////////////////
|
||||
|
||||
class RunContext : public IResultCapture, public IRunner {
|
||||
@ -63,35 +36,54 @@ namespace Catch {
|
||||
RunContext( RunContext const& ) = delete;
|
||||
RunContext& operator =( RunContext const& ) = delete;
|
||||
|
||||
explicit RunContext(IConfigPtr const& _config, IStreamingReporterPtr&& reporter);
|
||||
explicit RunContext( IConfigPtr const& _config, IStreamingReporterPtr&& reporter );
|
||||
|
||||
virtual ~RunContext();
|
||||
~RunContext() override;
|
||||
|
||||
void testGroupStarting(std::string const& testSpec, std::size_t groupIndex, std::size_t groupsCount);
|
||||
void testGroupEnded(std::string const& testSpec, Totals const& totals, std::size_t groupIndex, std::size_t groupsCount);
|
||||
void testGroupStarting( std::string const& testSpec, std::size_t groupIndex, std::size_t groupsCount );
|
||||
void testGroupEnded( std::string const& testSpec, Totals const& totals, std::size_t groupIndex, std::size_t groupsCount );
|
||||
|
||||
Totals runTest(TestCase const& testCase);
|
||||
|
||||
IConfigPtr config() const;
|
||||
IStreamingReporter& reporter() const;
|
||||
|
||||
private: // IResultCapture
|
||||
public: // IResultCapture
|
||||
|
||||
|
||||
void assertionStarting(AssertionInfo const& info) override;
|
||||
void assertionEnded(AssertionResult const& result) override;
|
||||
// Assertion handlers
|
||||
void handleExpr
|
||||
( AssertionInfo const& info,
|
||||
ITransientExpression const& expr,
|
||||
AssertionReaction& reaction ) override;
|
||||
void handleMessage
|
||||
( AssertionInfo const& info,
|
||||
ResultWas::OfType resultType,
|
||||
StringRef const& message,
|
||||
AssertionReaction& reaction ) override;
|
||||
void handleUnexpectedExceptionNotThrown
|
||||
( AssertionInfo const& info,
|
||||
AssertionReaction& reaction ) override;
|
||||
void handleUnexpectedInflightException
|
||||
( AssertionInfo const& info,
|
||||
std::string const& message,
|
||||
AssertionReaction& reaction ) override;
|
||||
void handleIncomplete
|
||||
( AssertionInfo const& info ) override;
|
||||
void handleNonExpr
|
||||
( AssertionInfo const &info,
|
||||
ResultWas::OfType resultType,
|
||||
AssertionReaction &reaction ) override;
|
||||
|
||||
bool sectionStarted( SectionInfo const& sectionInfo, Counts& assertions ) override;
|
||||
bool testForMissingAssertions(Counts& assertions);
|
||||
|
||||
void sectionEnded(SectionEndInfo const& endInfo) override;
|
||||
void sectionEndedEarly(SectionEndInfo const& endInfo) override;
|
||||
void sectionEnded( SectionEndInfo const& endInfo ) override;
|
||||
void sectionEndedEarly( SectionEndInfo const& endInfo ) override;
|
||||
|
||||
void benchmarkStarting( BenchmarkInfo const& info ) override;
|
||||
void benchmarkEnded( BenchmarkStats const& stats ) override;
|
||||
|
||||
void pushScopedMessage(MessageInfo const& message) override;
|
||||
void popScopedMessage(MessageInfo const& message) override;
|
||||
void pushScopedMessage( MessageInfo const& message ) override;
|
||||
void popScopedMessage( MessageInfo const& message ) override;
|
||||
|
||||
std::string getCurrentTestName() const override;
|
||||
|
||||
@ -105,18 +97,26 @@ namespace Catch {
|
||||
|
||||
void assertionPassed() override;
|
||||
|
||||
void assertionRun() override;
|
||||
|
||||
public:
|
||||
// !TBD We need to do this another way!
|
||||
bool aborting() const override;
|
||||
|
||||
private:
|
||||
|
||||
void runCurrentTest(std::string& redirectedCout, std::string& redirectedCerr);
|
||||
void runCurrentTest( std::string& redirectedCout, std::string& redirectedCerr );
|
||||
void invokeActiveTestCase();
|
||||
|
||||
void resetAssertionInfo();
|
||||
bool testForMissingAssertions( Counts& assertions );
|
||||
|
||||
void assertionEnded( AssertionResult const& result );
|
||||
void reportExpr
|
||||
( AssertionInfo const &info,
|
||||
ResultWas::OfType resultType,
|
||||
ITransientExpression const *expr,
|
||||
bool negated );
|
||||
|
||||
void populateReaction( AssertionReaction& reaction );
|
||||
|
||||
private:
|
||||
|
||||
@ -136,12 +136,11 @@ namespace Catch {
|
||||
std::vector<SectionEndInfo> m_unfinishedSections;
|
||||
std::vector<ITracker*> m_activeSections;
|
||||
TrackerContext m_trackerContext;
|
||||
std::size_t m_prevPassed = 0;
|
||||
bool m_lastAssertionPassed = false;
|
||||
bool m_shouldReportUnexpected = true;
|
||||
bool m_includeSuccessfulResults;
|
||||
};
|
||||
|
||||
IResultCapture& getResultCapture();
|
||||
|
||||
} // end namespace Catch
|
||||
|
||||
#endif // TWOBLUECUBES_CATCH_RUNNER_IMPL_HPP_INCLUDED
|
||||
|
@ -22,97 +22,91 @@
|
||||
#include <cstdlib>
|
||||
#include <iomanip>
|
||||
|
||||
namespace Catch {
|
||||
|
||||
namespace {
|
||||
const int MaxExitCode = 255;
|
||||
using Catch::IStreamingReporterPtr;
|
||||
using Catch::IConfigPtr;
|
||||
using Catch::Config;
|
||||
namespace {
|
||||
const int MaxExitCode = 255;
|
||||
|
||||
IStreamingReporterPtr createReporter(std::string const& reporterName, IConfigPtr const& config) {
|
||||
auto reporter = Catch::getRegistryHub().getReporterRegistry().create(reporterName, config);
|
||||
CATCH_ENFORCE(reporter, "No reporter registered with name: '" << reporterName << "'");
|
||||
IStreamingReporterPtr createReporter(std::string const& reporterName, IConfigPtr const& config) {
|
||||
auto reporter = Catch::getRegistryHub().getReporterRegistry().create(reporterName, config);
|
||||
CATCH_ENFORCE(reporter, "No reporter registered with name: '" << reporterName << "'");
|
||||
|
||||
return reporter;
|
||||
}
|
||||
return reporter;
|
||||
}
|
||||
|
||||
#ifndef CATCH_CONFIG_DEFAULT_REPORTER
|
||||
#define CATCH_CONFIG_DEFAULT_REPORTER "console"
|
||||
#endif
|
||||
|
||||
IStreamingReporterPtr makeReporter(std::shared_ptr<Config> const& config) {
|
||||
auto const& reporterNames = config->getReporterNames();
|
||||
if (reporterNames.empty())
|
||||
return createReporter(CATCH_CONFIG_DEFAULT_REPORTER, config);
|
||||
IStreamingReporterPtr makeReporter(std::shared_ptr<Config> const& config) {
|
||||
auto const& reporterNames = config->getReporterNames();
|
||||
if (reporterNames.empty())
|
||||
return createReporter(CATCH_CONFIG_DEFAULT_REPORTER, config);
|
||||
|
||||
IStreamingReporterPtr reporter;
|
||||
for (auto const& name : reporterNames)
|
||||
addReporter(reporter, createReporter(name, config));
|
||||
return reporter;
|
||||
}
|
||||
IStreamingReporterPtr reporter;
|
||||
for (auto const& name : reporterNames)
|
||||
addReporter(reporter, createReporter(name, config));
|
||||
return reporter;
|
||||
}
|
||||
|
||||
#undef CATCH_CONFIG_DEFAULT_REPORTER
|
||||
|
||||
void addListeners(IStreamingReporterPtr& reporters, IConfigPtr const& config) {
|
||||
auto const& listeners = Catch::getRegistryHub().getReporterRegistry().getListeners();
|
||||
for (auto const& listener : listeners)
|
||||
addReporter(reporters, listener->create(Catch::ReporterConfig(config)));
|
||||
}
|
||||
|
||||
|
||||
Catch::Totals runTests(std::shared_ptr<Config> const& config) {
|
||||
using namespace Catch;
|
||||
IStreamingReporterPtr reporter = makeReporter(config);
|
||||
addListeners(reporter, config);
|
||||
|
||||
RunContext context(config, std::move(reporter));
|
||||
|
||||
Totals totals;
|
||||
|
||||
context.testGroupStarting(config->name(), 1, 1);
|
||||
|
||||
TestSpec testSpec = config->testSpec();
|
||||
if (!testSpec.hasFilters())
|
||||
testSpec = TestSpecParser(ITagAliasRegistry::get()).parse("~[.]").testSpec(); // All not hidden tests
|
||||
|
||||
auto const& allTestCases = getAllTestCasesSorted(*config);
|
||||
for (auto const& testCase : allTestCases) {
|
||||
if (!context.aborting() && matchTest(testCase, testSpec, *config))
|
||||
totals += context.runTest(testCase);
|
||||
else
|
||||
context.reporter().skipTest(testCase);
|
||||
void addListeners(IStreamingReporterPtr& reporters, IConfigPtr const& config) {
|
||||
auto const& listeners = Catch::getRegistryHub().getReporterRegistry().getListeners();
|
||||
for (auto const& listener : listeners)
|
||||
addReporter(reporters, listener->create(Catch::ReporterConfig(config)));
|
||||
}
|
||||
|
||||
context.testGroupEnded(config->name(), totals, 1, 1);
|
||||
return totals;
|
||||
}
|
||||
|
||||
void applyFilenamesAsTags(Catch::IConfig const& config) {
|
||||
using namespace Catch;
|
||||
auto& tests = const_cast<std::vector<TestCase>&>(getAllTestCasesSorted(config));
|
||||
for (auto& testCase : tests) {
|
||||
auto tags = testCase.tags;
|
||||
Catch::Totals runTests(std::shared_ptr<Config> const& config) {
|
||||
IStreamingReporterPtr reporter = makeReporter(config);
|
||||
addListeners(reporter, config);
|
||||
|
||||
std::string filename = testCase.lineInfo.file;
|
||||
auto lastSlash = filename.find_last_of("\\/");
|
||||
if (lastSlash != std::string::npos) {
|
||||
filename.erase(0, lastSlash);
|
||||
filename[0] = '#';
|
||||
RunContext context(config, std::move(reporter));
|
||||
|
||||
Totals totals;
|
||||
|
||||
context.testGroupStarting(config->name(), 1, 1);
|
||||
|
||||
TestSpec testSpec = config->testSpec();
|
||||
if (!testSpec.hasFilters())
|
||||
testSpec = TestSpecParser(ITagAliasRegistry::get()).parse("~[.]").testSpec(); // All not hidden tests
|
||||
|
||||
auto const& allTestCases = getAllTestCasesSorted(*config);
|
||||
for (auto const& testCase : allTestCases) {
|
||||
if (!context.aborting() && matchTest(testCase, testSpec, *config))
|
||||
totals += context.runTest(testCase);
|
||||
else
|
||||
context.reporter().skipTest(testCase);
|
||||
}
|
||||
|
||||
auto lastDot = filename.find_last_of('.');
|
||||
if (lastDot != std::string::npos) {
|
||||
filename.erase(lastDot);
|
||||
}
|
||||
|
||||
tags.push_back(std::move(filename));
|
||||
setTags(testCase, tags);
|
||||
context.testGroupEnded(config->name(), totals, 1, 1);
|
||||
return totals;
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
void applyFilenamesAsTags(Catch::IConfig const& config) {
|
||||
auto& tests = const_cast<std::vector<TestCase>&>(getAllTestCasesSorted(config));
|
||||
for (auto& testCase : tests) {
|
||||
auto tags = testCase.tags;
|
||||
|
||||
namespace Catch {
|
||||
std::string filename = testCase.lineInfo.file;
|
||||
auto lastSlash = filename.find_last_of("\\/");
|
||||
if (lastSlash != std::string::npos) {
|
||||
filename.erase(0, lastSlash);
|
||||
filename[0] = '#';
|
||||
}
|
||||
|
||||
auto lastDot = filename.find_last_of('.');
|
||||
if (lastDot != std::string::npos) {
|
||||
filename.erase(lastDot);
|
||||
}
|
||||
|
||||
tags.push_back(std::move(filename));
|
||||
setTags(testCase, tags);
|
||||
}
|
||||
}
|
||||
|
||||
} // anon namespace
|
||||
|
||||
Session::Session() {
|
||||
static bool alreadyInstantiated = false;
|
||||
|
@ -145,6 +145,7 @@ namespace Catch {
|
||||
std::vector<std::unique_ptr<std::ostringstream>> m_streams;
|
||||
std::vector<std::size_t> m_unused;
|
||||
std::ostringstream m_referenceStream; // Used for copy state/ flags from
|
||||
static StringStreams* s_instance;
|
||||
|
||||
auto add() -> std::size_t {
|
||||
if( m_unused.empty() ) {
|
||||
@ -165,11 +166,21 @@ namespace Catch {
|
||||
|
||||
// !TBD: put in TLS
|
||||
static auto instance() -> StringStreams& {
|
||||
static StringStreams s_stringStreams;
|
||||
return s_stringStreams;
|
||||
if( !s_instance )
|
||||
s_instance = new StringStreams();
|
||||
return *s_instance;
|
||||
}
|
||||
static void cleanup() {
|
||||
delete s_instance;
|
||||
s_instance = nullptr;
|
||||
}
|
||||
};
|
||||
|
||||
StringStreams* StringStreams::s_instance = nullptr;
|
||||
|
||||
void ReusableStringStream::cleanup() {
|
||||
StringStreams::cleanup();
|
||||
}
|
||||
|
||||
ReusableStringStream::ReusableStringStream()
|
||||
: m_index( StringStreams::instance().add() ),
|
||||
|
@ -43,6 +43,8 @@ namespace Catch {
|
||||
return *this;
|
||||
}
|
||||
auto get() -> std::ostream& { return *m_oss; }
|
||||
|
||||
static void cleanup();
|
||||
};
|
||||
}
|
||||
|
||||
|
@ -10,9 +10,6 @@
|
||||
# pragma warning(push)
|
||||
# pragma warning(disable: 161 1682)
|
||||
# else // __ICC
|
||||
# pragma clang diagnostic ignored "-Wglobal-constructors"
|
||||
# pragma clang diagnostic ignored "-Wvariadic-macros"
|
||||
# pragma clang diagnostic ignored "-Wc99-extensions"
|
||||
# pragma clang diagnostic ignored "-Wunused-variable"
|
||||
# pragma clang diagnostic push
|
||||
# pragma clang diagnostic ignored "-Wpadded"
|
||||
@ -20,10 +17,8 @@
|
||||
# pragma clang diagnostic ignored "-Wcovered-switch-default"
|
||||
# endif
|
||||
#elif defined __GNUC__
|
||||
# pragma GCC diagnostic ignored "-Wvariadic-macros"
|
||||
# pragma GCC diagnostic ignored "-Wunused-variable"
|
||||
# pragma GCC diagnostic ignored "-Wparentheses"
|
||||
|
||||
# pragma GCC diagnostic push
|
||||
# pragma GCC diagnostic ignored "-Wpadded"
|
||||
#endif
|
||||
|
@ -17,6 +17,9 @@ namespace Catch {
|
||||
|
||||
} // end namespace Catch
|
||||
|
||||
#define CATCH_REGISTER_TAG_ALIAS( alias, spec ) namespace{ Catch::RegistrarForTagAliases INTERNAL_CATCH_UNIQUE_NAME( AutoRegisterTagAlias )( alias, spec, CATCH_INTERNAL_LINEINFO ); }
|
||||
#define CATCH_REGISTER_TAG_ALIAS( alias, spec ) \
|
||||
CATCH_INTERNAL_SUPPRESS_GLOBALS_WARNINGS \
|
||||
namespace{ Catch::RegistrarForTagAliases INTERNAL_CATCH_UNIQUE_NAME( AutoRegisterTagAlias )( alias, spec, CATCH_INTERNAL_LINEINFO ); } \
|
||||
CATCH_INTERNAL_UNSUPPRESS_GLOBALS_WARNINGS
|
||||
|
||||
#endif // TWOBLUECUBES_CATCH_TAG_ALIAS_AUTOREGISTRAR_H_INCLUDED
|
||||
|
@ -45,11 +45,11 @@ namespace Catch {
|
||||
void Timer::start() {
|
||||
m_nanoseconds = getCurrentNanosecondsSinceEpoch();
|
||||
}
|
||||
auto Timer::getElapsedNanoseconds() const -> unsigned int {
|
||||
return static_cast<unsigned int>(getCurrentNanosecondsSinceEpoch() - m_nanoseconds);
|
||||
auto Timer::getElapsedNanoseconds() const -> uint64_t {
|
||||
return getCurrentNanosecondsSinceEpoch() - m_nanoseconds;
|
||||
}
|
||||
auto Timer::getElapsedMicroseconds() const -> unsigned int {
|
||||
return static_cast<unsigned int>(getElapsedNanoseconds()/1000);
|
||||
auto Timer::getElapsedMicroseconds() const -> uint64_t {
|
||||
return getElapsedNanoseconds()/1000;
|
||||
}
|
||||
auto Timer::getElapsedMilliseconds() const -> unsigned int {
|
||||
return static_cast<unsigned int>(getElapsedMicroseconds()/1000);
|
||||
|
@ -19,8 +19,8 @@ namespace Catch {
|
||||
uint64_t m_nanoseconds = 0;
|
||||
public:
|
||||
void start();
|
||||
auto getElapsedNanoseconds() const -> unsigned int;
|
||||
auto getElapsedMicroseconds() const -> unsigned int;
|
||||
auto getElapsedNanoseconds() const -> uint64_t;
|
||||
auto getElapsedMicroseconds() const -> uint64_t;
|
||||
auto getElapsedMilliseconds() const -> unsigned int;
|
||||
auto getElapsedSeconds() const -> double;
|
||||
};
|
||||
|
@ -210,7 +210,7 @@ class Duration {
|
||||
Unit m_units;
|
||||
|
||||
public:
|
||||
Duration(uint64_t inNanoseconds, Unit units = Unit::Auto)
|
||||
explicit Duration(uint64_t inNanoseconds, Unit units = Unit::Auto)
|
||||
: m_inNanoseconds(inNanoseconds),
|
||||
m_units(units) {
|
||||
if (m_units == Unit::Auto) {
|
||||
@ -273,9 +273,9 @@ class TablePrinter {
|
||||
bool m_isOpen = false;
|
||||
|
||||
public:
|
||||
TablePrinter(std::ostream& os, std::vector<ColumnInfo> const& columnInfos)
|
||||
: m_os(os),
|
||||
m_columnInfos(columnInfos) {}
|
||||
TablePrinter( std::ostream& os, std::vector<ColumnInfo> columnInfos )
|
||||
: m_os( os ),
|
||||
m_columnInfos( std::move( columnInfos ) ) {}
|
||||
|
||||
auto columnInfos() const -> std::vector<ColumnInfo> const& {
|
||||
return m_columnInfos;
|
||||
@ -346,7 +346,8 @@ ConsoleReporter::ConsoleReporter(ReporterConfig const& config)
|
||||
{ "elapsed ns", 14, ColumnInfo::Right },
|
||||
{ "average", 14, ColumnInfo::Right }
|
||||
})) {}
|
||||
ConsoleReporter::~ConsoleReporter() {}
|
||||
ConsoleReporter::~ConsoleReporter() = default;
|
||||
|
||||
std::string ConsoleReporter::getDescription() {
|
||||
return "Reports test results as plain lines of text";
|
||||
}
|
||||
@ -402,7 +403,7 @@ void ConsoleReporter::sectionEnded(SectionStats const& _sectionStats) {
|
||||
void ConsoleReporter::benchmarkStarting(BenchmarkInfo const& info) {
|
||||
lazyPrintWithoutClosingBenchmarkTable();
|
||||
|
||||
auto nameCol = Column(info.name).width(m_tablePrinter->columnInfos()[0].width - 2);
|
||||
auto nameCol = Column( info.name ).width( static_cast<std::size_t>( m_tablePrinter->columnInfos()[0].width - 2 ) );
|
||||
|
||||
bool firstLine = true;
|
||||
for (auto line : nameCol) {
|
||||
@ -528,10 +529,10 @@ void ConsoleReporter::printHeaderString(std::string const& _string, std::size_t
|
||||
|
||||
struct SummaryColumn {
|
||||
|
||||
SummaryColumn(std::string const& _label, Colour::Code _colour)
|
||||
: label(_label),
|
||||
colour(_colour) {}
|
||||
SummaryColumn addRow(std::size_t count) {
|
||||
SummaryColumn( std::string _label, Colour::Code _colour )
|
||||
: label( std::move( _label ) ),
|
||||
colour( _colour ) {}
|
||||
SummaryColumn addRow( std::size_t count ) {
|
||||
ReusableStringStream rss;
|
||||
rss << count;
|
||||
std::string row = rss.str();
|
||||
@ -551,7 +552,7 @@ struct SummaryColumn {
|
||||
|
||||
};
|
||||
|
||||
void ConsoleReporter::printTotals(Totals const& totals) {
|
||||
void ConsoleReporter::printTotals( Totals const& totals ) {
|
||||
if (totals.testCases.total() == 0) {
|
||||
stream << Colour(Colour::Warning) << "No tests ran\n";
|
||||
} else if (totals.assertions.total() > 0 && totals.testCases.allPassed()) {
|
||||
|
@ -14,8 +14,6 @@
|
||||
// file can be distributed as a single header that works with the main
|
||||
// Catch single header.
|
||||
|
||||
#include "../internal/catch_enforce.h"
|
||||
|
||||
#include <cstring>
|
||||
|
||||
#ifdef __clang__
|
||||
@ -99,12 +97,12 @@ namespace Catch {
|
||||
case ResultWas::Ok:
|
||||
case ResultWas::Info:
|
||||
case ResultWas::Warning:
|
||||
CATCH_ERROR( "Internal error in TeamCity reporter" );
|
||||
throw std::domain_error( "Internal error in TeamCity reporter" );
|
||||
// These cases are here to prevent compiler warnings
|
||||
case ResultWas::Unknown:
|
||||
case ResultWas::FailureBit:
|
||||
case ResultWas::Exception:
|
||||
CATCH_ERROR( "Not implemented" );
|
||||
throw std::domain_error( "Not implemented" );
|
||||
}
|
||||
if( assertionStats.infoMessages.size() == 1 )
|
||||
msg << " with message:";
|
||||
|
@ -26,7 +26,7 @@ namespace Catch {
|
||||
m_reporterPrefs.shouldRedirectStdOut = true;
|
||||
}
|
||||
|
||||
XmlReporter::~XmlReporter() {};
|
||||
XmlReporter::~XmlReporter() = default;
|
||||
|
||||
std::string XmlReporter::getDescription() {
|
||||
return "Reports test results as an XML document";
|
||||
@ -95,10 +95,10 @@ namespace Catch {
|
||||
|
||||
bool includeResults = m_config->includeSuccessfulResults() || !result.isOk();
|
||||
|
||||
if( includeResults ) {
|
||||
if( includeResults || result.getResultType() == ResultWas::Warning ) {
|
||||
// Print any info messages in <Info> tags.
|
||||
for( auto const& msg : assertionStats.infoMessages ) {
|
||||
if( msg.type == ResultWas::Info ) {
|
||||
if( msg.type == ResultWas::Info && includeResults ) {
|
||||
m_xml.scopedElement( "Info" )
|
||||
.writeText( msg.message );
|
||||
} else if ( msg.type == ResultWas::Warning ) {
|
||||
|
11
misc/CMakeLists.txt
Normal file
11
misc/CMakeLists.txt
Normal file
@ -0,0 +1,11 @@
|
||||
cmake_minimum_required(VERSION 3.0)
|
||||
|
||||
project(CatchCoverageHelper)
|
||||
|
||||
add_executable(CoverageHelper coverage-helper.cpp)
|
||||
set_property(TARGET CoverageHelper PROPERTY CXX_STANDARD 11)
|
||||
set_property(TARGET CoverageHelper PROPERTY CXX_STANDARD_REQUIRED ON)
|
||||
set_property(TARGET CoverageHelper PROPERTY CXX_EXTENSIONS OFF)
|
||||
if (MSVC)
|
||||
target_compile_options( CoverageHelper PRIVATE /W4 /w44265 /WX /w44061 /w44062 )
|
||||
endif()
|
13
misc/appveyorBuildConfigurationScript.bat
Normal file
13
misc/appveyorBuildConfigurationScript.bat
Normal file
@ -0,0 +1,13 @@
|
||||
@REM # In debug build, we want to
|
||||
@REM # 1) Prebuild memcheck redirecter
|
||||
@REM # 2) Regenerate single header include for examples
|
||||
@REM # 3) Enable building examples
|
||||
if "%CONFIGURATION%"=="Debug" (
|
||||
python scripts\generateSingleHeader.py
|
||||
cmake -Hmisc -Bbuild-misc -A%PLATFORM%
|
||||
cmake --build build-misc
|
||||
cmake -H. -BBuild -A%PLATFORM% -DUSE_WMAIN=%wmain% -DBUILD_EXAMPLES=ON -DMEMORYCHECK_COMMAND=build-misc\Debug\CoverageHelper.exe -DMEMORYCHECK_COMMAND_OPTIONS=--sep-- -DMEMORYCHECK_TYPE=Valgrind
|
||||
)
|
||||
if "%CONFIGURATION%"=="Release" (
|
||||
cmake -H. -BBuild -A%PLATFORM% -DUSE_WMAIN=%wmain%
|
||||
)
|
9
misc/appveyorMergeCoverageScript.py
Normal file
9
misc/appveyorMergeCoverageScript.py
Normal file
@ -0,0 +1,9 @@
|
||||
#!/usr/bin/env python2
|
||||
|
||||
import glob
|
||||
import subprocess
|
||||
|
||||
if __name__ == '__main__':
|
||||
cov_files = list(glob.glob('cov-report*.bin'))
|
||||
base_cmd = ['OpenCppCoverage', '--quiet', '--export_type=cobertura:cobertura.xml'] + ['--input_coverage={}'.format(f) for f in cov_files]
|
||||
subprocess.call(base_cmd)
|
9
misc/appveyorTestRunScript.bat
Normal file
9
misc/appveyorTestRunScript.bat
Normal file
@ -0,0 +1,9 @@
|
||||
cd Build
|
||||
if "%CONFIGURATION%"=="Debug" (
|
||||
ctest -j 2 -C %CONFIGURATION% -D ExperimentalMemCheck
|
||||
python ..\misc\appveyorMergeCoverageScript.py
|
||||
codecov --root .. --no-color --disable gcov -f cobertura.xml -t %CODECOV_TOKEN%
|
||||
)
|
||||
if "%CONFIGURATION%"=="Release" (
|
||||
ctest -j 2 -C %CONFIGURATION%
|
||||
)
|
100
misc/coverage-helper.cpp
Normal file
100
misc/coverage-helper.cpp
Normal file
@ -0,0 +1,100 @@
|
||||
#include <algorithm>
|
||||
#include <array>
|
||||
#include <cassert>
|
||||
#include <fstream>
|
||||
#include <iostream>
|
||||
#include <memory>
|
||||
#include <numeric>
|
||||
#include <regex>
|
||||
#include <string>
|
||||
#include <vector>
|
||||
|
||||
|
||||
void create_empty_file(std::string const& path) {
|
||||
std::ofstream ofs(path);
|
||||
ofs << '\n';
|
||||
}
|
||||
|
||||
const std::string separator = "--sep--";
|
||||
const std::string logfile_prefix = "--log-file=";
|
||||
|
||||
bool starts_with(std::string const& str, std::string const& pref) {
|
||||
return str.find(pref) == 0;
|
||||
}
|
||||
|
||||
int parse_log_file_arg(std::string const& arg) {
|
||||
assert(starts_with(arg, logfile_prefix) && "Attempting to parse incorrect arg!");
|
||||
auto fname = arg.substr(logfile_prefix.size());
|
||||
create_empty_file(fname);
|
||||
std::regex regex("MemoryChecker\\.(\\d+)\\.log", std::regex::icase);
|
||||
std::smatch match;
|
||||
if (std::regex_search(fname, match, regex)) {
|
||||
return std::stoi(match[1]);
|
||||
} else {
|
||||
throw std::domain_error("Couldn't find desired expression in string: " + fname);
|
||||
}
|
||||
}
|
||||
|
||||
std::string catch_path(std::string path) {
|
||||
auto start = path.find("catch");
|
||||
// try capitalized instead
|
||||
if (start == std::string::npos) {
|
||||
start = path.find("Catch");
|
||||
}
|
||||
if (start == std::string::npos) {
|
||||
throw std::domain_error("Couldn't find Catch's base path");
|
||||
}
|
||||
auto end = path.find_first_of("\\/", start);
|
||||
return path.substr(0, end);
|
||||
}
|
||||
|
||||
std::string windowsify_path(std::string path) {
|
||||
for (auto& c : path) {
|
||||
if (c == '/') {
|
||||
c = '\\';
|
||||
}
|
||||
}
|
||||
return path;
|
||||
}
|
||||
|
||||
void exec_cmd(std::string const& cmd, int log_num, std::string const& path) {
|
||||
std::array<char, 128> buffer;
|
||||
#if defined(_WIN32)
|
||||
auto real_cmd = "OpenCppCoverage --export_type binary:cov-report" + std::to_string(log_num)
|
||||
+ ".bin --quiet " + "--sources " + path + " --cover_children -- " + cmd;
|
||||
std::cout << "=== Marker ===: Cmd: " << real_cmd << '\n';
|
||||
std::shared_ptr<FILE> pipe(_popen(real_cmd.c_str(), "r"), _pclose);
|
||||
#else // Just for testing, in the real world we will always work under WIN32
|
||||
(void)log_num; (void)path;
|
||||
std::shared_ptr<FILE> pipe(popen(cmd.c_str(), "r"), pclose);
|
||||
#endif
|
||||
|
||||
if (!pipe) {
|
||||
throw std::runtime_error("popen() failed!");
|
||||
}
|
||||
while (!feof(pipe.get())) {
|
||||
if (fgets(buffer.data(), 128, pipe.get()) != nullptr) {
|
||||
std::cout << buffer.data();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// argv should be:
|
||||
// [0]: our path
|
||||
// [1]: "--log-file=<path>"
|
||||
// [2]: "--sep--"
|
||||
// [3]+: the actual command
|
||||
|
||||
int main(int argc, char** argv) {
|
||||
std::vector<std::string> args(argv, argv + argc);
|
||||
auto sep = std::find(begin(args), end(args), separator);
|
||||
assert(sep - begin(args) == 2 && "Structure differs from expected!");
|
||||
|
||||
auto num = parse_log_file_arg(args[1]);
|
||||
|
||||
auto cmdline = std::accumulate(++sep, end(args), std::string{}, [] (const std::string& lhs, const std::string& rhs) {
|
||||
return lhs + ' ' + rhs;
|
||||
});
|
||||
|
||||
exec_cmd(cmdline, num, windowsify_path(catch_path(args[0])));
|
||||
}
|
19
misc/installOpenCppCoverage.ps1
Normal file
19
misc/installOpenCppCoverage.ps1
Normal file
@ -0,0 +1,19 @@
|
||||
# Downloads are done from the oficial github release page links
|
||||
$downloadUrl = "https://github.com/OpenCppCoverage/OpenCppCoverage/releases/download/release-0.9.6.1/OpenCppCoverageSetup-x64-0.9.6.1.exe"
|
||||
$installerPath = [System.IO.Path]::Combine($Env:USERPROFILE, "Downloads", "OpenCppCoverageSetup.exe")
|
||||
|
||||
if(-Not (Test-Path $installerPath)) {
|
||||
Write-Host -ForegroundColor White ("Downloading OpenCppCoverage from: " + $downloadUrl)
|
||||
Start-FileDownload $downloadUrl -FileName $installerPath
|
||||
}
|
||||
|
||||
Write-Host -ForegroundColor White "About to install OpenCppCoverage..."
|
||||
|
||||
$installProcess = (Start-Process $installerPath -ArgumentList '/VERYSILENT' -PassThru -Wait)
|
||||
if($installProcess.ExitCode -ne 0) {
|
||||
throw [System.String]::Format("Failed to install OpenCppCoverage, ExitCode: {0}.", $installProcess.ExitCode)
|
||||
}
|
||||
|
||||
# Assume standard, boring, installation path of ".../Program Files/OpenCppCoverage"
|
||||
$installPath = [System.IO.Path]::Combine(${Env:ProgramFiles}, "OpenCppCoverage")
|
||||
$env:Path="$env:Path;$installPath"
|
1035
projects/SelfTest/Baselines/compact.sw.approved.txt
Normal file
1035
projects/SelfTest/Baselines/compact.sw.approved.txt
Normal file
File diff suppressed because it is too large
Load Diff
@ -700,6 +700,7 @@ with expansion:
|
||||
|
||||
A string sent directly to stdout
|
||||
A string sent directly to stderr
|
||||
A string sent to stderr via clog
|
||||
Message from section one
|
||||
Message from section two
|
||||
-------------------------------------------------------------------------------
|
||||
@ -808,6 +809,33 @@ Matchers.tests.cpp:<line number>: FAILED:
|
||||
with expansion:
|
||||
{ 1, 2, 3 } Equals: { }
|
||||
|
||||
-------------------------------------------------------------------------------
|
||||
Vector matchers that fail
|
||||
UnorderedEquals
|
||||
-------------------------------------------------------------------------------
|
||||
Matchers.tests.cpp:<line number>
|
||||
...............................................................................
|
||||
|
||||
Matchers.tests.cpp:<line number>: FAILED:
|
||||
CHECK_THAT( v, UnorderedEquals(empty) )
|
||||
with expansion:
|
||||
{ 1, 2, 3 } UnorderedEquals: { }
|
||||
|
||||
Matchers.tests.cpp:<line number>: FAILED:
|
||||
CHECK_THAT( empty, UnorderedEquals(v) )
|
||||
with expansion:
|
||||
{ } UnorderedEquals: { 1, 2, 3 }
|
||||
|
||||
Matchers.tests.cpp:<line number>: FAILED:
|
||||
CHECK_THAT( permuted, UnorderedEquals(v) )
|
||||
with expansion:
|
||||
{ 1, 3 } UnorderedEquals: { 1, 2, 3 }
|
||||
|
||||
Matchers.tests.cpp:<line number>: FAILED:
|
||||
CHECK_THAT( permuted, UnorderedEquals(v) )
|
||||
with expansion:
|
||||
{ 3, 1 } UnorderedEquals: { 1, 2, 3 }
|
||||
|
||||
-------------------------------------------------------------------------------
|
||||
When unchecked exceptions are thrown directly they are always failures
|
||||
-------------------------------------------------------------------------------
|
||||
@ -1052,6 +1080,6 @@ with expansion:
|
||||
"{?}" == "1"
|
||||
|
||||
===============================================================================
|
||||
test cases: 189 | 137 passed | 48 failed | 4 failed as expected
|
||||
assertions: 952 | 828 passed | 103 failed | 21 failed as expected
|
||||
test cases: 191 | 139 passed | 48 failed | 4 failed as expected
|
||||
assertions: 971 | 843 passed | 107 failed | 21 failed as expected
|
||||
|
||||
|
@ -762,9 +762,7 @@ Tricky.tests.cpp:<line number>
|
||||
|
||||
Tricky.tests.cpp:<line number>:
|
||||
PASSED:
|
||||
REQUIRE( Catch::alwaysTrue() )
|
||||
with expansion:
|
||||
true
|
||||
REQUIRE( true )
|
||||
|
||||
-------------------------------------------------------------------------------
|
||||
Assertions then sections
|
||||
@ -775,9 +773,7 @@ Tricky.tests.cpp:<line number>
|
||||
|
||||
Tricky.tests.cpp:<line number>:
|
||||
PASSED:
|
||||
REQUIRE( Catch::alwaysTrue() )
|
||||
with expansion:
|
||||
true
|
||||
REQUIRE( true )
|
||||
|
||||
-------------------------------------------------------------------------------
|
||||
Assertions then sections
|
||||
@ -789,9 +785,7 @@ Tricky.tests.cpp:<line number>
|
||||
|
||||
Tricky.tests.cpp:<line number>:
|
||||
PASSED:
|
||||
REQUIRE( Catch::alwaysTrue() )
|
||||
with expansion:
|
||||
true
|
||||
REQUIRE( true )
|
||||
|
||||
-------------------------------------------------------------------------------
|
||||
Assertions then sections
|
||||
@ -801,9 +795,7 @@ Tricky.tests.cpp:<line number>
|
||||
|
||||
Tricky.tests.cpp:<line number>:
|
||||
PASSED:
|
||||
REQUIRE( Catch::alwaysTrue() )
|
||||
with expansion:
|
||||
true
|
||||
REQUIRE( true )
|
||||
|
||||
-------------------------------------------------------------------------------
|
||||
Assertions then sections
|
||||
@ -814,9 +806,7 @@ Tricky.tests.cpp:<line number>
|
||||
|
||||
Tricky.tests.cpp:<line number>:
|
||||
PASSED:
|
||||
REQUIRE( Catch::alwaysTrue() )
|
||||
with expansion:
|
||||
true
|
||||
REQUIRE( true )
|
||||
|
||||
-------------------------------------------------------------------------------
|
||||
Assertions then sections
|
||||
@ -828,9 +818,7 @@ Tricky.tests.cpp:<line number>
|
||||
|
||||
Tricky.tests.cpp:<line number>:
|
||||
PASSED:
|
||||
REQUIRE( Catch::alwaysTrue() )
|
||||
with expansion:
|
||||
true
|
||||
REQUIRE( true )
|
||||
|
||||
-------------------------------------------------------------------------------
|
||||
Assorted miscellaneous tests
|
||||
@ -1153,13 +1141,13 @@ with expansion:
|
||||
|
||||
Approx.tests.cpp:<line number>:
|
||||
PASSED:
|
||||
REQUIRE( td >= Approx(10.0) )
|
||||
REQUIRE( td >= Approx(td) )
|
||||
with expansion:
|
||||
StrongDoubleTypedef(10) >= Approx( 10.0 )
|
||||
|
||||
Approx.tests.cpp:<line number>:
|
||||
PASSED:
|
||||
REQUIRE( Approx(10.0) >= td )
|
||||
REQUIRE( Approx(td) >= td )
|
||||
with expansion:
|
||||
Approx( 10.0 ) >= StrongDoubleTypedef(10)
|
||||
|
||||
@ -1884,6 +1872,29 @@ PASSED:
|
||||
with expansion:
|
||||
nanf not ( is within 100.0 of nan or is within 123 ULPs of nanf )
|
||||
|
||||
-------------------------------------------------------------------------------
|
||||
Floating point matchers: double
|
||||
Constructor validation
|
||||
-------------------------------------------------------------------------------
|
||||
Matchers.tests.cpp:<line number>
|
||||
...............................................................................
|
||||
|
||||
Matchers.tests.cpp:<line number>:
|
||||
PASSED:
|
||||
REQUIRE_NOTHROW( WithinAbs(1., 0.) )
|
||||
|
||||
Matchers.tests.cpp:<line number>:
|
||||
PASSED:
|
||||
REQUIRE_THROWS_AS( WithinAbs(1., -1.), std::domain_error )
|
||||
|
||||
Matchers.tests.cpp:<line number>:
|
||||
PASSED:
|
||||
REQUIRE_NOTHROW( WithinULP(1., 0) )
|
||||
|
||||
Matchers.tests.cpp:<line number>:
|
||||
PASSED:
|
||||
REQUIRE_THROWS_AS( WithinULP(1., -1), std::domain_error )
|
||||
|
||||
-------------------------------------------------------------------------------
|
||||
Floating point matchers: float
|
||||
Margin
|
||||
@ -2001,6 +2012,29 @@ PASSED:
|
||||
with expansion:
|
||||
nanf not ( is within 100.0 of nan or is within 123 ULPs of nanf )
|
||||
|
||||
-------------------------------------------------------------------------------
|
||||
Floating point matchers: float
|
||||
Constructor validation
|
||||
-------------------------------------------------------------------------------
|
||||
Matchers.tests.cpp:<line number>
|
||||
...............................................................................
|
||||
|
||||
Matchers.tests.cpp:<line number>:
|
||||
PASSED:
|
||||
REQUIRE_NOTHROW( WithinAbs(1.f, 0.f) )
|
||||
|
||||
Matchers.tests.cpp:<line number>:
|
||||
PASSED:
|
||||
REQUIRE_THROWS_AS( WithinAbs(1.f, -1.f), std::domain_error )
|
||||
|
||||
Matchers.tests.cpp:<line number>:
|
||||
PASSED:
|
||||
REQUIRE_NOTHROW( WithinULP(1.f, 0) )
|
||||
|
||||
Matchers.tests.cpp:<line number>:
|
||||
PASSED:
|
||||
REQUIRE_THROWS_AS( WithinULP(1.f, -1), std::domain_error )
|
||||
|
||||
-------------------------------------------------------------------------------
|
||||
Greater-than inequalities with different epsilons
|
||||
-------------------------------------------------------------------------------
|
||||
@ -4585,6 +4619,7 @@ with expansion:
|
||||
|
||||
A string sent directly to stdout
|
||||
A string sent directly to stderr
|
||||
A string sent to stderr via clog
|
||||
-------------------------------------------------------------------------------
|
||||
Some simple comparisons between doubles
|
||||
-------------------------------------------------------------------------------
|
||||
@ -5139,6 +5174,12 @@ PASSED:
|
||||
with expansion:
|
||||
1 [30/1]s != 1 fs
|
||||
|
||||
ToStringChrono.tests.cpp:<line number>:
|
||||
PASSED:
|
||||
REQUIRE( pico_second != atto_second )
|
||||
with expansion:
|
||||
1 ps != 1 as
|
||||
|
||||
-------------------------------------------------------------------------------
|
||||
Stringifying std::chrono::time_point<system_clock>
|
||||
-------------------------------------------------------------------------------
|
||||
@ -6439,6 +6480,37 @@ PASSED:
|
||||
with expansion:
|
||||
{ 1, 2, 3 } Equals: { 1, 2, 3 }
|
||||
|
||||
-------------------------------------------------------------------------------
|
||||
Vector matchers
|
||||
UnorderedEquals
|
||||
-------------------------------------------------------------------------------
|
||||
Matchers.tests.cpp:<line number>
|
||||
...............................................................................
|
||||
|
||||
Matchers.tests.cpp:<line number>:
|
||||
PASSED:
|
||||
CHECK_THAT( v, UnorderedEquals(v) )
|
||||
with expansion:
|
||||
{ 1, 2, 3 } UnorderedEquals: { 1, 2, 3 }
|
||||
|
||||
Matchers.tests.cpp:<line number>:
|
||||
PASSED:
|
||||
CHECK_THAT( empty, UnorderedEquals(empty) )
|
||||
with expansion:
|
||||
{ } UnorderedEquals: { }
|
||||
|
||||
Matchers.tests.cpp:<line number>:
|
||||
PASSED:
|
||||
REQUIRE_THAT( permuted, UnorderedEquals(v) )
|
||||
with expansion:
|
||||
{ 1, 3, 2 } UnorderedEquals: { 1, 2, 3 }
|
||||
|
||||
Matchers.tests.cpp:<line number>:
|
||||
PASSED:
|
||||
REQUIRE_THAT( permuted, UnorderedEquals(v) )
|
||||
with expansion:
|
||||
{ 2, 3, 1 } UnorderedEquals: { 1, 2, 3 }
|
||||
|
||||
-------------------------------------------------------------------------------
|
||||
Vector matchers that fail
|
||||
Contains (element)
|
||||
@ -6500,6 +6572,33 @@ Matchers.tests.cpp:<line number>: FAILED:
|
||||
with expansion:
|
||||
{ 1, 2, 3 } Equals: { }
|
||||
|
||||
-------------------------------------------------------------------------------
|
||||
Vector matchers that fail
|
||||
UnorderedEquals
|
||||
-------------------------------------------------------------------------------
|
||||
Matchers.tests.cpp:<line number>
|
||||
...............................................................................
|
||||
|
||||
Matchers.tests.cpp:<line number>: FAILED:
|
||||
CHECK_THAT( v, UnorderedEquals(empty) )
|
||||
with expansion:
|
||||
{ 1, 2, 3 } UnorderedEquals: { }
|
||||
|
||||
Matchers.tests.cpp:<line number>: FAILED:
|
||||
CHECK_THAT( empty, UnorderedEquals(v) )
|
||||
with expansion:
|
||||
{ } UnorderedEquals: { 1, 2, 3 }
|
||||
|
||||
Matchers.tests.cpp:<line number>: FAILED:
|
||||
CHECK_THAT( permuted, UnorderedEquals(v) )
|
||||
with expansion:
|
||||
{ 1, 3 } UnorderedEquals: { 1, 2, 3 }
|
||||
|
||||
Matchers.tests.cpp:<line number>: FAILED:
|
||||
CHECK_THAT( permuted, UnorderedEquals(v) )
|
||||
with expansion:
|
||||
{ 3, 1 } UnorderedEquals: { 1, 2, 3 }
|
||||
|
||||
-------------------------------------------------------------------------------
|
||||
When checked exceptions are thrown they can be expected or unexpected
|
||||
-------------------------------------------------------------------------------
|
||||
@ -7529,7 +7628,7 @@ with expansion:
|
||||
""wide load"" == ""wide load""
|
||||
|
||||
-------------------------------------------------------------------------------
|
||||
toString( vectors<has_maker )
|
||||
toString( vectors<has_maker> )
|
||||
-------------------------------------------------------------------------------
|
||||
ToStringWhich.tests.cpp:<line number>
|
||||
...............................................................................
|
||||
@ -7542,6 +7641,34 @@ with expansion:
|
||||
==
|
||||
"{ StringMaker<has_maker> }"
|
||||
|
||||
-------------------------------------------------------------------------------
|
||||
toString( vectors<has_maker_and_operator> )
|
||||
-------------------------------------------------------------------------------
|
||||
ToStringWhich.tests.cpp:<line number>
|
||||
...............................................................................
|
||||
|
||||
ToStringWhich.tests.cpp:<line number>:
|
||||
PASSED:
|
||||
REQUIRE( ::Catch::Detail::stringify( v ) == "{ StringMaker<has_maker_and_operator> }" )
|
||||
with expansion:
|
||||
"{ StringMaker<has_maker_and_operator> }"
|
||||
==
|
||||
"{ StringMaker<has_maker_and_operator> }"
|
||||
|
||||
-------------------------------------------------------------------------------
|
||||
toString( vectors<has_operator> )
|
||||
-------------------------------------------------------------------------------
|
||||
ToStringWhich.tests.cpp:<line number>
|
||||
...............................................................................
|
||||
|
||||
ToStringWhich.tests.cpp:<line number>:
|
||||
PASSED:
|
||||
REQUIRE( ::Catch::Detail::stringify( v ) == "{ operator<<( has_operator ) }" )
|
||||
with expansion:
|
||||
"{ operator<<( has_operator ) }"
|
||||
==
|
||||
"{ operator<<( has_operator ) }"
|
||||
|
||||
-------------------------------------------------------------------------------
|
||||
toString(enum class w/operator<<)
|
||||
-------------------------------------------------------------------------------
|
||||
@ -7987,6 +8114,6 @@ Misc.tests.cpp:<line number>:
|
||||
PASSED:
|
||||
|
||||
===============================================================================
|
||||
test cases: 189 | 135 passed | 50 failed | 4 failed as expected
|
||||
assertions: 951 | 824 passed | 106 failed | 21 failed as expected
|
||||
test cases: 191 | 137 passed | 50 failed | 4 failed as expected
|
||||
assertions: 970 | 839 passed | 110 failed | 21 failed as expected
|
||||
|
||||
|
@ -1,7 +1,7 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<testsuitesloose text artifact
|
||||
>
|
||||
<testsuite name="<exe-name>" errors="15" failures="92" tests="952" hostname="tbd" time="{duration}" timestamp="{iso8601-timestamp}">
|
||||
<testsuite name="<exe-name>" errors="15" failures="96" tests="971" hostname="tbd" time="{duration}" timestamp="{iso8601-timestamp}">
|
||||
<testcase classname="<exe-name>.global" name="# A test name that starts with a #" time="{duration}"/>
|
||||
<testcase classname="<exe-name>.global" name="#1005: Comparing pointer to int and long (NULL can be either on various systems)" time="{duration}"/>
|
||||
<testcase classname="<exe-name>.global" name="#1027" time="{duration}"/>
|
||||
@ -276,9 +276,11 @@ Message.tests.cpp:<line number>
|
||||
<testcase classname="<exe-name>.global" name="Floating point matchers: double/Margin" time="{duration}"/>
|
||||
<testcase classname="<exe-name>.global" name="Floating point matchers: double/ULPs" time="{duration}"/>
|
||||
<testcase classname="<exe-name>.global" name="Floating point matchers: double/Composed" time="{duration}"/>
|
||||
<testcase classname="<exe-name>.global" name="Floating point matchers: double/Constructor validation" time="{duration}"/>
|
||||
<testcase classname="<exe-name>.global" name="Floating point matchers: float/Margin" time="{duration}"/>
|
||||
<testcase classname="<exe-name>.global" name="Floating point matchers: float/ULPs" time="{duration}"/>
|
||||
<testcase classname="<exe-name>.global" name="Floating point matchers: float/Composed" time="{duration}"/>
|
||||
<testcase classname="<exe-name>.global" name="Floating point matchers: float/Constructor validation" time="{duration}"/>
|
||||
<testcase classname="<exe-name>.global" name="Greater-than inequalities with different epsilons" time="{duration}"/>
|
||||
<testcase classname="<exe-name>.global" name="INFO and WARN do not abort tests" time="{duration}"/>
|
||||
<testcase classname="<exe-name>.global" name="INFO gets logged on failure" time="{duration}">
|
||||
@ -517,6 +519,7 @@ A string sent directly to stdout
|
||||
</system-out>
|
||||
<system-err>
|
||||
A string sent directly to stderr
|
||||
A string sent to stderr via clog
|
||||
</system-err>
|
||||
</testcase>
|
||||
<testcase classname="<exe-name>.global" name="Some simple comparisons between doubles" time="{duration}"/>
|
||||
@ -605,6 +608,7 @@ Exception.tests.cpp:<line number>
|
||||
<testcase classname="<exe-name>.global" name="Vector matchers/Contains (vector)" time="{duration}"/>
|
||||
<testcase classname="<exe-name>.global" name="Vector matchers/Contains (element), composed" time="{duration}"/>
|
||||
<testcase classname="<exe-name>.global" name="Vector matchers/Equals" time="{duration}"/>
|
||||
<testcase classname="<exe-name>.global" name="Vector matchers/UnorderedEquals" time="{duration}"/>
|
||||
<testcase classname="<exe-name>.global" name="Vector matchers that fail/Contains (element)" time="{duration}">
|
||||
<failure message="{ 1, 2, 3 } Contains: -1" type="CHECK_THAT">
|
||||
Matchers.tests.cpp:<line number>
|
||||
@ -632,6 +636,20 @@ Matchers.tests.cpp:<line number>
|
||||
Matchers.tests.cpp:<line number>
|
||||
</failure>
|
||||
<failure message="{ 1, 2, 3 } Equals: { }" type="CHECK_THAT">
|
||||
Matchers.tests.cpp:<line number>
|
||||
</failure>
|
||||
</testcase>
|
||||
<testcase classname="<exe-name>.global" name="Vector matchers that fail/UnorderedEquals" time="{duration}">
|
||||
<failure message="{ 1, 2, 3 } UnorderedEquals: { }" type="CHECK_THAT">
|
||||
Matchers.tests.cpp:<line number>
|
||||
</failure>
|
||||
<failure message="{ } UnorderedEquals: { 1, 2, 3 }" type="CHECK_THAT">
|
||||
Matchers.tests.cpp:<line number>
|
||||
</failure>
|
||||
<failure message="{ 1, 3 } UnorderedEquals: { 1, 2, 3 }" type="CHECK_THAT">
|
||||
Matchers.tests.cpp:<line number>
|
||||
</failure>
|
||||
<failure message="{ 3, 1 } UnorderedEquals: { 1, 2, 3 }" type="CHECK_THAT">
|
||||
Matchers.tests.cpp:<line number>
|
||||
</failure>
|
||||
</testcase>
|
||||
@ -796,7 +814,9 @@ Tricky.tests.cpp:<line number>
|
||||
<testcase classname="<exe-name>.global" name="toString on const wchar_t pointer returns the string contents" time="{duration}"/>
|
||||
<testcase classname="<exe-name>.global" name="toString on wchar_t const pointer returns the string contents" time="{duration}"/>
|
||||
<testcase classname="<exe-name>.global" name="toString on wchar_t returns the string contents" time="{duration}"/>
|
||||
<testcase classname="<exe-name>.global" name="toString( vectors<has_maker )" time="{duration}"/>
|
||||
<testcase classname="<exe-name>.global" name="toString( vectors<has_maker> )" time="{duration}"/>
|
||||
<testcase classname="<exe-name>.global" name="toString( vectors<has_maker_and_operator> )" time="{duration}"/>
|
||||
<testcase classname="<exe-name>.global" name="toString( vectors<has_operator> )" time="{duration}"/>
|
||||
<testcase classname="<exe-name>.global" name="toString(enum class w/operator<<)" time="{duration}"/>
|
||||
<testcase classname="<exe-name>.global" name="toString(enum class)" time="{duration}">
|
||||
<failure message=""{?}" == "0"" type="CHECK">
|
||||
@ -833,6 +853,7 @@ Message from section two
|
||||
</system-out>
|
||||
<system-err>
|
||||
A string sent directly to stderr
|
||||
A string sent to stderr via clog
|
||||
</system-err>
|
||||
</testsuite>
|
||||
</testsuites>
|
||||
|
@ -819,7 +819,7 @@
|
||||
<TestCase name="Assertions then sections" tags="[Tricky]" filename="projects/<exe-name>/UsageTests/Tricky.tests.cpp" >
|
||||
<Expression success="true" type="REQUIRE" filename="projects/<exe-name>/UsageTests/Tricky.tests.cpp" >
|
||||
<Original>
|
||||
Catch::alwaysTrue()
|
||||
true
|
||||
</Original>
|
||||
<Expanded>
|
||||
true
|
||||
@ -828,7 +828,7 @@
|
||||
<Section name="A section" filename="projects/<exe-name>/UsageTests/Tricky.tests.cpp" >
|
||||
<Expression success="true" type="REQUIRE" filename="projects/<exe-name>/UsageTests/Tricky.tests.cpp" >
|
||||
<Original>
|
||||
Catch::alwaysTrue()
|
||||
true
|
||||
</Original>
|
||||
<Expanded>
|
||||
true
|
||||
@ -837,7 +837,7 @@
|
||||
<Section name="Another section" filename="projects/<exe-name>/UsageTests/Tricky.tests.cpp" >
|
||||
<Expression success="true" type="REQUIRE" filename="projects/<exe-name>/UsageTests/Tricky.tests.cpp" >
|
||||
<Original>
|
||||
Catch::alwaysTrue()
|
||||
true
|
||||
</Original>
|
||||
<Expanded>
|
||||
true
|
||||
@ -849,7 +849,7 @@
|
||||
</Section>
|
||||
<Expression success="true" type="REQUIRE" filename="projects/<exe-name>/UsageTests/Tricky.tests.cpp" >
|
||||
<Original>
|
||||
Catch::alwaysTrue()
|
||||
true
|
||||
</Original>
|
||||
<Expanded>
|
||||
true
|
||||
@ -858,7 +858,7 @@
|
||||
<Section name="A section" filename="projects/<exe-name>/UsageTests/Tricky.tests.cpp" >
|
||||
<Expression success="true" type="REQUIRE" filename="projects/<exe-name>/UsageTests/Tricky.tests.cpp" >
|
||||
<Original>
|
||||
Catch::alwaysTrue()
|
||||
true
|
||||
</Original>
|
||||
<Expanded>
|
||||
true
|
||||
@ -867,7 +867,7 @@
|
||||
<Section name="Another other section" filename="projects/<exe-name>/UsageTests/Tricky.tests.cpp" >
|
||||
<Expression success="true" type="REQUIRE" filename="projects/<exe-name>/UsageTests/Tricky.tests.cpp" >
|
||||
<Original>
|
||||
Catch::alwaysTrue()
|
||||
true
|
||||
</Original>
|
||||
<Expanded>
|
||||
true
|
||||
@ -1273,7 +1273,7 @@
|
||||
</Expression>
|
||||
<Expression success="true" type="REQUIRE" filename="projects/<exe-name>/UsageTests/Approx.tests.cpp" >
|
||||
<Original>
|
||||
td >= Approx(10.0)
|
||||
td >= Approx(td)
|
||||
</Original>
|
||||
<Expanded>
|
||||
StrongDoubleTypedef(10) >= Approx( 10.0 )
|
||||
@ -1281,7 +1281,7 @@
|
||||
</Expression>
|
||||
<Expression success="true" type="REQUIRE" filename="projects/<exe-name>/UsageTests/Approx.tests.cpp" >
|
||||
<Original>
|
||||
Approx(10.0) >= td
|
||||
Approx(td) >= td
|
||||
</Original>
|
||||
<Expanded>
|
||||
Approx( 10.0 ) >= StrongDoubleTypedef(10)
|
||||
@ -2142,6 +2142,41 @@
|
||||
</Expression>
|
||||
<OverallResults successes="3" failures="0" expectedFailures="0"/>
|
||||
</Section>
|
||||
<Section name="Constructor validation" filename="projects/<exe-name>/UsageTests/Matchers.tests.cpp" >
|
||||
<Expression success="true" type="REQUIRE_NOTHROW" filename="projects/<exe-name>/UsageTests/Matchers.tests.cpp" >
|
||||
<Original>
|
||||
WithinAbs(1., 0.)
|
||||
</Original>
|
||||
<Expanded>
|
||||
WithinAbs(1., 0.)
|
||||
</Expanded>
|
||||
</Expression>
|
||||
<Expression success="true" type="REQUIRE_THROWS_AS" filename="projects/<exe-name>/UsageTests/Matchers.tests.cpp" >
|
||||
<Original>
|
||||
WithinAbs(1., -1.), std::domain_error
|
||||
</Original>
|
||||
<Expanded>
|
||||
WithinAbs(1., -1.), std::domain_error
|
||||
</Expanded>
|
||||
</Expression>
|
||||
<Expression success="true" type="REQUIRE_NOTHROW" filename="projects/<exe-name>/UsageTests/Matchers.tests.cpp" >
|
||||
<Original>
|
||||
WithinULP(1., 0)
|
||||
</Original>
|
||||
<Expanded>
|
||||
WithinULP(1., 0)
|
||||
</Expanded>
|
||||
</Expression>
|
||||
<Expression success="true" type="REQUIRE_THROWS_AS" filename="projects/<exe-name>/UsageTests/Matchers.tests.cpp" >
|
||||
<Original>
|
||||
WithinULP(1., -1), std::domain_error
|
||||
</Original>
|
||||
<Expanded>
|
||||
WithinULP(1., -1), std::domain_error
|
||||
</Expanded>
|
||||
</Expression>
|
||||
<OverallResults successes="4" failures="0" expectedFailures="0"/>
|
||||
</Section>
|
||||
<OverallResult success="true"/>
|
||||
</TestCase>
|
||||
<TestCase name="Floating point matchers: float" tags="[floating-point][matchers]" filename="projects/<exe-name>/UsageTests/Matchers.tests.cpp" >
|
||||
@ -2282,6 +2317,41 @@
|
||||
</Expression>
|
||||
<OverallResults successes="3" failures="0" expectedFailures="0"/>
|
||||
</Section>
|
||||
<Section name="Constructor validation" filename="projects/<exe-name>/UsageTests/Matchers.tests.cpp" >
|
||||
<Expression success="true" type="REQUIRE_NOTHROW" filename="projects/<exe-name>/UsageTests/Matchers.tests.cpp" >
|
||||
<Original>
|
||||
WithinAbs(1.f, 0.f)
|
||||
</Original>
|
||||
<Expanded>
|
||||
WithinAbs(1.f, 0.f)
|
||||
</Expanded>
|
||||
</Expression>
|
||||
<Expression success="true" type="REQUIRE_THROWS_AS" filename="projects/<exe-name>/UsageTests/Matchers.tests.cpp" >
|
||||
<Original>
|
||||
WithinAbs(1.f, -1.f), std::domain_error
|
||||
</Original>
|
||||
<Expanded>
|
||||
WithinAbs(1.f, -1.f), std::domain_error
|
||||
</Expanded>
|
||||
</Expression>
|
||||
<Expression success="true" type="REQUIRE_NOTHROW" filename="projects/<exe-name>/UsageTests/Matchers.tests.cpp" >
|
||||
<Original>
|
||||
WithinULP(1.f, 0)
|
||||
</Original>
|
||||
<Expanded>
|
||||
WithinULP(1.f, 0)
|
||||
</Expanded>
|
||||
</Expression>
|
||||
<Expression success="true" type="REQUIRE_THROWS_AS" filename="projects/<exe-name>/UsageTests/Matchers.tests.cpp" >
|
||||
<Original>
|
||||
WithinULP(1.f, -1), std::domain_error
|
||||
</Original>
|
||||
<Expanded>
|
||||
WithinULP(1.f, -1), std::domain_error
|
||||
</Expanded>
|
||||
</Expression>
|
||||
<OverallResults successes="4" failures="0" expectedFailures="0"/>
|
||||
</Section>
|
||||
<OverallResult success="true"/>
|
||||
</TestCase>
|
||||
<TestCase name="Greater-than inequalities with different epsilons" tags="[Approx]" filename="projects/<exe-name>/UsageTests/Approx.tests.cpp" >
|
||||
@ -5260,6 +5330,7 @@ A string sent directly to stdout
|
||||
</StdOut>
|
||||
<StdErr>
|
||||
A string sent directly to stderr
|
||||
A string sent to stderr via clog
|
||||
</StdErr>
|
||||
</OverallResult>
|
||||
</TestCase>
|
||||
@ -5878,6 +5949,14 @@ Message from section two
|
||||
1 [30/1]s != 1 fs
|
||||
</Expanded>
|
||||
</Expression>
|
||||
<Expression success="true" type="REQUIRE" filename="projects/<exe-name>/UsageTests/ToStringChrono.tests.cpp" >
|
||||
<Original>
|
||||
pico_second != atto_second
|
||||
</Original>
|
||||
<Expanded>
|
||||
1 ps != 1 as
|
||||
</Expanded>
|
||||
</Expression>
|
||||
<OverallResult success="true"/>
|
||||
</TestCase>
|
||||
<TestCase name="Stringifying std::chrono::time_point<system_clock>" tags="[chrono][toString]" filename="projects/<exe-name>/UsageTests/ToStringChrono.tests.cpp" >
|
||||
@ -7298,6 +7377,41 @@ Message from section two
|
||||
</Expression>
|
||||
<OverallResults successes="3" failures="0" expectedFailures="0"/>
|
||||
</Section>
|
||||
<Section name="UnorderedEquals" filename="projects/<exe-name>/UsageTests/Matchers.tests.cpp" >
|
||||
<Expression success="true" type="CHECK_THAT" filename="projects/<exe-name>/UsageTests/Matchers.tests.cpp" >
|
||||
<Original>
|
||||
v, UnorderedEquals(v)
|
||||
</Original>
|
||||
<Expanded>
|
||||
{ 1, 2, 3 } UnorderedEquals: { 1, 2, 3 }
|
||||
</Expanded>
|
||||
</Expression>
|
||||
<Expression success="true" type="CHECK_THAT" filename="projects/<exe-name>/UsageTests/Matchers.tests.cpp" >
|
||||
<Original>
|
||||
empty, UnorderedEquals(empty)
|
||||
</Original>
|
||||
<Expanded>
|
||||
{ } UnorderedEquals: { }
|
||||
</Expanded>
|
||||
</Expression>
|
||||
<Expression success="true" type="REQUIRE_THAT" filename="projects/<exe-name>/UsageTests/Matchers.tests.cpp" >
|
||||
<Original>
|
||||
permuted, UnorderedEquals(v)
|
||||
</Original>
|
||||
<Expanded>
|
||||
{ 1, 3, 2 } UnorderedEquals: { 1, 2, 3 }
|
||||
</Expanded>
|
||||
</Expression>
|
||||
<Expression success="true" type="REQUIRE_THAT" filename="projects/<exe-name>/UsageTests/Matchers.tests.cpp" >
|
||||
<Original>
|
||||
permuted, UnorderedEquals(v)
|
||||
</Original>
|
||||
<Expanded>
|
||||
{ 2, 3, 1 } UnorderedEquals: { 1, 2, 3 }
|
||||
</Expanded>
|
||||
</Expression>
|
||||
<OverallResults successes="4" failures="0" expectedFailures="0"/>
|
||||
</Section>
|
||||
<OverallResult success="true"/>
|
||||
</TestCase>
|
||||
<TestCase name="Vector matchers that fail" tags="[.][failing][matchers][vector]" filename="projects/<exe-name>/UsageTests/Matchers.tests.cpp" >
|
||||
@ -7374,6 +7488,41 @@ Message from section two
|
||||
</Expression>
|
||||
<OverallResults successes="0" failures="4" expectedFailures="0"/>
|
||||
</Section>
|
||||
<Section name="UnorderedEquals" filename="projects/<exe-name>/UsageTests/Matchers.tests.cpp" >
|
||||
<Expression success="false" type="CHECK_THAT" filename="projects/<exe-name>/UsageTests/Matchers.tests.cpp" >
|
||||
<Original>
|
||||
v, UnorderedEquals(empty)
|
||||
</Original>
|
||||
<Expanded>
|
||||
{ 1, 2, 3 } UnorderedEquals: { }
|
||||
</Expanded>
|
||||
</Expression>
|
||||
<Expression success="false" type="CHECK_THAT" filename="projects/<exe-name>/UsageTests/Matchers.tests.cpp" >
|
||||
<Original>
|
||||
empty, UnorderedEquals(v)
|
||||
</Original>
|
||||
<Expanded>
|
||||
{ } UnorderedEquals: { 1, 2, 3 }
|
||||
</Expanded>
|
||||
</Expression>
|
||||
<Expression success="false" type="CHECK_THAT" filename="projects/<exe-name>/UsageTests/Matchers.tests.cpp" >
|
||||
<Original>
|
||||
permuted, UnorderedEquals(v)
|
||||
</Original>
|
||||
<Expanded>
|
||||
{ 1, 3 } UnorderedEquals: { 1, 2, 3 }
|
||||
</Expanded>
|
||||
</Expression>
|
||||
<Expression success="false" type="CHECK_THAT" filename="projects/<exe-name>/UsageTests/Matchers.tests.cpp" >
|
||||
<Original>
|
||||
permuted, UnorderedEquals(v)
|
||||
</Original>
|
||||
<Expanded>
|
||||
{ 3, 1 } UnorderedEquals: { 1, 2, 3 }
|
||||
</Expanded>
|
||||
</Expression>
|
||||
<OverallResults successes="0" failures="4" expectedFailures="0"/>
|
||||
</Section>
|
||||
<OverallResult success="false"/>
|
||||
</TestCase>
|
||||
<TestCase name="When checked exceptions are thrown they can be expected or unexpected" tags="[!throws]" filename="projects/<exe-name>/UsageTests/Exception.tests.cpp" >
|
||||
@ -8409,7 +8558,7 @@ loose text artifact
|
||||
</Expression>
|
||||
<OverallResult success="true"/>
|
||||
</TestCase>
|
||||
<TestCase name="toString( vectors<has_maker )" tags="[toString]" filename="projects/<exe-name>/UsageTests/ToStringWhich.tests.cpp" >
|
||||
<TestCase name="toString( vectors<has_maker> )" tags="[toString]" filename="projects/<exe-name>/UsageTests/ToStringWhich.tests.cpp" >
|
||||
<Expression success="true" type="REQUIRE" filename="projects/<exe-name>/UsageTests/ToStringWhich.tests.cpp" >
|
||||
<Original>
|
||||
::Catch::Detail::stringify( v ) == "{ StringMaker<has_maker> }"
|
||||
@ -8422,6 +8571,32 @@ loose text artifact
|
||||
</Expression>
|
||||
<OverallResult success="true"/>
|
||||
</TestCase>
|
||||
<TestCase name="toString( vectors<has_maker_and_operator> )" tags="[toString]" filename="projects/<exe-name>/UsageTests/ToStringWhich.tests.cpp" >
|
||||
<Expression success="true" type="REQUIRE" filename="projects/<exe-name>/UsageTests/ToStringWhich.tests.cpp" >
|
||||
<Original>
|
||||
::Catch::Detail::stringify( v ) == "{ StringMaker<has_maker_and_operator> }"
|
||||
</Original>
|
||||
<Expanded>
|
||||
"{ StringMaker<has_maker_and_operator> }"
|
||||
==
|
||||
"{ StringMaker<has_maker_and_operator> }"
|
||||
</Expanded>
|
||||
</Expression>
|
||||
<OverallResult success="true"/>
|
||||
</TestCase>
|
||||
<TestCase name="toString( vectors<has_operator> )" tags="[toString]" filename="projects/<exe-name>/UsageTests/ToStringWhich.tests.cpp" >
|
||||
<Expression success="true" type="REQUIRE" filename="projects/<exe-name>/UsageTests/ToStringWhich.tests.cpp" >
|
||||
<Original>
|
||||
::Catch::Detail::stringify( v ) == "{ operator<<( has_operator ) }"
|
||||
</Original>
|
||||
<Expanded>
|
||||
"{ operator<<( has_operator ) }"
|
||||
==
|
||||
"{ operator<<( has_operator ) }"
|
||||
</Expanded>
|
||||
</Expression>
|
||||
<OverallResult success="true"/>
|
||||
</TestCase>
|
||||
<TestCase name="toString(enum class w/operator<<)" tags="[enum][enumClass][toString]" filename="projects/<exe-name>/UsageTests/EnumToString.tests.cpp" >
|
||||
<Expression success="true" type="CHECK" filename="projects/<exe-name>/UsageTests/EnumToString.tests.cpp" >
|
||||
<Original>
|
||||
@ -8863,7 +9038,7 @@ loose text artifact
|
||||
</Section>
|
||||
<OverallResult success="true"/>
|
||||
</TestCase>
|
||||
<OverallResults successes="824" failures="107" expectedFailures="21"/>
|
||||
<OverallResults successes="839" failures="111" expectedFailures="21"/>
|
||||
</Group>
|
||||
<OverallResults successes="824" failures="106" expectedFailures="21"/>
|
||||
<OverallResults successes="839" failures="110" expectedFailures="21"/>
|
||||
</Catch>
|
||||
|
@ -189,8 +189,8 @@ TEST_CASE( "Comparison with explicitly convertible types", "[Approx]" )
|
||||
REQUIRE(Approx(9.0) <= td);
|
||||
|
||||
REQUIRE(td >= Approx(9.0));
|
||||
REQUIRE(td >= Approx(10.0));
|
||||
REQUIRE(Approx(10.0) >= td);
|
||||
REQUIRE(td >= Approx(td));
|
||||
REQUIRE(Approx(td) >= td);
|
||||
REQUIRE(Approx(11.0) >= td);
|
||||
|
||||
}
|
||||
|
@ -8,7 +8,10 @@
|
||||
#ifdef __clang__
|
||||
# pragma clang diagnostic push
|
||||
# pragma clang diagnostic ignored "-Wpadded"
|
||||
# pragma clang diagnostic ignored "-Wdouble-promotion"
|
||||
// Wdouble-promotion is not supported until 3.8
|
||||
# if (__clang_major__ > 3) || (__clang_major__ == 3 && __clang_minor__ > 7)
|
||||
# pragma clang diagnostic ignored "-Wdouble-promotion"
|
||||
# endif
|
||||
#endif
|
||||
|
||||
#include "catch.hpp"
|
||||
|
@ -12,11 +12,12 @@
|
||||
#include <stdexcept>
|
||||
|
||||
#ifdef _MSC_VER
|
||||
#pragma warning(disable:4702) // Unreachable code -- MSVC 19 (VS 2015) sees right through the indirection
|
||||
#pragma warning(disable:4702) // Unreachable code -- uncoditional throws and so on
|
||||
#endif
|
||||
#ifdef __clang__
|
||||
#pragma clang diagnostic push
|
||||
#pragma clang diagnostic ignored "-Wweak-vtables"
|
||||
#pragma clang diagnostic ignored "-Wmissing-noreturn"
|
||||
#endif
|
||||
|
||||
namespace { namespace ExceptionTests {
|
||||
@ -24,9 +25,8 @@ namespace { namespace ExceptionTests {
|
||||
#ifndef EXCEPTION_TEST_HELPERS_INCLUDED // Don't compile this more than once per TU
|
||||
#define EXCEPTION_TEST_HELPERS_INCLUDED
|
||||
|
||||
inline int thisThrows() {
|
||||
if( Catch::alwaysTrue() )
|
||||
throw std::domain_error( "expected exception" );
|
||||
int thisThrows() {
|
||||
throw std::domain_error( "expected exception" );
|
||||
return 1;
|
||||
}
|
||||
|
||||
@ -36,8 +36,8 @@ int thisDoesntThrow() {
|
||||
|
||||
class CustomException {
|
||||
public:
|
||||
CustomException( const std::string& msg )
|
||||
: m_msg( msg )
|
||||
explicit CustomException( const std::string& msg )
|
||||
: m_msg( msg )
|
||||
{}
|
||||
|
||||
std::string getMessage() const {
|
||||
@ -50,10 +50,10 @@ private:
|
||||
|
||||
class CustomStdException : public std::exception {
|
||||
public:
|
||||
CustomStdException( const std::string& msg )
|
||||
: m_msg( msg )
|
||||
explicit CustomStdException( const std::string& msg )
|
||||
: m_msg( msg )
|
||||
{}
|
||||
~CustomStdException() noexcept {}
|
||||
~CustomStdException() noexcept override {}
|
||||
|
||||
std::string getMessage() const {
|
||||
return m_msg;
|
||||
@ -63,9 +63,8 @@ private:
|
||||
std::string m_msg;
|
||||
};
|
||||
|
||||
inline void throwCustom() {
|
||||
if( Catch::alwaysTrue() )
|
||||
throw CustomException( "custom exception - not std" );
|
||||
[[noreturn]] void throwCustom() {
|
||||
throw CustomException( "custom exception - not std" );
|
||||
}
|
||||
|
||||
#endif
|
||||
@ -83,20 +82,17 @@ TEST_CASE( "Expected exceptions that don't throw or unexpected exceptions fail t
|
||||
}
|
||||
|
||||
TEST_CASE( "When unchecked exceptions are thrown directly they are always failures", "[.][failing][!throws]" ) {
|
||||
if( Catch::alwaysTrue() )
|
||||
throw std::domain_error( "unexpected exception" );
|
||||
throw std::domain_error( "unexpected exception" );
|
||||
}
|
||||
|
||||
TEST_CASE( "An unchecked exception reports the line of the last assertion", "[.][failing][!throws]" ) {
|
||||
CHECK( 1 == 1 );
|
||||
if( Catch::alwaysTrue() )
|
||||
throw std::domain_error( "unexpected exception" );
|
||||
throw std::domain_error( "unexpected exception" );
|
||||
}
|
||||
|
||||
TEST_CASE( "When unchecked exceptions are thrown from sections they are always failures", "[.][failing][!throws]" ) {
|
||||
SECTION( "section name" ) {
|
||||
if( Catch::alwaysTrue() )
|
||||
throw std::domain_error( "unexpected exception" );
|
||||
throw std::domain_error("unexpected exception");
|
||||
}
|
||||
}
|
||||
|
||||
@ -139,13 +135,11 @@ CATCH_TRANSLATE_EXCEPTION( double& ex ) {
|
||||
}
|
||||
|
||||
TEST_CASE("Non-std exceptions can be translated", "[.][failing][!throws]" ) {
|
||||
if( Catch::alwaysTrue() )
|
||||
throw CustomException( "custom exception" );
|
||||
throw CustomException( "custom exception" );
|
||||
}
|
||||
|
||||
TEST_CASE("Custom std-exceptions can be custom translated", "[.][failing][!throws]" ) {
|
||||
if( Catch::alwaysTrue() )
|
||||
throw CustomException( "custom std exception" );
|
||||
throw CustomException( "custom std exception" );
|
||||
}
|
||||
|
||||
TEST_CASE( "Custom exceptions can be translated when testing for nothrow", "[.][failing][!throws]" ) {
|
||||
@ -157,8 +151,7 @@ TEST_CASE( "Custom exceptions can be translated when testing for throwing as som
|
||||
}
|
||||
|
||||
TEST_CASE( "Unexpected exceptions can be translated", "[.][failing][!throws]" ) {
|
||||
if( Catch::alwaysTrue() )
|
||||
throw double( 3.14 );
|
||||
throw double( 3.14 );
|
||||
}
|
||||
|
||||
#ifndef CATCH_CONFIG_DISABLE_MATCHERS
|
||||
|
@ -9,6 +9,7 @@
|
||||
#include "catch.hpp"
|
||||
|
||||
#include <sstream>
|
||||
#include <algorithm>
|
||||
|
||||
#ifdef __clang__
|
||||
#pragma clang diagnostic push
|
||||
@ -216,6 +217,17 @@ namespace { namespace MatchersTests {
|
||||
v2.push_back(3);
|
||||
CHECK_THAT(v, Equals(v2));
|
||||
}
|
||||
SECTION("UnorderedEquals") {
|
||||
CHECK_THAT(v, UnorderedEquals(v));
|
||||
CHECK_THAT(empty, UnorderedEquals(empty));
|
||||
|
||||
auto permuted = v;
|
||||
std::next_permutation(begin(permuted), end(permuted));
|
||||
REQUIRE_THAT(permuted, UnorderedEquals(v));
|
||||
|
||||
std::reverse(begin(permuted), end(permuted));
|
||||
REQUIRE_THAT(permuted, UnorderedEquals(v));
|
||||
}
|
||||
}
|
||||
|
||||
TEST_CASE("Vector matchers that fail", "[matchers][vector][.][failing]") {
|
||||
@ -247,6 +259,18 @@ namespace { namespace MatchersTests {
|
||||
CHECK_THAT(empty, Equals(v));
|
||||
CHECK_THAT(v, Equals(empty));
|
||||
}
|
||||
SECTION("UnorderedEquals") {
|
||||
CHECK_THAT(v, UnorderedEquals(empty));
|
||||
CHECK_THAT(empty, UnorderedEquals(v));
|
||||
|
||||
auto permuted = v;
|
||||
std::next_permutation(begin(permuted), end(permuted));
|
||||
permuted.pop_back();
|
||||
CHECK_THAT(permuted, UnorderedEquals(v));
|
||||
|
||||
std::reverse(begin(permuted), end(permuted));
|
||||
CHECK_THAT(permuted, UnorderedEquals(v));
|
||||
}
|
||||
}
|
||||
|
||||
TEST_CASE("Exception matchers that succeed", "[matchers][exceptions][!throws]") {
|
||||
@ -298,6 +322,13 @@ namespace { namespace MatchersTests {
|
||||
|
||||
REQUIRE_THAT(NAN, !(WithinAbs(NAN, 100) || WithinULP(NAN, 123)));
|
||||
}
|
||||
SECTION("Constructor validation") {
|
||||
REQUIRE_NOTHROW(WithinAbs(1.f, 0.f));
|
||||
REQUIRE_THROWS_AS(WithinAbs(1.f, -1.f), std::domain_error);
|
||||
|
||||
REQUIRE_NOTHROW(WithinULP(1.f, 0));
|
||||
REQUIRE_THROWS_AS(WithinULP(1.f, -1), std::domain_error);
|
||||
}
|
||||
}
|
||||
|
||||
TEST_CASE("Floating point matchers: double", "[matchers][floating-point]") {
|
||||
@ -328,6 +359,13 @@ namespace { namespace MatchersTests {
|
||||
|
||||
REQUIRE_THAT(NAN, !(WithinAbs(NAN, 100) || WithinULP(NAN, 123)));
|
||||
}
|
||||
SECTION("Constructor validation") {
|
||||
REQUIRE_NOTHROW(WithinAbs(1., 0.));
|
||||
REQUIRE_THROWS_AS(WithinAbs(1., -1.), std::domain_error);
|
||||
|
||||
REQUIRE_NOTHROW(WithinULP(1., 0));
|
||||
REQUIRE_THROWS_AS(WithinULP(1., -1), std::domain_error);
|
||||
}
|
||||
}
|
||||
|
||||
} } // namespace MatchersTests
|
||||
|
@ -57,7 +57,9 @@ struct AutoTestReg {
|
||||
REGISTER_TEST_CASE( manuallyRegisteredTestFunction, "ManuallyRegistered" );
|
||||
}
|
||||
};
|
||||
CATCH_INTERNAL_SUPPRESS_GLOBALS_WARNINGS
|
||||
static AutoTestReg autoTestReg;
|
||||
CATCH_INTERNAL_UNSUPPRESS_GLOBALS_WARNINGS
|
||||
|
||||
#endif
|
||||
|
||||
@ -146,8 +148,8 @@ TEST_CASE( "looped tests", "[.][failing]" ) {
|
||||
|
||||
TEST_CASE( "Sends stuff to stdout and stderr", "[.]" ) {
|
||||
std::cout << "A string sent directly to stdout" << std::endl;
|
||||
|
||||
std::cerr << "A string sent directly to stderr" << std::endl;
|
||||
std::clog << "A string sent to stderr via clog" << std::endl;
|
||||
}
|
||||
|
||||
TEST_CASE( "null strings" ) {
|
||||
@ -301,13 +303,13 @@ TEST_CASE( "toString on const wchar_t pointer returns the string contents", "[to
|
||||
}
|
||||
|
||||
TEST_CASE( "toString on wchar_t const pointer returns the string contents", "[toString]" ) {
|
||||
wchar_t * const s = const_cast<wchar_t* const>( L"wide load" );
|
||||
auto const s = const_cast<wchar_t* const>( L"wide load" );
|
||||
std::string result = ::Catch::Detail::stringify( s );
|
||||
CHECK( result == "\"wide load\"" );
|
||||
}
|
||||
|
||||
TEST_CASE( "toString on wchar_t returns the string contents", "[toString]" ) {
|
||||
wchar_t * s = const_cast<wchar_t*>( L"wide load" );
|
||||
auto s = const_cast<wchar_t*>( L"wide load" );
|
||||
std::string result = ::Catch::Detail::stringify( s );
|
||||
CHECK( result == "\"wide load\"" );
|
||||
}
|
||||
|
@ -20,8 +20,11 @@ TEST_CASE("Stringifying std::chrono::duration helpers", "[toString][chrono]") {
|
||||
|
||||
TEST_CASE("Stringifying std::chrono::duration with weird ratios", "[toString][chrono]") {
|
||||
std::chrono::duration<int64_t, std::ratio<30>> half_minute(1);
|
||||
std::chrono::duration<int64_t, std::ratio<1, 1000000000000>> pico_second(1);
|
||||
std::chrono::duration<int64_t, std::ratio<1, 1000000000000000>> femto_second(1);
|
||||
std::chrono::duration<int64_t, std::ratio<1, 1000000000000000000>> atto_second(1);
|
||||
REQUIRE(half_minute != femto_second);
|
||||
REQUIRE(pico_second != atto_second);
|
||||
}
|
||||
|
||||
TEST_CASE("Stringifying std::chrono::time_point<system_clock>", "[toString][chrono]") {
|
||||
|
@ -55,19 +55,19 @@ TEST_CASE( "stringify( has_maker_and_toString )", "[.][toString]" ) {
|
||||
// Vectors...
|
||||
|
||||
// Don't run this in approval tests as it is sensitive to two phase lookup differences
|
||||
TEST_CASE( "toString( vectors<has_toString )", "[.][toString][!nonportable]" ) {
|
||||
TEST_CASE( "toString( vectors<has_operator> )", "[toString]" ) {
|
||||
std::vector<has_operator> v(1);
|
||||
REQUIRE( ::Catch::Detail::stringify( v ) == "{ operator<<( has_operator ) }" );
|
||||
}
|
||||
|
||||
TEST_CASE( "toString( vectors<has_maker )", "[toString]" ) {
|
||||
TEST_CASE( "toString( vectors<has_maker> )", "[toString]" ) {
|
||||
std::vector<has_maker> v(1);
|
||||
REQUIRE( ::Catch::Detail::stringify( v ) == "{ StringMaker<has_maker> }" );
|
||||
}
|
||||
|
||||
|
||||
// Don't run this in approval tests as it is sensitive to two phase lookup differences
|
||||
TEST_CASE( "toString( vectors<has_maker_and_toString )", "[.][toString][!nonportable]" ) {
|
||||
TEST_CASE( "toString( vectors<has_maker_and_operator> )", "[toString]" ) {
|
||||
std::vector<has_maker_and_operator> v(1);
|
||||
REQUIRE( ::Catch::Detail::stringify( v ) == "{ StringMaker<has_maker_and_toString> }" );
|
||||
REQUIRE( ::Catch::Detail::stringify( v ) == "{ StringMaker<has_maker_and_operator> }" );
|
||||
}
|
||||
|
@ -298,19 +298,19 @@ TEST_CASE( "Assertions then sections", "[Tricky]" )
|
||||
// This was causing a failure due to the way the console reporter was handling
|
||||
// the current section
|
||||
|
||||
REQUIRE( Catch::alwaysTrue() );
|
||||
REQUIRE( true );
|
||||
|
||||
SECTION( "A section" )
|
||||
{
|
||||
REQUIRE( Catch::alwaysTrue() );
|
||||
REQUIRE( true );
|
||||
|
||||
SECTION( "Another section" )
|
||||
{
|
||||
REQUIRE( Catch::alwaysTrue() );
|
||||
REQUIRE( true );
|
||||
}
|
||||
SECTION( "Another other section" )
|
||||
{
|
||||
REQUIRE( Catch::alwaysTrue() );
|
||||
REQUIRE( true );
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -167,6 +167,8 @@ def approve(baseName, args):
|
||||
print("Running approvals against executable:")
|
||||
print(" " + cmdPath)
|
||||
|
||||
|
||||
### Keep default reporters here
|
||||
# Standard console reporter
|
||||
approve("console.std", ["~[!nonportable]~[!benchmark]~[approvals]", "--order", "lex"])
|
||||
# console reporter, include passes, warn about No Assertions
|
||||
@ -177,6 +179,8 @@ approve("console.swa4", ["~[!nonportable]~[!benchmark]~[approvals]", "-s", "-w",
|
||||
approve("junit.sw", ["~[!nonportable]~[!benchmark]~[approvals]", "-s", "-w", "NoAssertions", "-r", "junit", "--order", "lex"])
|
||||
# xml reporter, include passes, warn about No Assertions
|
||||
approve("xml.sw", ["~[!nonportable]~[!benchmark]~[approvals]", "-s", "-w", "NoAssertions", "-r", "xml", "--order", "lex"])
|
||||
# compact reporter, include passes, warn about No Assertions
|
||||
approve('compact.sw', ['~[!nonportable]~[!benchmark]~[approvals]', '-s', '-w', 'NoAssertions', '-r', 'compact', '--order', 'lex'])
|
||||
|
||||
if overallResult != 0:
|
||||
print("If these differences are expected, run approve.py to approve new baselines.")
|
||||
|
Loading…
Reference in New Issue
Block a user