git subrepo pull Telegram/ThirdParty/Catch

subrepo:
  subdir:   "Telegram/ThirdParty/Catch"
  merged:   "11c89a5f7"
upstream:
  origin:   "https://github.com/catchorg/Catch2"
  branch:   "master"
  commit:   "11c89a5f7"
git-subrepo:
  version:  "0.3.1"
  origin:   "https://github.com/ingydotnet/git-subrepo"
  commit:   "a7ee886"
This commit is contained in:
crackedmind 2018-03-11 03:44:20 +03:00 committed by Alex
parent a3ba1d6192
commit c02a20a5c1
138 changed files with 9732 additions and 3604 deletions

View File

@ -8,4 +8,15 @@
*.hpp text *.hpp text
# Windows specific files should retain windows line-endings # Windows specific files should retain windows line-endings
*.sln text eol=crlf *.sln text eol=crlf
# Keep executable scripts with LFs so they can be run after being
# checked out on Windows
*.py text eol=lf
# Keep the single include header with LFs to make sure it is uploaded,
# hashed etc with LF
single_include/*.hpp eol=lf
# Also keep the LICENCE file with LFs for the same reason
LICENCE.txt eol=lf

View File

@ -26,3 +26,4 @@ Build
.idea .idea
.vs .vs
cmake-build-* cmake-build-*
benchmark-dir

View File

@ -6,6 +6,6 @@
[subrepo] [subrepo]
remote = https://github.com/catchorg/Catch2 remote = https://github.com/catchorg/Catch2
branch = master branch = master
commit = 63c097a07775f939b5b77c88dd61fe933f4d7050 commit = 11c89a5f7de008c2a21c9cd2a6b96d94f9a55cef
parent = f27cd81fd0d3ecd9dc02be18c70e767a4a3fee6e parent = 0c4e81f37a8489a0b26508d55e6d6d1fa7f78c07
cmdver = 0.3.1 cmdver = 0.3.1

View File

@ -1,6 +1,10 @@
language: cpp language: cpp
sudo: false sudo: false
branches:
except:
- /dev-appveyor.*/
common_sources: &all_sources common_sources: &all_sources
- ubuntu-toolchain-r-test - ubuntu-toolchain-r-test
- llvm-toolchain-trusty - llvm-toolchain-trusty
@ -17,145 +21,140 @@ matrix:
addons: addons:
apt: apt:
sources: *all_sources sources: *all_sources
packages: ['valgrind', 'clang-3.5'] packages: ['clang-3.5']
env: COMPILER='clang++-3.5' VALGRIND=1 env: COMPILER='clang++-3.5'
- os: linux - os: linux
compiler: clang compiler: clang
addons: addons:
apt: apt:
sources: *all_sources sources: *all_sources
packages: ['valgrind', 'clang-3.6'] packages: ['clang-3.6']
env: COMPILER='clang++-3.6' VALGRIND=1 env: COMPILER='clang++-3.6'
# Travis's containers do not seem to have Clang 3.7 in apt, no matter what sources I add. # Clang 3.7 is intentionally skipped as we cannot get it easily on
# - os: linux # TravisCI container
# compiler: clang
# addons:
# apt:
# sources: *all_sources
# packages: ['valgrind', 'clang-3.7']
# env: COMPILER='clang++-3.7' VALGRIND=1
- os: linux - os: linux
compiler: clang compiler: clang
addons: addons:
apt: apt:
sources: *all_sources sources: *all_sources
packages: ['valgrind', 'clang-3.8'] packages: ['lcov', 'clang-3.8']
env: COMPILER='clang++-3.8' VALGRIND=1 env: COMPILER='clang++-3.8'
- os: linux - os: linux
compiler: clang compiler: clang
addons: addons:
apt: apt:
sources: *all_sources sources: *all_sources
packages: ['clang-3.9', 'valgrind'] packages: ['clang-3.9']
env: COMPILER='clang++-3.9' VALGRIND=1 env: COMPILER='clang++-3.9'
- os: linux - os: linux
compiler: clang compiler: clang
addons: addons:
apt: apt:
sources: *all_sources sources: *all_sources
packages: ['clang-4.0', 'valgrind'] packages: ['clang-4.0']
env: COMPILER='clang++-4.0' VALGRIND=1 env: COMPILER='clang++-4.0'
- os: linux - os: linux
compiler: clang compiler: clang
addons: addons:
apt: apt:
sources: *all_sources sources: *all_sources
packages: ['clang-5.0', 'valgrind'] packages: ['clang-5.0']
env: COMPILER='clang++-5.0' VALGRIND=1 env: COMPILER='clang++-5.0'
# 2/ Linux GCC Builds # 2/ Linux GCC Builds
- os: linux - os: linux
compiler: gcc compiler: gcc
addons: addons:
apt: apt:
sources: ['ubuntu-toolchain-r-test'] sources: *all_sources
packages: ['valgrind', 'g++-4.8'] packages: ['g++-4.8']
env: COMPILER='g++-4.8' VALGRIND=1 env: COMPILER='g++-4.8'
- os: linux - os: linux
compiler: gcc compiler: gcc
addons: addons:
apt: apt:
sources: *all_sources sources: *all_sources
packages: ['valgrind', 'g++-4.9'] packages: ['g++-4.9']
env: COMPILER='g++-4.9' VALGRIND=1 env: COMPILER='g++-4.9'
- os: linux - os: linux
compiler: gcc compiler: gcc
addons: addons:
apt: apt:
sources: *all_sources sources: *all_sources
packages: ['valgrind', 'g++-5'] packages: ['g++-5']
env: COMPILER='g++-5' VALGRIND=1 env: COMPILER='g++-5'
- os: linux - os: linux
compiler: gcc compiler: gcc
addons: &gcc6 addons: &gcc6
apt: apt:
sources: *all_sources sources: *all_sources
packages: ['valgrind', 'g++-6'] packages: ['g++-6']
env: COMPILER='g++-6' VALGRIND=1 env: COMPILER='g++-6'
- os: linux - os: linux
compiler: gcc compiler: gcc
addons: &gcc7 addons: &gcc7
apt: apt:
sources: *all_sources sources: *all_sources
packages: ['valgrind', 'g++-7'] packages: ['g++-7']
env: COMPILER='g++-7' VALGRIND=1 env: COMPILER='g++-7'
# 3b/ Linux C++14 Clang builds # 3b/ Linux C++14 Clang builds
# Note that we need newer libstdc++ for C++14 support
- os: linux - os: linux
compiler: clang compiler: clang
addons: addons:
apt: apt:
packages: ['clang-3.8', 'valgrind', 'libstdc++-6-dev'] packages: ['clang-3.8', 'libstdc++-6-dev']
sources: sources:
- ubuntu-toolchain-r-test - ubuntu-toolchain-r-test
- llvm-toolchain-trusty - llvm-toolchain-trusty
env: COMPILER='clang++-3.8' CPP14=1 VALGRIND=1 env: COMPILER='clang++-3.8' CPP14=1
- os: linux - os: linux
compiler: clang compiler: clang
addons: addons:
apt: apt:
sources: *all_sources sources: *all_sources
packages: ['clang-3.9', 'valgrind', 'libstdc++-6-dev'] packages: ['clang-3.9', 'libstdc++-6-dev']
env: COMPILER='clang++-3.9' CPP14=1 VALGRIND=1 env: COMPILER='clang++-3.9' CPP14=1
- os: linux - os: linux
compiler: clang compiler: clang
addons: addons:
apt: apt:
sources: *all_sources sources: *all_sources
packages: ['clang-4.0', 'valgrind', 'libstdc++-6-dev'] packages: ['clang-4.0', 'libstdc++-6-dev']
env: COMPILER='clang++-4.0' CPP14=1 VALGRIND=1 env: COMPILER='clang++-4.0' CPP14=1
- os: linux - os: linux
compiler: clang compiler: clang
addons: addons:
apt: apt:
sources: *all_sources sources: *all_sources
packages: ['clang-5.0', 'valgrind', 'libstdc++-6-dev'] packages: ['clang-5.0', 'libstdc++-6-dev']
env: COMPILER='clang++-5.0' CPP14=1 VALGRIND=1 env: COMPILER='clang++-5.0' CPP14=1
# 4a/ Linux C++14 GCC builds # 4a/ Linux C++14 GCC builds
- os: linux - os: linux
compiler: gcc compiler: gcc
addons: *gcc6 addons: *gcc6
env: COMPILER='g++-6' CPP14=1 VALGRIND=1 env: COMPILER='g++-6' CPP14=1
- os: linux - os: linux
compiler: gcc compiler: gcc
addons: *gcc7 addons: *gcc7
env: COMPILER='g++-7' CPP14=1 VALGRIND=1 env: COMPILER='g++-7' CPP14=1
# 5/ OSX Clang Builds # 5/ OSX Clang Builds
- os: osx - os: osx
@ -181,7 +180,39 @@ matrix:
- os: osx - os: osx
osx_image: xcode9.1 osx_image: xcode9.1
compiler: clang compiler: clang
env: COMPILER='clang++' USE_CPP14=1 env: COMPILER='clang++' CPP14=1
# 6/ Special builds -- examples, coverage, valgrind, etc.
- os: linux
compiler: gcc
addons:
apt:
sources: *all_sources
packages: ['lcov', 'g++-7']
env: COMPILER='g++-7' CPP14=1 EXAMPLES=1 COVERAGE=1
- os: linux
compiler: clang
addons:
apt:
packages: ['clang-3.8', 'lcov']
sources:
- ubuntu-toolchain-r-test
- llvm-toolchain-trusty
env: COMPILER='clang++-3.8' EXAMPLES=1 COVERAGE=1
- os: linux
compiler: gcc
addons:
apt:
sources: *all_sources
packages: ['valgrind', 'lcov', 'g++-7']
env: COMPILER='g++-7' CPP14=1 VALGRIND=1
- os: osx
osx_image: xcode9.1
compiler: clang
env: COMPILER='clang++' CPP14=1 EXAMPLES=1 COVERAGE=1
install: install:
- DEPS_DIR="${TRAVIS_BUILD_DIR}/deps" - DEPS_DIR="${TRAVIS_BUILD_DIR}/deps"
@ -192,7 +223,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:
@ -201,15 +232,27 @@ 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 - |
- cmake -H. -BBuild-Debug -DCMAKE_BUILD_TYPE=Debug -Wdev -DUSE_CPP14=${CPP14} -DUSE_VALGRIND=${VALGRIND} -DBUILD_EXAMPLES=ON # Use Debug builds for running Valgrind and building examples
# Check that we don't miscompile with optimalizations... cmake -H. -BBuild-Debug -DCMAKE_BUILD_TYPE=Debug -Wdev -DUSE_CPP14=${CPP14} -DCATCH_USE_VALGRIND=${VALGRIND} -DCATCH_BUILD_EXAMPLES=${EXAMPLES} -DCATCH_ENABLE_COVERAGE=${COVERAGE}
- cmake -H. -BBuild-Release -DCMAKE_BUILD_TYPE=Release -Wdev -DUSE_CPP14=${CPP14} # Don't bother with release build for coverage build
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 echo "${TRAVIS_OS_NAME}";
echo "${COVERAGE}";
if [[ "${TRAVIS_OS_NAME}" == "linux" ]] && [[ "${COVERAGE}" == "1" ]]; 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

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)

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)

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)

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

@ -1,9 +1,19 @@
cmake_minimum_required(VERSION 3.0) cmake_minimum_required(VERSION 3.1)
project(CatchSelfTest) # detect if Catch is being bundled,
# disable testsuite in that case
if(NOT DEFINED PROJECT_NAME)
set(NOT_SUBPROJECT ON)
endif()
option(USE_VALGRIND "Perform SelfTests with Valgrind" OFF) project(Catch2 LANGUAGES CXX VERSION 2.2.0)
option(BUILD_EXAMPLES "Build documentation examples" OFF)
include(GNUInstallDirs)
option(CATCH_USE_VALGRIND "Perform SelfTests with Valgrind" OFF)
option(CATCH_BUILD_EXAMPLES "Build documentation examples" OFF)
option(CATCH_ENABLE_COVERAGE "Generate coverage for codecov.io" OFF)
option(CATCH_ENABLE_WERROR "Enable all warnings as errors" ON)
set_property(GLOBAL PROPERTY USE_FOLDERS ON) set_property(GLOBAL PROPERTY USE_FOLDERS ON)
@ -12,7 +22,6 @@ set(CATCH_DIR ${CMAKE_CURRENT_SOURCE_DIR})
set(SELF_TEST_DIR ${CATCH_DIR}/projects/SelfTest) set(SELF_TEST_DIR ${CATCH_DIR}/projects/SelfTest)
set(BENCHMARK_DIR ${CATCH_DIR}/projects/Benchmark) set(BENCHMARK_DIR ${CATCH_DIR}/projects/Benchmark)
set(HEADER_DIR ${CATCH_DIR}/include) set(HEADER_DIR ${CATCH_DIR}/include)
set(CATCH_VERSION_NUMBER 2.0.1)
if(USE_WMAIN) if(USE_WMAIN)
set(CMAKE_EXE_LINKER_FLAGS "${CMAKE_EXE_LINKER_FLAGS} /ENTRY:wmainCRTStartup") set(CMAKE_EXE_LINKER_FLAGS "${CMAKE_EXE_LINKER_FLAGS} /ENTRY:wmainCRTStartup")
@ -178,6 +187,7 @@ set(INTERNAL_HEADERS
${HEADER_DIR}/internal/catch_timer.h ${HEADER_DIR}/internal/catch_timer.h
${HEADER_DIR}/internal/catch_tostring.h ${HEADER_DIR}/internal/catch_tostring.h
${HEADER_DIR}/internal/catch_totals.h ${HEADER_DIR}/internal/catch_totals.h
${HEADER_DIR}/internal/catch_uncaught_exceptions.h
${HEADER_DIR}/internal/catch_user_interfaces.h ${HEADER_DIR}/internal/catch_user_interfaces.h
${HEADER_DIR}/internal/catch_version.h ${HEADER_DIR}/internal/catch_version.h
${HEADER_DIR}/internal/catch_wildcard_pattern.h ${HEADER_DIR}/internal/catch_wildcard_pattern.h
@ -238,6 +248,7 @@ set(IMPL_SOURCES
${HEADER_DIR}/internal/catch_timer.cpp ${HEADER_DIR}/internal/catch_timer.cpp
${HEADER_DIR}/internal/catch_tostring.cpp ${HEADER_DIR}/internal/catch_tostring.cpp
${HEADER_DIR}/internal/catch_totals.cpp ${HEADER_DIR}/internal/catch_totals.cpp
${HEADER_DIR}/internal/catch_uncaught_exceptions.cpp
${HEADER_DIR}/internal/catch_version.cpp ${HEADER_DIR}/internal/catch_version.cpp
${HEADER_DIR}/internal/catch_wildcard_pattern.cpp ${HEADER_DIR}/internal/catch_wildcard_pattern.cpp
${HEADER_DIR}/internal/catch_xmlwriter.cpp ${HEADER_DIR}/internal/catch_xmlwriter.cpp
@ -280,11 +291,22 @@ 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.
if (NOT NO_SELFTEST)
if(DEFINED NO_SELFTEST)
message(DEPRECATION "*** CMake option NO_SELFTEST is deprecated; use BUILD_TESTING instead")
if (NO_SELFTEST)
set(BUILD_TESTING OFF CACHE BOOL "Disable Catch2 internal testsuite" FORCE)
else()
set(BUILD_TESTING ON CACHE BOOL "Disable Catch2 internal testsuite" FORCE)
endif()
endif()
include(CTest)
if (BUILD_TESTING AND NOT_SUBPROJECT)
add_executable(SelfTest ${TEST_SOURCES} ${IMPL_SOURCES} ${REPORTER_SOURCES} ${SURROGATE_SOURCES} ${HEADERS}) add_executable(SelfTest ${TEST_SOURCES} ${IMPL_SOURCES} ${REPORTER_SOURCES} ${SURROGATE_SOURCES} ${HEADERS})
target_include_directories(SelfTest PRIVATE ${HEADER_DIR}) target_include_directories(SelfTest PRIVATE ${HEADER_DIR})
@ -299,59 +321,95 @@ 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 (CATCH_ENABLE_COVERAGE)
# Add desired warnings set(ENABLE_COVERAGE ON CACHE BOOL "Enable coverage build." FORCE)
if ( CMAKE_CXX_COMPILER_ID MATCHES "Clang|AppleClang|GNU" ) list(APPEND CMAKE_MODULE_PATH "${CMAKE_CURRENT_LIST_DIR}/CMake")
target_compile_options( SelfTest PRIVATE -Wall -Wextra -Wunreachable-code -Werror ) find_package(codecov)
add_coverage(SelfTest)
list(APPEND LCOV_REMOVE_PATTERNS "'/usr/*'")
coverage_evaluate()
endif() endif()
# Clang specific warning go here
# Add per compiler options
if ( CMAKE_CXX_COMPILER_ID MATCHES "Clang|AppleClang|GNU" )
target_compile_options( SelfTest PRIVATE -Wall -Wextra -Wunreachable-code -Wpedantic)
if (CATCH_ENABLE_WERROR)
target_compile_options( SelfTest PRIVATE -Werror)
endif()
endif()
# Clang specific options go here
if ( CMAKE_CXX_COMPILER_ID MATCHES "Clang" ) if ( CMAKE_CXX_COMPILER_ID MATCHES "Clang" )
# Actually keep these
target_compile_options( SelfTest PRIVATE -Wweak-vtables -Wexit-time-destructors -Wglobal-constructors -Wmissing-noreturn ) target_compile_options( SelfTest PRIVATE -Wweak-vtables -Wexit-time-destructors -Wglobal-constructors -Wmissing-noreturn )
endif() endif()
if ( CMAKE_CXX_COMPILER_ID MATCHES "MSVC" ) if ( CMAKE_CXX_COMPILER_ID MATCHES "MSVC" )
target_compile_options( SelfTest PRIVATE /W4 /w44265 /WX /w44061 /w44062 ) STRING(REGEX REPLACE "/W[0-9]" "/W4" CMAKE_CXX_FLAGS ${CMAKE_CXX_FLAGS}) # override default warning level
target_compile_options( SelfTest PRIVATE /w44265 /w44061 /w44062 )
if (CATCH_ENABLE_WERROR)
target_compile_options( SelfTest PRIVATE /WX)
endif()
# Force MSVC to consider everything as encoded in utf-8
target_compile_options( SelfTest PRIVATE /utf-8 )
endif() endif()
# 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"
FAIL_REGULAR_EXPRESSION "Hidden Test"
)
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"
FAIL_REGULAR_EXPRESSION "[.]")
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"
FAIL_REGULAR_EXPRESSION "Hidden Test")
add_test(NAME NoAssertions COMMAND $<TARGET_FILE:SelfTest> -w NoAssertions)
set_tests_properties(NoAssertions PROPERTIES PASS_REGULAR_EXPRESSION "No assertions in test case")
add_test(NAME NoTest COMMAND $<TARGET_FILE:SelfTest> -w NoTests "___nonexistent_test___")
set_tests_properties(NoTest PROPERTIES PASS_REGULAR_EXPRESSION "No test cases matched")
# 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 (CATCH_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")
endif() endif()
endif() # !NO_SELFTEST endif() # !NO_SELFTEST
if(BUILD_EXAMPLES) if(CATCH_BUILD_EXAMPLES)
add_subdirectory(examples) add_subdirectory(examples)
endif() endif()
install(DIRECTORY "single_include/" DESTINATION "${CMAKE_INSTALL_INCLUDEDIR}/catch")
install(DIRECTORY "single_include/" DESTINATION "include/catch") install(DIRECTORY docs/ DESTINATION "${CMAKE_INSTALL_DOCDIR}")
## Provide some pkg-config integration ## Provide some pkg-config integration
# Don't bother on Windows # Don't bother on Windows
if(NOT WIN32 OR NOT CMAKE_HOST_SYSTEM_NAME MATCHES Windows) if(NOT WIN32 OR NOT CMAKE_HOST_SYSTEM_NAME MATCHES Windows)
set(PKGCONFIG_INSTALL_DIR set(PKGCONFIG_INSTALL_DIR
"${CMAKE_INSTALL_PREFIX}/share/pkgconfig" "${CMAKE_INSTALL_DATAROOTDIR}/pkgconfig"
CACHE PATH "Path where catch.pc is installed" CACHE PATH "Path where catch.pc is installed"
) )
@ -359,3 +417,38 @@ if(NOT WIN32 OR NOT CMAKE_HOST_SYSTEM_NAME MATCHES Windows)
install(FILES ${CMAKE_CURRENT_BINARY_DIR}/catch.pc DESTINATION ${PKGCONFIG_INSTALL_DIR}) install(FILES ${CMAKE_CURRENT_BINARY_DIR}/catch.pc DESTINATION ${PKGCONFIG_INSTALL_DIR})
endif() endif()
# add catch as a 'linkable' target
add_library(Catch INTERFACE)
# depend on some obvious c++11 features so the dependency is transitively added dependants
target_compile_features(Catch INTERFACE cxx_auto_type cxx_constexpr cxx_noexcept)
target_include_directories(Catch
INTERFACE
$<BUILD_INTERFACE:${CMAKE_CURRENT_LIST_DIR}/single_include>
$<INSTALL_INTERFACE:include/catch>
$<INSTALL_INTERFACE:include>)
# provide a namespaced alias for clients to 'link' against if catch is included as a sub-project
add_library(Catch2::Catch ALIAS Catch)
set(CATCH_CMAKE_CONFIG_DESTINATION "${CMAKE_INSTALL_LIBDIR}/cmake/Catch2")
# create and install an export set for catch target as Catch2::Catch
install(TARGETS Catch EXPORT Catch2Config DESTINATION ${CMAKE_INSTALL_LIBDIR})
install(EXPORT Catch2Config
NAMESPACE Catch2::
DESTINATION ${CATCH_CMAKE_CONFIG_DESTINATION})
# install Catch2ConfigVersion.cmake file to handle versions in find_package
include(CMakePackageConfigHelpers)
write_basic_package_version_file(
"${CMAKE_CURRENT_BINARY_DIR}/Catch2ConfigVersion.cmake"
COMPATIBILITY SameMajorVersion)
install(FILES
"${CMAKE_CURRENT_BINARY_DIR}/Catch2ConfigVersion.cmake"
DESTINATION ${CATCH_CMAKE_CONFIG_DESTINATION})

View File

@ -2,15 +2,16 @@
![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) [![codecov](https://codecov.io/gh/catchorg/Catch2/branch/master/graph/badge.svg)](https://codecov.io/gh/catchorg/Catch2)
[![Try online](https://img.shields.io/badge/try-online-blue.svg)](https://wandbox.org/permlink/vOtfjd7LKmcj4JqD)
<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.2.0/catch.hpp">The latest version of the single header can be downloaded directly using this link</a>
## Catch2 is released! ## Catch2 is released!
If you've been using an earlier version of Catch, please see the If you've been using an earlier version of Catch, please see the
Breaking Changes section of [the release notes](https://github.com/catchorg/Catch2/releases/tag/v2.0.1) Breaking Changes section of [the release notes](https://github.com/catchorg/Catch2/releases/tag/v2.0.1)
before moving to Catch2. You might also like to read [this blog post](http://www.levelofindirection.com/journal/2017/11/3/catch2-released.html) for more details. before moving to Catch2. You might also like to read [this blog post](http://www.levelofindirection.com/journal/2017/11/3/catch2-released.html) for more details.

View File

@ -19,19 +19,51 @@ environment:
- additional_flags: "/D_UNICODE /DUNICODE" - additional_flags: "/D_UNICODE /DUNICODE"
wmain: 1 wmain: 1
coverage: 0
# Have a coverage dimension
- additional_flags: ""
wmain: 0
coverage: 1
# Have an examples dimension
- additional_flags: ""
wmain: 0
examples: 1
matrix: matrix:
exclude: exclude:
- os: Visual Studio 2015 - os: Visual Studio 2015
additional_flags: "/permissive- /std:c++latest" additional_flags: "/permissive- /std:c++latest"
init: - os: Visual Studio 2015
- git config --global core.autocrlf input additional_flags: "/D_UNICODE /DUNICODE"
# Set build version to git commit-hash
- ps: Update-AppveyorBuild -Version "$($env:APPVEYOR_REPO_BRANCH) - $($env:APPVEYOR_REPO_COMMIT)"
# fetch repository as zip archive # Exclude unwanted coverage configurations
shallow_clone: true - coverage: 1
platform: Win32
- coverage: 1
os: Visual Studio 2015
- coverage: 1
configuration: Release
# Exclude unwanted examples configurations
- examples: 1
platform: Win32
- examples: 1
os: Visual Studio 2015
- examples: 1
configuration: Release
install:
- ps: if (($env:CONFIGURATION) -eq "Debug" -And ($env:coverage) -eq "1" ) { python -m pip install codecov }
- ps: if (($env:CONFIGURATION) -eq "Debug" -And ($env:coverage) -eq "1" ) { .\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,16 +78,19 @@ 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:
project: Build\CatchSelfTest.sln # path to Visual Studio solution or project project: Build\Catch2.sln # path to Visual Studio solution or project
parallel: true # enable MSBuild parallel builds parallel: true # enable MSBuild parallel builds
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

View File

@ -1,9 +1,6 @@
prefix=@CMAKE_INSTALL_PREFIX@ includedir=@CMAKE_INSTALL_FULL_INCLUDEDIR@
exec_prefix=${prefix}
Name: Catch Name: Catch
Description: Testing library for C++ Description: Testing library for C++
Requires: Version: @Catch2_VERSION@
Version: @CATCH_VERSION_NUMBER@ Cflags: -I${includedir}
Libs:
Cflags: -I${prefix}/@INCLUDE_INSTALL_DIR@/include

26
Telegram/ThirdParty/Catch/codecov.yml vendored Normal file
View File

@ -0,0 +1,26 @@
coverage:
precision: 2
round: nearest
range: "60...90"
status:
project:
default:
threshold: 2%
patch:
default:
target: 80%
codecov:
branch: master
comment:
layout: "diff"
coverage:
ignore:
- "projects/SelfTest"
- "**/catch_reporter_tap.hpp"
- "**/catch_reporter_automake.hpp"
- "**/catch_reporter_teamcity.hpp"
- "**/external/clara.hpp"

View File

@ -4,7 +4,7 @@ from conans import ConanFile
class CatchConan(ConanFile): class CatchConan(ConanFile):
name = "Catch" name = "Catch"
version = "2.0.1" version = "2.2.0"
description = "A modern, C++-native, header-only, framework for unit-tests, TDD and BDD" description = "A modern, C++-native, header-only, framework for unit-tests, TDD and BDD"
author = "philsquared" author = "philsquared"
generators = "cmake" generators = "cmake"

View File

@ -0,0 +1,175 @@
# Distributed under the OSI-approved BSD 3-Clause License. See accompanying
# file Copyright.txt or https://cmake.org/licensing for details.
#[=======================================================================[.rst:
Catch
-----
This module defines a function to help use the Catch test framework.
The :command:`catch_discover_tests` discovers tests by asking the compiled test
executable to enumerate its tests. This does not require CMake to be re-run
when tests change. However, it may not work in a cross-compiling environment,
and setting test properties is less convenient.
This command is intended to replace use of :command:`add_test` to register
tests, and will create a separate CTest test for each Catch test case. Note
that this is in some cases less efficient, as common set-up and tear-down logic
cannot be shared by multiple test cases executing in the same instance.
However, it provides more fine-grained pass/fail information to CTest, which is
usually considered as more beneficial. By default, the CTest test name is the
same as the Catch name; see also ``TEST_PREFIX`` and ``TEST_SUFFIX``.
.. command:: catch_discover_tests
Automatically add tests with CTest by querying the compiled test executable
for available tests::
catch_discover_tests(target
[TEST_SPEC arg1...]
[EXTRA_ARGS arg1...]
[WORKING_DIRECTORY dir]
[TEST_PREFIX prefix]
[TEST_SUFFIX suffix]
[PROPERTIES name1 value1...]
[TEST_LIST var]
)
``catch_discover_tests`` sets up a post-build command on the test executable
that generates the list of tests by parsing the output from running the test
with the ``--list-test-names-only`` argument. This ensures that the full
list of tests is obtained. Since test discovery occurs at build time, it is
not necessary to re-run CMake when the list of tests changes.
However, it requires that :prop_tgt:`CROSSCOMPILING_EMULATOR` is properly set
in order to function in a cross-compiling environment.
Additionally, setting properties on tests is somewhat less convenient, since
the tests are not available at CMake time. Additional test properties may be
assigned to the set of tests as a whole using the ``PROPERTIES`` option. If
more fine-grained test control is needed, custom content may be provided
through an external CTest script using the :prop_dir:`TEST_INCLUDE_FILES`
directory property. The set of discovered tests is made accessible to such a
script via the ``<target>_TESTS`` variable.
The options are:
``target``
Specifies the Catch executable, which must be a known CMake executable
target. CMake will substitute the location of the built executable when
running the test.
``TEST_SPEC arg1...``
Specifies test cases, wildcarded test cases, tags and tag expressions to
pass to the Catch executable with the ``--list-test-names-only`` argument.
``EXTRA_ARGS arg1...``
Any extra arguments to pass on the command line to each test case.
``WORKING_DIRECTORY dir``
Specifies the directory in which to run the discovered test cases. If this
option is not provided, the current binary directory is used.
``TEST_PREFIX prefix``
Specifies a ``prefix`` to be prepended to the name of each discovered test
case. This can be useful when the same test executable is being used in
multiple calls to ``catch_discover_tests()`` but with different
``TEST_SPEC`` or ``EXTRA_ARGS``.
``TEST_SUFFIX suffix``
Similar to ``TEST_PREFIX`` except the ``suffix`` is appended to the name of
every discovered test case. Both ``TEST_PREFIX`` and ``TEST_SUFFIX`` may
be specified.
``PROPERTIES name1 value1...``
Specifies additional properties to be set on all tests discovered by this
invocation of ``catch_discover_tests``.
``TEST_LIST var``
Make the list of tests available in the variable ``var``, rather than the
default ``<target>_TESTS``. This can be useful when the same test
executable is being used in multiple calls to ``catch_discover_tests()``.
Note that this variable is only available in CTest.
#]=======================================================================]
#------------------------------------------------------------------------------
function(catch_discover_tests TARGET)
cmake_parse_arguments(
""
""
"TEST_PREFIX;TEST_SUFFIX;WORKING_DIRECTORY;TEST_LIST"
"TEST_SPEC;EXTRA_ARGS;PROPERTIES"
${ARGN}
)
if(NOT _WORKING_DIRECTORY)
set(_WORKING_DIRECTORY "${CMAKE_CURRENT_BINARY_DIR}")
endif()
if(NOT _TEST_LIST)
set(_TEST_LIST ${TARGET}_TESTS)
endif()
## Generate a unique name based on the extra arguments
string(SHA1 args_hash "${_TEST_SPEC} ${_EXTRA_ARGS}")
string(SUBSTRING ${args_hash} 0 7 args_hash)
# Define rule to generate test list for aforementioned test executable
set(ctest_include_file "${CMAKE_CURRENT_BINARY_DIR}/${TARGET}_include-${args_hash}.cmake")
set(ctest_tests_file "${CMAKE_CURRENT_BINARY_DIR}/${TARGET}_tests-${args_hash}.cmake")
get_property(crosscompiling_emulator
TARGET ${TARGET}
PROPERTY CROSSCOMPILING_EMULATOR
)
add_custom_command(
TARGET ${TARGET} POST_BUILD
BYPRODUCTS "${ctest_tests_file}"
COMMAND "${CMAKE_COMMAND}"
-D "TEST_TARGET=${TARGET}"
-D "TEST_EXECUTABLE=$<TARGET_FILE:${TARGET}>"
-D "TEST_EXECUTOR=${crosscompiling_emulator}"
-D "TEST_WORKING_DIR=${_WORKING_DIRECTORY}"
-D "TEST_SPEC=${_TEST_SPEC}"
-D "TEST_EXTRA_ARGS=${_EXTRA_ARGS}"
-D "TEST_PROPERTIES=${_PROPERTIES}"
-D "TEST_PREFIX=${_TEST_PREFIX}"
-D "TEST_SUFFIX=${_TEST_SUFFIX}"
-D "TEST_LIST=${_TEST_LIST}"
-D "CTEST_FILE=${ctest_tests_file}"
-P "${_CATCH_DISCOVER_TESTS_SCRIPT}"
VERBATIM
)
file(WRITE "${ctest_include_file}"
"if(EXISTS \"${ctest_tests_file}\")\n"
" include(\"${ctest_tests_file}\")\n"
"else()\n"
" add_test(${TARGET}_NOT_BUILT-${args_hash} ${TARGET}_NOT_BUILT-${args_hash})\n"
"endif()\n"
)
if(NOT ${CMAKE_VERSION} VERSION_LESS "3.10.0")
# Add discovered tests to directory TEST_INCLUDE_FILES
set_property(DIRECTORY
APPEND PROPERTY TEST_INCLUDE_FILES "${ctest_include_file}"
)
else()
# Add discovered tests as directory TEST_INCLUDE_FILE if possible
get_property(test_include_file_set DIRECTORY PROPERTY TEST_INCLUDE_FILE SET)
if (NOT ${test_include_file_set})
set_property(DIRECTORY
PROPERTY TEST_INCLUDE_FILE "${ctest_include_file}"
)
else()
message(FATAL_ERROR
"Cannot set more than one TEST_INCLUDE_FILE"
)
endif()
endif()
endfunction()
###############################################################################
set(_CATCH_DISCOVER_TESTS_SCRIPT
${CMAKE_CURRENT_LIST_DIR}/CatchAddTests.cmake
)

View File

@ -0,0 +1,77 @@
# Distributed under the OSI-approved BSD 3-Clause License. See accompanying
# file Copyright.txt or https://cmake.org/licensing for details.
set(prefix "${TEST_PREFIX}")
set(suffix "${TEST_SUFFIX}")
set(spec ${TEST_SPEC})
set(extra_args ${TEST_EXTRA_ARGS})
set(properties ${TEST_PROPERTIES})
set(script)
set(suite)
set(tests)
function(add_command NAME)
set(_args "")
foreach(_arg ${ARGN})
if(_arg MATCHES "[^-./:a-zA-Z0-9_]")
set(_args "${_args} [==[${_arg}]==]") # form a bracket_argument
else()
set(_args "${_args} ${_arg}")
endif()
endforeach()
set(script "${script}${NAME}(${_args})\n" PARENT_SCOPE)
endfunction()
# Run test executable to get list of available tests
if(NOT EXISTS "${TEST_EXECUTABLE}")
message(FATAL_ERROR
"Specified test executable '${TEST_EXECUTABLE}' does not exist"
)
endif()
execute_process(
COMMAND ${TEST_EXECUTOR} "${TEST_EXECUTABLE}" ${spec} --list-test-names-only
OUTPUT_VARIABLE output
RESULT_VARIABLE result
)
# Catch --list-test-names-only reports the number of tests, so 0 is... surprising
if(${result} EQUAL 0)
message(WARNING
"Test executable '${TEST_EXECUTABLE}' contains no tests!\n"
)
elseif(${result} LESS 0)
message(FATAL_ERROR
"Error running test executable '${TEST_EXECUTABLE}':\n"
" Result: ${result}\n"
" Output: ${output}\n"
)
endif()
string(REPLACE "\n" ";" output "${output}")
# Parse output
foreach(line ${output})
# Test name; strip spaces to get just the name...
string(REGEX REPLACE " +" "" test "${line}")
# ...and add to script
add_command(add_test
"${prefix}${test}${suffix}"
${TEST_EXECUTOR}
"${TEST_EXECUTABLE}"
"${test}"
${extra_args}
)
add_command(set_tests_properties
"${prefix}${test}${suffix}"
PROPERTIES
WORKING_DIRECTORY "${TEST_WORKING_DIR}"
${properties}
)
list(APPEND tests "${prefix}${test}${suffix}")
endforeach()
# Create a list of all discovered tests, which users may use to e.g. set
# properties on the tests
add_command(set ${TEST_LIST} ${tests})
# Write CTest script
file(WRITE "${CTEST_FILE}" "${script}")

View File

@ -9,7 +9,7 @@
Most test frameworks have a large collection of assertion macros to capture all possible conditional forms (```_EQUALS```, ```_NOTEQUALS```, ```_GREATER_THAN``` etc). Most test frameworks have a large collection of assertion macros to capture all possible conditional forms (```_EQUALS```, ```_NOTEQUALS```, ```_GREATER_THAN``` etc).
Catch is different. Because it decomposes natural C-style conditional expressions most of these forms are reduced to one or two that you will use all the time. That said there are a rich set of auxilliary macros as well. We'll describe all of these here. Catch is different. Because it decomposes natural C-style conditional expressions most of these forms are reduced to one or two that you will use all the time. That said there are a rich set of auxiliary macros as well. We'll describe all of these here.
Most of these macros come in two forms: Most of these macros come in two forms:
@ -21,7 +21,7 @@ The ```CHECK``` family are equivalent but execution continues in the same test c
* **REQUIRE(** _expression_ **)** and * **REQUIRE(** _expression_ **)** and
* **CHECK(** _expression_ **)** * **CHECK(** _expression_ **)**
Evaluates the expression and records the result. If an exception is thrown it is caught, reported, and counted as a failure. These are the macros you will use most of the time Evaluates the expression and records the result. If an exception is thrown, it is caught, reported, and counted as a failure. These are the macros you will use most of the time.
Examples: Examples:
``` ```

View File

@ -28,9 +28,14 @@ The advantage of this format is that the JUnit Ant schema is widely understood b
The disadvantage is that this schema was designed to correspond to how JUnit works - and there is a significant mismatch with how Catch works. Additionally the format is not streamable (because opening elements hold counts of failed and passing tests as attributes) - so the whole test run must complete before it can be written. The disadvantage is that this schema was designed to correspond to how JUnit works - and there is a significant mismatch with how Catch works. Additionally the format is not streamable (because opening elements hold counts of failed and passing tests as attributes) - so the whole test run must complete before it can be written.
## Other reporters ## Other reporters
Other reporters are not part of the single-header distribution and need to be downloaded and included separately. All reporters are stored in `include/reporters` directory in the git repository, and are named `catch_reporter_*.hpp`. For example, to use the TeamCity reporter you need to download `include/reporters/catch_reporter_teamcity.hpp` and include it after Catch itself. Other reporters are not part of the single-header distribution and need
to be downloaded and included separately. All reporters are stored in
`single_include` directory in the git repository, and are named
`catch_reporter_*.hpp`. For example, to use the TeamCity reporter you
need to download `single_include/catch_reporter_teamcity.hpp` and include
it after Catch itself.
``` ```cpp
#define CATCH_CONFIG_MAIN #define CATCH_CONFIG_MAIN
#include "catch.hpp" #include "catch.hpp"
#include "catch_reporter_teamcity.hpp" #include "catch_reporter_teamcity.hpp"
@ -124,7 +129,15 @@ The advantage of this approach is that you can always automatically update Catch
### Automatic test registration ### Automatic test registration
If you are also using ctest, `contrib/ParseAndAddCatchTests.cmake` is a CMake script that attempts to parse your test files and automatically register all test cases, using tags as labels. This means that these We provide 2 CMake scripts that can automatically register Catch-based
tests with CTest,
* `contrib/ParseAndAddCatchTests.cmake`
* `contrib/CatchAddTests.cmake`
The first is based on parsing the test implementation files, and attempts
to register all `TEST_CASE`s using their tags as labels. This means that
these:
```cpp ```cpp
TEST_CASE("Test1", "[unit]") { TEST_CASE("Test1", "[unit]") {
int a = 1; int a = 1;
@ -144,7 +157,14 @@ TEST_CASE("Test3", "[a][b][c]") {
REQUIRE(a == b); REQUIRE(a == b);
} }
``` ```
would be registered as 3 tests, `Test1`, `Test2` and `Test3`, and 4 ctest labels would be created, `a`, `b`, `c` and `unit`. would be registered as 3 tests, `Test1`, `Test2` and `Test3`,
and 4 CTest labels would be created, `a`, `b`, `c` and `unit`.
The second is based on parsing the output of a Catch binary given
`--list-test-names-only`. This means that it deals with inactive
(e.g. commented-out) tests better, but requires CMake 3.10 for full
functionality.
### CodeCoverage module (GCOV, LCOV...) ### CodeCoverage module (GCOV, LCOV...)

View File

@ -78,7 +78,7 @@ Wildcards consist of the `*` character at the beginning and/or end of test case
Test specs are case insensitive. Test specs are case insensitive.
If a spec is prefixed with `exclude:` or the `~` character then the pattern matches an exclusion. This means that tests matching the pattern are excluded from the set - even if a prior inclusion spec included them. Subsequent inclusion specs will take precendence, however. If a spec is prefixed with `exclude:` or the `~` character then the pattern matches an exclusion. This means that tests matching the pattern are excluded from the set - even if a prior inclusion spec included them. Subsequent inclusion specs will take precedence, however.
Inclusions and exclusions are evaluated in left-to-right order. Inclusions and exclusions are evaluated in left-to-right order.
Test case examples: Test case examples:
@ -94,7 +94,7 @@ a* ~ab* abc Matches all tests that start with 'a', except those that
</pre> </pre>
Names within square brackets are interpreted as tags. Names within square brackets are interpreted as tags.
A series of tags form an AND expression wheras a comma-separated sequence forms an OR expression. e.g.: A series of tags form an AND expression whereas a comma-separated sequence forms an OR expression. e.g.:
<pre>[one][two],[three]</pre> <pre>[one][two],[three]</pre>
This matches all tests tagged `[one]` and `[two]`, as well as all tests tagged `[three]` This matches all tests tagged `[one]` and `[two]`, as well as all tests tagged `[three]`
@ -192,9 +192,16 @@ This option transforms tabs and newline characters into ```\t``` and ```\n``` re
## Warnings ## Warnings
<pre>-w, --warn &lt;warning name></pre> <pre>-w, --warn &lt;warning name></pre>
Enables reporting of warnings (only one, at time of this writing). If a warning is issued it fails the test. Enables reporting of suspicious test states. There are currently two
available warnings
```
NoAssertions // Fail test case / leaf section if no assertions
// (e.g. `REQUIRE`) is encountered.
NoTests // Return non-zero exit code when no test cases were run
// Also calls reporter's noMatchingTestCases method
```
The ony available warning, presently, is ```NoAssertions```. This warning fails a test case, or (leaf) section if no assertions (```REQUIRE```/ ```CHECK``` etc) are encountered.
<a id="reporting-timings"></a> <a id="reporting-timings"></a>
## Reporting timings ## Reporting timings
@ -262,7 +269,7 @@ either before running any tests, after running all tests - or both, depending on
When running benchmarks the clock resolution is estimated. Benchmarks are then run for exponentially increasing When running benchmarks the clock resolution is estimated. Benchmarks are then run for exponentially increasing
numbers of iterations until some multiple of the estimated resolution is exceed. By default that multiple is 100, but numbers of iterations until some multiple of the estimated resolution is exceed. By default that multiple is 100, but
it can be overriden here. it can be overridden here.
<a id="usage"></a> <a id="usage"></a>
## Usage ## Usage

View File

@ -7,6 +7,8 @@
[Terminal colour](#terminal-colour)<br> [Terminal colour](#terminal-colour)<br>
[Console width](#console-width)<br> [Console width](#console-width)<br>
[stdout](#stdout)<br> [stdout](#stdout)<br>
[Fallback stringifier](#fallback-stringifier)<br>
[Default reporter](#default-reporter)<br>
[Other toggles](#other-toggles)<br> [Other toggles](#other-toggles)<br>
[Windows header clutter](#windows-header-clutter)<br> [Windows header clutter](#windows-header-clutter)<br>
[Enabling stringification](#enabling-stringification)<br> [Enabling stringification](#enabling-stringification)<br>
@ -24,7 +26,7 @@ Although Catch is header only it still, internally, maintains a distinction betw
# Reporter / Listener interfaces # Reporter / Listener interfaces
CATCH_CONFIG_EXTERNAL_INTERFACES // Brings in neccessary headers for Reporter/Listener implementation CATCH_CONFIG_EXTERNAL_INTERFACES // Brings in necessary headers for Reporter/Listener implementation
Brings in various parts of Catch that are required for user defined Reporters and Listeners. This means that new Reporters and Listeners can be defined in this file as well as in the main file. Brings in various parts of Catch that are required for user defined Reporters and Listeners. This means that new Reporters and Listeners can be defined in this file as well as in the main file.
@ -73,6 +75,40 @@ Catch does not use ```std::cout```, ```std::cerr``` and ```std::clog``` directly
This can be useful on certain platforms that do not provide the standard iostreams, such as certain embedded systems. This can be useful on certain platforms that do not provide the standard iostreams, such as certain embedded systems.
## Fallback stringifier
By default Catch's stringification machinery falls back to a "{?}". To
let projects reuse their own existing stringification machinery, this
fallback can be overridden by defining `CATCH_CONFIG_FALLBACK_STRINGIFIER`
to a name of a function that should perform the stringification instead.
The provided function must return std::string and must accept any type
(e.g. via overloading).
_Note that if the provided function does not handle a type and this type
requires to be stringified, the compilation will fail._
## Default reporter
Catch's default reporter can be changed by defining macro
`CATCH_CONFIG_DEFAULT_REPORTER` to string literal naming the desired
default reporter.
This means that defining `CATCH_CONFIG_DEFAULT_REPORTER` to `"console"`
is equivalent with the out-of-the-box experience.
## C++17 toggles
CATCH_CONFIG_CPP17_UNCAUGHT_EXCEPTIONS // Use std::uncaught_exceptions instead of std::uncaught_exception
Catch contains basic compiler/standard detection and attempts to use
some C++17 features whenever appropriate. This automatic detection
can be manually overridden in both directions, that is, a feature
can be enabled by defining the macro in the table above, and disabled
by using `_NO_` in the macro, e.g. `CATCH_CONFIG_NO_CPP17_UNCAUGHT_EXCEPTIONS`.
## Other toggles ## Other toggles
@ -84,6 +120,7 @@ This can be useful on certain platforms that do not provide the standard iostrea
CATCH_CONFIG_WINDOWS_CRTDBG // Enable leak checking using Windows's CRT Debug Heap CATCH_CONFIG_WINDOWS_CRTDBG // Enable leak checking using Windows's CRT Debug Heap
CATCH_CONFIG_DISABLE_STRINGIFICATION // Disable stringifying the original expression CATCH_CONFIG_DISABLE_STRINGIFICATION // Disable stringifying the original expression
CATCH_CONFIG_DISABLE // Disables assertions and test case registration CATCH_CONFIG_DISABLE // Disables assertions and test case registration
CATCH_CONFIG_WCHAR // Enables use of wchart_t
Currently Catch enables `CATCH_CONFIG_WINDOWS_SEH` only when compiled with MSVC, because some versions of MinGW do not have the necessary Win32 API support. Currently Catch enables `CATCH_CONFIG_WINDOWS_SEH` only when compiled with MSVC, because some versions of MinGW do not have the necessary Win32 API support.
@ -91,12 +128,15 @@ Currently Catch enables `CATCH_CONFIG_WINDOWS_SEH` only when compiled with MSVC,
`CATCH_CONFIG_WINDOWS_CRTDBG` is off by default. If enabled, Windows's CRT is used to check for memory leaks, and displays them after the tests finish running. `CATCH_CONFIG_WINDOWS_CRTDBG` is off by default. If enabled, Windows's CRT is used to check for memory leaks, and displays them after the tests finish running.
`CATCH_CONFIG_WCHAR` is on by default, but can be disabled. Currently
it is only used in support for DJGPP cross-compiler.
These toggles can be disabled by using `_NO_` form of the toggle, e.g. `CATCH_CONFIG_NO_WINDOWS_SEH`. These toggles can be disabled by using `_NO_` form of the toggle, e.g. `CATCH_CONFIG_NO_WINDOWS_SEH`.
### `CATCH_CONFIG_FAST_COMPILE` ### `CATCH_CONFIG_FAST_COMPILE`
Defining this flag speeds up compilation of test files by ~20%, by making 2 changes: Defining this flag speeds up compilation of test files by ~20%, by making 2 changes:
* The `-b` (`--break`) flag no longer makes Catch break into debugger in the same stack frame as the failed test, but rather in a stack frame *below*. * The `-b` (`--break`) flag no longer makes Catch break into debugger in the same stack frame as the failed test, but rather in a stack frame *below*.
* Non-exception family of macros ({`REQUIRE`,`CHECK`}{`_`,`_FALSE`, `_FALSE`}, no longer use local try-cache block. This disables exception translation, but should not lead to false negatives. * Non-exception family of macros ({`REQUIRE`,`CHECK`}{`_`,`_FALSE`, `_THAT`}, no longer use local try-catch block. This disables exception translation, but should not lead to false negatives.
`CATCH_CONFIG_FAST_COMPILE` has to be either defined, or not defined, in all translation units that are linked into single test binary, or the behaviour of setting `-b` flag and throwing unexpected exceptions will be unpredictable. `CATCH_CONFIG_FAST_COMPILE` has to be either defined, or not defined, in all translation units that are linked into single test binary, or the behaviour of setting `-b` flag and throwing unexpected exceptions will be unpredictable.

View File

@ -44,7 +44,7 @@ _Note that you should not use any assertion macros within a Listener!_
## Events that can be hooked ## Events that can be hooked
The following are the methods that can be overriden in the Listener: The following are the methods that can be overridden in the Listener:
```c++ ```c++
// The whole test run, starting and ending // The whole test run, starting and ending

View File

@ -96,6 +96,14 @@ with expansion:
""\" == ""\" ""\" == ""\"
``` ```
### Visual Studio 2015 -- Alignment compilation error (C2718)
VS 2015 has a known bug, where `declval<T>` can cause compilation error
if `T` has alignment requirements that it cannot meet.
A workaround is to explicitly specialize `Catch::is_range` for given
type (this avoids code path that uses `declval<T>` in a SFINAE context).
### Visual Studio 2015 -- Wrong line number reported in debug mode ### Visual Studio 2015 -- Wrong line number reported in debug mode

View File

@ -1,18 +1,18 @@
<a id="top"></a> <a id="top"></a>
# Open Source projects using Catch # Open Source projects using Catch
Catch is great for open source. With its [liberal license](../LICENSE.txt) and single-header, dependency-free, distribution Catch is great for open source. With its [liberal license](../LICENSE.txt) and single-header, dependency-free, distribution
it's easy to just drop the header into your project and start writing tests - what's not to like? it's easy to just drop the header into your project and start writing tests - what's not to like?
As a result Catch is now being used in many Open Source projects, including some quite well known ones. As a result Catch is now being used in many Open Source projects, including some quite well known ones.
This page is an attempt to track those projects. Obviously it can never be complete. This page is an attempt to track those projects. Obviously it can never be complete.
This effort largely relies on the maintainers of the projects themselves updating this page and submitting a PR This effort largely relies on the maintainers of the projects themselves updating this page and submitting a PR
(or, if you prefer contact one of the maintainers of Catch directly, use the (or, if you prefer contact one of the maintainers of Catch directly, use the
[forums](https://groups.google.com/forum/?fromgroups#!forum/catch-forum)), or raise an [issue](https://github.com/philsquared/Catch/issues) to let us know). [forums](https://groups.google.com/forum/?fromgroups#!forum/catch-forum)), or raise an [issue](https://github.com/philsquared/Catch/issues) to let us know).
Of course users of those projects might want to update this page too. That's fine - as long you're confident the project maintainers won't mind. Of course users of those projects might want to update this page too. That's fine - as long you're confident the project maintainers won't mind.
If you're an Open Source project maintainer and see your project listed here but would rather it wasn't - If you're an Open Source project maintainer and see your project listed here but would rather it wasn't -
just let us know via any of the previously mentioned means - although I'm sure there won't be many who feel that way. just let us know via any of the previously mentioned means - although I'm sure there won't be many who feel that way.
Listing a project here does not imply endorsement and the plan is to keep these ordered alphabetically to avoid an implication of relative importance. Listing a project here does not imply endorsement and the plan is to keep these ordered alphabetically to avoid an implication of relative importance.
## Libraries & Frameworks ## Libraries & Frameworks
@ -21,7 +21,7 @@ Listing a project here does not imply endorsement and the plan is to keep these
Boost Asio style bindings for ZeroMQ Boost Asio style bindings for ZeroMQ
### [ChakraCore](https://github.com/Microsoft/ChakraCore) ### [ChakraCore](https://github.com/Microsoft/ChakraCore)
The core part of the Chakra Javascript engine that powers Microsoft Edge The core part of the Chakra JavaScript engine that powers Microsoft Edge
### [ChaiScript](https://github.com/ChaiScript/ChaiScript) ### [ChaiScript](https://github.com/ChaiScript/ChaiScript)
A, header-only, embedded scripting language designed from the ground up to directly target C++ and take advantage of modern C++ development techniques A, header-only, embedded scripting language designed from the ground up to directly target C++ and take advantage of modern C++ development techniques
@ -30,7 +30,16 @@ A, header-only, embedded scripting language designed from the ground up to direc
A, single-header-only, type-safe, command line parser - which also prints formatted usage strings. A, single-header-only, type-safe, command line parser - which also prints formatted usage strings.
### [Couchbase-lite-core](https://github.com/couchbase/couchbase-lite-core) ### [Couchbase-lite-core](https://github.com/couchbase/couchbase-lite-core)
The next-generation core storage and query engine for Couchbase Lite/ The next-generation core storage and query engine for Couchbase Lite
### [DtCraft](https://github.com/twhuang-uiuc/DtCraft)
A High-performance Cluster Computing Engine
### [forest](https://github.com/xorz57/forest)
Forest is an open-source, template library of tree data structures written in C++11.
### [Fuxedo](https://github.com/fuxedo/fuxedo)
Open source Oracle Tuxedo-like XATMI middleware for C and C++.
### [Inja](https://github.com/pantor/inja) ### [Inja](https://github.com/pantor/inja)
A header-only template engine for modern C++. A header-only template engine for modern C++.
@ -82,6 +91,9 @@ MAME originally stood for Multiple Arcade Machine Emulator
### [Newsbeuter](https://github.com/akrennmair/newsbeuter) ### [Newsbeuter](https://github.com/akrennmair/newsbeuter)
Newsbeuter is an open-source RSS/Atom feed reader for text terminals. Newsbeuter is an open-source RSS/Atom feed reader for text terminals.
### [SpECTRE](https://github.com/sxs-collaboration/spectre)
SpECTRE is a code for multi-scale, multi-physics problems in astrophysics and gravitational physics.
### [Standardese](https://github.com/foonathan/standardese) ### [Standardese](https://github.com/foonathan/standardese)
Standardese aims to be a nextgen Doxygen Standardese aims to be a nextgen Doxygen

View File

@ -30,7 +30,7 @@ int main( int argc, char* argv[] ) {
## Amending the config ## Amending the config
If you still want Catch to process the command line, but you want to programatically tweak the config, you can do so in one of two ways: If you still want Catch to process the command line, but you want to programmatically tweak the config, you can do so in one of two ways:
```c++ ```c++
#define CATCH_CONFIG_RUNNER #define CATCH_CONFIG_RUNNER
@ -81,6 +81,7 @@ int main( int argc, char* argv[] )
int height = 0; // Some user variable you want to be able to set int height = 0; // Some user variable you want to be able to set
// Build a new parser on top of Catch's // Build a new parser on top of Catch's
using namespace Catch::clara;
auto cli auto cli
= session.cli() // Get Catch's composite command line parser = session.cli() // Get Catch's composite command line parser
| Opt( height, "height" ) // bind variable to a new option, with a hint string | Opt( height, "height" ) // bind variable to a new option, with a hint string
@ -105,6 +106,20 @@ int main( int argc, char* argv[] )
See the [Clara documentation](https://github.com/philsquared/Clara/blob/master/README.md) for more details. See the [Clara documentation](https://github.com/philsquared/Clara/blob/master/README.md) for more details.
## Version detection
Catch provides a triplet of macros providing the header's version,
* `CATCH_VERSION_MAJOR`
* `CATCH_VERSION_MINOR`
* `CATCH_VERSION_PATCH`
these macros expand into a single number, that corresponds to the appropriate
part of the version. As an example, given single header version v2.3.4,
the macros would expand into `2`, `3`, and `4` respectively.
--- ---
[Home](Readme.md#top) [Home](Readme.md#top)

View File

@ -1,4 +1,104 @@
<a id="top"></a> <a id="top"></a>
# 2.2.0
## Fixes
* Hidden tests are not listed by default when listing tests (#1175)
* This makes `catch_discover_tests` CMake script work better
* Fixed regression that meant `<windows.h>` could potentially not be included properly (#1197)
* Fixed installing `Catch2ConfigVersion.cmake` when Catch2 is a subproject.
## Improvements
* Added an option to warn (+ exit with error) when no tests were ran (#1158)
* Use as `-w NoTests`
* Added provisional support for Emscripten (#1114)
* [Added a way to override the fallback stringifier](https://github.com/catchorg/Catch2/blob/master/docs/configuration.md#fallback-stringifier) (#1024)
* This allows project's own stringification machinery to be easily reused for Catch
* `Catch::Session::run()` now accepts `char const * const *`, allowing it to accept array of string literals (#1031, #1178)
* The embedded version of Clara was bumped to v1.1.3
* Various minor performance improvements
* Added support for DJGPP DOS crosscompiler (#1206)
# 2.1.2
## Fixes
* Fixed compilation error with `-fno-rtti` (#1165)
* Fixed NoAssertion warnings
* `operator<<` is used before range-based stringification (#1172)
* Fixed `-Wpedantic` warnings (extra semicolons and binary literals) (#1173)
## Improvements
* Added `CATCH_VERSION_{MAJOR,MINOR,PATCH}` macros (#1131)
* Added `BrightYellow` colour for use in reporters (#979)
* It is also used by ConsoleReporter for reconstructed expressions
## Other changes
* Catch is now exported as a CMake package and linkable target (#1170)
# 2.1.1
## Improvements
* Static arrays are now properly stringified like ranges across MSVC/GCC/Clang
* Embedded newer version of Clara -- v1.1.1
* This should fix some warnings dragged in from Clara
* MSVC's CLR exceptions are supported
## Fixes
* Fixed compilation when comparison operators do not return bool (#1147)
* Fixed CLR exceptions blowing up the executable during translation (#1138)
## Other changes
* Many CMake changes
* `NO_SELFTEST` option is deprecated, use `BUILD_TESTING` instead.
* Catch specific CMake options were prefixed with `CATCH_` for namespacing purposes
* Other changes to simplify Catch2's packaging
# 2.1.0
## Improvements
* Various performance improvements
* On top of the performance regression fixes
* Experimental support for PCH was added (#1061)
* `CATCH_CONFIG_EXTERNAL_INTERFACES` now brings in declarations of Console, Compact, XML and JUnit reporters
* `MatcherBase` no longer has a pointless second template argument
* Reduced the number of warning suppressions that leak into user's code
* Bugs in g++ 4.x and 5.x mean that some of them have to be left in
## Fixes
* Fixed performance regression from Catch classic
* One of the performance improvement patches for Catch classic was not applied to Catch2
* Fixed platform detection for iOS (#1084)
* Fixed compilation when `g++` is used together with `libc++` (#1110)
* Fixed TeamCity reporter compilation with the single header version
* To fix the underlying issue we will be versioning reporters in single_include folder per release
* The XML reporter will now report `WARN` messages even when not used with `-s`
* Fixed compilation when `VectorContains` matcher was combined using `&&` (#1092)
* Fixed test duration overflowing after 10 seconds (#1125, #1129)
* Fixed `std::uncaught_exception` deprecation warning (#1124)
## New features
* New Matchers
* Regex matcher for strings, `Matches`.
* Set-equal matcher for vectors, `UnorderedEquals`
* Floating point matchers, `WithinAbs` and `WithinULP`.
* Stringification now attempts to decompose all containers (#606)
* Containers are objects that respond to ADL `begin(T)` and `end(T)`.
## Other changes
* Reporters will now be versioned in the `single_include` folder to ensure their compatibility with the last released version
# 2.0.1 # 2.0.1
## Breaking changes ## Breaking changes
@ -87,6 +187,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
@ -264,8 +399,8 @@ Cygwin issue with `gettimeofday` - `#define` was not early enough
* Cygwin compatibility fixes * Cygwin compatibility fixes
* Signal handling is no longer compiled by default. * Signal handling is no longer compiled by default.
* Usage of `gettimeofday` inside Catch should no longer cause compilation errors. * Usage of `gettimeofday` inside Catch should no longer cause compilation errors.
* Improved `-Wparentheses` supression for gcc (#674) * Improved `-Wparentheses` suppression for gcc (#674)
* When compiled with gcc 4.8 or newer, the supression is localized to assertions only * When compiled with gcc 4.8 or newer, the suppression is localized to assertions only
* Otherwise it is supressed for the whole TU * Otherwise it is supressed for the whole TU
* Fixed test spec parser issue (with escapes in multiple names) * Fixed test spec parser issue (with escapes in multiple names)
@ -284,7 +419,7 @@ Xml:
* C-escape control characters instead of XML encoding them (which requires XML 1.1) * C-escape control characters instead of XML encoding them (which requires XML 1.1)
* Revert XML output to XML 1.0 * Revert XML output to XML 1.0
* Can provide stylesheet references by extending the XML reporter * Can provide stylesheet references by extending the XML reporter
* Added description and tags attribites to XML Reporter * Added description and tags attributes to XML Reporter
* Tags are closed and the stream flushed more eagerly to avoid stdout interpolation * Tags are closed and the stream flushed more eagerly to avoid stdout interpolation

View File

@ -1,11 +1,11 @@
<a id="top"></a> <a id="top"></a>
# How to release # How to release
When enough changes have accumulated, it is time to release new version of Catch. This document describes the proces in doing so, that no steps are forgotten. Note that all referenced scripts can be found in the `scripts/` directory. When enough changes have accumulated, it is time to release new version of Catch. This document describes the process in doing so, that no steps are forgotten. Note that all referenced scripts can be found in the `scripts/` directory.
## Neccessary steps ## Necessary steps
These steps are neccessary and have to be performed before each new release. They serve to make sure that the new release is correct and linked-to from the standard places. These steps are necessary and have to be performed before each new release. They serve to make sure that the new release is correct and linked-to from the standard places.
### Approval testing ### Approval testing
@ -21,10 +21,8 @@ Catch uses a variant of [semantic versioning](http://semver.org/), with breaking
After deciding which part of version number should be incremented, you can use one of the `*Release.py` scripts to perform the required changes to Catch. After deciding which part of version number should be incremented, you can use one of the `*Release.py` scripts to perform the required changes to Catch.
This will take care of generating the single include header, updating
### Generate updated single-include header version numbers everywhere and pushing the new version to Wandbox.
After updating version number, regenerate single-include header using `generateSingleHeader.py`.
### Release notes ### Release notes
@ -39,16 +37,24 @@ After version number is incremented, single-include header is regenerated and re
### Release on GitHub ### Release on GitHub
After pushing changes to GitHub, GitHub release *needs* to be created. Tag version and release title should be same as the new version, description should contain the release notes for the current release. Single header version of `catch.hpp` *needs* to be attached as a binary, as that is where the official download link links to. Preferably it should use linux line endings. After pushing changes to GitHub, GitHub release *needs* to be created.
Tag version and release title should be same as the new version,
description should contain the release notes for the current release.
Single header version of `catch.hpp` *needs* to be attached as a binary,
as that is where the official download link links to. Preferably
it should use linux line endings. All non-bundled reporters (Automake,
TAP, TeamCity) should also be attached as binaries, as they are dependent
on a specific version of the single-include header.
## Optional steps ## Optional steps
The following steps are optional, and do not have to be performed when releasing new version of Catch. However, they are *should* happen, but they can happen the next day without losing anything significant. The following steps are optional, and do not have to be performed when releasing new version of Catch. However, they *should* happen, but they can happen the next day without losing anything significant.
### vcpkg update ### vcpkg update
Catch is maintaining its own port in Microsoft's package manager [vcpkg](https://github.com/Microsoft/vcpkg). This means that when new version of Catch is released, it should be posted there as well. `updateVcpkgPackage.py` can do a lot of neccessary work for you, it creates a branch and commits neccessary changes. You should review these changes, push and open a PR against vcpkg's upstream. Catch is maintaining its own port in Microsoft's package manager [vcpkg](https://github.com/Microsoft/vcpkg). This means that when new version of Catch is released, it should be posted there as well. `updateVcpkgPackage.py` can do a lot of necessary work for you, it creates a branch and commits necessary changes. You should review these changes, push and open a PR against vcpkg's upstream.
Note that the script assumes you have your fork of vcpkg checked out in a directory next to the directory where you have checked out Catch, like so: Note that the script assumes you have your fork of vcpkg checked out in a directory next to the directory where you have checked out Catch, like so:
``` ```
@ -57,8 +63,3 @@ GitHub
vcpkg vcpkg
``` ```
### Wandbox update
Recently we also included a link to wandbox with preloaded Catch on the main page. Strictly speaking it is unneccessary to update this after every release, Catch usually does not change that much between versions, but it should be kept up to date anyway.

View File

@ -33,6 +33,23 @@ namespace Catch {
} }
``` ```
## Catch::is_range<T> specialisation
As a fallback, Catch attempts to detect if the type can be iterated
(`begin(T)` and `end(T)` are valid) and if it can be, it is stringified
as a range. For certain types this can lead to infinite recursion, so
it can be disabled by specializing `Catch::is_range` like so:
```cpp
namespace Catch {
template<>
struct is_range<T> {
static const bool value = false;
};
}
```
## Exceptions ## Exceptions
By default all exceptions deriving from `std::exception` will be translated to strings by calling the `what()` method. For exception types that do not derive from `std::exception` - or if `what()` does not return a suitable string - use `CATCH_TRANSLATE_EXCEPTION`. This defines a function that takes your exception type, by reference, and returns a string. It can appear anywhere in the code - it doesn't have to be in the same translation unit. For example: By default all exceptions deriving from `std::exception` will be translated to strings by calling the `what()` method. For exception types that do not derive from `std::exception` - or if `what()` does not return a suitable string - use `CATCH_TRANSLATE_EXCEPTION`. This defines a function that takes your exception type, by reference, and returns a string. It can appear anywhere in the code - it doesn't have to be in the same translation unit. For example:

View File

@ -1,7 +1,7 @@
# #
# Build examples. # Build examples.
# #
# Requires BUILD_EXAMPLES to be defined 'true', see ../CMakeLists.txt. # Requires CATCH_BUILD_EXAMPLES to be defined 'true', see ../CMakeLists.txt.
# #
cmake_minimum_required( VERSION 3.0 ) cmake_minimum_required( VERSION 3.0 )

View File

@ -9,6 +9,9 @@
#ifndef TWOBLUECUBES_CATCH_HPP_INCLUDED #ifndef TWOBLUECUBES_CATCH_HPP_INCLUDED
#define TWOBLUECUBES_CATCH_HPP_INCLUDED #define TWOBLUECUBES_CATCH_HPP_INCLUDED
#define CATCH_VERSION_MAJOR 2
#define CATCH_VERSION_MINOR 2
#define CATCH_VERSION_PATCH 0
#ifdef __clang__ #ifdef __clang__
# pragma clang system_header # pragma clang system_header

View File

@ -1,5 +1,11 @@
// v1.0-develop.2 // Copyright 2017 Two Blue Cubes Ltd. All rights reserved.
// See https://github.com/philsquared/Clara //
// Distributed under the Boost Software License, Version 1.0. (See accompanying
// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
//
// See https://github.com/philsquared/Clara for more details
// Clara v1.1.4
#ifndef CATCH_CLARA_HPP_INCLUDED #ifndef CATCH_CLARA_HPP_INCLUDED
#define CATCH_CLARA_HPP_INCLUDED #define CATCH_CLARA_HPP_INCLUDED
@ -12,6 +18,16 @@
#define CATCH_CLARA_TEXTFLOW_CONFIG_CONSOLE_WIDTH CATCH_CLARA_CONFIG_CONSOLE_WIDTH #define CATCH_CLARA_TEXTFLOW_CONFIG_CONSOLE_WIDTH CATCH_CLARA_CONFIG_CONSOLE_WIDTH
#endif #endif
#ifndef CLARA_CONFIG_OPTIONAL_TYPE
#ifdef __has_include
#if __has_include(<optional>) && __cplusplus >= 201703L
#include <optional>
#define CLARA_CONFIG_OPTIONAL_TYPE std::optional
#endif
#endif
#endif
// ----------- #included from clara_textflow.hpp ----------- // ----------- #included from clara_textflow.hpp -----------
// TextFlowCpp // TextFlowCpp
@ -370,7 +386,7 @@ namespace detail {
template<typename ClassT, typename ReturnT, typename ArgT> template<typename ClassT, typename ReturnT, typename ArgT>
struct UnaryLambdaTraits<ReturnT( ClassT::* )( ArgT ) const> { struct UnaryLambdaTraits<ReturnT( ClassT::* )( ArgT ) const> {
static const bool isValid = true; static const bool isValid = true;
using ArgType = typename std::remove_const<typename std::remove_reference<ArgT>::type>::type;; using ArgType = typename std::remove_const<typename std::remove_reference<ArgT>::type>::type;
using ReturnType = ReturnT; using ReturnType = ReturnT;
}; };
@ -383,11 +399,9 @@ namespace detail {
std::vector<std::string> m_args; std::vector<std::string> m_args;
public: public:
Args( int argc, char *argv[] ) { Args( int argc, char const* const* argv )
m_exeName = argv[0]; : m_exeName(argv[0]),
for( int i = 1; i < argc; ++i ) m_args(argv + 1, argv + argc) {}
m_args.push_back( argv[i] );
}
Args( std::initializer_list<std::string> args ) Args( std::initializer_list<std::string> args )
: m_exeName( *args.begin() ), : m_exeName( *args.begin() ),
@ -535,7 +549,7 @@ namespace detail {
return *this; return *this;
} }
~ResultValueBase() { ~ResultValueBase() override {
if( m_type == Ok ) if( m_type == Ok )
m_value.~T(); m_value.~T();
} }
@ -573,16 +587,14 @@ namespace detail {
auto errorMessage() const -> std::string { return m_errorMessage; } auto errorMessage() const -> std::string { return m_errorMessage; }
protected: protected:
virtual void enforceOk() const { void enforceOk() const override {
// !TBD: If no exceptions, std::terminate here or something
switch( m_type ) { // Errors shouldn't reach this point, but if they do
case ResultBase::LogicError: // the actual error message will be in m_errorMessage
throw std::logic_error( m_errorMessage ); assert( m_type != ResultBase::LogicError );
case ResultBase::RuntimeError: assert( m_type != ResultBase::RuntimeError );
throw std::runtime_error( m_errorMessage ); if( m_type != ResultBase::Ok )
case ResultBase::Ok: std::abort();
break;
}
} }
std::string m_errorMessage; // Only populated if resultType is an error std::string m_errorMessage; // Only populated if resultType is an error
@ -652,47 +664,43 @@ namespace detail {
return ParserResult::runtimeError( "Expected a boolean value but did not recognise: '" + source + "'" ); return ParserResult::runtimeError( "Expected a boolean value but did not recognise: '" + source + "'" );
return ParserResult::ok( ParseResultType::Matched ); return ParserResult::ok( ParseResultType::Matched );
} }
#ifdef CLARA_CONFIG_OPTIONAL_TYPE
template<typename T>
inline auto convertInto( std::string const &source, CLARA_CONFIG_OPTIONAL_TYPE<T>& target ) -> ParserResult {
T temp;
auto result = convertInto( source, temp );
if( result )
target = std::move(temp);
return result;
}
#endif // CLARA_CONFIG_OPTIONAL_TYPE
struct BoundRefBase { struct NonCopyable {
BoundRefBase() = default; NonCopyable() = default;
BoundRefBase( BoundRefBase const & ) = delete; NonCopyable( NonCopyable const & ) = delete;
BoundRefBase( BoundRefBase && ) = delete; NonCopyable( NonCopyable && ) = delete;
BoundRefBase &operator=( BoundRefBase const & ) = delete; NonCopyable &operator=( NonCopyable const & ) = delete;
BoundRefBase &operator=( BoundRefBase && ) = delete; NonCopyable &operator=( NonCopyable && ) = delete;
};
virtual ~BoundRefBase() = default; struct BoundRef : NonCopyable {
virtual ~BoundRef() = default;
virtual auto isFlag() const -> bool = 0;
virtual auto isContainer() const -> bool { return false; } virtual auto isContainer() const -> bool { return false; }
virtual auto isFlag() const -> bool { return false; }
};
struct BoundValueRefBase : BoundRef {
virtual auto setValue( std::string const &arg ) -> ParserResult = 0; virtual auto setValue( std::string const &arg ) -> ParserResult = 0;
};
struct BoundFlagRefBase : BoundRef {
virtual auto setFlag( bool flag ) -> ParserResult = 0; virtual auto setFlag( bool flag ) -> ParserResult = 0;
}; virtual auto isFlag() const -> bool { return true; }
struct BoundValueRefBase : BoundRefBase {
auto isFlag() const -> bool override { return false; }
auto setFlag( bool ) -> ParserResult override {
return ParserResult::logicError( "Flags can only be set on boolean fields" );
}
};
struct BoundFlagRefBase : BoundRefBase {
auto isFlag() const -> bool override { return true; }
auto setValue( std::string const &arg ) -> ParserResult override {
bool flag;
auto result = convertInto( arg, flag );
if( result )
setFlag( flag );
return result;
}
}; };
template<typename T> template<typename T>
struct BoundRef : BoundValueRefBase { struct BoundValueRef : BoundValueRefBase {
T &m_ref; T &m_ref;
explicit BoundRef( T &ref ) : m_ref( ref ) {} explicit BoundValueRef( T &ref ) : m_ref( ref ) {}
auto setValue( std::string const &arg ) -> ParserResult override { auto setValue( std::string const &arg ) -> ParserResult override {
return convertInto( arg, m_ref ); return convertInto( arg, m_ref );
@ -700,10 +708,10 @@ namespace detail {
}; };
template<typename T> template<typename T>
struct BoundRef<std::vector<T>> : BoundValueRefBase { struct BoundValueRef<std::vector<T>> : BoundValueRefBase {
std::vector<T> &m_ref; std::vector<T> &m_ref;
explicit BoundRef( std::vector<T> &ref ) : m_ref( ref ) {} explicit BoundValueRef( std::vector<T> &ref ) : m_ref( ref ) {}
auto isContainer() const -> bool override { return true; } auto isContainer() const -> bool override { return true; }
@ -748,12 +756,12 @@ namespace detail {
template<typename ArgType, typename L> template<typename ArgType, typename L>
inline auto invokeLambda( L const &lambda, std::string const &arg ) -> ParserResult { inline auto invokeLambda( L const &lambda, std::string const &arg ) -> ParserResult {
ArgType temp; ArgType temp{};
auto result = convertInto( arg, temp ); auto result = convertInto( arg, temp );
return !result return !result
? result ? result
: LambdaInvoker<typename UnaryLambdaTraits<L>::ReturnType>::invoke( lambda, temp ); : LambdaInvoker<typename UnaryLambdaTraits<L>::ReturnType>::invoke( lambda, temp );
}; }
template<typename L> template<typename L>
@ -803,6 +811,9 @@ namespace detail {
public: public:
template<typename T> template<typename T>
auto operator|( T const &other ) const -> Parser; auto operator|( T const &other ) const -> Parser;
template<typename T>
auto operator+( T const &other ) const -> Parser;
}; };
// Common code and state for Args and Opts // Common code and state for Args and Opts
@ -810,16 +821,16 @@ namespace detail {
class ParserRefImpl : public ComposableParserImpl<DerivedT> { class ParserRefImpl : public ComposableParserImpl<DerivedT> {
protected: protected:
Optionality m_optionality = Optionality::Optional; Optionality m_optionality = Optionality::Optional;
std::shared_ptr<BoundRefBase> m_ref; std::shared_ptr<BoundRef> m_ref;
std::string m_hint; std::string m_hint;
std::string m_description; std::string m_description;
explicit ParserRefImpl( std::shared_ptr<BoundRefBase> const &ref ) : m_ref( ref ) {} explicit ParserRefImpl( std::shared_ptr<BoundRef> const &ref ) : m_ref( ref ) {}
public: public:
template<typename T> template<typename T>
ParserRefImpl( T &ref, std::string const &hint ) ParserRefImpl( T &ref, std::string const &hint )
: m_ref( std::make_shared<BoundRef<T>>( ref ) ), : m_ref( std::make_shared<BoundValueRef<T>>( ref ) ),
m_hint( hint ) m_hint( hint )
{} {}
@ -860,10 +871,10 @@ namespace detail {
class ExeName : public ComposableParserImpl<ExeName> { class ExeName : public ComposableParserImpl<ExeName> {
std::shared_ptr<std::string> m_name; std::shared_ptr<std::string> m_name;
std::shared_ptr<BoundRefBase> m_ref; std::shared_ptr<BoundValueRefBase> m_ref;
template<typename LambdaT> template<typename LambdaT>
static auto makeRef(LambdaT const &lambda) -> std::shared_ptr<BoundRefBase> { static auto makeRef(LambdaT const &lambda) -> std::shared_ptr<BoundValueRefBase> {
return std::make_shared<BoundLambda<LambdaT>>( lambda) ; return std::make_shared<BoundLambda<LambdaT>>( lambda) ;
} }
@ -871,7 +882,7 @@ namespace detail {
ExeName() : m_name( std::make_shared<std::string>( "<executable>" ) ) {} ExeName() : m_name( std::make_shared<std::string>( "<executable>" ) ) {}
explicit ExeName( std::string &ref ) : ExeName() { explicit ExeName( std::string &ref ) : ExeName() {
m_ref = std::make_shared<BoundRef<std::string>>( ref ); m_ref = std::make_shared<BoundValueRef<std::string>>( ref );
} }
template<typename LambdaT> template<typename LambdaT>
@ -914,7 +925,10 @@ namespace detail {
if( token.type != TokenType::Argument ) if( token.type != TokenType::Argument )
return InternalParseResult::ok( ParseState( ParseResultType::NoMatch, remainingTokens ) ); return InternalParseResult::ok( ParseState( ParseResultType::NoMatch, remainingTokens ) );
auto result = m_ref->setValue( remainingTokens->token ); assert( !m_ref->isFlag() );
auto valueRef = static_cast<detail::BoundValueRefBase*>( m_ref.get() );
auto result = valueRef->setValue( remainingTokens->token );
if( !result ) if( !result )
return InternalParseResult( result ); return InternalParseResult( result );
else else
@ -988,19 +1002,21 @@ namespace detail {
auto const &token = *remainingTokens; auto const &token = *remainingTokens;
if( isMatch(token.token ) ) { if( isMatch(token.token ) ) {
if( m_ref->isFlag() ) { if( m_ref->isFlag() ) {
auto result = m_ref->setFlag( true ); auto flagRef = static_cast<detail::BoundFlagRefBase*>( m_ref.get() );
auto result = flagRef->setFlag( true );
if( !result ) if( !result )
return InternalParseResult( result ); return InternalParseResult( result );
if( result.value() == ParseResultType::ShortCircuitAll ) if( result.value() == ParseResultType::ShortCircuitAll )
return InternalParseResult::ok( ParseState( result.value(), remainingTokens ) ); return InternalParseResult::ok( ParseState( result.value(), remainingTokens ) );
} else { } else {
auto valueRef = static_cast<detail::BoundValueRefBase*>( m_ref.get() );
++remainingTokens; ++remainingTokens;
if( !remainingTokens ) if( !remainingTokens )
return InternalParseResult::runtimeError( "Expected argument following " + token.token ); return InternalParseResult::runtimeError( "Expected argument following " + token.token );
auto const &argToken = *remainingTokens; auto const &argToken = *remainingTokens;
if( argToken.type != TokenType::Argument ) if( argToken.type != TokenType::Argument )
return InternalParseResult::runtimeError( "Expected argument following " + token.token ); return InternalParseResult::runtimeError( "Expected argument following " + token.token );
auto result = m_ref->setValue( argToken.token ); auto result = valueRef->setValue( argToken.token );
if( !result ) if( !result )
return InternalParseResult( result ); return InternalParseResult( result );
if( result.value() == ParseResultType::ShortCircuitAll ) if( result.value() == ParseResultType::ShortCircuitAll )
@ -1077,6 +1093,12 @@ namespace detail {
return Parser( *this ) |= other; return Parser( *this ) |= other;
} }
// Forward deprecated interface with '+' instead of '|'
template<typename T>
auto operator+=( T const &other ) -> Parser & { return operator|=( other ); }
template<typename T>
auto operator+( T const &other ) const -> Parser { return operator|( other ); }
auto getHelpColumns() const -> std::vector<HelpColumns> { auto getHelpColumns() const -> std::vector<HelpColumns> {
std::vector<HelpColumns> cols; std::vector<HelpColumns> cols;
for (auto const &o : m_options) { for (auto const &o : m_options) {
@ -1116,6 +1138,8 @@ namespace detail {
for( auto const &cols : rows ) for( auto const &cols : rows )
optWidth = (std::max)(optWidth, cols.left.size() + 2); optWidth = (std::max)(optWidth, cols.left.size() + 2);
optWidth = (std::min)(optWidth, consoleWidth/2);
for( auto const &cols : rows ) { for( auto const &cols : rows ) {
auto row = auto row =
TextFlow::Column( cols.left ).width( optWidth ).indent( 2 ) + TextFlow::Column( cols.left ).width( optWidth ).indent( 2 ) +

View File

@ -8,15 +8,13 @@
#include "catch_assertionhandler.h" #include "catch_assertionhandler.h"
#include "catch_assertionresult.h" #include "catch_assertionresult.h"
#include "catch_interfaces_capture.h"
#include "catch_interfaces_runner.h" #include "catch_interfaces_runner.h"
#include "catch_interfaces_config.h" #include "catch_interfaces_config.h"
#include "catch_context.h" #include "catch_context.h"
#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 {
@ -56,93 +54,55 @@ 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 ); {}
void AssertionHandler::handleExpr( ITransientExpression const& expr ) {
m_resultCapture.handleExpr( m_assertionInfo, expr, m_reaction );
} }
AssertionHandler::~AssertionHandler() { void AssertionHandler::handleMessage(ResultWas::OfType resultType, StringRef const& message) {
if ( m_inExceptionGuard ) { m_resultCapture.handleMessage( m_assertionInfo, resultType, message, m_reaction );
handle( ResultWas::ThrewException, "Exception translation was disabled by CATCH_CONFIG_FAST_COMPILE" );
getCurrentContext().getResultCapture()->exceptionEarlyReported();
}
}
void AssertionHandler::handle( ITransientExpression const& expr ) {
bool negated = isFalseTest( m_assertionInfo.resultDisposition );
bool result = expr.getResult() != negated;
if(result && !getCurrentContext().getConfig()->includeSuccessfulResults())
{
getCurrentContext().getResultCapture()->assertionRun();
getCurrentContext().getResultCapture()->assertionPassed();
return;
}
handle( result ? ResultWas::Ok : ResultWas::ExpressionFailed, &expr, negated );
}
void AssertionHandler::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);
}
} }
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

@ -8,17 +8,21 @@
#ifndef TWOBLUECUBES_CATCH_ASSERTIONHANDLER_H_INCLUDED #ifndef TWOBLUECUBES_CATCH_ASSERTIONHANDLER_H_INCLUDED
#define TWOBLUECUBES_CATCH_ASSERTIONHANDLER_H_INCLUDED #define TWOBLUECUBES_CATCH_ASSERTIONHANDLER_H_INCLUDED
#include "catch_decomposer.h"
#include "catch_assertioninfo.h" #include "catch_assertioninfo.h"
#include "catch_decomposer.h"
#include "catch_interfaces_capture.h"
namespace Catch { 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;
@ -32,11 +36,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
@ -44,26 +53,32 @@ namespace Catch {
SourceLineInfo const& lineInfo, SourceLineInfo const& lineInfo,
StringRef capturedExpression, StringRef capturedExpression,
ResultDisposition::Flags resultDisposition ); ResultDisposition::Flags resultDisposition );
~AssertionHandler(); ~AssertionHandler() {
if ( !m_completed ) {
m_resultCapture.handleIncomplete( m_assertionInfo );
}
}
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

@ -65,9 +65,9 @@ namespace Catch {
expr = m_info.capturedExpression; expr = m_info.capturedExpression;
else { else {
expr.reserve( m_info.macroName.size() + m_info.capturedExpression.size() + 4 ); expr.reserve( m_info.macroName.size() + m_info.capturedExpression.size() + 4 );
expr += m_info.macroName.c_str(); expr += m_info.macroName;
expr += "( "; expr += "( ";
expr += m_info.capturedExpression.c_str(); expr += m_info.capturedExpression;
expr += " )"; expr += " )";
} }
return expr; return expr;

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

@ -13,12 +13,12 @@ namespace Catch {
using StringMatcher = Matchers::Impl::MatcherBase<std::string>; using StringMatcher = Matchers::Impl::MatcherBase<std::string>;
// This is the general overload that takes a any string matcher // This is the general overload that takes a any string matcher
// There is another overload, in catch_assertinhandler.h/.cpp, that only takes a string and infers // There is another overload, in catch_assertionhandler.h/.cpp, that only takes a string and infers
// the Equals matcher (so the header does not mention matchers) // the Equals matcher (so the header does not mention matchers)
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

@ -20,9 +20,19 @@ namespace Catch {
using namespace clara; using namespace clara;
auto const setWarning = [&]( std::string const& warning ) { auto const setWarning = [&]( std::string const& warning ) {
if( warning != "NoAssertions" ) auto warningSet = [&]() {
if( warning == "NoAssertions" )
return WarnAbout::NoAssertions;
if ( warning == "NoTests" )
return WarnAbout::NoTests;
return WarnAbout::Nothing;
}();
if (warningSet == WarnAbout::Nothing)
return ParserResult::runtimeError( "Unrecognised warning: '" + warning + "'" ); return ParserResult::runtimeError( "Unrecognised warning: '" + warning + "'" );
config.warnings = static_cast<WarnAbout::What>( config.warnings | WarnAbout::NoAssertions ); config.warnings = static_cast<WarnAbout::What>( config.warnings | warningSet );
return ParserResult::ok( ParseResultType::Matched ); return ParserResult::ok( ParseResultType::Matched );
}; };
auto const loadTestNamesFromFile = [&]( std::string const& filename ) { auto const loadTestNamesFromFile = [&]( std::string const& filename ) {

View File

@ -15,10 +15,6 @@
namespace Catch { namespace Catch {
SourceLineInfo::SourceLineInfo( char const* _file, std::size_t _line ) noexcept
: file( _file ),
line( _line )
{}
bool SourceLineInfo::empty() const noexcept { bool SourceLineInfo::empty() const noexcept {
return file[0] == '\0'; return file[0] == '\0';
} }
@ -38,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

@ -43,7 +43,10 @@ namespace Catch {
struct SourceLineInfo { struct SourceLineInfo {
SourceLineInfo() = delete; SourceLineInfo() = delete;
SourceLineInfo( char const* _file, std::size_t _line ) noexcept; SourceLineInfo( char const* _file, std::size_t _line ) noexcept
: file( _file ),
line( _line )
{}
SourceLineInfo( SourceLineInfo const& other ) = default; SourceLineInfo( SourceLineInfo const& other ) = default;
SourceLineInfo( SourceLineInfo && ) = default; SourceLineInfo( SourceLineInfo && ) = default;
@ -60,11 +63,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

@ -31,6 +31,14 @@
# define CATCH_CPP14_OR_GREATER # define CATCH_CPP14_OR_GREATER
# endif # endif
# if __cplusplus >= 201703L
# define CATCH_CPP17_OR_GREATER
# endif
#endif
#if defined(CATCH_CPP17_OR_GREATER)
# define CATCH_INTERNAL_CONFIG_CPP17_UNCAUGHT_EXCEPTIONS
#endif #endif
#ifdef __clang__ #ifdef __clang__
@ -51,14 +59,16 @@
#endif // __clang__ #endif // __clang__
////////////////////////////////////////////////////////////////////////////////
// Assume that non-Windows platforms support posix signals by default
#if !defined(CATCH_PLATFORM_WINDOWS)
#define CATCH_INTERNAL_CONFIG_POSIX_SIGNALS
#endif
//////////////////////////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////////////////////////
// We know some environments not to support full POSIX signals // We know some environments not to support full POSIX signals
#if defined(__CYGWIN__) || defined(__QNX__) #if defined(__CYGWIN__) || defined(__QNX__) || defined(__EMSCRIPTEN__) || defined(__DJGPP__)
#define CATCH_INTERNAL_CONFIG_NO_POSIX_SIGNALS
# if !defined(CATCH_CONFIG_POSIX_SIGNALS)
# define CATCH_INTERNAL_CONFIG_NO_POSIX_SIGNALS
# endif
#endif #endif
#ifdef __OS400__ #ifdef __OS400__
@ -80,6 +90,11 @@
// Visual C++ // Visual C++
#ifdef _MSC_VER #ifdef _MSC_VER
# if _MSC_VER >= 1900 // Visual Studio 2015 or newer
# define CATCH_INTERNAL_CONFIG_CPP17_UNCAUGHT_EXCEPTIONS
# endif
// Universal Windows platform does not support SEH // Universal Windows platform does not support SEH
// Or console colours (or console at all...) // Or console colours (or console at all...)
# if defined(WINAPI_FAMILY) && (WINAPI_FAMILY == WINAPI_FAMILY_APP) # if defined(WINAPI_FAMILY) && (WINAPI_FAMILY == WINAPI_FAMILY_APP)
@ -92,6 +107,13 @@
//////////////////////////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////////////////////////
// DJGPP
#ifdef __DJGPP__
# define CATCH_INTERNAL_CONFIG_NO_WCHAR
#endif // __DJGPP__
////////////////////////////////////////////////////////////////////////////////
// Use of __COUNTER__ is suppressed during code analysis in // Use of __COUNTER__ is suppressed during code analysis in
// CLion/AppCode 2017.2.x and former, because __COUNTER__ is not properly // CLion/AppCode 2017.2.x and former, because __COUNTER__ is not properly
// handled by it. // handled by it.
@ -108,9 +130,18 @@
# define CATCH_CONFIG_WINDOWS_SEH # define CATCH_CONFIG_WINDOWS_SEH
#endif #endif
// This is set by default, because we assume that unix compilers are posix-signal-compatible by default. // This is set by default, because we assume that unix compilers are posix-signal-compatible by default.
#if !defined(CATCH_INTERNAL_CONFIG_NO_POSIX_SIGNALS) && !defined(CATCH_CONFIG_NO_POSIX_SIGNALS) && !defined(CATCH_CONFIG_POSIX_SIGNALS) #if defined(CATCH_INTERNAL_CONFIG_POSIX_SIGNALS) && !defined(CATCH_INTERNAL_CONFIG_NO_POSIX_SIGNALS) && !defined(CATCH_CONFIG_NO_POSIX_SIGNALS) && !defined(CATCH_CONFIG_POSIX_SIGNALS)
# define CATCH_CONFIG_POSIX_SIGNALS # define CATCH_CONFIG_POSIX_SIGNALS
#endif #endif
// This is set by default, because we assume that compilers with no wchar_t support are just rare exceptions.
#if !defined(CATCH_INTERNAL_CONFIG_NO_WCHAR) && !defined(CATCH_CONFIG_NO_WCHAR) && !defined(CATCH_CONFIG_WCHAR)
# define CATCH_CONFIG_WCHAR
#endif
#if defined(CATCH_INTERNAL_CONFIG_CPP17_UNCAUGHT_EXCEPTIONS) && !defined(CATCH_CONFIG_NO_CPP17_UNCAUGHT_EXCEPTIONS) && !defined(CATCH_CONFIG_CPP17_UNCAUGHT_EXCEPTIONS)
# define CATCH_CONFIG_CPP17_UNCAUGHT_EXCEPTIONS
#endif
#if !defined(CATCH_INTERNAL_SUPPRESS_PARENTHESES_WARNINGS) #if !defined(CATCH_INTERNAL_SUPPRESS_PARENTHESES_WARNINGS)
# define CATCH_INTERNAL_SUPPRESS_PARENTHESES_WARNINGS # define CATCH_INTERNAL_SUPPRESS_PARENTHESES_WARNINGS

View File

@ -15,12 +15,16 @@ namespace Catch {
: m_data( data ), : m_data( data ),
m_stream( openStream() ) m_stream( openStream() )
{ {
if( !data.testsOrTags.empty() ) { TestSpecParser parser(ITagAliasRegistry::get());
TestSpecParser parser( ITagAliasRegistry::get() ); if (data.testsOrTags.empty()) {
parser.parse("~[.]"); // All not hidden tests
}
else {
m_hasTestFilters = true;
for( auto const& testOrTags : data.testsOrTags ) for( auto const& testOrTags : data.testsOrTags )
parser.parse( testOrTags ); parser.parse( testOrTags );
m_testSpec = parser.testSpec();
} }
m_testSpec = parser.testSpec();
} }
std::string const& Config::getFilename() const { std::string const& Config::getFilename() const {
@ -35,9 +39,11 @@ namespace Catch {
std::string Config::getProcessName() const { return m_data.processName; } std::string Config::getProcessName() const { return m_data.processName; }
std::vector<std::string> const& Config::getReporterNames() const { return m_data.reporterNames; } std::vector<std::string> const& Config::getReporterNames() const { return m_data.reporterNames; }
std::vector<std::string> const& Config::getTestsOrTags() const { return m_data.testsOrTags; }
std::vector<std::string> const& Config::getSectionsToRun() const { return m_data.sectionsToRun; } std::vector<std::string> const& Config::getSectionsToRun() const { return m_data.sectionsToRun; }
TestSpec const& Config::testSpec() const { return m_testSpec; } TestSpec const& Config::testSpec() const { return m_testSpec; }
bool Config::hasTestFilters() const { return m_hasTestFilters; }
bool Config::showHelp() const { return m_data.showHelp; } bool Config::showHelp() const { return m_data.showHelp; }
@ -46,7 +52,8 @@ namespace Catch {
std::ostream& Config::stream() const { return m_stream->stream(); } std::ostream& Config::stream() const { return m_stream->stream(); }
std::string Config::name() const { return m_data.name.empty() ? m_data.processName : m_data.name; } std::string Config::name() const { return m_data.name.empty() ? m_data.processName : m_data.name; }
bool Config::includeSuccessfulResults() const { return m_data.showSuccessfulTests; } bool Config::includeSuccessfulResults() const { return m_data.showSuccessfulTests; }
bool Config::warnAboutMissingAssertions() const { return m_data.warnings & WarnAbout::NoAssertions; } bool Config::warnAboutMissingAssertions() const { return !!(m_data.warnings & WarnAbout::NoAssertions); }
bool Config::warnAboutNoTests() const { return !!(m_data.warnings & WarnAbout::NoTests); }
ShowDurations::OrNot Config::showDurations() const { return m_data.showDurations; } ShowDurations::OrNot Config::showDurations() const { return m_data.showDurations; }
RunTests::InWhatOrder Config::runOrder() const { return m_data.runOrder; } RunTests::InWhatOrder Config::runOrder() const { return m_data.runOrder; }
unsigned int Config::rngSeed() const { return m_data.rngSeed; } unsigned int Config::rngSeed() const { return m_data.rngSeed; }

View File

@ -78,9 +78,11 @@ namespace Catch {
std::string getProcessName() const; std::string getProcessName() const;
std::vector<std::string> const& getReporterNames() const; std::vector<std::string> const& getReporterNames() const;
std::vector<std::string> const& getTestsOrTags() const;
std::vector<std::string> const& getSectionsToRun() const override; std::vector<std::string> const& getSectionsToRun() const override;
virtual TestSpec const& testSpec() const override; virtual TestSpec const& testSpec() const override;
bool hasTestFilters() const override;
bool showHelp() const; bool showHelp() const;
@ -90,6 +92,7 @@ namespace Catch {
std::string name() const override; std::string name() const override;
bool includeSuccessfulResults() const override; bool includeSuccessfulResults() const override;
bool warnAboutMissingAssertions() const override; bool warnAboutMissingAssertions() const override;
bool warnAboutNoTests() const override;
ShowDurations::OrNot showDurations() const override; ShowDurations::OrNot showDurations() const override;
RunTests::InWhatOrder runOrder() const override; RunTests::InWhatOrder runOrder() const override;
unsigned int rngSeed() const override; unsigned int rngSeed() const override;
@ -107,6 +110,7 @@ namespace Catch {
std::unique_ptr<IStream const> m_stream; std::unique_ptr<IStream const> m_stream;
TestSpec m_testSpec; TestSpec m_testSpec;
bool m_hasTestFilters = false;
}; };
} // end namespace Catch } // end namespace Catch

View File

@ -11,8 +11,8 @@
# pragma clang diagnostic push # pragma clang diagnostic push
# pragma clang diagnostic ignored "-Wexit-time-destructors" # pragma clang diagnostic ignored "-Wexit-time-destructors"
#endif #endif
#include "catch_console_colour.h" #include "catch_console_colour.h"
#include "catch_enforce.h" #include "catch_enforce.h"
#include "catch_errno_guard.h" #include "catch_errno_guard.h"
@ -84,8 +84,12 @@ namespace {
case Colour::BrightRed: return setTextAttribute( FOREGROUND_INTENSITY | FOREGROUND_RED ); case Colour::BrightRed: return setTextAttribute( FOREGROUND_INTENSITY | FOREGROUND_RED );
case Colour::BrightGreen: return setTextAttribute( FOREGROUND_INTENSITY | FOREGROUND_GREEN ); case Colour::BrightGreen: return setTextAttribute( FOREGROUND_INTENSITY | FOREGROUND_GREEN );
case Colour::BrightWhite: return setTextAttribute( FOREGROUND_INTENSITY | FOREGROUND_GREEN | FOREGROUND_RED | FOREGROUND_BLUE ); case Colour::BrightWhite: return setTextAttribute( FOREGROUND_INTENSITY | FOREGROUND_GREEN | FOREGROUND_RED | FOREGROUND_BLUE );
case Colour::BrightYellow: return setTextAttribute( FOREGROUND_INTENSITY | FOREGROUND_RED | FOREGROUND_GREEN );
case Colour::Bright: CATCH_INTERNAL_ERROR( "not a colour" ); case Colour::Bright: CATCH_INTERNAL_ERROR( "not a colour" );
default:
CATCH_ERROR( "Unknown colour requested" );
} }
} }
@ -143,8 +147,10 @@ namespace {
case Colour::BrightRed: return setColour( "[1;31m" ); case Colour::BrightRed: return setColour( "[1;31m" );
case Colour::BrightGreen: return setColour( "[1;32m" ); case Colour::BrightGreen: return setColour( "[1;32m" );
case Colour::BrightWhite: return setColour( "[1;37m" ); case Colour::BrightWhite: return setColour( "[1;37m" );
case Colour::BrightYellow: return setColour( "[1;33m" );
case Colour::Bright: CATCH_INTERNAL_ERROR( "not a colour" ); case Colour::Bright: CATCH_INTERNAL_ERROR( "not a colour" );
default: CATCH_INTERNAL_ERROR( "Unknown colour requested" );
} }
} }
static IColourImpl* instance() { static IColourImpl* instance() {
@ -163,7 +169,12 @@ namespace {
#ifdef CATCH_PLATFORM_MAC #ifdef CATCH_PLATFORM_MAC
!isDebuggerActive() && !isDebuggerActive() &&
#endif #endif
isatty(STDOUT_FILENO); #if !(defined(__DJGPP__) && defined(__STRICT_ANSI__))
isatty(STDOUT_FILENO)
#else
false
#endif
;
} }
IColourImpl* platformColourInstance() { IColourImpl* platformColourInstance() {
ErrnoGuard guard; ErrnoGuard guard;
@ -196,7 +207,7 @@ namespace Catch {
namespace Catch { namespace Catch {
Colour::Colour( Code _colourCode ) { use( _colourCode ); } Colour::Colour( Code _colourCode ) { use( _colourCode ); }
Colour::Colour( Colour&& rhs ) noexcept { Colour::Colour( Colour&& rhs ) noexcept {
m_moved = rhs.m_moved; m_moved = rhs.m_moved;
rhs.m_moved = true; rhs.m_moved = true;
} }
@ -205,7 +216,7 @@ namespace Catch {
rhs.m_moved = true; rhs.m_moved = true;
return *this; return *this;
} }
Colour::~Colour(){ if( !m_moved ) use( None ); } Colour::~Colour(){ if( !m_moved ) use( None ); }
void Colour::use( Code _colourCode ) { void Colour::use( Code _colourCode ) {

View File

@ -30,10 +30,11 @@ namespace Catch {
BrightGreen = Bright | Green, BrightGreen = Bright | Green,
LightGrey = Bright | Grey, LightGrey = Bright | Grey,
BrightWhite = Bright | White, BrightWhite = Bright | White,
BrightYellow = Bright | Yellow,
// By intention // By intention
FileName = LightGrey, FileName = LightGrey,
Warning = Yellow, Warning = BrightYellow,
ResultError = BrightRed, ResultError = BrightRed,
ResultSuccess = BrightGreen, ResultSuccess = BrightGreen,
ResultExpectedFailure = Warning, ResultExpectedFailure = Warning,
@ -42,7 +43,7 @@ namespace Catch {
Success = Green, Success = Green,
OriginalExpression = Cyan, OriginalExpression = Cyan,
ReconstructedExpression = Yellow, ReconstructedExpression = BrightYellow,
SecondaryText = LightGrey, SecondaryText = LightGrey,
Headers = White Headers = White

View File

@ -20,7 +20,7 @@ namespace Catch {
return m_runner; return m_runner;
} }
virtual IConfigPtr getConfig() const override { virtual IConfigPtr const& getConfig() const override {
return m_config; return m_config;
} }

View File

@ -25,7 +25,7 @@ namespace Catch {
virtual IResultCapture* getResultCapture() = 0; virtual IResultCapture* getResultCapture() = 0;
virtual IRunner* getRunner() = 0; virtual IRunner* getRunner() = 0;
virtual IConfigPtr getConfig() const = 0; virtual IConfigPtr const& getConfig() const = 0;
}; };
struct IMutableContext : IContext struct IMutableContext : IContext

View File

@ -9,21 +9,23 @@
#include "catch_debug_console.h" #include "catch_debug_console.h"
#include "catch_stream.h" #include "catch_stream.h"
#include "catch_platform.h" #include "catch_platform.h"
#include "catch_windows_h_proxy.h"
#ifdef CATCH_PLATFORM_WINDOWS #ifdef CATCH_PLATFORM_WINDOWS
#include "catch_windows_h_proxy.h"
namespace Catch { namespace Catch {
void writeToDebugConsole( std::string const& text ) { void writeToDebugConsole( std::string const& text ) {
::OutputDebugStringA( text.c_str() ); ::OutputDebugStringA( text.c_str() );
} }
} }
#else #else
namespace Catch { namespace Catch {
void writeToDebugConsole( std::string const& text ) { void writeToDebugConsole( std::string const& text ) {
// !TBD: Need a version for Mac/ XCode and other IDEs // !TBD: Need a version for Mac/ XCode and other IDEs
Catch::cout() << text; Catch::cout() << text;
} }
} }
#endif // Platform #endif // Platform

View File

@ -40,7 +40,10 @@ 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(); namespace Catch {
inline void doNothing() {}
}
#define CATCH_BREAK_INTO_DEBUGGER() Catch::doNothing()
#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,21 +68,21 @@ 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 )
{}
}; };
// Specialised comparison functions to handle equality comparisons between ints and pointers (NULL deduces as an int) // Specialised comparison functions to handle equality comparisons between ints and pointers (NULL deduces as an int)
template<typename LhsT, typename RhsT> template<typename LhsT, typename RhsT>
auto compareEqual( LhsT const& lhs, RhsT const& rhs ) -> bool { return lhs == rhs; }; auto compareEqual( LhsT const& lhs, RhsT const& rhs ) -> bool { return static_cast<bool>(lhs == rhs); }
template<typename T> template<typename T>
auto compareEqual( T* const& lhs, int rhs ) -> bool { return lhs == reinterpret_cast<void const*>( rhs ); } auto compareEqual( T* const& lhs, int rhs ) -> bool { return lhs == reinterpret_cast<void const*>( rhs ); }
template<typename T> template<typename T>
@ -88,7 +93,7 @@ namespace Catch {
auto compareEqual( long lhs, T* const& rhs ) -> bool { return reinterpret_cast<void const*>( lhs ) == rhs; } auto compareEqual( long lhs, T* const& rhs ) -> bool { return reinterpret_cast<void const*>( lhs ) == rhs; }
template<typename LhsT, typename RhsT> template<typename LhsT, typename RhsT>
auto compareNotEqual( LhsT const& lhs, RhsT&& rhs ) -> bool { return lhs != rhs; }; auto compareNotEqual( LhsT const& lhs, RhsT&& rhs ) -> bool { return static_cast<bool>(lhs != rhs); }
template<typename T> template<typename T>
auto compareNotEqual( T* const& lhs, int rhs ) -> bool { return lhs != reinterpret_cast<void const*>( rhs ); } auto compareNotEqual( T* const& lhs, int rhs ) -> bool { return lhs != reinterpret_cast<void const*>( rhs ); }
template<typename T> template<typename T>
@ -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 { static_cast<bool>(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 { static_cast<bool>(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 { static_cast<bool>(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 { static_cast<bool>(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,7 +12,7 @@
#ifndef __OBJC__ #ifndef __OBJC__
#if defined(WIN32) && defined(_UNICODE) && !defined(DO_NOT_USE_WMAIN) #if defined(CATCH_CONFIG_WCHAR) && defined(WIN32) && defined(_UNICODE) && !defined(DO_NOT_USE_WMAIN)
// Standard C/C++ Win32 Unicode wmain entry point // Standard C/C++ Win32 Unicode wmain entry point
extern "C" int wmain (int argc, wchar_t * argv[], wchar_t * []) { extern "C" int wmain (int argc, wchar_t * argv[], wchar_t * []) {
#else #else

View File

@ -11,10 +11,9 @@
#include "catch_stream.h" #include "catch_stream.h"
#include <stdexcept> #include <stdexcept>
#include <iosfwd>
#define CATCH_PREPARE_EXCEPTION( type, msg ) \ #define CATCH_PREPARE_EXCEPTION( type, msg ) \
type( static_cast<std::ostringstream&&>( Catch::ReusableStringStream().get() << msg ).str() ) type( ( Catch::ReusableStringStream() << msg ).str() )
#define CATCH_INTERNAL_ERROR( msg ) \ #define CATCH_INTERNAL_ERROR( msg ) \
throw CATCH_PREPARE_EXCEPTION( std::logic_error, CATCH_INTERNAL_LINEINFO << ": Internal Catch error: " << msg); throw CATCH_PREPARE_EXCEPTION( std::logic_error, CATCH_INTERNAL_LINEINFO << ": Internal Catch error: " << msg);
#define CATCH_ERROR( msg ) \ #define CATCH_ERROR( msg ) \

View File

@ -33,6 +33,17 @@ namespace Catch {
return Catch::Detail::stringify( [exception description] ); return Catch::Detail::stringify( [exception description] );
} }
#else #else
// Compiling a mixed mode project with MSVC means that CLR
// exceptions will be caught in (...) as well. However, these
// do not fill-in std::current_exception and thus lead to crash
// when attempting rethrow.
// /EHa switch also causes structured exceptions to be caught
// here, but they fill-in current_exception properly, so
// at worst the output should be a little weird, instead of
// causing a crash.
if (std::current_exception() == nullptr) {
return "Non C++ exception. Possibly a CLR exception.";
}
return tryTranslators(); return tryTranslators();
#endif #endif
} }

View File

@ -17,6 +17,8 @@
# pragma GCC diagnostic ignored "-Wmissing-field-initializers" # pragma GCC diagnostic ignored "-Wmissing-field-initializers"
#endif #endif
#if defined( CATCH_CONFIG_WINDOWS_SEH ) || defined( CATCH_CONFIG_POSIX_SIGNALS )
namespace { namespace {
// Report the error condition // Report the error condition
void reportFatal( char const * const message ) { void reportFatal( char const * const message ) {
@ -24,15 +26,9 @@ namespace {
} }
} }
#if defined ( CATCH_PLATFORM_WINDOWS ) ///////////////////////////////////////// #endif // signals/SEH handling
# if !defined ( CATCH_CONFIG_WINDOWS_SEH ) #if defined( CATCH_CONFIG_WINDOWS_SEH )
namespace Catch {
void FatalConditionHandler::reset() {}
}
# else // CATCH_CONFIG_WINDOWS_SEH is defined
namespace Catch { namespace Catch {
struct SignalDefs { DWORD id; const char* name; }; struct SignalDefs { DWORD id; const char* name; };
@ -72,7 +68,6 @@ namespace Catch {
void FatalConditionHandler::reset() { void FatalConditionHandler::reset() {
if (isSet) { if (isSet) {
// Unregister handler and restore the old guarantee
RemoveVectoredExceptionHandler(exceptionHandlerHandle); RemoveVectoredExceptionHandler(exceptionHandlerHandle);
SetThreadStackGuarantee(&guaranteeSize); SetThreadStackGuarantee(&guaranteeSize);
exceptionHandlerHandle = nullptr; exceptionHandlerHandle = nullptr;
@ -91,20 +86,7 @@ PVOID FatalConditionHandler::exceptionHandlerHandle = nullptr;
} // namespace Catch } // namespace Catch
# endif // CATCH_CONFIG_WINDOWS_SEH #elif defined( CATCH_CONFIG_POSIX_SIGNALS )
#else // Not Windows - assumed to be POSIX compatible //////////////////////////
# if !defined(CATCH_CONFIG_POSIX_SIGNALS)
namespace Catch {
void FatalConditionHandler::reset() {}
}
# else // CATCH_CONFIG_POSIX_SIGNALS is defined
#include <signal.h>
namespace Catch { namespace Catch {
@ -176,9 +158,13 @@ namespace Catch {
} // namespace Catch } // namespace Catch
# endif // CATCH_CONFIG_POSIX_SIGNALS #else
#endif // not Windows namespace Catch {
void FatalConditionHandler::reset() {}
}
#endif // signals/SEH handling
#if defined(__GNUC__) #if defined(__GNUC__)
# pragma GCC diagnostic pop # pragma GCC diagnostic pop

View File

@ -9,23 +9,12 @@
#ifndef TWOBLUECUBES_CATCH_FATAL_CONDITION_H_INCLUDED #ifndef TWOBLUECUBES_CATCH_FATAL_CONDITION_H_INCLUDED
#define TWOBLUECUBES_CATCH_FATAL_CONDITION_H_INCLUDED #define TWOBLUECUBES_CATCH_FATAL_CONDITION_H_INCLUDED
#include <string>
#include "catch_platform.h" #include "catch_platform.h"
#include "catch_compiler_capabilities.h" #include "catch_compiler_capabilities.h"
#if defined ( CATCH_PLATFORM_WINDOWS ) /////////////////////////////////////////
#include "catch_windows_h_proxy.h" #include "catch_windows_h_proxy.h"
# if !defined ( CATCH_CONFIG_WINDOWS_SEH )
namespace Catch { #if defined( CATCH_CONFIG_WINDOWS_SEH )
struct FatalConditionHandler {
void reset();
};
}
# else // CATCH_CONFIG_WINDOWS_SEH is defined
namespace Catch { namespace Catch {
@ -44,20 +33,7 @@ namespace Catch {
} // namespace Catch } // namespace Catch
# endif // CATCH_CONFIG_WINDOWS_SEH #elif defined ( CATCH_CONFIG_POSIX_SIGNALS )
#else // Not Windows - assumed to be POSIX compatible //////////////////////////
# if !defined(CATCH_CONFIG_POSIX_SIGNALS)
namespace Catch {
struct FatalConditionHandler {
void reset();
};
}
# else // CATCH_CONFIG_POSIX_SIGNALS is defined
#include <signal.h> #include <signal.h>
@ -66,7 +42,7 @@ namespace Catch {
struct FatalConditionHandler { struct FatalConditionHandler {
static bool isSet; static bool isSet;
static struct sigaction oldSigActions[];// [sizeof(signalDefs) / sizeof(SignalDefs)]; static struct sigaction oldSigActions[];
static stack_t oldSigStack; static stack_t oldSigStack;
static char altStackMem[]; static char altStackMem[];
@ -79,8 +55,15 @@ namespace Catch {
} // namespace Catch } // namespace Catch
# endif // CATCH_CONFIG_POSIX_SIGNALS
#endif // not Windows #else
namespace Catch {
struct FatalConditionHandler {
void reset();
};
}
#endif
#endif // TWOBLUECUBES_CATCH_FATAL_CONDITION_H_INCLUDED #endif // TWOBLUECUBES_CATCH_FATAL_CONDITION_H_INCLUDED

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

@ -25,7 +25,8 @@ namespace Catch {
struct WarnAbout { enum What { struct WarnAbout { enum What {
Nothing = 0x00, Nothing = 0x00,
NoAssertions = 0x01 NoAssertions = 0x01,
NoTests = 0x02
}; }; }; };
struct ShowDurations { enum OrNot { struct ShowDurations { enum OrNot {
@ -62,10 +63,12 @@ namespace Catch {
virtual bool includeSuccessfulResults() const = 0; virtual bool includeSuccessfulResults() const = 0;
virtual bool shouldDebugBreak() const = 0; virtual bool shouldDebugBreak() const = 0;
virtual bool warnAboutMissingAssertions() const = 0; virtual bool warnAboutMissingAssertions() const = 0;
virtual bool warnAboutNoTests() const = 0;
virtual int abortAfter() const = 0; virtual int abortAfter() const = 0;
virtual bool showInvisibles() const = 0; virtual bool showInvisibles() const = 0;
virtual ShowDurations::OrNot showDurations() const = 0; virtual ShowDurations::OrNot showDurations() const = 0;
virtual TestSpec const& testSpec() const = 0; virtual TestSpec const& testSpec() const = 0;
virtual bool hasTestFilters() const = 0;
virtual RunTests::InWhatOrder runOrder() const = 0; virtual RunTests::InWhatOrder runOrder() const = 0;
virtual unsigned int rngSeed() const = 0; virtual unsigned int rngSeed() const = 0;
virtual int benchmarkResolutionMultiple() const = 0; virtual int benchmarkResolutionMultiple() const = 0;

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

@ -28,11 +28,10 @@ namespace Catch {
std::size_t listTests( Config const& config ) { std::size_t listTests( Config const& config ) {
TestSpec testSpec = config.testSpec(); TestSpec testSpec = config.testSpec();
if( config.testSpec().hasFilters() ) if( config.hasTestFilters() )
Catch::cout() << "Matching test cases:\n"; Catch::cout() << "Matching test cases:\n";
else { else {
Catch::cout() << "All available test cases:\n"; Catch::cout() << "All available test cases:\n";
testSpec = TestSpecParser( ITagAliasRegistry::get() ).parse( "*" ).testSpec();
} }
auto matchedTestCases = filterTests( getAllTestCasesSorted( config ), testSpec, config ); auto matchedTestCases = filterTests( getAllTestCasesSorted( config ), testSpec, config );
@ -54,7 +53,7 @@ namespace Catch {
Catch::cout() << Column( testCaseInfo.tagsAsString() ).indent( 6 ) << "\n"; Catch::cout() << Column( testCaseInfo.tagsAsString() ).indent( 6 ) << "\n";
} }
if( !config.testSpec().hasFilters() ) if( !config.hasTestFilters() )
Catch::cout() << pluralise( matchedTestCases.size(), "test case" ) << '\n' << std::endl; Catch::cout() << pluralise( matchedTestCases.size(), "test case" ) << '\n' << std::endl;
else else
Catch::cout() << pluralise( matchedTestCases.size(), "matching test case" ) << '\n' << std::endl; Catch::cout() << pluralise( matchedTestCases.size(), "matching test case" ) << '\n' << std::endl;
@ -63,8 +62,6 @@ namespace Catch {
std::size_t listTestsNamesOnly( Config const& config ) { std::size_t listTestsNamesOnly( Config const& config ) {
TestSpec testSpec = config.testSpec(); TestSpec testSpec = config.testSpec();
if( !config.testSpec().hasFilters() )
testSpec = TestSpecParser( ITagAliasRegistry::get() ).parse( "*" ).testSpec();
std::size_t matchedTests = 0; std::size_t matchedTests = 0;
std::vector<TestCase> matchedTestCases = filterTests( getAllTestCasesSorted( config ), testSpec, config ); std::vector<TestCase> matchedTestCases = filterTests( getAllTestCasesSorted( config ), testSpec, config );
for( auto const& testCaseInfo : matchedTestCases ) { for( auto const& testCaseInfo : matchedTestCases ) {
@ -94,11 +91,10 @@ namespace Catch {
std::size_t listTags( Config const& config ) { std::size_t listTags( Config const& config ) {
TestSpec testSpec = config.testSpec(); TestSpec testSpec = config.testSpec();
if( config.testSpec().hasFilters() ) if( config.hasTestFilters() )
Catch::cout() << "Tags for matching test cases:\n"; Catch::cout() << "Tags for matching test cases:\n";
else { else {
Catch::cout() << "All available tags:\n"; Catch::cout() << "All available tags:\n";
testSpec = TestSpecParser( ITagAliasRegistry::get() ).parse( "*" ).testSpec();
} }
std::map<std::string, TagInfo> tagCounts; std::map<std::string, TagInfo> tagCounts;

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

@ -8,6 +8,7 @@
#include "catch_message.h" #include "catch_message.h"
#include "catch_interfaces_capture.h" #include "catch_interfaces_capture.h"
#include "catch_uncaught_exceptions.h"
namespace Catch { namespace Catch {
@ -50,10 +51,8 @@ namespace Catch {
} }
ScopedMessage::~ScopedMessage() { ScopedMessage::~ScopedMessage() {
if ( !std::uncaught_exception() ){ if ( !uncaught_exceptions() ){
getResultCapture().popScopedMessage(m_info); getResultCapture().popScopedMessage(m_info);
} }
} }
} // 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

@ -22,7 +22,6 @@ namespace Catch {
} }
bool shouldContinueOnFailure( int flags ) { return ( flags & ResultDisposition::ContinueOnFailure ) != 0; } bool shouldContinueOnFailure( int flags ) { return ( flags & ResultDisposition::ContinueOnFailure ) != 0; }
bool isFalseTest( int flags ) { return ( flags & ResultDisposition::FalseTest ) != 0; }
bool shouldSuppressFailure( int flags ) { return ( flags & ResultDisposition::SuppressFail ) != 0; } bool shouldSuppressFailure( int flags ) { return ( flags & ResultDisposition::SuppressFail ) != 0; }
} // end namespace Catch } // end namespace Catch

View File

@ -47,7 +47,7 @@ namespace Catch {
ResultDisposition::Flags operator | ( ResultDisposition::Flags lhs, ResultDisposition::Flags rhs ); ResultDisposition::Flags operator | ( ResultDisposition::Flags lhs, ResultDisposition::Flags rhs );
bool shouldContinueOnFailure( int flags ); bool shouldContinueOnFailure( int flags );
bool isFalseTest( int flags ); inline bool isFalseTest( int flags ) { return ( flags & ResultDisposition::FalseTest ) != 0; }
bool shouldSuppressFailure( int flags ); bool shouldSuppressFailure( int flags );
} // end namespace Catch } // end namespace Catch

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{ StringRef(), SourceLineInfo("",0), StringRef(), 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);
@ -66,7 +84,7 @@ namespace Catch {
std::string redirectedCout; std::string redirectedCout;
std::string redirectedCerr; std::string redirectedCerr;
TestCaseInfo testInfo = testCase.getTestCaseInfo(); auto const& testInfo = testCase.getTestCaseInfo();
m_reporter->testCaseStarting(testInfo); m_reporter->testCaseStarting(testInfo);
@ -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());
} }
@ -275,17 +292,20 @@ namespace Catch {
Counts prevAssertions = m_totals.assertions; Counts prevAssertions = m_totals.assertions;
double duration = 0; double duration = 0;
m_shouldReportUnexpected = true; m_shouldReportUnexpected = true;
m_lastAssertionInfo = { "TEST_CASE", testCaseInfo.lineInfo, "", ResultDisposition::Normal }; m_lastAssertionInfo = { "TEST_CASE"_sr, testCaseInfo.lineInfo, StringRef(), ResultDisposition::Normal };
seedRng(*m_config); seedRng(*m_config);
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,20 +316,18 @@ 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();
} }
} }
Counts assertions = m_totals.assertions - prevAssertions;
bool missingAssertions = testForMissingAssertions(assertions);
m_testCaseTracker->close(); m_testCaseTracker->close();
handleUnfinishedSections(); handleUnfinishedSections();
m_messages.clear(); m_messages.clear();
Counts assertions = m_totals.assertions - prevAssertions;
bool missingAssertions = testForMissingAssertions(assertions);
SectionStats testCaseSectionStats(testCaseSection, assertions, duration, missingAssertions); SectionStats testCaseSectionStats(testCaseSection, assertions, duration, missingAssertions);
m_reporter->sectionEnded(testCaseSectionStats); m_reporter->sectionEnded(testCaseSectionStats);
} }
@ -331,6 +349,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

@ -8,6 +8,7 @@
#include "catch_section.h" #include "catch_section.h"
#include "catch_capture.hpp" #include "catch_capture.hpp"
#include "catch_uncaught_exceptions.h"
namespace Catch { namespace Catch {
@ -18,22 +19,15 @@ namespace Catch {
m_timer.start(); m_timer.start();
} }
#if defined(_MSC_VER)
#pragma warning(push)
#pragma warning(disable:4996) // std::uncaught_exception is deprecated in C++17
#endif
Section::~Section() { Section::~Section() {
if( m_sectionIncluded ) { if( m_sectionIncluded ) {
SectionEndInfo endInfo( m_info, m_assertions, m_timer.getElapsedSeconds() ); SectionEndInfo endInfo( m_info, m_assertions, m_timer.getElapsedSeconds() );
if( std::uncaught_exception() ) if( uncaught_exceptions() )
getResultCapture().sectionEndedEarly( endInfo ); getResultCapture().sectionEndedEarly( endInfo );
else else
getResultCapture().sectionEnded( endInfo ); getResultCapture().sectionEnded( endInfo );
} }
} }
#if defined(_MSC_VER)
#pragma warning(pop)
#endif
// This indicates whether the section should be executed or not // This indicates whether the section should be executed or not
Section::operator bool() const { Section::operator bool() const {

View File

@ -18,101 +18,109 @@
#include "catch_random_number_generator.h" #include "catch_random_number_generator.h"
#include "catch_startup_exception_registry.h" #include "catch_startup_exception_registry.h"
#include "catch_text.h" #include "catch_text.h"
#include "catch_stream.h"
#include "catch_windows_h_proxy.h"
#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);
CATCH_ENFORCE(reporter, "No reporter registered with name: '" << reporterName << "'"); CATCH_ENFORCE(reporter, "No reporter registered with name: '" << reporterName << "'");
return reporter; return reporter;
} }
#ifndef CATCH_CONFIG_DEFAULT_REPORTER #ifndef CATCH_CONFIG_DEFAULT_REPORTER
#define CATCH_CONFIG_DEFAULT_REPORTER "console" #define CATCH_CONFIG_DEFAULT_REPORTER "console"
#endif #endif
IStreamingReporterPtr makeReporter(std::shared_ptr<Config> const& config) { IStreamingReporterPtr makeReporter(std::shared_ptr<Config> const& config) {
auto const& reporterNames = config->getReporterNames(); auto const& reporterNames = config->getReporterNames();
if (reporterNames.empty()) if (reporterNames.empty())
return createReporter(CATCH_CONFIG_DEFAULT_REPORTER, config); return createReporter(CATCH_CONFIG_DEFAULT_REPORTER, config);
IStreamingReporterPtr reporter; IStreamingReporterPtr reporter;
for (auto const& name : reporterNames) for (auto const& name : reporterNames)
addReporter(reporter, createReporter(name, config)); addReporter(reporter, createReporter(name, config));
return reporter; return reporter;
} }
#undef CATCH_CONFIG_DEFAULT_REPORTER #undef CATCH_CONFIG_DEFAULT_REPORTER
void addListeners(IStreamingReporterPtr& reporters, IConfigPtr const& config) { void addListeners(IStreamingReporterPtr& reporters, IConfigPtr const& config) {
auto const& listeners = Catch::getRegistryHub().getReporterRegistry().getListeners(); auto const& listeners = Catch::getRegistryHub().getReporterRegistry().getListeners();
for (auto const& listener : listeners) for (auto const& listener : listeners)
addReporter(reporters, listener->create(Catch::ReporterConfig(config))); addReporter(reporters, listener->create(Catch::ReporterConfig(config)));
}
Catch::Totals runTests(std::shared_ptr<Config> const& config) {
using namespace Catch;
IStreamingReporterPtr reporter = makeReporter(config);
addListeners(reporter, config);
RunContext context(config, std::move(reporter));
Totals totals;
context.testGroupStarting(config->name(), 1, 1);
TestSpec testSpec = config->testSpec();
if (!testSpec.hasFilters())
testSpec = TestSpecParser(ITagAliasRegistry::get()).parse("~[.]").testSpec(); // All not hidden tests
auto const& allTestCases = getAllTestCasesSorted(*config);
for (auto const& testCase : allTestCases) {
if (!context.aborting() && matchTest(testCase, testSpec, *config))
totals += context.runTest(testCase);
else
context.reporter().skipTest(testCase);
} }
context.testGroupEnded(config->name(), totals, 1, 1);
return totals;
}
void applyFilenamesAsTags(Catch::IConfig const& config) { Catch::Totals runTests(std::shared_ptr<Config> const& config) {
using namespace Catch; IStreamingReporterPtr reporter = makeReporter(config);
auto& tests = const_cast<std::vector<TestCase>&>(getAllTestCasesSorted(config)); addListeners(reporter, config);
for (auto& testCase : tests) {
auto tags = testCase.tags;
std::string filename = testCase.lineInfo.file; RunContext context(config, std::move(reporter));
auto lastSlash = filename.find_last_of("\\/");
if (lastSlash != std::string::npos) { Totals totals;
filename.erase(0, lastSlash);
filename[0] = '#'; context.testGroupStarting(config->name(), 1, 1);
TestSpec testSpec = config->testSpec();
auto const& allTestCases = getAllTestCasesSorted(*config);
for (auto const& testCase : allTestCases) {
if (!context.aborting() && matchTest(testCase, testSpec, *config))
totals += context.runTest(testCase);
else
context.reporter().skipTest(testCase);
} }
auto lastDot = filename.find_last_of('.'); if (config->warnAboutNoTests() && totals.testCases.total() == 0) {
if (lastDot != std::string::npos) { ReusableStringStream testConfig;
filename.erase(lastDot);
bool first = true;
for (const auto& input : config->getTestsOrTags()) {
if (!first) { testConfig << ' '; }
first = false;
testConfig << input;
}
context.reporter().noMatchingTestCases(testConfig.str());
totals.error = -1;
} }
tags.push_back(std::move(filename)); context.testGroupEnded(config->name(), totals, 1, 1);
setTags(testCase, tags); return totals;
} }
}
} void applyFilenamesAsTags(Catch::IConfig const& config) {
auto& tests = const_cast<std::vector<TestCase>&>(getAllTestCasesSorted(config));
for (auto& testCase : tests) {
auto tags = testCase.tags;
namespace Catch { std::string filename = testCase.lineInfo.file;
auto lastSlash = filename.find_last_of("\\/");
if (lastSlash != std::string::npos) {
filename.erase(0, lastSlash);
filename[0] = '#';
}
auto lastDot = filename.find_last_of('.');
if (lastDot != std::string::npos) {
filename.erase(lastDot);
}
tags.push_back(std::move(filename));
setTags(testCase, tags);
}
}
} // anon namespace
Session::Session() { Session::Session() {
static bool alreadyInstantiated = false; static bool alreadyInstantiated = false;
@ -125,7 +133,7 @@ namespace Catch {
if ( !exceptions.empty() ) { if ( !exceptions.empty() ) {
m_startupExceptions = true; m_startupExceptions = true;
Colour colourGuard( Colour::Red ); Colour colourGuard( Colour::Red );
Catch::cerr() << "Errors occured during startup!" << '\n'; Catch::cerr() << "Errors occurred during startup!" << '\n';
// iterate over all exceptions and notify user // iterate over all exceptions and notify user
for ( const auto& ex_ptr : exceptions ) { for ( const auto& ex_ptr : exceptions ) {
try { try {
@ -157,7 +165,7 @@ namespace Catch {
<< std::left << std::setw(16) << "version: " << libraryVersion() << std::endl; << std::left << std::setw(16) << "version: " << libraryVersion() << std::endl;
} }
int Session::applyCommandLine( int argc, char* argv[] ) { int Session::applyCommandLine( int argc, char const * const * argv ) {
if( m_startupExceptions ) if( m_startupExceptions )
return 1; return 1;
@ -194,7 +202,7 @@ namespace Catch {
return returnCode; return returnCode;
} }
#if defined(WIN32) && defined(UNICODE) #if defined(CATCH_CONFIG_WCHAR) && defined(WIN32) && defined(UNICODE)
int Session::run( int argc, wchar_t* const argv[] ) { int Session::run( int argc, wchar_t* const argv[] ) {
char **utf8Argv = new char *[ argc ]; char **utf8Argv = new char *[ argc ];
@ -265,7 +273,11 @@ namespace Catch {
if( Option<std::size_t> listed = list( config() ) ) if( Option<std::size_t> listed = list( config() ) )
return static_cast<int>( *listed ); return static_cast<int>( *listed );
return (std::min)( MaxExitCode, static_cast<int>( runTests( m_config ).assertions.failed ) ); auto totals = runTests( m_config );
// Note that on unices only the lower 8 bits are usually used, clamping
// the return value to 255 prevents false negative when some multiple
// of 256 tests has failed
return (std::min) (MaxExitCode, (std::max) (totals.error, static_cast<int>(totals.assertions.failed)));
} }
catch( std::exception& ex ) { catch( std::exception& ex ) {
Catch::cerr() << ex.what() << std::endl; Catch::cerr() << ex.what() << std::endl;

View File

@ -25,12 +25,12 @@ namespace Catch {
void showHelp() const; void showHelp() const;
void libIdentify(); void libIdentify();
int applyCommandLine( int argc, char* argv[] ); int applyCommandLine( int argc, char const * const * argv );
void useConfigData( ConfigData const& configData ); void useConfigData( ConfigData const& configData );
int run( int argc, char* argv[] ); int run( int argc, char* argv[] );
#if defined(WIN32) && defined(UNICODE) #if defined(CATCH_CONFIG_WCHAR) && defined(WIN32) && defined(UNICODE)
int run( int argc, wchar_t* const argv[] ); int run( int argc, wchar_t* const argv[] );
#endif #endif
int run(); int run();

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

@ -15,6 +15,13 @@
#include <ostream> #include <ostream>
#include <cstring> #include <cstring>
#include <cstdint>
namespace {
const uint32_t byte_2_lead = 0xC0;
const uint32_t byte_3_lead = 0xE0;
const uint32_t byte_4_lead = 0xF0;
}
namespace Catch { namespace Catch {
StringRef::StringRef( char const* rawChars ) noexcept StringRef::StringRef( char const* rawChars ) noexcept
@ -36,7 +43,7 @@ namespace Catch {
const_cast<StringRef*>( this )->takeOwnership(); const_cast<StringRef*>( this )->takeOwnership();
return m_start; return m_start;
} }
auto StringRef::data() const noexcept -> char const* { auto StringRef::currentData() const noexcept -> char const* {
return m_start; return m_start;
} }
@ -79,13 +86,12 @@ namespace Catch {
// Make adjustments for uft encodings // Make adjustments for uft encodings
for( size_type i=0; i < m_size; ++i ) { for( size_type i=0; i < m_size; ++i ) {
char c = m_start[i]; char c = m_start[i];
if( ( c & 0b11000000 ) == 0b11000000 ) { if( ( c & byte_2_lead ) == byte_2_lead ) {
if( ( c & 0b11100000 ) == 0b11000000 ) noChars--;
if (( c & byte_3_lead ) == byte_3_lead )
noChars--;
if( ( c & byte_4_lead ) == byte_4_lead )
noChars--; noChars--;
else if( ( c & 0b11110000 ) == 0b11100000 )
noChars-=2;
else if( ( c & 0b11111000 ) == 0b11110000 )
noChars-=3;
} }
} }
return noChars; return noChars;
@ -106,7 +112,12 @@ namespace Catch {
} }
auto operator << ( std::ostream& os, StringRef const& str ) -> std::ostream& { auto operator << ( std::ostream& os, StringRef const& str ) -> std::ostream& {
return os << str.c_str(); return os.write(str.currentData(), str.size());
}
auto operator+=( std::string& lhs, StringRef const& rhs ) -> std::string& {
lhs.append(rhs.currentData(), rhs.size());
return lhs;
} }
} // namespace Catch } // namespace Catch

View File

@ -12,7 +12,7 @@
#include <iosfwd> #include <iosfwd>
namespace Catch { namespace Catch {
class StringData; class StringData;
/// A non-owning string class (similar to the forthcoming std::string_view) /// A non-owning string class (similar to the forthcoming std::string_view)
@ -31,13 +31,13 @@ namespace Catch {
char const* m_start; char const* m_start;
size_type m_size; size_type m_size;
char* m_data = nullptr; char* m_data = nullptr;
void takeOwnership(); void takeOwnership();
static constexpr char const* const s_empty = ""; static constexpr char const* const s_empty = "";
public: // construction/ assignment public: // construction/ assignment
StringRef() noexcept StringRef() noexcept
: StringRef( s_empty, 0 ) : StringRef( s_empty, 0 )
@ -83,13 +83,13 @@ namespace Catch {
operator std::string() const; operator std::string() const;
void swap( StringRef& other ) noexcept; void swap( StringRef& other ) noexcept;
public: // operators public: // operators
auto operator == ( StringRef const& other ) const noexcept -> bool; auto operator == ( StringRef const& other ) const noexcept -> bool;
auto operator != ( StringRef const& other ) const noexcept -> bool; auto operator != ( StringRef const& other ) const noexcept -> bool;
auto operator[] ( size_type index ) const noexcept -> char; auto operator[] ( size_type index ) const noexcept -> char;
public: // named queries public: // named queries
auto empty() const noexcept -> bool { auto empty() const noexcept -> bool {
return m_size == 0; return m_size == 0;
@ -100,22 +100,27 @@ namespace Catch {
auto numberOfCharacters() const noexcept -> size_type; auto numberOfCharacters() const noexcept -> size_type;
auto c_str() const -> char const*; auto c_str() const -> char const*;
public: // substrings and searches public: // substrings and searches
auto substr( size_type start, size_type size ) const noexcept -> StringRef; auto substr( size_type start, size_type size ) const noexcept -> StringRef;
// Returns the current start pointer.
// Note that the pointer can change when if the StringRef is a substring
auto currentData() const noexcept -> char const*;
private: // ownership queries - may not be consistent between calls private: // ownership queries - may not be consistent between calls
auto isOwned() const noexcept -> bool; auto isOwned() const noexcept -> bool;
auto isSubstring() const noexcept -> bool; auto isSubstring() const noexcept -> bool;
auto data() const noexcept -> char const*;
}; };
auto operator + ( StringRef const& lhs, StringRef const& rhs ) -> std::string; auto operator + ( StringRef const& lhs, StringRef const& rhs ) -> std::string;
auto operator + ( StringRef const& lhs, char const* rhs ) -> std::string; auto operator + ( StringRef const& lhs, char const* rhs ) -> std::string;
auto operator + ( char const* lhs, StringRef const& rhs ) -> std::string; auto operator + ( char const* lhs, StringRef const& rhs ) -> std::string;
auto operator += ( std::string& lhs, StringRef const& sr ) -> std::string&;
auto operator << ( std::ostream& os, StringRef const& sr ) -> std::ostream&; auto operator << ( std::ostream& os, StringRef const& sr ) -> std::ostream&;
inline auto operator "" _sr( char const* rawChars, std::size_t size ) noexcept -> StringRef { inline auto operator "" _sr( char const* rawChars, std::size_t size ) noexcept -> StringRef {
return StringRef( rawChars, size ); return StringRef( rawChars, size );
} }

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

@ -48,8 +48,7 @@ namespace Catch {
TestCase makeTestCase( ITestInvoker* _testCase, TestCase makeTestCase( ITestInvoker* _testCase,
std::string const& _className, std::string const& _className,
std::string const& _name, NameAndTags const& nameAndTags,
std::string const& _descOrTags,
SourceLineInfo const& _lineInfo ) SourceLineInfo const& _lineInfo )
{ {
bool isHidden = false; bool isHidden = false;
@ -58,6 +57,7 @@ namespace Catch {
std::vector<std::string> tags; std::vector<std::string> tags;
std::string desc, tag; std::string desc, tag;
bool inTag = false; bool inTag = false;
std::string _descOrTags = nameAndTags.tags;
for (char c : _descOrTags) { for (char c : _descOrTags) {
if( !inTag ) { if( !inTag ) {
if( c == '[' ) if( c == '[' )
@ -85,8 +85,8 @@ namespace Catch {
tags.push_back( "." ); tags.push_back( "." );
} }
TestCaseInfo info( _name, _className, desc, tags, _lineInfo ); TestCaseInfo info( nameAndTags.name, _className, desc, tags, _lineInfo );
return TestCase( _testCase, info ); return TestCase( _testCase, std::move(info) );
} }
void setTags( TestCaseInfo& testCaseInfo, std::vector<std::string> tags ) { void setTags( TestCaseInfo& testCaseInfo, std::vector<std::string> tags ) {
@ -147,7 +147,7 @@ namespace Catch {
} }
TestCase::TestCase( ITestInvoker* testCase, TestCaseInfo const& info ) : TestCaseInfo( info ), test( testCase ) {} TestCase::TestCase( ITestInvoker* testCase, TestCaseInfo&& info ) : TestCaseInfo( std::move(info) ), test( testCase ) {}
TestCase TestCase::withName( std::string const& _newName ) const { TestCase TestCase::withName( std::string const& _newName ) const {

View File

@ -9,6 +9,7 @@
#define TWOBLUECUBES_CATCH_TEST_CASE_INFO_H_INCLUDED #define TWOBLUECUBES_CATCH_TEST_CASE_INFO_H_INCLUDED
#include "catch_common.h" #include "catch_common.h"
#include "catch_test_registry.h"
#include <string> #include <string>
#include <vector> #include <vector>
@ -61,7 +62,7 @@ namespace Catch {
class TestCase : public TestCaseInfo { class TestCase : public TestCaseInfo {
public: public:
TestCase( ITestInvoker* testCase, TestCaseInfo const& info ); TestCase( ITestInvoker* testCase, TestCaseInfo&& info );
TestCase withName( std::string const& _newName ) const; TestCase withName( std::string const& _newName ) const;
@ -78,8 +79,7 @@ namespace Catch {
TestCase makeTestCase( ITestInvoker* testCase, TestCase makeTestCase( ITestInvoker* testCase,
std::string const& className, std::string const& className,
std::string const& name, NameAndTags const& nameAndTags,
std::string const& description,
SourceLineInfo const& lineInfo ); SourceLineInfo const& lineInfo );
} }

View File

@ -96,7 +96,7 @@ namespace Catch {
m_testAsFunction(); m_testAsFunction();
} }
std::string extractClassName( std::string const& classOrQualifiedMethodName ) { std::string extractClassName( StringRef const& classOrQualifiedMethodName ) {
std::string className = classOrQualifiedMethodName; std::string className = classOrQualifiedMethodName;
if( startsWith( className, '&' ) ) if( startsWith( className, '&' ) )
{ {

View File

@ -22,7 +22,7 @@ namespace Catch {
class TestCase; class TestCase;
struct IConfig; struct IConfig;
std::vector<TestCase> sortTests( IConfig const& config, std::vector<TestCase> const& unsortedTestCases ); std::vector<TestCase> sortTests( IConfig const& config, std::vector<TestCase> const& unsortedTestCases );
bool matchTest( TestCase const& testCase, TestSpec const& testSpec, IConfig const& config ); bool matchTest( TestCase const& testCase, TestSpec const& testSpec, IConfig const& config );
void enforceNoDuplicateTestCases( std::vector<TestCase> const& functions ); void enforceNoDuplicateTestCases( std::vector<TestCase> const& functions );
@ -58,7 +58,7 @@ namespace Catch {
}; };
std::string extractClassName( std::string const& classOrQualifiedMethodName ); std::string extractClassName( StringRef const& classOrQualifiedMethodName );
/////////////////////////////////////////////////////////////////////////// ///////////////////////////////////////////////////////////////////////////

View File

@ -15,23 +15,22 @@ namespace Catch {
return new(std::nothrow) TestInvokerAsFunction( testAsFunction ); return new(std::nothrow) TestInvokerAsFunction( testAsFunction );
} }
NameAndTags::NameAndTags( StringRef name_ , StringRef tags_ ) noexcept : name( name_ ), tags( tags_ ) {} NameAndTags::NameAndTags( StringRef const& name_ , StringRef const& tags_ ) noexcept : name( name_ ), tags( tags_ ) {}
AutoReg::AutoReg( ITestInvoker* invoker, SourceLineInfo const& lineInfo, StringRef classOrMethod, NameAndTags const& nameAndTags ) noexcept { AutoReg::AutoReg( ITestInvoker* invoker, SourceLineInfo const& lineInfo, StringRef const& classOrMethod, NameAndTags const& nameAndTags ) noexcept {
try { try {
getMutableRegistryHub() getMutableRegistryHub()
.registerTest( .registerTest(
makeTestCase( makeTestCase(
invoker, invoker,
extractClassName( classOrMethod ), extractClassName( classOrMethod ),
nameAndTags.name, nameAndTags,
nameAndTags.tags,
lineInfo)); lineInfo));
} catch (...) { } catch (...) {
// Do not throw when constructing global objects, instead register the exception to be processed later // Do not throw when constructing global objects, instead register the exception to be processed later
getMutableRegistryHub().registerStartupException(); getMutableRegistryHub().registerStartupException();
} }
} }
AutoReg::~AutoReg() = default; AutoReg::~AutoReg() = default;
} }

View File

@ -35,13 +35,13 @@ auto makeTestInvoker( void (C::*testAsMethod)() ) noexcept -> ITestInvoker* {
} }
struct NameAndTags { struct NameAndTags {
NameAndTags( StringRef name_ = StringRef(), StringRef tags_ = StringRef() ) noexcept; NameAndTags( StringRef const& name_ = StringRef(), StringRef const& tags_ = StringRef() ) noexcept;
StringRef name; StringRef name;
StringRef tags; StringRef tags;
}; };
struct AutoReg : NonCopyable { struct AutoReg : NonCopyable {
AutoReg( ITestInvoker* invoker, SourceLineInfo const& lineInfo, StringRef classOrMethod, NameAndTags const& nameAndTags ) noexcept; AutoReg( ITestInvoker* invoker, SourceLineInfo const& lineInfo, StringRef const& classOrMethod, NameAndTags const& nameAndTags ) noexcept;
~AutoReg(); ~AutoReg();
}; };

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

@ -116,6 +116,7 @@ std::string StringMaker<std::string>::convert(const std::string& str) {
return s; return s;
} }
#ifdef CATCH_CONFIG_WCHAR
std::string StringMaker<std::wstring>::convert(const std::wstring& wstr) { std::string StringMaker<std::wstring>::convert(const std::wstring& wstr) {
std::string s; std::string s;
s.reserve(wstr.size()); s.reserve(wstr.size());
@ -124,6 +125,7 @@ std::string StringMaker<std::wstring>::convert(const std::wstring& wstr) {
} }
return ::Catch::Detail::stringify(s); return ::Catch::Detail::stringify(s);
} }
#endif
std::string StringMaker<char const*>::convert(char const* str) { std::string StringMaker<char const*>::convert(char const* str) {
if (str) { if (str) {
@ -139,6 +141,7 @@ std::string StringMaker<char*>::convert(char* str) {
return{ "{null string}" }; return{ "{null string}" };
} }
} }
#ifdef CATCH_CONFIG_WCHAR
std::string StringMaker<wchar_t const*>::convert(wchar_t const * str) { std::string StringMaker<wchar_t const*>::convert(wchar_t const * str) {
if (str) { if (str) {
return ::Catch::Detail::stringify(std::wstring{ str }); return ::Catch::Detail::stringify(std::wstring{ str });
@ -153,6 +156,7 @@ std::string StringMaker<wchar_t *>::convert(wchar_t * str) {
return{ "{null string}" }; return{ "{null string}" };
} }
} }
#endif
std::string StringMaker<int>::convert(int value) { std::string StringMaker<int>::convert(int value) {

View File

@ -13,6 +13,7 @@
#include <cstddef> #include <cstddef>
#include <type_traits> #include <type_traits>
#include <string> #include <string>
#include "catch_compiler_capabilities.h"
#include "catch_stream.h" #include "catch_stream.h"
#ifdef __OBJC__ #ifdef __OBJC__
@ -57,25 +58,43 @@ namespace Catch {
static const bool value = decltype(test<std::ostream, const T&>(0))::value; static const bool value = decltype(test<std::ostream, const T&>(0))::value;
}; };
template<typename E>
std::string convertUnknownEnumToString( E e );
template<typename T>
typename std::enable_if<!std::is_enum<T>::value, std::string>::type convertUnstreamable( T const& value ) {
#if !defined(CATCH_CONFIG_FALLBACK_STRINGIFIER)
(void)value;
return Detail::unprintableString;
#else
return CATCH_CONFIG_FALLBACK_STRINGIFIER(value);
#endif
}
template<typename T>
typename std::enable_if<std::is_enum<T>::value, std::string>::type convertUnstreamable( T const& value ) {
return convertUnknownEnumToString( value );
}
} // namespace Detail } // namespace Detail
// If we decide for C++14, change these to enable_if_ts // If we decide for C++14, change these to enable_if_ts
template <typename T> template <typename T, typename = void>
struct StringMaker { struct StringMaker {
template <typename Fake = T> template <typename Fake = T>
static static
typename std::enable_if<::Catch::Detail::IsStreamInsertable<Fake>::value, std::string>::type typename std::enable_if<::Catch::Detail::IsStreamInsertable<Fake>::value, std::string>::type
convert(const Fake& t) { convert(const Fake& value) {
ReusableStringStream rss; ReusableStringStream rss;
rss << t; rss << value;
return rss.str(); return rss.str();
} }
template <typename Fake = T> template <typename Fake = T>
static static
typename std::enable_if<!::Catch::Detail::IsStreamInsertable<Fake>::value, std::string>::type typename std::enable_if<!::Catch::Detail::IsStreamInsertable<Fake>::value, std::string>::type
convert(const Fake&) { convert( const Fake& value ) {
return Detail::unprintableString; return Detail::convertUnstreamable( value );
} }
}; };
@ -88,8 +107,12 @@ namespace Catch {
return ::Catch::StringMaker<typename std::remove_cv<typename std::remove_reference<T>::type>::type>::convert(e); return ::Catch::StringMaker<typename std::remove_cv<typename std::remove_reference<T>::type>::type>::convert(e);
} }
} // namespace Detail template<typename E>
std::string convertUnknownEnumToString( E e ) {
return ::Catch::Detail::stringify(static_cast<typename std::underlying_type<E>::type>(e));
}
} // namespace Detail
// Some predefined specializations // Some predefined specializations
@ -97,10 +120,12 @@ namespace Catch {
struct StringMaker<std::string> { struct StringMaker<std::string> {
static std::string convert(const std::string& str); static std::string convert(const std::string& str);
}; };
#ifdef CATCH_CONFIG_WCHAR
template<> template<>
struct StringMaker<std::wstring> { struct StringMaker<std::wstring> {
static std::string convert(const std::wstring& wstr); static std::string convert(const std::wstring& wstr);
}; };
#endif
template<> template<>
struct StringMaker<char const *> { struct StringMaker<char const *> {
@ -110,6 +135,7 @@ namespace Catch {
struct StringMaker<char *> { struct StringMaker<char *> {
static std::string convert(char * str); static std::string convert(char * str);
}; };
#ifdef CATCH_CONFIG_WCHAR
template<> template<>
struct StringMaker<wchar_t const *> { struct StringMaker<wchar_t const *> {
static std::string convert(wchar_t const * str); static std::string convert(wchar_t const * str);
@ -118,6 +144,7 @@ namespace Catch {
struct StringMaker<wchar_t *> { struct StringMaker<wchar_t *> {
static std::string convert(wchar_t * str); static std::string convert(wchar_t * str);
}; };
#endif
template<int SZ> template<int SZ>
struct StringMaker<char[SZ]> { struct StringMaker<char[SZ]> {
@ -233,20 +260,6 @@ namespace Catch {
} }
} }
template<typename T, typename Allocator>
struct StringMaker<std::vector<T, Allocator> > {
static std::string convert( std::vector<T,Allocator> const& v ) {
return ::Catch::Detail::rangeToString( v.begin(), v.end() );
}
};
template<typename T>
struct EnumStringMaker {
static std::string convert(const T& t) {
return ::Catch::Detail::stringify(static_cast<typename std::underlying_type<T>::type>(t));
}
};
#ifdef __OBJC__ #ifdef __OBJC__
template<> template<>
struct StringMaker<NSString*> { struct StringMaker<NSString*> {
@ -344,6 +357,61 @@ namespace Catch {
} }
#endif // CATCH_CONFIG_ENABLE_TUPLE_STRINGMAKER #endif // CATCH_CONFIG_ENABLE_TUPLE_STRINGMAKER
namespace Catch {
struct not_this_one {}; // Tag type for detecting which begin/ end are being selected
// Import begin/ end from std here so they are considered alongside the fallback (...) overloads in this namespace
using std::begin;
using std::end;
not_this_one begin( ... );
not_this_one end( ... );
template <typename T>
struct is_range {
static const bool value =
!std::is_same<decltype(begin(std::declval<T>())), not_this_one>::value &&
!std::is_same<decltype(end(std::declval<T>())), not_this_one>::value;
};
template<typename Range>
std::string rangeToString( Range const& range ) {
return ::Catch::Detail::rangeToString( begin( range ), end( range ) );
}
// Handle vector<bool> specially
template<typename Allocator>
std::string rangeToString( std::vector<bool, Allocator> const& v ) {
ReusableStringStream rss;
rss << "{ ";
bool first = true;
for( bool b : v ) {
if( first )
first = false;
else
rss << ", ";
rss << ::Catch::Detail::stringify( b );
}
rss << " }";
return rss.str();
}
template<typename R>
struct StringMaker<R, typename std::enable_if<is_range<R>::value && !::Catch::Detail::IsStreamInsertable<R>::value>::type> {
static std::string convert( R const& range ) {
return rangeToString( range );
}
};
template <typename T, int SZ>
struct StringMaker<T[SZ]> {
static std::string convert(T const(&arr)[SZ]) {
return rangeToString(arr);
}
};
} // namespace Catch
// Separate std::chrono::duration specialization // Separate std::chrono::duration specialization
#if defined(CATCH_CONFIG_ENABLE_CHRONO_STRINGMAKER) #if defined(CATCH_CONFIG_ENABLE_CHRONO_STRINGMAKER)

View File

@ -32,7 +32,7 @@ namespace Catch {
Totals delta( Totals const& prevTotals ) const; Totals delta( Totals const& prevTotals ) const;
int error = 0;
Counts assertions; Counts assertions;
Counts testCases; Counts testCases;
}; };

View File

@ -0,0 +1,21 @@
/*
* Created by Josh on 1/2/2018.
* Copyright 2018 Two Blue Cubes Ltd. All rights reserved.
*
* Distributed under the Boost Software License, Version 1.0. (See accompanying
* file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
*/
#include "catch_compiler_capabilities.h"
#include "catch_uncaught_exceptions.h"
#include <exception>
namespace Catch {
bool uncaught_exceptions() {
#if defined(CATCH_CONFIG_CPP17_UNCAUGHT_EXCEPTIONS)
return std::uncaught_exceptions() > 0;
#else
return std::uncaught_exception();
#endif
}
} // end namespace Catch

View File

@ -0,0 +1,15 @@
/*
* Created by Josh on 1/2/2018.
* Copyright 2018 Two Blue Cubes Ltd. All rights reserved.
*
* Distributed under the Boost Software License, Version 1.0. (See accompanying
* file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
*/
#ifndef TWOBLUECUBES_CATCH_UNCAUGHT_EXCEPTIONS_H_INCLUDED
#define TWOBLUECUBES_CATCH_UNCAUGHT_EXCEPTIONS_H_INCLUDED
namespace Catch {
bool uncaught_exceptions();
} // end namespace Catch
#endif // TWOBLUECUBES_CATCH_UNCAUGHT_EXCEPTIONS_H_INCLUDED

View File

@ -37,7 +37,7 @@ namespace Catch {
} }
Version const& libraryVersion() { Version const& libraryVersion() {
static Version version( 2, 0, 1, "", 0 ); static Version version( 2, 2, 0, "", 0 );
return version; return version;
} }

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

@ -64,7 +64,7 @@ namespace Catch {
m_reporterPrefs.shouldRedirectStdOut = true; m_reporterPrefs.shouldRedirectStdOut = true;
} }
JunitReporter::~JunitReporter() {}; JunitReporter::~JunitReporter() {}
std::string JunitReporter::getDescription() { std::string JunitReporter::getDescription() {
return "Reports test results in an XML format that looks like Ant's junitreport target"; return "Reports test results in an XML format that looks like Ant's junitreport target";

View File

@ -26,7 +26,7 @@ namespace Catch {
~TAPReporter() override; ~TAPReporter() override;
static std::string getDescription() { static std::string getDescription() {
return "Reports test results in TAP format, suitable for test harneses"; return "Reports test results in TAP format, suitable for test harnesses";
} }
ReporterPreferences getPreferences() const override { ReporterPreferences getPreferences() const override {

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

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,27 @@
@REM # Possibilities:
@REM # Debug build + coverage
@REM # Debug build + examples
@REM # Debug build + ---
@REM # Release build
if "%CONFIGURATION%"=="Debug" (
if "%coverage%"=="1" (
@REM # coverage needs to build the special helper as well as the main
cmake -Hmisc -Bbuild-misc -A%PLATFORM%
cmake --build build-misc
cmake -H. -BBuild -A%PLATFORM% -DUSE_WMAIN=%wmain% -DMEMORYCHECK_COMMAND=build-misc\Debug\CoverageHelper.exe -DMEMORYCHECK_COMMAND_OPTIONS=--sep-- -DMEMORYCHECK_TYPE=Valgrind
) else (
@REM # We know that coverage is 0
if "%examples%"=="1" (
@REM # Examples live off the single header, so it needs to be regenerated
python scripts\generateSingleHeader.py
cmake -H. -BBuild -A%PLATFORM% -DUSE_WMAIN=%wmain% -DCATCH_BUILD_EXAMPLES=ON
) else (
@REM # This is just a plain debug build
cmake -H. -BBuild -A%PLATFORM% -DUSE_WMAIN=%wmain%
)
)
)
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)

Some files were not shown because too many files have changed in this diff Show More