Merge remote-tracking branch 'upstream/master' into contrib

This commit is contained in:
Martin Hořeňovský 2017-12-09 20:04:47 +01:00
commit 9a1791ab9f
62 changed files with 3199 additions and 509 deletions

View File

@ -17,7 +17,7 @@ matrix:
addons: addons:
apt: apt:
sources: *all_sources sources: *all_sources
packages: ['valgrind', 'clang-3.5'] packages: ['valgrind', 'lcov', 'clang-3.5']
env: COMPILER='clang++-3.5' VALGRIND=1 env: COMPILER='clang++-3.5' VALGRIND=1
- os: linux - os: linux
@ -25,7 +25,7 @@ matrix:
addons: addons:
apt: apt:
sources: *all_sources sources: *all_sources
packages: ['valgrind', 'clang-3.6'] packages: ['valgrind', 'lcov', 'clang-3.6']
env: COMPILER='clang++-3.6' VALGRIND=1 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. # 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: addons:
apt: apt:
sources: *all_sources sources: *all_sources
packages: ['valgrind', 'clang-3.8'] packages: ['valgrind', 'lcov', 'clang-3.8']
env: COMPILER='clang++-3.8' VALGRIND=1 env: COMPILER='clang++-3.8' VALGRIND=1
- os: linux - os: linux
@ -50,7 +50,7 @@ matrix:
addons: addons:
apt: apt:
sources: *all_sources sources: *all_sources
packages: ['clang-3.9', 'valgrind'] packages: ['clang-3.9', 'valgrind', 'lcov']
env: COMPILER='clang++-3.9' VALGRIND=1 env: COMPILER='clang++-3.9' VALGRIND=1
- os: linux - os: linux
@ -58,7 +58,7 @@ matrix:
addons: addons:
apt: apt:
sources: *all_sources sources: *all_sources
packages: ['clang-4.0', 'valgrind'] packages: ['clang-4.0', 'valgrind', 'lcov']
env: COMPILER='clang++-4.0' VALGRIND=1 env: COMPILER='clang++-4.0' VALGRIND=1
- os: linux - os: linux
@ -66,7 +66,7 @@ matrix:
addons: addons:
apt: apt:
sources: *all_sources sources: *all_sources
packages: ['clang-5.0', 'valgrind'] packages: ['clang-5.0', 'valgrind', 'lcov']
env: COMPILER='clang++-5.0' VALGRIND=1 env: COMPILER='clang++-5.0' VALGRIND=1
# 2/ Linux GCC Builds # 2/ Linux GCC Builds
@ -75,7 +75,7 @@ matrix:
addons: addons:
apt: apt:
sources: ['ubuntu-toolchain-r-test'] sources: ['ubuntu-toolchain-r-test']
packages: ['valgrind', 'g++-4.8'] packages: ['valgrind', 'lcov', 'g++-4.8']
env: COMPILER='g++-4.8' VALGRIND=1 env: COMPILER='g++-4.8' VALGRIND=1
- os: linux - os: linux
@ -83,7 +83,7 @@ matrix:
addons: addons:
apt: apt:
sources: *all_sources sources: *all_sources
packages: ['valgrind', 'g++-4.9'] packages: ['valgrind', 'lcov', 'g++-4.9']
env: COMPILER='g++-4.9' VALGRIND=1 env: COMPILER='g++-4.9' VALGRIND=1
- os: linux - os: linux
@ -91,7 +91,7 @@ matrix:
addons: addons:
apt: apt:
sources: *all_sources sources: *all_sources
packages: ['valgrind', 'g++-5'] packages: ['valgrind', 'lcov', 'g++-5']
env: COMPILER='g++-5' VALGRIND=1 env: COMPILER='g++-5' VALGRIND=1
- os: linux - os: linux
@ -99,7 +99,7 @@ matrix:
addons: &gcc6 addons: &gcc6
apt: apt:
sources: *all_sources sources: *all_sources
packages: ['valgrind', 'g++-6'] packages: ['valgrind', 'lcov', 'g++-6']
env: COMPILER='g++-6' VALGRIND=1 env: COMPILER='g++-6' VALGRIND=1
- os: linux - os: linux
@ -107,20 +107,15 @@ matrix:
addons: &gcc7 addons: &gcc7
apt: apt:
sources: *all_sources sources: *all_sources
packages: ['valgrind', 'g++-7'] packages: ['valgrind', 'lcov', 'g++-7']
env: COMPILER='g++-7' VALGRIND=1 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 # 3b/ Linux C++14 Clang builds
- os: linux - os: linux
compiler: clang compiler: clang
addons: addons:
apt: apt:
packages: ['clang-3.8', 'valgrind', 'libstdc++-6-dev'] packages: ['clang-3.8', 'valgrind', 'lcov', 'libstdc++-6-dev']
sources: sources:
- ubuntu-toolchain-r-test - ubuntu-toolchain-r-test
- llvm-toolchain-trusty - llvm-toolchain-trusty
@ -131,7 +126,7 @@ matrix:
addons: addons:
apt: apt:
sources: *all_sources 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 env: COMPILER='clang++-3.9' CPP14=1 VALGRIND=1
- os: linux - os: linux
@ -139,7 +134,7 @@ matrix:
addons: addons:
apt: apt:
sources: *all_sources 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 env: COMPILER='clang++-4.0' CPP14=1 VALGRIND=1
- os: linux - os: linux
@ -147,7 +142,7 @@ matrix:
addons: addons:
apt: apt:
sources: *all_sources 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 env: COMPILER='clang++-5.0' CPP14=1 VALGRIND=1
@ -188,6 +183,7 @@ matrix:
compiler: clang compiler: clang
env: COMPILER='clang++' USE_CPP14=1 env: COMPILER='clang++' USE_CPP14=1
install: install:
- DEPS_DIR="${TRAVIS_BUILD_DIR}/deps" - DEPS_DIR="${TRAVIS_BUILD_DIR}/deps"
- mkdir -p ${DEPS_DIR} && cd ${DEPS_DIR} - 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 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} export PATH=${DEPS_DIR}/cmake/bin:${PATH}
elif [[ "${TRAVIS_OS_NAME}" == "osx" ]]; then elif [[ "${TRAVIS_OS_NAME}" == "osx" ]]; then
which cmake || brew install cmake which cmake || brew install cmake;
fi fi
before_script: before_script:
@ -206,15 +202,25 @@ before_script:
# Regenerate single header file, so it is tested in the examples... # Regenerate single header file, so it is tested in the examples...
- python scripts/generateSingleHeader.py - python scripts/generateSingleHeader.py
- |
# Use Debug builds for running Valgrind and building examples # 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 cmake -H. -BBuild-Debug -DCMAKE_BUILD_TYPE=Debug -Wdev -DUSE_CPP14=${CPP14} -DUSE_VALGRIND=${VALGRIND} -DBUILD_EXAMPLES=ON -DENABLE_COVERAGE=ON
# Check that we don't miscompile with optimalizations... # Don't bother with release build for coverage build
- cmake -H. -BBuild-Release -DCMAKE_BUILD_TYPE=Release -Wdev -DUSE_CPP14=${CPP14} cmake -H. -BBuild-Release -DCMAKE_BUILD_TYPE=Release -Wdev -DUSE_CPP14=${CPP14}
script: script:
- cd Build-Debug - |
- make -j 2 cd Build-Debug
- ctest -V -j 2 make -j 2
- cd ../Build-Release CTEST_OUTPUT_ON_FAILURE=1 ctest -j 2
- make -j 2 # Coverage collection does not work for OS X atm
- ctest -V -j 2 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
View 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
View 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
View 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
View 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 $@

View File

@ -4,6 +4,7 @@ project(CatchSelfTest)
option(USE_VALGRIND "Perform SelfTests with Valgrind" OFF) option(USE_VALGRIND "Perform SelfTests with Valgrind" OFF)
option(BUILD_EXAMPLES "Build documentation examples" OFF) option(BUILD_EXAMPLES "Build documentation examples" OFF)
option(ENABLE_COVERAGE "Generate coverage for codecov.io" OFF)
set_property(GLOBAL PROPERTY USE_FOLDERS ON) set_property(GLOBAL PROPERTY USE_FOLDERS ON)
@ -280,7 +281,6 @@ set(HEADERS
SOURCE_GROUP("Tests" FILES ${TEST_SOURCES}) SOURCE_GROUP("Tests" FILES ${TEST_SOURCES})
SOURCE_GROUP("Surrogates" FILES ${SURROGATE_SOURCES}) SOURCE_GROUP("Surrogates" FILES ${SURROGATE_SOURCES})
# configure the executable
# Projects consuming Catch via ExternalProject_Add might want to use install step # Projects consuming Catch via ExternalProject_Add might want to use install step
# without building all of our selftests. # 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_STANDARD_REQUIRED ON)
set_property(TARGET SelfTest PROPERTY CXX_EXTENSIONS OFF) 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 # Add desired warnings
if ( CMAKE_CXX_COMPILER_ID MATCHES "Clang|AppleClang|GNU" ) 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() endif()
# Clang specific warning go here # Clang specific warning go here
if ( CMAKE_CXX_COMPILER_ID MATCHES "Clang" ) if ( CMAKE_CXX_COMPILER_ID MATCHES "Clang" )
@ -315,22 +322,30 @@ if (NOT NO_SELFTEST)
# configure unit tests via CTest # configure unit tests via CTest
enable_testing() include(CTest)
add_test(NAME RunTests COMMAND $<TARGET_FILE:SelfTest>) 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") set_tests_properties(ListTests PROPERTIES PASS_REGULAR_EXPRESSION "[0-9]+ test cases")
add_test(NAME ListTags COMMAND $<TARGET_FILE:SelfTest> --list-tags) add_test(NAME ListTags COMMAND $<TARGET_FILE:SelfTest> --list-tags)
set_tests_properties(ListTags PROPERTIES PASS_REGULAR_EXPRESSION "[0-9]+ 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 # 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>) 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") set_tests_properties(ApprovalTests PROPERTIES FAIL_REGULAR_EXPRESSION "Results differed")
if (USE_VALGRIND) if (USE_VALGRIND)
add_test(NAME ValgrindRunTests COMMAND valgrind --leak-check=full --error-exitcode=1 $<TARGET_FILE:SelfTest>) 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") 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) 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") 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) add_subdirectory(examples)
endif() endif()
install(DIRECTORY "single_include/" DESTINATION "include/catch") install(DIRECTORY "single_include/" DESTINATION "include/catch")
## Provide some pkg-config integration ## Provide some pkg-config integration

View File

@ -2,8 +2,8 @@
![catch logo](artwork/catch2-logo-small.png) ![catch logo](artwork/catch2-logo-small.png)
[![Github Releases](https://img.shields.io/github/release/catchorg/catch2.svg)](https://github.com/catchorg/catch2/releases) [![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://travis-ci.org/catchorg/Catch2.svg?branch=master)](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://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) [![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> <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>

View File

@ -27,11 +27,10 @@ matrix:
init: init:
- git config --global core.autocrlf input - 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 install:
shallow_clone: true - 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. # Win32 and x64 are CMake-compatible solution platform names.
# This allows us to pass %PLATFORM% to CMake -A. # This allows us to pass %PLATFORM% to CMake -A.
@ -46,9 +45,12 @@ configuration:
#Cmake will autodetect the compiler, but we set the arch #Cmake will autodetect the compiler, but we set the arch
before_build: before_build:
- python scripts/generateSingleHeader.py
- set CXXFLAGS=%additional_flags% - 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 with MSBuild
build: build:
@ -57,5 +59,5 @@ build:
verbosity: normal # MSBuild verbosity level {quiet|minimal|normal|detailed} verbosity: normal # MSBuild verbosity level {quiet|minimal|normal|detailed}
test_script: test_script:
- cd Build - set CTEST_OUTPUT_ON_FAILURE=1
- ctest -V -j 2 -C %CONFIGURATION% - cmd: .\misc\appveyorTestRunScript.bat

10
codecov.yml Normal file
View 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"

View File

@ -87,6 +87,41 @@
# Older versions # 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.x
### 1.9.6 ### 1.9.6

View File

@ -295,13 +295,16 @@ void print( std::ostream& os, int const level, std::string const& title, Catch::
// 2. My listener and registration: // 2. My listener and registration:
// //
const std::string dashed_line = char const * dashed_line =
"--------------------------------------------------------------------------"; "--------------------------------------------------------------------------";
struct MyListener : Catch::TestEventListenerBase { struct MyListener : Catch::TestEventListenerBase {
using TestEventListenerBase::TestEventListenerBase; // inherit constructor using TestEventListenerBase::TestEventListenerBase; // inherit constructor
// Get rid of Wweak-tables
~MyListener();
// The whole test run starting // The whole test run starting
virtual void testRunStarting( Catch::TestRunInfo const& testRunInfo ) override { virtual void testRunStarting( Catch::TestRunInfo const& testRunInfo ) override {
std::cout std::cout
@ -367,6 +370,10 @@ struct MyListener : Catch::TestEventListenerBase {
CATCH_REGISTER_LISTENER( MyListener ) CATCH_REGISTER_LISTENER( MyListener )
// Get rid of Wweak-tables
MyListener::~MyListener() {}
// ----------------------------------------------------------------------- // -----------------------------------------------------------------------
// 3. Test cases: // 3. Test cases:
// //

View File

@ -13,8 +13,7 @@
#include "catch_debugger.h" #include "catch_debugger.h"
#include "catch_interfaces_registry_hub.h" #include "catch_interfaces_registry_hub.h"
#include "catch_capture_matchers.h" #include "catch_capture_matchers.h"
#include "catch_run_context.h"
#include <cassert>
namespace Catch { namespace Catch {
@ -54,87 +53,60 @@ namespace Catch {
SourceLineInfo const& lineInfo, SourceLineInfo const& lineInfo,
StringRef capturedExpression, StringRef capturedExpression,
ResultDisposition::Flags resultDisposition ) ResultDisposition::Flags resultDisposition )
: m_assertionInfo{ macroName, lineInfo, capturedExpression, resultDisposition } : m_assertionInfo{ macroName, lineInfo, capturedExpression, resultDisposition },
{ m_resultCapture( getResultCapture() )
getCurrentContext().getResultCapture()->assertionStarting( m_assertionInfo ); {}
AssertionHandler::~AssertionHandler() {
if ( !m_completed )
m_resultCapture.handleIncomplete( m_assertionInfo );
} }
void AssertionHandler::handle( ITransientExpression const& expr ) { void AssertionHandler::handleExpr( ITransientExpression const& expr ) {
m_resultCapture.handleExpr( m_assertionInfo, expr, m_reaction );
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::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 { auto AssertionHandler::allowThrows() const -> bool {
return getCurrentContext().getConfig()->allowThrows(); return getCurrentContext().getConfig()->allowThrows();
} }
auto AssertionHandler::shouldDebugBreak() const -> bool { void AssertionHandler::complete() {
return m_shouldDebugBreak; setCompleted();
} if( m_reaction.shouldDebugBreak ) {
void AssertionHandler::reactWithDebugBreak() const {
if (m_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 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 // (To go back to the test and change execution, jump over the throw, next)
///////////////////////////////////////////////////////////////////
CATCH_BREAK_INTO_DEBUGGER(); CATCH_BREAK_INTO_DEBUGGER();
} }
reactWithoutDebugBreak(); if( m_reaction.shouldThrow )
}
void AssertionHandler::reactWithoutDebugBreak() const {
if( m_shouldThrow )
throw Catch::TestFailureException(); throw Catch::TestFailureException();
} }
void AssertionHandler::setCompleted() {
void AssertionHandler::useActiveException() { m_completed = true;
handle( ResultWas::ThrewException, Catch::translateActiveException() );
} }
void AssertionHandler::setExceptionGuard() { void AssertionHandler::handleUnexpectedInflightException() {
assert( m_inExceptionGuard == false ); m_resultCapture.handleUnexpectedInflightException( m_assertionInfo, Catch::translateActiveException(), m_reaction );
m_inExceptionGuard = true;
} }
void AssertionHandler::unsetExceptionGuard() {
assert( m_inExceptionGuard == true ); void AssertionHandler::handleExceptionThrownAsExpected() {
m_inExceptionGuard = false; 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 // This is the overload that takes a string and infers the Equals matcher from it

View File

@ -17,10 +17,13 @@ namespace Catch {
struct TestFailureException{}; struct TestFailureException{};
struct AssertionResultData; struct AssertionResultData;
struct IResultCapture;
class RunContext;
class LazyExpression { class LazyExpression {
friend class AssertionHandler; friend class AssertionHandler;
friend struct AssertionStats; friend struct AssertionStats;
friend class RunContext;
ITransientExpression const* m_transientExpression = nullptr; ITransientExpression const* m_transientExpression = nullptr;
bool m_isNegated; bool m_isNegated;
@ -34,11 +37,16 @@ namespace Catch {
friend auto operator << ( std::ostream& os, LazyExpression const& lazyExpr ) -> std::ostream&; friend auto operator << ( std::ostream& os, LazyExpression const& lazyExpr ) -> std::ostream&;
}; };
struct AssertionReaction {
bool shouldDebugBreak = false;
bool shouldThrow = false;
};
class AssertionHandler { class AssertionHandler {
AssertionInfo m_assertionInfo; AssertionInfo m_assertionInfo;
bool m_shouldDebugBreak = false; AssertionReaction m_reaction;
bool m_shouldThrow = false; bool m_completed = false;
bool m_inExceptionGuard = false; IResultCapture& m_resultCapture;
public: public:
AssertionHandler AssertionHandler
@ -54,24 +62,25 @@ namespace Catch {
} }
} }
void handle( ITransientExpression const& expr );
template<typename T> template<typename T>
void handle( ExprLhs<T> const& expr ) { void handleExpr( ExprLhs<T> const& expr ) {
handle( expr.makeUnaryExpr() ); handleExpr( expr.makeUnaryExpr() );
} }
void handle( ResultWas::OfType resultType ); void handleExpr( ITransientExpression const& expr );
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 );
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; 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 ); void handleExceptionMatchExpr( AssertionHandler& handler, std::string const& str, StringRef matcherString );

View File

@ -11,7 +11,6 @@
#include "catch_assertionhandler.h" #include "catch_assertionhandler.h"
#include "catch_message.h" #include "catch_message.h"
#include "catch_interfaces_capture.h" #include "catch_interfaces_capture.h"
#include "catch_debugger.h"
#if !defined(CATCH_CONFIG_DISABLE) #if !defined(CATCH_CONFIG_DISABLE)
@ -22,48 +21,33 @@
#endif #endif
#if defined(CATCH_CONFIG_FAST_COMPILE) #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* // Another way to speed-up compilation is to omit local try-catch for REQUIRE*
// macros. // macros.
// This can potentially cause false negative, if the test code catches #define INTERNAL_CATCH_TRY
// the exception before it propagates back up to the runner. #define INTERNAL_CATCH_CATCH( capturer )
#define INTERNAL_CATCH_TRY( capturer ) capturer.setExceptionGuard();
#define INTERNAL_CATCH_CATCH( capturer ) capturer.unsetExceptionGuard();
#else // CATCH_CONFIG_FAST_COMPILE #else // CATCH_CONFIG_FAST_COMPILE
/////////////////////////////////////////////////////////////////////////////// #define INTERNAL_CATCH_TRY try
// In the event of a failure works out if the debugger needs to be invoked #define INTERNAL_CATCH_CATCH( handler ) catch(...) { handler.handleUnexpectedInflightException(); }
// 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(); }
#endif #endif
#define INTERNAL_CATCH_REACT( handler ) handler.complete();
/////////////////////////////////////////////////////////////////////////////// ///////////////////////////////////////////////////////////////////////////////
#define INTERNAL_CATCH_TEST( macroName, resultDisposition, ... ) \ #define INTERNAL_CATCH_TEST( macroName, resultDisposition, ... ) \
do { \ do { \
Catch::AssertionHandler catchAssertionHandler( macroName, CATCH_INTERNAL_LINEINFO, CATCH_INTERNAL_STRINGIFY(__VA_ARGS__), resultDisposition ); \ 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 \ CATCH_INTERNAL_SUPPRESS_PARENTHESES_WARNINGS \
catchAssertionHandler.handle( Catch::Decomposer() <= __VA_ARGS__ ); \ catchAssertionHandler.handleExpr( Catch::Decomposer() <= __VA_ARGS__ ); \
CATCH_INTERNAL_UNSUPPRESS_PARENTHESES_WARNINGS \ CATCH_INTERNAL_UNSUPPRESS_PARENTHESES_WARNINGS \
} INTERNAL_CATCH_CATCH( catchAssertionHandler ) \ } INTERNAL_CATCH_CATCH( catchAssertionHandler ) \
INTERNAL_CATCH_REACT( 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 &&. // 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 ); \ Catch::AssertionHandler catchAssertionHandler( macroName, CATCH_INTERNAL_LINEINFO, CATCH_INTERNAL_STRINGIFY(__VA_ARGS__), resultDisposition ); \
try { \ try { \
static_cast<void>(__VA_ARGS__); \ static_cast<void>(__VA_ARGS__); \
catchAssertionHandler.handle( Catch::ResultWas::Ok ); \ catchAssertionHandler.handleExceptionNotThrownAsExpected(); \
} \ } \
catch( ... ) { \ catch( ... ) { \
catchAssertionHandler.useActiveException(); \ catchAssertionHandler.handleUnexpectedInflightException(); \
} \ } \
INTERNAL_CATCH_REACT( catchAssertionHandler ) \ INTERNAL_CATCH_REACT( catchAssertionHandler ) \
} while( Catch::alwaysFalse() ) } while( false )
/////////////////////////////////////////////////////////////////////////////// ///////////////////////////////////////////////////////////////////////////////
#define INTERNAL_CATCH_THROWS( macroName, resultDisposition, ... ) \ #define INTERNAL_CATCH_THROWS( macroName, resultDisposition, ... ) \
@ -97,15 +81,15 @@
if( catchAssertionHandler.allowThrows() ) \ if( catchAssertionHandler.allowThrows() ) \
try { \ try { \
static_cast<void>(__VA_ARGS__); \ static_cast<void>(__VA_ARGS__); \
catchAssertionHandler.handle( Catch::ResultWas::DidntThrowException ); \ catchAssertionHandler.handleUnexpectedExceptionNotThrown(); \
} \ } \
catch( ... ) { \ catch( ... ) { \
catchAssertionHandler.handle( Catch::ResultWas::Ok ); \ catchAssertionHandler.handleExceptionThrownAsExpected(); \
} \ } \
else \ else \
catchAssertionHandler.handle( Catch::ResultWas::Ok ); \ catchAssertionHandler.handleThrowingCallSkipped(); \
INTERNAL_CATCH_REACT( catchAssertionHandler ) \ INTERNAL_CATCH_REACT( catchAssertionHandler ) \
} while( Catch::alwaysFalse() ) } while( false )
/////////////////////////////////////////////////////////////////////////////// ///////////////////////////////////////////////////////////////////////////////
#define INTERNAL_CATCH_THROWS_AS( macroName, exceptionType, resultDisposition, expr ) \ #define INTERNAL_CATCH_THROWS_AS( macroName, exceptionType, resultDisposition, expr ) \
@ -114,31 +98,31 @@
if( catchAssertionHandler.allowThrows() ) \ if( catchAssertionHandler.allowThrows() ) \
try { \ try { \
static_cast<void>(expr); \ static_cast<void>(expr); \
catchAssertionHandler.handle( Catch::ResultWas::DidntThrowException ); \ catchAssertionHandler.handleUnexpectedExceptionNotThrown(); \
} \ } \
catch( exceptionType const& ) { \ catch( exceptionType const& ) { \
catchAssertionHandler.handle( Catch::ResultWas::Ok ); \ catchAssertionHandler.handleExceptionThrownAsExpected(); \
} \ } \
catch( ... ) { \ catch( ... ) { \
catchAssertionHandler.useActiveException(); \ catchAssertionHandler.handleUnexpectedInflightException(); \
} \ } \
else \ else \
catchAssertionHandler.handle( Catch::ResultWas::Ok ); \ catchAssertionHandler.handleThrowingCallSkipped(); \
INTERNAL_CATCH_REACT( catchAssertionHandler ) \ INTERNAL_CATCH_REACT( catchAssertionHandler ) \
} while( Catch::alwaysFalse() ) } while( false )
/////////////////////////////////////////////////////////////////////////////// ///////////////////////////////////////////////////////////////////////////////
#define INTERNAL_CATCH_MSG( macroName, messageType, resultDisposition, ... ) \ #define INTERNAL_CATCH_MSG( macroName, messageType, resultDisposition, ... ) \
do { \ do { \
Catch::AssertionHandler catchAssertionHandler( macroName, CATCH_INTERNAL_LINEINFO, "", resultDisposition ); \ 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 ) \ INTERNAL_CATCH_REACT( catchAssertionHandler ) \
} while( Catch::alwaysFalse() ) } while( false )
/////////////////////////////////////////////////////////////////////////////// ///////////////////////////////////////////////////////////////////////////////
#define INTERNAL_CATCH_INFO( macroName, log ) \ #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 // Although this is matcher-based, it can be used with just a string
@ -148,15 +132,15 @@
if( catchAssertionHandler.allowThrows() ) \ if( catchAssertionHandler.allowThrows() ) \
try { \ try { \
static_cast<void>(__VA_ARGS__); \ static_cast<void>(__VA_ARGS__); \
catchAssertionHandler.handle( Catch::ResultWas::DidntThrowException ); \ catchAssertionHandler.handleUnexpectedExceptionNotThrown(); \
} \ } \
catch( ... ) { \ catch( ... ) { \
handleExceptionMatchExpr( catchAssertionHandler, matcher, #matcher ); \ Catch::handleExceptionMatchExpr( catchAssertionHandler, matcher, #matcher ); \
} \ } \
else \ else \
catchAssertionHandler.handle( Catch::ResultWas::Ok ); \ catchAssertionHandler.handleThrowingCallSkipped(); \
INTERNAL_CATCH_REACT( catchAssertionHandler ) \ INTERNAL_CATCH_REACT( catchAssertionHandler ) \
} while( Catch::alwaysFalse() ) } while( false )
#endif // CATCH_CONFIG_DISABLE #endif // CATCH_CONFIG_DISABLE

View File

@ -18,7 +18,7 @@ namespace Catch {
void handleExceptionMatchExpr( AssertionHandler& handler, StringMatcher const& matcher, StringRef matcherString ) { void handleExceptionMatchExpr( AssertionHandler& handler, StringMatcher const& matcher, StringRef matcherString ) {
std::string exceptionMessage = Catch::translateActiveException(); std::string exceptionMessage = Catch::translateActiveException();
MatchExpr<std::string, StringMatcher const&> expr( exceptionMessage, matcher, matcherString ); MatchExpr<std::string, StringMatcher const&> expr( exceptionMessage, matcher, matcherString );
handler.handle( expr ); handler.handleExpr( expr );
} }
} // namespace Catch } // namespace Catch

View File

@ -21,18 +21,14 @@ namespace Catch {
ArgT const& m_arg; ArgT const& m_arg;
MatcherT m_matcher; MatcherT m_matcher;
StringRef m_matcherString; StringRef m_matcherString;
bool m_result;
public: public:
MatchExpr( ArgT const& arg, MatcherT const& matcher, StringRef matcherString ) MatchExpr( ArgT const& arg, MatcherT const& matcher, StringRef matcherString )
: m_arg( arg ), : ITransientExpression{ true, matcher.match( arg ) },
m_arg( arg ),
m_matcher( matcher ), m_matcher( matcher ),
m_matcherString( matcherString ), m_matcherString( matcherString )
m_result( matcher.match( arg ) )
{} {}
auto isBinaryExpression() const -> bool override { return true; }
auto getResult() const -> bool override { return m_result; }
void streamReconstructedExpression( std::ostream &os ) const override { void streamReconstructedExpression( std::ostream &os ) const override {
auto matcherAsString = m_matcher.toString(); auto matcherAsString = m_matcher.toString();
os << Catch::Detail::stringify( m_arg ) << ' '; os << Catch::Detail::stringify( m_arg ) << ' ';
@ -59,11 +55,11 @@ namespace Catch {
#define INTERNAL_CHECK_THAT( macroName, matcher, resultDisposition, arg ) \ #define INTERNAL_CHECK_THAT( macroName, matcher, resultDisposition, arg ) \
do { \ do { \
Catch::AssertionHandler catchAssertionHandler( macroName, CATCH_INTERNAL_LINEINFO, CATCH_INTERNAL_STRINGIFY(arg) ", " CATCH_INTERNAL_STRINGIFY(matcher), resultDisposition ); \ Catch::AssertionHandler catchAssertionHandler( macroName, CATCH_INTERNAL_LINEINFO, CATCH_INTERNAL_STRINGIFY(arg) ", " CATCH_INTERNAL_STRINGIFY(matcher), resultDisposition ); \
INTERNAL_CATCH_TRY( catchAssertionHandler ) { \ INTERNAL_CATCH_TRY { \
catchAssertionHandler.handle( Catch::makeMatchExpr( arg, matcher, #matcher ) ); \ catchAssertionHandler.handleExpr( Catch::makeMatchExpr( arg, matcher, #matcher ) ); \
} INTERNAL_CATCH_CATCH( catchAssertionHandler ) \ } INTERNAL_CATCH_CATCH( catchAssertionHandler ) \
INTERNAL_CATCH_REACT( catchAssertionHandler ) \ INTERNAL_CATCH_REACT( catchAssertionHandler ) \
} while( Catch::alwaysFalse() ) } while( false )
/////////////////////////////////////////////////////////////////////////////// ///////////////////////////////////////////////////////////////////////////////
@ -73,17 +69,17 @@ namespace Catch {
if( catchAssertionHandler.allowThrows() ) \ if( catchAssertionHandler.allowThrows() ) \
try { \ try { \
static_cast<void>(__VA_ARGS__ ); \ static_cast<void>(__VA_ARGS__ ); \
catchAssertionHandler.handle( Catch::ResultWas::DidntThrowException ); \ catchAssertionHandler.handleUnexpectedExceptionNotThrown(); \
} \ } \
catch( exceptionType const& ex ) { \ catch( exceptionType const& ex ) { \
catchAssertionHandler.handle( Catch::makeMatchExpr( ex, matcher, #matcher ) ); \ catchAssertionHandler.handleExpr( Catch::makeMatchExpr( ex, matcher, #matcher ) ); \
} \ } \
catch( ... ) { \ catch( ... ) { \
catchAssertionHandler.useActiveException(); \ catchAssertionHandler.handleUnexpectedInflightException(); \
} \ } \
else \ else \
catchAssertionHandler.handle( Catch::ResultWas::Ok ); \ catchAssertionHandler.handleThrowingCallSkipped(); \
INTERNAL_CATCH_REACT( catchAssertionHandler ) \ INTERNAL_CATCH_REACT( catchAssertionHandler ) \
} while( Catch::alwaysFalse() ) } while( false )
#endif // TWOBLUECUBES_CATCH_CAPTURE_MATCHERS_HPP_INCLUDED #endif // TWOBLUECUBES_CATCH_CAPTURE_MATCHERS_HPP_INCLUDED

View File

@ -34,9 +34,6 @@ namespace Catch {
return os; return os;
} }
bool alwaysTrue() { return true; }
bool alwaysFalse() { return false; }
std::string StreamEndStop::operator+() const { std::string StreamEndStop::operator+() const {
return std::string(); return std::string();
} }

View File

@ -61,12 +61,6 @@ namespace Catch {
std::ostream& operator << ( std::ostream& os, SourceLineInfo const& info ); 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 // Use this in variadic streaming macros to allow
// >> +StreamEndStop // >> +StreamEndStop
// as well as // as well as

View File

@ -40,7 +40,7 @@ namespace Catch {
#ifdef CATCH_TRAP #ifdef CATCH_TRAP
#define CATCH_BREAK_INTO_DEBUGGER() if( Catch::isDebuggerActive() ) { CATCH_TRAP(); } #define CATCH_BREAK_INTO_DEBUGGER() if( Catch::isDebuggerActive() ) { CATCH_TRAP(); }
#else #else
#define CATCH_BREAK_INTO_DEBUGGER() Catch::alwaysTrue(); #define CATCH_BREAK_INTO_DEBUGGER() (void)0, 0
#endif #endif
#endif // TWOBLUECUBES_CATCH_DEBUGGER_H_INCLUDED #endif // TWOBLUECUBES_CATCH_DEBUGGER_H_INCLUDED

View File

@ -24,27 +24,32 @@
namespace Catch { namespace Catch {
struct ITransientExpression { struct ITransientExpression {
virtual auto isBinaryExpression() const -> bool = 0; auto isBinaryExpression() const -> bool { return m_isBinaryExpression; }
virtual auto getResult() const -> bool = 0; auto getResult() const -> bool { return m_result; }
virtual void streamReconstructedExpression( std::ostream &os ) const = 0; 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 :-( // complain if it's not here :-(
virtual ~ITransientExpression(); virtual ~ITransientExpression();
bool m_isBinaryExpression;
bool m_result;
}; };
void formatReconstructedExpression( std::ostream &os, std::string const& lhs, StringRef op, std::string const& rhs ); void formatReconstructedExpression( std::ostream &os, std::string const& lhs, StringRef op, std::string const& rhs );
template<typename LhsT, typename RhsT> template<typename LhsT, typename RhsT>
class BinaryExpr : public ITransientExpression { class BinaryExpr : public ITransientExpression {
bool m_result;
LhsT m_lhs; LhsT m_lhs;
StringRef m_op; StringRef m_op;
RhsT m_rhs; 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 { void streamReconstructedExpression( std::ostream &os ) const override {
formatReconstructedExpression formatReconstructedExpression
( os, Catch::Detail::stringify( m_lhs ), m_op, Catch::Detail::stringify( m_rhs ) ); ( os, Catch::Detail::stringify( m_lhs ), m_op, Catch::Detail::stringify( m_rhs ) );
@ -52,7 +57,7 @@ namespace Catch {
public: public:
BinaryExpr( bool comparisonResult, LhsT lhs, StringRef op, RhsT rhs ) BinaryExpr( bool comparisonResult, LhsT lhs, StringRef op, RhsT rhs )
: m_result( comparisonResult ), : ITransientExpression{ true, comparisonResult },
m_lhs( lhs ), m_lhs( lhs ),
m_op( op ), m_op( op ),
m_rhs( rhs ) m_rhs( rhs )
@ -63,15 +68,15 @@ namespace Catch {
class UnaryExpr : public ITransientExpression { class UnaryExpr : public ITransientExpression {
LhsT m_lhs; 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 { void streamReconstructedExpression( std::ostream &os ) const override {
os << Catch::Detail::stringify( m_lhs ); os << Catch::Detail::stringify( m_lhs );
} }
public: 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 { class ExprLhs {
LhsT m_lhs; LhsT m_lhs;
public: public:
ExprLhs( LhsT lhs ) : m_lhs( lhs ) {} explicit ExprLhs( LhsT lhs ) : m_lhs( lhs ) {}
template<typename RhsT> template<typename RhsT>
auto operator == ( RhsT const& rhs ) -> BinaryExpr<LhsT, RhsT const&> const { 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 { 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> template<typename RhsT>
auto operator != ( RhsT const& rhs ) -> BinaryExpr<LhsT, RhsT const&> const { 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 { 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> template<typename RhsT>
auto operator > ( RhsT const& rhs ) -> BinaryExpr<LhsT, RhsT const&> const { 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> template<typename RhsT>
auto operator < ( RhsT const& rhs ) -> BinaryExpr<LhsT, RhsT const&> const { 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> template<typename RhsT>
auto operator >= ( RhsT const& rhs ) -> BinaryExpr<LhsT, RhsT const&> const { 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> template<typename RhsT>
auto operator <= ( RhsT const& rhs ) -> BinaryExpr<LhsT, RhsT const&> const { 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> { auto makeUnaryExpr() const -> UnaryExpr<LhsT> {
return UnaryExpr<LhsT>( m_lhs ); return UnaryExpr<LhsT>{ m_lhs };
} }
}; };
@ -153,10 +158,11 @@ namespace Catch {
struct Decomposer { struct Decomposer {
template<typename T> template<typename T>
auto operator <= ( T const& lhs ) -> ExprLhs<T const&> { auto operator <= ( T const& lhs ) -> ExprLhs<T const&> {
return ExprLhs<T const&>( lhs ); return ExprLhs<T const&>{ lhs };
} }
auto operator <=( bool value ) -> ExprLhs<bool> { auto operator <=( bool value ) -> ExprLhs<bool> {
return ExprLhs<bool>( value ); return ExprLhs<bool>{ value };
} }
}; };

View File

@ -12,6 +12,11 @@
#include "catch_context.h" #include "catch_context.h"
#include "catch_interfaces_capture.h" #include "catch_interfaces_capture.h"
#if defined(__GNUC__)
# pragma GCC diagnostic push
# pragma GCC diagnostic ignored "-Wmissing-field-initializers"
#endif
namespace { namespace {
// Report the error condition // Report the error condition
void reportFatal( char const * const message ) { void reportFatal( char const * const message ) {
@ -174,3 +179,7 @@ namespace Catch {
# endif // CATCH_CONFIG_POSIX_SIGNALS # endif // CATCH_CONFIG_POSIX_SIGNALS
#endif // not Windows #endif // not Windows
#if defined(__GNUC__)
# pragma GCC diagnostic pop
#endif

View File

@ -11,6 +11,7 @@
#include <string> #include <string>
#include "catch_stringref.h" #include "catch_stringref.h"
#include "catch_result_type.h"
namespace Catch { namespace Catch {
@ -22,13 +23,14 @@ namespace Catch {
struct Counts; struct Counts;
struct BenchmarkInfo; struct BenchmarkInfo;
struct BenchmarkStats; struct BenchmarkStats;
struct AssertionReaction;
struct ITransientExpression;
struct IResultCapture { struct IResultCapture {
virtual ~IResultCapture(); virtual ~IResultCapture();
virtual void assertionStarting( AssertionInfo const& info ) = 0;
virtual void assertionEnded( AssertionResult const& result ) = 0;
virtual bool sectionStarted( SectionInfo const& sectionInfo, virtual bool sectionStarted( SectionInfo const& sectionInfo,
Counts& assertions ) = 0; Counts& assertions ) = 0;
virtual void sectionEnded( SectionEndInfo const& endInfo ) = 0; virtual void sectionEnded( SectionEndInfo const& endInfo ) = 0;
@ -40,16 +42,40 @@ namespace Catch {
virtual void pushScopedMessage( MessageInfo const& message ) = 0; virtual void pushScopedMessage( MessageInfo const& message ) = 0;
virtual void popScopedMessage( 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 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 bool lastAssertionPassed() = 0;
virtual void assertionPassed() = 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(); IResultCapture& getResultCapture();

View File

@ -73,7 +73,9 @@ namespace Catch {
/////////////////////////////////////////////////////////////////////////////// ///////////////////////////////////////////////////////////////////////////////
#define INTERNAL_CATCH_TRANSLATE_EXCEPTION2( translatorName, signature ) \ #define INTERNAL_CATCH_TRANSLATE_EXCEPTION2( translatorName, signature ) \
static std::string 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 ) static std::string translatorName( signature )
#define INTERNAL_CATCH_TRANSLATE_EXCEPTION( signature ) INTERNAL_CATCH_TRANSLATE_EXCEPTION2( INTERNAL_CATCH_UNIQUE_NAME( catch_internal_ExceptionTranslator ), signature ) #define INTERNAL_CATCH_TRANSLATE_EXCEPTION( signature ) INTERNAL_CATCH_TRANSLATE_EXCEPTION2( INTERNAL_CATCH_UNIQUE_NAME( catch_internal_ExceptionTranslator ), signature )

View File

@ -8,11 +8,11 @@
#include "catch_leak_detector.h" #include "catch_leak_detector.h"
namespace Catch {
#ifdef CATCH_CONFIG_WINDOWS_CRTDBG #ifdef CATCH_CONFIG_WINDOWS_CRTDBG
#include <crtdbg.h> #include <crtdbg.h>
namespace Catch {
LeakDetector::LeakDetector() { LeakDetector::LeakDetector() {
int flag = _CrtSetDbgFlag(_CRTDBG_REPORT_FLAG); int flag = _CrtSetDbgFlag(_CRTDBG_REPORT_FLAG);
flag |= _CRTDBG_LEAK_CHECK_DF; flag |= _CRTDBG_LEAK_CHECK_DF;
@ -23,11 +23,10 @@ namespace Catch {
// Change this to leaking allocation's number to break there // Change this to leaking allocation's number to break there
_CrtSetBreakAlloc(-1); _CrtSetBreakAlloc(-1);
} }
}
#else #else
LeakDetector::LeakDetector(){} Catch::LeakDetector::LeakDetector() {}
#endif #endif
}

View File

@ -79,7 +79,11 @@ namespace Catch {
namespace Matchers { namespace Matchers {
namespace Floating { namespace Floating {
WithinAbsMatcher::WithinAbsMatcher(double target, double margin) 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 // Performs equivalent check of std::fabs(lhs - rhs) <= margin
// But without the subtraction to allow for INFINITY in comparison // But without the subtraction to allow for INFINITY in comparison
@ -95,7 +99,7 @@ namespace Floating {
WithinUlpsMatcher::WithinUlpsMatcher(double target, int ulps, FloatingPointKind baseType) WithinUlpsMatcher::WithinUlpsMatcher(double target, int ulps, FloatingPointKind baseType)
:m_target{ target }, m_ulps{ ulps }, m_type{ baseType } { :m_target{ target }, m_ulps{ ulps }, m_type{ baseType } {
if (m_ulps < 0) { 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");
} }
} }

View File

@ -10,10 +10,33 @@
#include "catch_matchers.h" #include "catch_matchers.h"
#include <algorithm>
namespace Catch { namespace Catch {
namespace Matchers { namespace Matchers {
namespace Vector { 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> template<typename T>
struct ContainsElementMatcher : MatcherBase<std::vector<T>> { struct ContainsElementMatcher : MatcherBase<std::vector<T>> {
@ -89,6 +112,46 @@ namespace Matchers {
std::vector<T> const& m_comparator; 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 } // namespace Vector
// The following functions create the actual matcher objects. // The following functions create the actual matcher objects.
@ -109,6 +172,11 @@ namespace Matchers {
return Vector::EqualsMatcher<T>( comparator ); 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 Matchers
} // namespace Catch } // namespace Catch

View File

@ -49,11 +49,18 @@ namespace Catch {
getResultCapture().pushScopedMessage( m_info ); 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() { ScopedMessage::~ScopedMessage() {
if ( !std::uncaught_exception() ){ if ( !std::uncaught_exception() ){
getResultCapture().popScopedMessage(m_info); getResultCapture().popScopedMessage(m_info);
} }
} }
#if defined(_MSC_VER)
#pragma warning(pop)
#endif
} // end namespace Catch } // end namespace Catch

View File

@ -59,7 +59,7 @@ namespace Catch {
class ScopedMessage { class ScopedMessage {
public: public:
ScopedMessage( MessageBuilder const& builder ); explicit ScopedMessage( MessageBuilder const& builder );
~ScopedMessage(); ~ScopedMessage();
MessageInfo m_info; MessageInfo m_info;

View File

@ -87,6 +87,7 @@ namespace Catch {
delete getTheRegistryHub(); delete getTheRegistryHub();
getTheRegistryHub() = nullptr; getTheRegistryHub() = nullptr;
cleanUpContext(); cleanUpContext();
ReusableStringStream::cleanup();
} }
std::string translateActiveException() { std::string translateActiveException() {
return getRegistryHub().getExceptionTranslatorRegistry().translateActiveException(); return getRegistryHub().getExceptionTranslatorRegistry().translateActiveException();

View File

@ -29,7 +29,7 @@ namespace Catch {
public: public:
ReporterRegistrar( std::string const& name ) { explicit ReporterRegistrar( std::string const& name ) {
getMutableRegistryHub().registerReporter( name, std::make_shared<ReporterFactory>() ); getMutableRegistryHub().registerReporter( name, std::make_shared<ReporterFactory>() );
} }
}; };

View File

@ -10,37 +10,55 @@
namespace Catch { namespace Catch {
StreamRedirect::StreamRedirect(std::ostream& stream, std::string& targetString) class RedirectedStream {
: m_stream(stream), std::ostream& m_originalStream;
m_prevBuf(stream.rdbuf()), std::ostream& m_redirectionStream;
m_targetString(targetString) { std::streambuf* m_prevBuf;
stream.rdbuf(m_oss.get().rdbuf());
}
StreamRedirect::~StreamRedirect() { public:
m_targetString += m_oss.str(); RedirectedStream( std::ostream& originalStream, std::ostream& redirectionStream )
m_stream.rdbuf(m_prevBuf); : 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) class RedirectedStdOut {
:m_cerrBuf(cerr().rdbuf()), m_clogBuf(clog().rdbuf()), ReusableStringStream m_rss;
m_targetString(targetString) { RedirectedStream m_cout;
cerr().rdbuf(m_oss.get().rdbuf()); public:
clog().rdbuf(m_oss.get().rdbuf()); 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) RunContext::RunContext(IConfigPtr const& _config, IStreamingReporterPtr&& reporter)
: m_runInfo(_config->name()), : m_runInfo(_config->name()),
m_context(getCurrentMutableContext()), m_context(getCurrentMutableContext()),
m_config(_config), m_config(_config),
m_reporter(std::move(reporter)), 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.setRunner(this);
m_context.setConfig(m_config); m_context.setConfig(m_config);
@ -109,18 +127,20 @@ namespace Catch {
return *m_reporter; return *m_reporter;
} }
void RunContext::assertionStarting(AssertionInfo const& info) {
m_reporter->assertionStarting( info );
}
void RunContext::assertionEnded(AssertionResult const & result) { void RunContext::assertionEnded(AssertionResult const & result) {
if (result.getResultType() == ResultWas::Ok) { if (result.getResultType() == ResultWas::Ok) {
m_totals.assertions.passed++; m_totals.assertions.passed++;
m_lastAssertionPassed = true;
} else if (!result.isOk()) { } else if (!result.isOk()) {
m_lastAssertionPassed = false;
if( m_activeTestCase->getTestCaseInfo().okToFail() ) if( m_activeTestCase->getTestCaseInfo().okToFail() )
m_totals.assertions.failedButOk++; m_totals.assertions.failedButOk++;
else else
m_totals.assertions.failed++; 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 // 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. // and should be let to clear themselves out.
@ -223,7 +243,7 @@ namespace Catch {
tempResult.message = message; tempResult.message = message;
AssertionResult result(m_lastAssertionInfo, tempResult); AssertionResult result(m_lastAssertionInfo, tempResult);
getResultCapture().assertionEnded(result); assertionEnded(result);
handleUnfinishedSections(); handleUnfinishedSections();
@ -252,18 +272,15 @@ namespace Catch {
} }
bool RunContext::lastAssertionPassed() { bool RunContext::lastAssertionPassed() {
return m_totals.assertions.passed == (m_prevPassed + 1); return m_lastAssertionPassed;
} }
void RunContext::assertionPassed() { void RunContext::assertionPassed() {
m_lastAssertionPassed = true;
++m_totals.assertions.passed; ++m_totals.assertions.passed;
resetAssertionInfo(); resetAssertionInfo();
} }
void RunContext::assertionRun() {
m_prevPassed = m_totals.assertions.passed;
}
bool RunContext::aborting() const { bool RunContext::aborting() const {
return m_totals.assertions.failed == static_cast<std::size_t>(m_config->abortAfter()); return m_totals.assertions.failed == static_cast<std::size_t>(m_config->abortAfter());
} }
@ -282,10 +299,13 @@ namespace Catch {
Timer timer; Timer timer;
try { try {
if (m_reporter->getPreferences().shouldRedirectStdOut) { if (m_reporter->getPreferences().shouldRedirectStdOut) {
StreamRedirect coutRedir(cout(), redirectedCout); RedirectedStdOut redirectedStdOut;
StdErrRedirect errRedir(redirectedCerr); RedirectedStdErr redirectedStdErr;
timer.start(); timer.start();
invokeActiveTestCase(); invokeActiveTestCase();
redirectedCout += redirectedStdOut.str();
redirectedCerr += redirectedStdErr.str();
} else { } else {
timer.start(); timer.start();
invokeActiveTestCase(); invokeActiveTestCase();
@ -296,12 +316,9 @@ namespace Catch {
} catch (...) { } catch (...) {
// Under CATCH_CONFIG_FAST_COMPILE, unexpected exceptions under REQUIRE assertions // Under CATCH_CONFIG_FAST_COMPILE, unexpected exceptions under REQUIRE assertions
// are reported without translation at the point of origin. // are reported without translation at the point of origin.
if (m_shouldReportUnexpected) { if( m_shouldReportUnexpected ) {
AssertionHandler AssertionReaction dummyReaction;
( m_lastAssertionInfo.macroName, handleUnexpectedInflightException( m_lastAssertionInfo, translateActiveException(), dummyReaction );
m_lastAssertionInfo.lineInfo,
m_lastAssertionInfo.capturedExpression,
m_lastAssertionInfo.resultDisposition ).useActiveException();
} }
} }
m_testCaseTracker->close(); m_testCaseTracker->close();
@ -331,6 +348,113 @@ namespace Catch {
m_unfinishedSections.clear(); 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() { IResultCapture& getResultCapture() {
if (auto* capture = getCurrentContext().getResultCapture()) if (auto* capture = getCurrentContext().getResultCapture())
return *capture; return *capture;

View File

@ -28,33 +28,6 @@ namespace Catch {
struct IMutableContext; 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 { class RunContext : public IResultCapture, public IRunner {
@ -63,35 +36,54 @@ namespace Catch {
RunContext( RunContext const& ) = delete; RunContext( RunContext const& ) = delete;
RunContext& operator =( 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 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 testGroupEnded( std::string const& testSpec, Totals const& totals, std::size_t groupIndex, std::size_t groupsCount );
Totals runTest(TestCase const& testCase); Totals runTest(TestCase const& testCase);
IConfigPtr config() const; IConfigPtr config() const;
IStreamingReporter& reporter() const; IStreamingReporter& reporter() const;
private: // IResultCapture public: // IResultCapture
// Assertion handlers
void assertionStarting(AssertionInfo const& info) override; void handleExpr
void assertionEnded(AssertionResult const& result) override; ( 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 sectionStarted( SectionInfo const& sectionInfo, Counts& assertions ) override;
bool testForMissingAssertions(Counts& assertions);
void sectionEnded(SectionEndInfo const& endInfo) override; void sectionEnded( SectionEndInfo const& endInfo ) override;
void sectionEndedEarly(SectionEndInfo const& endInfo) override; void sectionEndedEarly( SectionEndInfo const& endInfo ) override;
void benchmarkStarting( BenchmarkInfo const& info ) override; void benchmarkStarting( BenchmarkInfo const& info ) override;
void benchmarkEnded( BenchmarkStats const& stats ) override; void benchmarkEnded( BenchmarkStats const& stats ) override;
void pushScopedMessage(MessageInfo const& message) override; void pushScopedMessage( MessageInfo const& message ) override;
void popScopedMessage(MessageInfo const& message) override; void popScopedMessage( MessageInfo const& message ) override;
std::string getCurrentTestName() const override; std::string getCurrentTestName() const override;
@ -105,18 +97,26 @@ namespace Catch {
void assertionPassed() override; void assertionPassed() override;
void assertionRun() override;
public: public:
// !TBD We need to do this another way! // !TBD We need to do this another way!
bool aborting() const override; bool aborting() const override;
private: private:
void runCurrentTest(std::string& redirectedCout, std::string& redirectedCerr); void runCurrentTest( std::string& redirectedCout, std::string& redirectedCerr );
void invokeActiveTestCase(); void invokeActiveTestCase();
void resetAssertionInfo(); 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: private:
@ -136,12 +136,11 @@ namespace Catch {
std::vector<SectionEndInfo> m_unfinishedSections; std::vector<SectionEndInfo> m_unfinishedSections;
std::vector<ITracker*> m_activeSections; std::vector<ITracker*> m_activeSections;
TrackerContext m_trackerContext; TrackerContext m_trackerContext;
std::size_t m_prevPassed = 0; bool m_lastAssertionPassed = false;
bool m_shouldReportUnexpected = true; bool m_shouldReportUnexpected = true;
bool m_includeSuccessfulResults;
}; };
IResultCapture& getResultCapture();
} // end namespace Catch } // end namespace Catch
#endif // TWOBLUECUBES_CATCH_RUNNER_IMPL_HPP_INCLUDED #endif // TWOBLUECUBES_CATCH_RUNNER_IMPL_HPP_INCLUDED

View File

@ -22,12 +22,10 @@
#include <cstdlib> #include <cstdlib>
#include <iomanip> #include <iomanip>
namespace Catch {
namespace { namespace {
const int MaxExitCode = 255; const int MaxExitCode = 255;
using Catch::IStreamingReporterPtr;
using Catch::IConfigPtr;
using Catch::Config;
IStreamingReporterPtr createReporter(std::string const& reporterName, IConfigPtr const& config) { IStreamingReporterPtr createReporter(std::string const& reporterName, IConfigPtr const& config) {
auto reporter = Catch::getRegistryHub().getReporterRegistry().create(reporterName, config); auto reporter = Catch::getRegistryHub().getReporterRegistry().create(reporterName, config);
@ -61,7 +59,6 @@ namespace {
Catch::Totals runTests(std::shared_ptr<Config> const& config) { Catch::Totals runTests(std::shared_ptr<Config> const& config) {
using namespace Catch;
IStreamingReporterPtr reporter = makeReporter(config); IStreamingReporterPtr reporter = makeReporter(config);
addListeners(reporter, config); addListeners(reporter, config);
@ -88,7 +85,6 @@ namespace {
} }
void applyFilenamesAsTags(Catch::IConfig const& config) { void applyFilenamesAsTags(Catch::IConfig const& config) {
using namespace Catch;
auto& tests = const_cast<std::vector<TestCase>&>(getAllTestCasesSorted(config)); auto& tests = const_cast<std::vector<TestCase>&>(getAllTestCasesSorted(config));
for (auto& testCase : tests) { for (auto& testCase : tests) {
auto tags = testCase.tags; auto tags = testCase.tags;
@ -110,9 +106,7 @@ namespace {
} }
} }
} } // anon namespace
namespace Catch {
Session::Session() { Session::Session() {
static bool alreadyInstantiated = false; static bool alreadyInstantiated = false;

View File

@ -145,6 +145,7 @@ namespace Catch {
std::vector<std::unique_ptr<std::ostringstream>> m_streams; std::vector<std::unique_ptr<std::ostringstream>> m_streams;
std::vector<std::size_t> m_unused; std::vector<std::size_t> m_unused;
std::ostringstream m_referenceStream; // Used for copy state/ flags from std::ostringstream m_referenceStream; // Used for copy state/ flags from
static StringStreams* s_instance;
auto add() -> std::size_t { auto add() -> std::size_t {
if( m_unused.empty() ) { if( m_unused.empty() ) {
@ -165,11 +166,21 @@ namespace Catch {
// !TBD: put in TLS // !TBD: put in TLS
static auto instance() -> StringStreams& { static auto instance() -> StringStreams& {
static StringStreams s_stringStreams; if( !s_instance )
return s_stringStreams; 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() ReusableStringStream::ReusableStringStream()
: m_index( StringStreams::instance().add() ), : m_index( StringStreams::instance().add() ),

View File

@ -43,6 +43,8 @@ namespace Catch {
return *this; return *this;
} }
auto get() -> std::ostream& { return *m_oss; } auto get() -> std::ostream& { return *m_oss; }
static void cleanup();
}; };
} }

View File

@ -10,9 +10,6 @@
# pragma warning(push) # pragma warning(push)
# pragma warning(disable: 161 1682) # pragma warning(disable: 161 1682)
# else // __ICC # 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 ignored "-Wunused-variable"
# pragma clang diagnostic push # pragma clang diagnostic push
# pragma clang diagnostic ignored "-Wpadded" # pragma clang diagnostic ignored "-Wpadded"
@ -20,10 +17,8 @@
# pragma clang diagnostic ignored "-Wcovered-switch-default" # pragma clang diagnostic ignored "-Wcovered-switch-default"
# endif # endif
#elif defined __GNUC__ #elif defined __GNUC__
# pragma GCC diagnostic ignored "-Wvariadic-macros"
# pragma GCC diagnostic ignored "-Wunused-variable" # pragma GCC diagnostic ignored "-Wunused-variable"
# pragma GCC diagnostic ignored "-Wparentheses" # pragma GCC diagnostic ignored "-Wparentheses"
# pragma GCC diagnostic push # pragma GCC diagnostic push
# pragma GCC diagnostic ignored "-Wpadded" # pragma GCC diagnostic ignored "-Wpadded"
#endif #endif

View File

@ -17,6 +17,9 @@ namespace Catch {
} // end 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 #endif // TWOBLUECUBES_CATCH_TAG_ALIAS_AUTOREGISTRAR_H_INCLUDED

View File

@ -45,11 +45,11 @@ namespace Catch {
void Timer::start() { void Timer::start() {
m_nanoseconds = getCurrentNanosecondsSinceEpoch(); m_nanoseconds = getCurrentNanosecondsSinceEpoch();
} }
auto Timer::getElapsedNanoseconds() const -> unsigned int { auto Timer::getElapsedNanoseconds() const -> uint64_t {
return static_cast<unsigned int>(getCurrentNanosecondsSinceEpoch() - m_nanoseconds); return getCurrentNanosecondsSinceEpoch() - m_nanoseconds;
} }
auto Timer::getElapsedMicroseconds() const -> unsigned int { auto Timer::getElapsedMicroseconds() const -> uint64_t {
return static_cast<unsigned int>(getElapsedNanoseconds()/1000); return getElapsedNanoseconds()/1000;
} }
auto Timer::getElapsedMilliseconds() const -> unsigned int { auto Timer::getElapsedMilliseconds() const -> unsigned int {
return static_cast<unsigned int>(getElapsedMicroseconds()/1000); return static_cast<unsigned int>(getElapsedMicroseconds()/1000);

View File

@ -19,8 +19,8 @@ namespace Catch {
uint64_t m_nanoseconds = 0; uint64_t m_nanoseconds = 0;
public: public:
void start(); void start();
auto getElapsedNanoseconds() const -> unsigned int; auto getElapsedNanoseconds() const -> uint64_t;
auto getElapsedMicroseconds() const -> unsigned int; auto getElapsedMicroseconds() const -> uint64_t;
auto getElapsedMilliseconds() const -> unsigned int; auto getElapsedMilliseconds() const -> unsigned int;
auto getElapsedSeconds() const -> double; auto getElapsedSeconds() const -> double;
}; };

View File

@ -210,7 +210,7 @@ class Duration {
Unit m_units; Unit m_units;
public: public:
Duration(uint64_t inNanoseconds, Unit units = Unit::Auto) explicit Duration(uint64_t inNanoseconds, Unit units = Unit::Auto)
: m_inNanoseconds(inNanoseconds), : m_inNanoseconds(inNanoseconds),
m_units(units) { m_units(units) {
if (m_units == Unit::Auto) { if (m_units == Unit::Auto) {
@ -273,9 +273,9 @@ class TablePrinter {
bool m_isOpen = false; bool m_isOpen = false;
public: public:
TablePrinter(std::ostream& os, std::vector<ColumnInfo> const& columnInfos) TablePrinter( std::ostream& os, std::vector<ColumnInfo> columnInfos )
: m_os(os), : m_os( os ),
m_columnInfos(columnInfos) {} m_columnInfos( std::move( columnInfos ) ) {}
auto columnInfos() const -> std::vector<ColumnInfo> const& { auto columnInfos() const -> std::vector<ColumnInfo> const& {
return m_columnInfos; return m_columnInfos;
@ -346,7 +346,8 @@ ConsoleReporter::ConsoleReporter(ReporterConfig const& config)
{ "elapsed ns", 14, ColumnInfo::Right }, { "elapsed ns", 14, ColumnInfo::Right },
{ "average", 14, ColumnInfo::Right } { "average", 14, ColumnInfo::Right }
})) {} })) {}
ConsoleReporter::~ConsoleReporter() {} ConsoleReporter::~ConsoleReporter() = default;
std::string ConsoleReporter::getDescription() { std::string ConsoleReporter::getDescription() {
return "Reports test results as plain lines of text"; 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) { void ConsoleReporter::benchmarkStarting(BenchmarkInfo const& info) {
lazyPrintWithoutClosingBenchmarkTable(); 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; bool firstLine = true;
for (auto line : nameCol) { for (auto line : nameCol) {
@ -528,10 +529,10 @@ void ConsoleReporter::printHeaderString(std::string const& _string, std::size_t
struct SummaryColumn { struct SummaryColumn {
SummaryColumn(std::string const& _label, Colour::Code _colour) SummaryColumn( std::string _label, Colour::Code _colour )
: label(_label), : label( std::move( _label ) ),
colour(_colour) {} colour( _colour ) {}
SummaryColumn addRow(std::size_t count) { SummaryColumn addRow( std::size_t count ) {
ReusableStringStream rss; ReusableStringStream rss;
rss << count; rss << count;
std::string row = rss.str(); 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) { if (totals.testCases.total() == 0) {
stream << Colour(Colour::Warning) << "No tests ran\n"; stream << Colour(Colour::Warning) << "No tests ran\n";
} else if (totals.assertions.total() > 0 && totals.testCases.allPassed()) { } else if (totals.assertions.total() > 0 && totals.testCases.allPassed()) {

View File

@ -14,8 +14,6 @@
// file can be distributed as a single header that works with the main // file can be distributed as a single header that works with the main
// Catch single header. // Catch single header.
#include "../internal/catch_enforce.h"
#include <cstring> #include <cstring>
#ifdef __clang__ #ifdef __clang__
@ -99,12 +97,12 @@ namespace Catch {
case ResultWas::Ok: case ResultWas::Ok:
case ResultWas::Info: case ResultWas::Info:
case ResultWas::Warning: 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 // These cases are here to prevent compiler warnings
case ResultWas::Unknown: case ResultWas::Unknown:
case ResultWas::FailureBit: case ResultWas::FailureBit:
case ResultWas::Exception: case ResultWas::Exception:
CATCH_ERROR( "Not implemented" ); throw std::domain_error( "Not implemented" );
} }
if( assertionStats.infoMessages.size() == 1 ) if( assertionStats.infoMessages.size() == 1 )
msg << " with message:"; msg << " with message:";

View File

@ -26,7 +26,7 @@ namespace Catch {
m_reporterPrefs.shouldRedirectStdOut = true; m_reporterPrefs.shouldRedirectStdOut = true;
} }
XmlReporter::~XmlReporter() {}; XmlReporter::~XmlReporter() = default;
std::string XmlReporter::getDescription() { std::string XmlReporter::getDescription() {
return "Reports test results as an XML document"; return "Reports test results as an XML document";
@ -95,10 +95,10 @@ namespace Catch {
bool includeResults = m_config->includeSuccessfulResults() || !result.isOk(); bool includeResults = m_config->includeSuccessfulResults() || !result.isOk();
if( includeResults ) { if( includeResults || result.getResultType() == ResultWas::Warning ) {
// Print any info messages in <Info> tags. // Print any info messages in <Info> tags.
for( auto const& msg : assertionStats.infoMessages ) { for( auto const& msg : assertionStats.infoMessages ) {
if( msg.type == ResultWas::Info ) { if( msg.type == ResultWas::Info && includeResults ) {
m_xml.scopedElement( "Info" ) m_xml.scopedElement( "Info" )
.writeText( msg.message ); .writeText( msg.message );
} else if ( msg.type == ResultWas::Warning ) { } else if ( msg.type == ResultWas::Warning ) {

11
misc/CMakeLists.txt Normal file
View 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()

View 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%
)

View 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)

View 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
View 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])));
}

View 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"

File diff suppressed because it is too large Load Diff

View File

@ -700,6 +700,7 @@ with expansion:
A string sent directly to stdout A string sent directly to stdout
A string sent directly to stderr A string sent directly to stderr
A string sent to stderr via clog
Message from section one Message from section one
Message from section two Message from section two
------------------------------------------------------------------------------- -------------------------------------------------------------------------------
@ -808,6 +809,33 @@ Matchers.tests.cpp:<line number>: FAILED:
with expansion: with expansion:
{ 1, 2, 3 } Equals: { } { 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 When unchecked exceptions are thrown directly they are always failures
------------------------------------------------------------------------------- -------------------------------------------------------------------------------
@ -1052,6 +1080,6 @@ with expansion:
"{?}" == "1" "{?}" == "1"
=============================================================================== ===============================================================================
test cases: 189 | 137 passed | 48 failed | 4 failed as expected test cases: 191 | 139 passed | 48 failed | 4 failed as expected
assertions: 952 | 828 passed | 103 failed | 21 failed as expected assertions: 971 | 843 passed | 107 failed | 21 failed as expected

View File

@ -762,9 +762,7 @@ Tricky.tests.cpp:<line number>
Tricky.tests.cpp:<line number>: Tricky.tests.cpp:<line number>:
PASSED: PASSED:
REQUIRE( Catch::alwaysTrue() ) REQUIRE( true )
with expansion:
true
------------------------------------------------------------------------------- -------------------------------------------------------------------------------
Assertions then sections Assertions then sections
@ -775,9 +773,7 @@ Tricky.tests.cpp:<line number>
Tricky.tests.cpp:<line number>: Tricky.tests.cpp:<line number>:
PASSED: PASSED:
REQUIRE( Catch::alwaysTrue() ) REQUIRE( true )
with expansion:
true
------------------------------------------------------------------------------- -------------------------------------------------------------------------------
Assertions then sections Assertions then sections
@ -789,9 +785,7 @@ Tricky.tests.cpp:<line number>
Tricky.tests.cpp:<line number>: Tricky.tests.cpp:<line number>:
PASSED: PASSED:
REQUIRE( Catch::alwaysTrue() ) REQUIRE( true )
with expansion:
true
------------------------------------------------------------------------------- -------------------------------------------------------------------------------
Assertions then sections Assertions then sections
@ -801,9 +795,7 @@ Tricky.tests.cpp:<line number>
Tricky.tests.cpp:<line number>: Tricky.tests.cpp:<line number>:
PASSED: PASSED:
REQUIRE( Catch::alwaysTrue() ) REQUIRE( true )
with expansion:
true
------------------------------------------------------------------------------- -------------------------------------------------------------------------------
Assertions then sections Assertions then sections
@ -814,9 +806,7 @@ Tricky.tests.cpp:<line number>
Tricky.tests.cpp:<line number>: Tricky.tests.cpp:<line number>:
PASSED: PASSED:
REQUIRE( Catch::alwaysTrue() ) REQUIRE( true )
with expansion:
true
------------------------------------------------------------------------------- -------------------------------------------------------------------------------
Assertions then sections Assertions then sections
@ -828,9 +818,7 @@ Tricky.tests.cpp:<line number>
Tricky.tests.cpp:<line number>: Tricky.tests.cpp:<line number>:
PASSED: PASSED:
REQUIRE( Catch::alwaysTrue() ) REQUIRE( true )
with expansion:
true
------------------------------------------------------------------------------- -------------------------------------------------------------------------------
Assorted miscellaneous tests Assorted miscellaneous tests
@ -1153,13 +1141,13 @@ with expansion:
Approx.tests.cpp:<line number>: Approx.tests.cpp:<line number>:
PASSED: PASSED:
REQUIRE( td >= Approx(10.0) ) REQUIRE( td >= Approx(td) )
with expansion: with expansion:
StrongDoubleTypedef(10) >= Approx( 10.0 ) StrongDoubleTypedef(10) >= Approx( 10.0 )
Approx.tests.cpp:<line number>: Approx.tests.cpp:<line number>:
PASSED: PASSED:
REQUIRE( Approx(10.0) >= td ) REQUIRE( Approx(td) >= td )
with expansion: with expansion:
Approx( 10.0 ) >= StrongDoubleTypedef(10) Approx( 10.0 ) >= StrongDoubleTypedef(10)
@ -1884,6 +1872,29 @@ PASSED:
with expansion: with expansion:
nanf not ( is within 100.0 of nan or is within 123 ULPs of nanf ) 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 Floating point matchers: float
Margin Margin
@ -2001,6 +2012,29 @@ PASSED:
with expansion: with expansion:
nanf not ( is within 100.0 of nan or is within 123 ULPs of nanf ) 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 Greater-than inequalities with different epsilons
------------------------------------------------------------------------------- -------------------------------------------------------------------------------
@ -4585,6 +4619,7 @@ with expansion:
A string sent directly to stdout A string sent directly to stdout
A string sent directly to stderr A string sent directly to stderr
A string sent to stderr via clog
------------------------------------------------------------------------------- -------------------------------------------------------------------------------
Some simple comparisons between doubles Some simple comparisons between doubles
------------------------------------------------------------------------------- -------------------------------------------------------------------------------
@ -5139,6 +5174,12 @@ PASSED:
with expansion: with expansion:
1 [30/1]s != 1 fs 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> Stringifying std::chrono::time_point<system_clock>
------------------------------------------------------------------------------- -------------------------------------------------------------------------------
@ -6439,6 +6480,37 @@ PASSED:
with expansion: with expansion:
{ 1, 2, 3 } Equals: { 1, 2, 3 } { 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 Vector matchers that fail
Contains (element) Contains (element)
@ -6500,6 +6572,33 @@ Matchers.tests.cpp:<line number>: FAILED:
with expansion: with expansion:
{ 1, 2, 3 } Equals: { } { 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 When checked exceptions are thrown they can be expected or unexpected
------------------------------------------------------------------------------- -------------------------------------------------------------------------------
@ -7529,7 +7628,7 @@ with expansion:
""wide load"" == ""wide load"" ""wide load"" == ""wide load""
------------------------------------------------------------------------------- -------------------------------------------------------------------------------
toString( vectors<has_maker ) toString( vectors<has_maker> )
------------------------------------------------------------------------------- -------------------------------------------------------------------------------
ToStringWhich.tests.cpp:<line number> ToStringWhich.tests.cpp:<line number>
............................................................................... ...............................................................................
@ -7542,6 +7641,34 @@ with expansion:
== ==
"{ StringMaker<has_maker> }" "{ 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<<) toString(enum class w/operator<<)
------------------------------------------------------------------------------- -------------------------------------------------------------------------------
@ -7987,6 +8114,6 @@ Misc.tests.cpp:<line number>:
PASSED: PASSED:
=============================================================================== ===============================================================================
test cases: 189 | 135 passed | 50 failed | 4 failed as expected test cases: 191 | 137 passed | 50 failed | 4 failed as expected
assertions: 951 | 824 passed | 106 failed | 21 failed as expected assertions: 970 | 839 passed | 110 failed | 21 failed as expected

View File

@ -1,7 +1,7 @@
<?xml version="1.0" encoding="UTF-8"?> <?xml version="1.0" encoding="UTF-8"?>
<testsuitesloose text artifact <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="# 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="#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}"/> <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/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/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/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/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/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/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="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 and WARN do not abort tests" time="{duration}"/>
<testcase classname="<exe-name>.global" name="INFO gets logged on failure" 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-out>
<system-err> <system-err>
A string sent directly to stderr A string sent directly to stderr
A string sent to stderr via clog
</system-err> </system-err>
</testcase> </testcase>
<testcase classname="<exe-name>.global" name="Some simple comparisons between doubles" time="{duration}"/> <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 (vector)" time="{duration}"/>
<testcase classname="<exe-name>.global" name="Vector matchers/Contains (element), composed" 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/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}"> <testcase classname="<exe-name>.global" name="Vector matchers that fail/Contains (element)" time="{duration}">
<failure message="{ 1, 2, 3 } Contains: -1" type="CHECK_THAT"> <failure message="{ 1, 2, 3 } Contains: -1" type="CHECK_THAT">
Matchers.tests.cpp:<line number> Matchers.tests.cpp:<line number>
@ -632,6 +636,20 @@ Matchers.tests.cpp:<line number>
Matchers.tests.cpp:<line number> Matchers.tests.cpp:<line number>
</failure> </failure>
<failure message="{ 1, 2, 3 } Equals: { }" type="CHECK_THAT"> <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> Matchers.tests.cpp:<line number>
</failure> </failure>
</testcase> </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 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 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 on wchar_t returns the string contents" time="{duration}"/>
<testcase classname="<exe-name>.global" name="toString( vectors&lt;has_maker )" time="{duration}"/> <testcase classname="<exe-name>.global" name="toString( vectors&lt;has_maker> )" time="{duration}"/>
<testcase classname="<exe-name>.global" name="toString( vectors&lt;has_maker_and_operator> )" time="{duration}"/>
<testcase classname="<exe-name>.global" name="toString( vectors&lt;has_operator> )" time="{duration}"/>
<testcase classname="<exe-name>.global" name="toString(enum class w/operator&lt;&lt;)" time="{duration}"/> <testcase classname="<exe-name>.global" name="toString(enum class w/operator&lt;&lt;)" time="{duration}"/>
<testcase classname="<exe-name>.global" name="toString(enum class)" time="{duration}"> <testcase classname="<exe-name>.global" name="toString(enum class)" time="{duration}">
<failure message="&quot;{?}&quot; == &quot;0&quot;" type="CHECK"> <failure message="&quot;{?}&quot; == &quot;0&quot;" type="CHECK">
@ -833,6 +853,7 @@ Message from section two
</system-out> </system-out>
<system-err> <system-err>
A string sent directly to stderr A string sent directly to stderr
A string sent to stderr via clog
</system-err> </system-err>
</testsuite> </testsuite>
</testsuites> </testsuites>

View File

@ -819,7 +819,7 @@
<TestCase name="Assertions then sections" tags="[Tricky]" filename="projects/<exe-name>/UsageTests/Tricky.tests.cpp" > <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" > <Expression success="true" type="REQUIRE" filename="projects/<exe-name>/UsageTests/Tricky.tests.cpp" >
<Original> <Original>
Catch::alwaysTrue() true
</Original> </Original>
<Expanded> <Expanded>
true true
@ -828,7 +828,7 @@
<Section name="A section" filename="projects/<exe-name>/UsageTests/Tricky.tests.cpp" > <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" > <Expression success="true" type="REQUIRE" filename="projects/<exe-name>/UsageTests/Tricky.tests.cpp" >
<Original> <Original>
Catch::alwaysTrue() true
</Original> </Original>
<Expanded> <Expanded>
true true
@ -837,7 +837,7 @@
<Section name="Another section" filename="projects/<exe-name>/UsageTests/Tricky.tests.cpp" > <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" > <Expression success="true" type="REQUIRE" filename="projects/<exe-name>/UsageTests/Tricky.tests.cpp" >
<Original> <Original>
Catch::alwaysTrue() true
</Original> </Original>
<Expanded> <Expanded>
true true
@ -849,7 +849,7 @@
</Section> </Section>
<Expression success="true" type="REQUIRE" filename="projects/<exe-name>/UsageTests/Tricky.tests.cpp" > <Expression success="true" type="REQUIRE" filename="projects/<exe-name>/UsageTests/Tricky.tests.cpp" >
<Original> <Original>
Catch::alwaysTrue() true
</Original> </Original>
<Expanded> <Expanded>
true true
@ -858,7 +858,7 @@
<Section name="A section" filename="projects/<exe-name>/UsageTests/Tricky.tests.cpp" > <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" > <Expression success="true" type="REQUIRE" filename="projects/<exe-name>/UsageTests/Tricky.tests.cpp" >
<Original> <Original>
Catch::alwaysTrue() true
</Original> </Original>
<Expanded> <Expanded>
true true
@ -867,7 +867,7 @@
<Section name="Another other section" filename="projects/<exe-name>/UsageTests/Tricky.tests.cpp" > <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" > <Expression success="true" type="REQUIRE" filename="projects/<exe-name>/UsageTests/Tricky.tests.cpp" >
<Original> <Original>
Catch::alwaysTrue() true
</Original> </Original>
<Expanded> <Expanded>
true true
@ -1273,7 +1273,7 @@
</Expression> </Expression>
<Expression success="true" type="REQUIRE" filename="projects/<exe-name>/UsageTests/Approx.tests.cpp" > <Expression success="true" type="REQUIRE" filename="projects/<exe-name>/UsageTests/Approx.tests.cpp" >
<Original> <Original>
td >= Approx(10.0) td >= Approx(td)
</Original> </Original>
<Expanded> <Expanded>
StrongDoubleTypedef(10) >= Approx( 10.0 ) StrongDoubleTypedef(10) >= Approx( 10.0 )
@ -1281,7 +1281,7 @@
</Expression> </Expression>
<Expression success="true" type="REQUIRE" filename="projects/<exe-name>/UsageTests/Approx.tests.cpp" > <Expression success="true" type="REQUIRE" filename="projects/<exe-name>/UsageTests/Approx.tests.cpp" >
<Original> <Original>
Approx(10.0) >= td Approx(td) >= td
</Original> </Original>
<Expanded> <Expanded>
Approx( 10.0 ) >= StrongDoubleTypedef(10) Approx( 10.0 ) >= StrongDoubleTypedef(10)
@ -2142,6 +2142,41 @@
</Expression> </Expression>
<OverallResults successes="3" failures="0" expectedFailures="0"/> <OverallResults successes="3" failures="0" expectedFailures="0"/>
</Section> </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"/> <OverallResult success="true"/>
</TestCase> </TestCase>
<TestCase name="Floating point matchers: float" tags="[floating-point][matchers]" filename="projects/<exe-name>/UsageTests/Matchers.tests.cpp" > <TestCase name="Floating point matchers: float" tags="[floating-point][matchers]" filename="projects/<exe-name>/UsageTests/Matchers.tests.cpp" >
@ -2282,6 +2317,41 @@
</Expression> </Expression>
<OverallResults successes="3" failures="0" expectedFailures="0"/> <OverallResults successes="3" failures="0" expectedFailures="0"/>
</Section> </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"/> <OverallResult success="true"/>
</TestCase> </TestCase>
<TestCase name="Greater-than inequalities with different epsilons" tags="[Approx]" filename="projects/<exe-name>/UsageTests/Approx.tests.cpp" > <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> </StdOut>
<StdErr> <StdErr>
A string sent directly to stderr A string sent directly to stderr
A string sent to stderr via clog
</StdErr> </StdErr>
</OverallResult> </OverallResult>
</TestCase> </TestCase>
@ -5878,6 +5949,14 @@ Message from section two
1 [30/1]s != 1 fs 1 [30/1]s != 1 fs
</Expanded> </Expanded>
</Expression> </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"/> <OverallResult success="true"/>
</TestCase> </TestCase>
<TestCase name="Stringifying std::chrono::time_point&lt;system_clock>" tags="[chrono][toString]" filename="projects/<exe-name>/UsageTests/ToStringChrono.tests.cpp" > <TestCase name="Stringifying std::chrono::time_point&lt;system_clock>" tags="[chrono][toString]" filename="projects/<exe-name>/UsageTests/ToStringChrono.tests.cpp" >
@ -7298,6 +7377,41 @@ Message from section two
</Expression> </Expression>
<OverallResults successes="3" failures="0" expectedFailures="0"/> <OverallResults successes="3" failures="0" expectedFailures="0"/>
</Section> </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"/> <OverallResult success="true"/>
</TestCase> </TestCase>
<TestCase name="Vector matchers that fail" tags="[.][failing][matchers][vector]" filename="projects/<exe-name>/UsageTests/Matchers.tests.cpp" > <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> </Expression>
<OverallResults successes="0" failures="4" expectedFailures="0"/> <OverallResults successes="0" failures="4" expectedFailures="0"/>
</Section> </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"/> <OverallResult success="false"/>
</TestCase> </TestCase>
<TestCase name="When checked exceptions are thrown they can be expected or unexpected" tags="[!throws]" filename="projects/<exe-name>/UsageTests/Exception.tests.cpp" > <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> </Expression>
<OverallResult success="true"/> <OverallResult success="true"/>
</TestCase> </TestCase>
<TestCase name="toString( vectors&lt;has_maker )" tags="[toString]" filename="projects/<exe-name>/UsageTests/ToStringWhich.tests.cpp" > <TestCase name="toString( vectors&lt;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" > <Expression success="true" type="REQUIRE" filename="projects/<exe-name>/UsageTests/ToStringWhich.tests.cpp" >
<Original> <Original>
::Catch::Detail::stringify( v ) == "{ StringMaker&lt;has_maker> }" ::Catch::Detail::stringify( v ) == "{ StringMaker&lt;has_maker> }"
@ -8422,6 +8571,32 @@ loose text artifact
</Expression> </Expression>
<OverallResult success="true"/> <OverallResult success="true"/>
</TestCase> </TestCase>
<TestCase name="toString( vectors&lt;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&lt;has_maker_and_operator> }"
</Original>
<Expanded>
"{ StringMaker&lt;has_maker_and_operator> }"
==
"{ StringMaker&lt;has_maker_and_operator> }"
</Expanded>
</Expression>
<OverallResult success="true"/>
</TestCase>
<TestCase name="toString( vectors&lt;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&lt;&lt;( has_operator ) }"
</Original>
<Expanded>
"{ operator&lt;&lt;( has_operator ) }"
==
"{ operator&lt;&lt;( has_operator ) }"
</Expanded>
</Expression>
<OverallResult success="true"/>
</TestCase>
<TestCase name="toString(enum class w/operator&lt;&lt;)" tags="[enum][enumClass][toString]" filename="projects/<exe-name>/UsageTests/EnumToString.tests.cpp" > <TestCase name="toString(enum class w/operator&lt;&lt;)" 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" > <Expression success="true" type="CHECK" filename="projects/<exe-name>/UsageTests/EnumToString.tests.cpp" >
<Original> <Original>
@ -8863,7 +9038,7 @@ loose text artifact
</Section> </Section>
<OverallResult success="true"/> <OverallResult success="true"/>
</TestCase> </TestCase>
<OverallResults successes="824" failures="107" expectedFailures="21"/> <OverallResults successes="839" failures="111" expectedFailures="21"/>
</Group> </Group>
<OverallResults successes="824" failures="106" expectedFailures="21"/> <OverallResults successes="839" failures="110" expectedFailures="21"/>
</Catch> </Catch>

View File

@ -189,8 +189,8 @@ TEST_CASE( "Comparison with explicitly convertible types", "[Approx]" )
REQUIRE(Approx(9.0) <= td); REQUIRE(Approx(9.0) <= td);
REQUIRE(td >= Approx(9.0)); REQUIRE(td >= Approx(9.0));
REQUIRE(td >= Approx(10.0)); REQUIRE(td >= Approx(td));
REQUIRE(Approx(10.0) >= td); REQUIRE(Approx(td) >= td);
REQUIRE(Approx(11.0) >= td); REQUIRE(Approx(11.0) >= td);
} }

View File

@ -8,7 +8,10 @@
#ifdef __clang__ #ifdef __clang__
# pragma clang diagnostic push # pragma clang diagnostic push
# pragma clang diagnostic ignored "-Wpadded" # pragma clang diagnostic ignored "-Wpadded"
// Wdouble-promotion is not supported until 3.8
# if (__clang_major__ > 3) || (__clang_major__ == 3 && __clang_minor__ > 7)
# pragma clang diagnostic ignored "-Wdouble-promotion" # pragma clang diagnostic ignored "-Wdouble-promotion"
# endif
#endif #endif
#include "catch.hpp" #include "catch.hpp"

View File

@ -12,11 +12,12 @@
#include <stdexcept> #include <stdexcept>
#ifdef _MSC_VER #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 #endif
#ifdef __clang__ #ifdef __clang__
#pragma clang diagnostic push #pragma clang diagnostic push
#pragma clang diagnostic ignored "-Wweak-vtables" #pragma clang diagnostic ignored "-Wweak-vtables"
#pragma clang diagnostic ignored "-Wmissing-noreturn"
#endif #endif
namespace { namespace ExceptionTests { namespace { namespace ExceptionTests {
@ -24,8 +25,7 @@ namespace { namespace ExceptionTests {
#ifndef EXCEPTION_TEST_HELPERS_INCLUDED // Don't compile this more than once per TU #ifndef EXCEPTION_TEST_HELPERS_INCLUDED // Don't compile this more than once per TU
#define EXCEPTION_TEST_HELPERS_INCLUDED #define EXCEPTION_TEST_HELPERS_INCLUDED
inline int thisThrows() { int thisThrows() {
if( Catch::alwaysTrue() )
throw std::domain_error( "expected exception" ); throw std::domain_error( "expected exception" );
return 1; return 1;
} }
@ -36,7 +36,7 @@ int thisDoesntThrow() {
class CustomException { class CustomException {
public: public:
CustomException( const std::string& msg ) explicit CustomException( const std::string& msg )
: m_msg( msg ) : m_msg( msg )
{} {}
@ -50,10 +50,10 @@ private:
class CustomStdException : public std::exception { class CustomStdException : public std::exception {
public: public:
CustomStdException( const std::string& msg ) explicit CustomStdException( const std::string& msg )
: m_msg( msg ) : m_msg( msg )
{} {}
~CustomStdException() noexcept {} ~CustomStdException() noexcept override {}
std::string getMessage() const { std::string getMessage() const {
return m_msg; return m_msg;
@ -63,8 +63,7 @@ private:
std::string m_msg; std::string m_msg;
}; };
inline void throwCustom() { [[noreturn]] void throwCustom() {
if( Catch::alwaysTrue() )
throw CustomException( "custom exception - not std" ); throw CustomException( "custom exception - not std" );
} }
@ -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]" ) { 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]" ) { TEST_CASE( "An unchecked exception reports the line of the last assertion", "[.][failing][!throws]" ) {
CHECK( 1 == 1 ); 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]" ) { TEST_CASE( "When unchecked exceptions are thrown from sections they are always failures", "[.][failing][!throws]" ) {
SECTION( "section name" ) { SECTION( "section name" ) {
if( Catch::alwaysTrue() ) throw std::domain_error("unexpected exception");
throw std::domain_error( "unexpected exception" );
} }
} }
@ -139,12 +135,10 @@ CATCH_TRANSLATE_EXCEPTION( double& ex ) {
} }
TEST_CASE("Non-std exceptions can be translated", "[.][failing][!throws]" ) { 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]" ) { TEST_CASE("Custom std-exceptions can be custom translated", "[.][failing][!throws]" ) {
if( Catch::alwaysTrue() )
throw CustomException( "custom std exception" ); throw CustomException( "custom std exception" );
} }
@ -157,7 +151,6 @@ TEST_CASE( "Custom exceptions can be translated when testing for throwing as som
} }
TEST_CASE( "Unexpected exceptions can be translated", "[.][failing][!throws]" ) { TEST_CASE( "Unexpected exceptions can be translated", "[.][failing][!throws]" ) {
if( Catch::alwaysTrue() )
throw double( 3.14 ); throw double( 3.14 );
} }

View File

@ -9,6 +9,7 @@
#include "catch.hpp" #include "catch.hpp"
#include <sstream> #include <sstream>
#include <algorithm>
#ifdef __clang__ #ifdef __clang__
#pragma clang diagnostic push #pragma clang diagnostic push
@ -216,6 +217,17 @@ namespace { namespace MatchersTests {
v2.push_back(3); v2.push_back(3);
CHECK_THAT(v, Equals(v2)); 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]") { TEST_CASE("Vector matchers that fail", "[matchers][vector][.][failing]") {
@ -247,6 +259,18 @@ namespace { namespace MatchersTests {
CHECK_THAT(empty, Equals(v)); CHECK_THAT(empty, Equals(v));
CHECK_THAT(v, Equals(empty)); 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]") { 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))); 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]") { 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))); 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 } } // namespace MatchersTests

View File

@ -57,7 +57,9 @@ struct AutoTestReg {
REGISTER_TEST_CASE( manuallyRegisteredTestFunction, "ManuallyRegistered" ); REGISTER_TEST_CASE( manuallyRegisteredTestFunction, "ManuallyRegistered" );
} }
}; };
CATCH_INTERNAL_SUPPRESS_GLOBALS_WARNINGS
static AutoTestReg autoTestReg; static AutoTestReg autoTestReg;
CATCH_INTERNAL_UNSUPPRESS_GLOBALS_WARNINGS
#endif #endif
@ -146,8 +148,8 @@ TEST_CASE( "looped tests", "[.][failing]" ) {
TEST_CASE( "Sends stuff to stdout and stderr", "[.]" ) { TEST_CASE( "Sends stuff to stdout and stderr", "[.]" ) {
std::cout << "A string sent directly to stdout" << std::endl; std::cout << "A string sent directly to stdout" << std::endl;
std::cerr << "A string sent directly to stderr" << 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" ) { 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]" ) { 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 ); std::string result = ::Catch::Detail::stringify( s );
CHECK( result == "\"wide load\"" ); CHECK( result == "\"wide load\"" );
} }
TEST_CASE( "toString on wchar_t returns the string contents", "[toString]" ) { 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 ); std::string result = ::Catch::Detail::stringify( s );
CHECK( result == "\"wide load\"" ); CHECK( result == "\"wide load\"" );
} }

View File

@ -20,8 +20,11 @@ TEST_CASE("Stringifying std::chrono::duration helpers", "[toString][chrono]") {
TEST_CASE("Stringifying std::chrono::duration with weird ratios", "[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<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, 1000000000000000>> femto_second(1);
std::chrono::duration<int64_t, std::ratio<1, 1000000000000000000>> atto_second(1);
REQUIRE(half_minute != femto_second); REQUIRE(half_minute != femto_second);
REQUIRE(pico_second != atto_second);
} }
TEST_CASE("Stringifying std::chrono::time_point<system_clock>", "[toString][chrono]") { TEST_CASE("Stringifying std::chrono::time_point<system_clock>", "[toString][chrono]") {

View File

@ -55,19 +55,19 @@ TEST_CASE( "stringify( has_maker_and_toString )", "[.][toString]" ) {
// Vectors... // Vectors...
// Don't run this in approval tests as it is sensitive to two phase lookup differences // 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); std::vector<has_operator> v(1);
REQUIRE( ::Catch::Detail::stringify( v ) == "{ operator<<( has_operator ) }" ); 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); std::vector<has_maker> v(1);
REQUIRE( ::Catch::Detail::stringify( v ) == "{ StringMaker<has_maker> }" ); REQUIRE( ::Catch::Detail::stringify( v ) == "{ StringMaker<has_maker> }" );
} }
// Don't run this in approval tests as it is sensitive to two phase lookup differences // 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); 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> }" );
} }

View File

@ -298,19 +298,19 @@ TEST_CASE( "Assertions then sections", "[Tricky]" )
// This was causing a failure due to the way the console reporter was handling // This was causing a failure due to the way the console reporter was handling
// the current section // the current section
REQUIRE( Catch::alwaysTrue() ); REQUIRE( true );
SECTION( "A section" ) SECTION( "A section" )
{ {
REQUIRE( Catch::alwaysTrue() ); REQUIRE( true );
SECTION( "Another section" ) SECTION( "Another section" )
{ {
REQUIRE( Catch::alwaysTrue() ); REQUIRE( true );
} }
SECTION( "Another other section" ) SECTION( "Another other section" )
{ {
REQUIRE( Catch::alwaysTrue() ); REQUIRE( true );
} }
} }
} }

View File

@ -167,6 +167,8 @@ def approve(baseName, args):
print("Running approvals against executable:") print("Running approvals against executable:")
print(" " + cmdPath) print(" " + cmdPath)
### Keep default reporters here
# Standard console reporter # Standard console reporter
approve("console.std", ["~[!nonportable]~[!benchmark]~[approvals]", "--order", "lex"]) approve("console.std", ["~[!nonportable]~[!benchmark]~[approvals]", "--order", "lex"])
# console reporter, include passes, warn about No Assertions # 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"]) approve("junit.sw", ["~[!nonportable]~[!benchmark]~[approvals]", "-s", "-w", "NoAssertions", "-r", "junit", "--order", "lex"])
# xml reporter, include passes, warn about No Assertions # xml reporter, include passes, warn about No Assertions
approve("xml.sw", ["~[!nonportable]~[!benchmark]~[approvals]", "-s", "-w", "NoAssertions", "-r", "xml", "--order", "lex"]) 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: if overallResult != 0:
print("If these differences are expected, run approve.py to approve new baselines.") print("If these differences are expected, run approve.py to approve new baselines.")