diff --git a/.travis.yml b/.travis.yml index 9e127233..5e98ce4f 100644 --- a/.travis.yml +++ b/.travis.yml @@ -17,7 +17,7 @@ matrix: addons: apt: sources: *all_sources - packages: ['valgrind', 'clang-3.5'] + packages: ['valgrind', 'lcov', 'clang-3.5'] env: COMPILER='clang++-3.5' VALGRIND=1 - os: linux @@ -25,7 +25,7 @@ matrix: addons: apt: sources: *all_sources - packages: ['valgrind', 'clang-3.6'] + packages: ['valgrind', 'lcov', 'clang-3.6'] env: COMPILER='clang++-3.6' VALGRIND=1 # Travis's containers do not seem to have Clang 3.7 in apt, no matter what sources I add. @@ -42,7 +42,7 @@ matrix: addons: apt: sources: *all_sources - packages: ['valgrind', 'clang-3.8'] + packages: ['valgrind', 'lcov', 'clang-3.8'] env: COMPILER='clang++-3.8' VALGRIND=1 - os: linux @@ -50,7 +50,7 @@ matrix: addons: apt: sources: *all_sources - packages: ['clang-3.9', 'valgrind'] + packages: ['clang-3.9', 'valgrind', 'lcov'] env: COMPILER='clang++-3.9' VALGRIND=1 - os: linux @@ -58,7 +58,7 @@ matrix: addons: apt: sources: *all_sources - packages: ['clang-4.0', 'valgrind'] + packages: ['clang-4.0', 'valgrind', 'lcov'] env: COMPILER='clang++-4.0' VALGRIND=1 - os: linux @@ -66,7 +66,7 @@ matrix: addons: apt: sources: *all_sources - packages: ['clang-5.0', 'valgrind'] + packages: ['clang-5.0', 'valgrind', 'lcov'] env: COMPILER='clang++-5.0' VALGRIND=1 # 2/ Linux GCC Builds @@ -75,7 +75,7 @@ matrix: addons: apt: sources: ['ubuntu-toolchain-r-test'] - packages: ['valgrind', 'g++-4.8'] + packages: ['valgrind', 'lcov', 'g++-4.8'] env: COMPILER='g++-4.8' VALGRIND=1 - os: linux @@ -83,7 +83,7 @@ matrix: addons: apt: sources: *all_sources - packages: ['valgrind', 'g++-4.9'] + packages: ['valgrind', 'lcov', 'g++-4.9'] env: COMPILER='g++-4.9' VALGRIND=1 - os: linux @@ -91,7 +91,7 @@ matrix: addons: apt: sources: *all_sources - packages: ['valgrind', 'g++-5'] + packages: ['valgrind', 'lcov', 'g++-5'] env: COMPILER='g++-5' VALGRIND=1 - os: linux @@ -99,7 +99,7 @@ matrix: addons: &gcc6 apt: sources: *all_sources - packages: ['valgrind', 'g++-6'] + packages: ['valgrind', 'lcov', 'g++-6'] env: COMPILER='g++-6' VALGRIND=1 - os: linux @@ -107,20 +107,15 @@ matrix: addons: &gcc7 apt: sources: *all_sources - packages: ['valgrind', 'g++-7'] + packages: ['valgrind', 'lcov', 'g++-7'] env: COMPILER='g++-7' VALGRIND=1 - - os: linux - compiler: gcc - addons: *gcc7 - env: COMPILER='g++-7' ENV_NO_SELFTEST=1 ENV_BUILD_EXAMPLES=1 - # 3b/ Linux C++14 Clang builds - os: linux compiler: clang addons: apt: - packages: ['clang-3.8', 'valgrind', 'libstdc++-6-dev'] + packages: ['clang-3.8', 'valgrind', 'lcov', 'libstdc++-6-dev'] sources: - ubuntu-toolchain-r-test - llvm-toolchain-trusty @@ -131,7 +126,7 @@ matrix: addons: apt: sources: *all_sources - packages: ['clang-3.9', 'valgrind', 'libstdc++-6-dev'] + packages: ['clang-3.9', 'valgrind', 'lcov', 'libstdc++-6-dev'] env: COMPILER='clang++-3.9' CPP14=1 VALGRIND=1 - os: linux @@ -139,7 +134,7 @@ matrix: addons: apt: sources: *all_sources - packages: ['clang-4.0', 'valgrind', 'libstdc++-6-dev'] + packages: ['clang-4.0', 'valgrind', 'lcov', 'libstdc++-6-dev'] env: COMPILER='clang++-4.0' CPP14=1 VALGRIND=1 - os: linux @@ -147,7 +142,7 @@ matrix: addons: apt: sources: *all_sources - packages: ['clang-5.0', 'valgrind', 'libstdc++-6-dev'] + packages: ['clang-5.0', 'valgrind', 'lcov', 'libstdc++-6-dev'] env: COMPILER='clang++-5.0' CPP14=1 VALGRIND=1 @@ -188,6 +183,7 @@ matrix: compiler: clang env: COMPILER='clang++' USE_CPP14=1 + install: - DEPS_DIR="${TRAVIS_BUILD_DIR}/deps" - mkdir -p ${DEPS_DIR} && cd ${DEPS_DIR} @@ -197,7 +193,7 @@ install: mkdir cmake && travis_retry wget --no-check-certificate --quiet -O - ${CMAKE_URL} | tar --strip-components=1 -xz -C cmake export PATH=${DEPS_DIR}/cmake/bin:${PATH} elif [[ "${TRAVIS_OS_NAME}" == "osx" ]]; then - which cmake || brew install cmake + which cmake || brew install cmake; fi before_script: @@ -206,15 +202,25 @@ before_script: # Regenerate single header file, so it is tested in the examples... - python scripts/generateSingleHeader.py - # Use Debug builds for running Valgrind and building examples - - cmake -H. -BBuild-Debug -DCMAKE_BUILD_TYPE=Debug -Wdev -DUSE_CPP14=${CPP14} -DUSE_VALGRIND=${VALGRIND} -DBUILD_EXAMPLES=ON - # Check that we don't miscompile with optimalizations... - - cmake -H. -BBuild-Release -DCMAKE_BUILD_TYPE=Release -Wdev -DUSE_CPP14=${CPP14} + - | + # Use Debug builds for running Valgrind and building examples + cmake -H. -BBuild-Debug -DCMAKE_BUILD_TYPE=Debug -Wdev -DUSE_CPP14=${CPP14} -DUSE_VALGRIND=${VALGRIND} -DBUILD_EXAMPLES=ON -DENABLE_COVERAGE=ON + # Don't bother with release build for coverage build + cmake -H. -BBuild-Release -DCMAKE_BUILD_TYPE=Release -Wdev -DUSE_CPP14=${CPP14} + script: - - cd Build-Debug - - make -j 2 - - ctest -V -j 2 - - cd ../Build-Release - - make -j 2 - - ctest -V -j 2 + - | + cd Build-Debug + make -j 2 + CTEST_OUTPUT_ON_FAILURE=1 ctest -j 2 + # Coverage collection does not work for OS X atm + if [[ "${TRAVIS_OS_NAME}" == "linux" ]]; then + make gcov + make lcov + bash <(curl -s https://codecov.io/bash) -X gcov || echo "Codecov did not collect coverage reports" + fi + # Go to release build + cd ../Build-Release + make -j 2 + CTEST_OUTPUT_ON_FAILURE=1 ctest -j 2 diff --git a/CMake/FindGcov.cmake b/CMake/FindGcov.cmake new file mode 100644 index 00000000..6ffd6eac --- /dev/null +++ b/CMake/FindGcov.cmake @@ -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 -gcov +# for each target. +if (NOT TARGET gcov) + add_custom_target(gcov) +endif (NOT TARGET gcov) + + + +# This function will add gcov evaluation for target . Only sources of +# this target will be evaluated and no dependencies will be added. It will call +# Gcov on any source file of 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 + add_custom_target(${TNAME}-gcov DEPENDS ${BUFFER}) + + # add evaluation target to the global gcov target. + add_dependencies(gcov ${TNAME}-gcov) +endfunction (add_gcov_target) diff --git a/CMake/FindLcov.cmake b/CMake/FindLcov.cmake new file mode 100644 index 00000000..beb925ae --- /dev/null +++ b/CMake/FindLcov.cmake @@ -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 . Only sources of +# this target will be evaluated and no dependencies will be added. It will call +# geninfo on any source file of 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 , +# 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 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 , 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 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 + # -geninfo and -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 -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 -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) diff --git a/CMake/Findcodecov.cmake b/CMake/Findcodecov.cmake new file mode 100644 index 00000000..fa135fa8 --- /dev/null +++ b/CMake/Findcodecov.cmake @@ -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) diff --git a/CMake/llvm-cov-wrapper b/CMake/llvm-cov-wrapper new file mode 100755 index 00000000..2ac33102 --- /dev/null +++ b/CMake/llvm-cov-wrapper @@ -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 $@ diff --git a/CMakeLists.txt b/CMakeLists.txt index e1208a9a..e06c26d9 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -4,6 +4,7 @@ project(CatchSelfTest) option(USE_VALGRIND "Perform SelfTests with Valgrind" OFF) option(BUILD_EXAMPLES "Build documentation examples" OFF) +option(ENABLE_COVERAGE "Generate coverage for codecov.io" OFF) set_property(GLOBAL PROPERTY USE_FOLDERS ON) @@ -280,7 +281,6 @@ set(HEADERS SOURCE_GROUP("Tests" FILES ${TEST_SOURCES}) SOURCE_GROUP("Surrogates" FILES ${SURROGATE_SOURCES}) -# configure the executable # Projects consuming Catch via ExternalProject_Add might want to use install step # without building all of our selftests. @@ -299,10 +299,17 @@ if (NOT NO_SELFTEST) set_property(TARGET SelfTest PROPERTY CXX_STANDARD_REQUIRED ON) set_property(TARGET SelfTest PROPERTY CXX_EXTENSIONS OFF) + if (ENABLE_COVERAGE) + list(APPEND CMAKE_MODULE_PATH "${CMAKE_CURRENT_LIST_DIR}/CMake") + find_package(codecov) + add_coverage(SelfTest) + list(APPEND LCOV_REMOVE_PATTERNS "/usr/") + coverage_evaluate() + endif() # Add desired warnings if ( CMAKE_CXX_COMPILER_ID MATCHES "Clang|AppleClang|GNU" ) - target_compile_options( SelfTest PRIVATE -Wall -Wextra -Wunreachable-code ) + target_compile_options( SelfTest PRIVATE -Wall -Wextra -Wunreachable-code -Werror ) endif() # Clang specific warning go here if ( CMAKE_CXX_COMPILER_ID MATCHES "Clang" ) @@ -315,27 +322,35 @@ if (NOT NO_SELFTEST) # configure unit tests via CTest - enable_testing() + include(CTest) add_test(NAME RunTests COMMAND $) - add_test(NAME ListTests COMMAND $ --list-tests) + add_test(NAME ListTests COMMAND $ --list-tests --verbosity high) set_tests_properties(ListTests PROPERTIES PASS_REGULAR_EXPRESSION "[0-9]+ test cases") add_test(NAME ListTags COMMAND $ --list-tags) set_tests_properties(ListTags PROPERTIES PASS_REGULAR_EXPRESSION "[0-9]+ tags") + add_test(NAME ListReporters COMMAND $ --list-reporters) + set_tests_properties(ListReporters PROPERTIES PASS_REGULAR_EXPRESSION "Available reporters:") + + add_test(NAME ListTestNamesOnly COMMAND $ --list-test-names-only) + set_tests_properties(ListTestNamesOnly PROPERTIES PASS_REGULAR_EXPRESSION "Regex string matcher") + + + # AppVeyor has a Python 2.7 in path, but doesn't have .py files as autorunnable add_test(NAME ApprovalTests COMMAND python ${CMAKE_CURRENT_SOURCE_DIR}/scripts/approvalTests.py $) set_tests_properties(ApprovalTests PROPERTIES FAIL_REGULAR_EXPRESSION "Results differed") - + if (USE_VALGRIND) add_test(NAME ValgrindRunTests COMMAND valgrind --leak-check=full --error-exitcode=1 $) - add_test(NAME ValgrindListTests COMMAND valgrind --leak-check=full --error-exitcode=1 $ --list-tests) + add_test(NAME ValgrindListTests COMMAND valgrind --leak-check=full --error-exitcode=1 $ --list-tests --verbosity high) set_tests_properties(ValgrindListTests PROPERTIES PASS_REGULAR_EXPRESSION "definitely lost: 0 bytes in 0 blocks") add_test(NAME ValgrindListTags COMMAND valgrind --leak-check=full --error-exitcode=1 $ --list-tags) set_tests_properties(ValgrindListTags PROPERTIES PASS_REGULAR_EXPRESSION "definitely lost: 0 bytes in 0 blocks") endif() - + endif() # !NO_SELFTEST @@ -343,7 +358,6 @@ if(BUILD_EXAMPLES) add_subdirectory(examples) endif() - install(DIRECTORY "single_include/" DESTINATION "include/catch") ## Provide some pkg-config integration diff --git a/README.md b/README.md index fc85b85e..7b5da99f 100644 --- a/README.md +++ b/README.md @@ -2,8 +2,8 @@ ![catch logo](artwork/catch2-logo-small.png) [![Github Releases](https://img.shields.io/github/release/catchorg/catch2.svg)](https://github.com/catchorg/catch2/releases) -[![Build Status](https://travis-ci.org/catchorg/Catch2.svg)](https://travis-ci.org/catchorg/Catch2) -[![Build status](https://ci.appveyor.com/api/projects/status/hrtk60hv6tw6fght?svg=true)](https://ci.appveyor.com/project/catchorg/catch2) +[![Build Status](https://travis-ci.org/catchorg/Catch2.svg?branch=master)](https://travis-ci.org/catchorg/Catch2) +[![Build status](https://ci.appveyor.com/api/projects/status/github/catchorg/Catch2?svg=true)](https://ci.appveyor.com/project/catchorg/catch2) [![Try online](https://img.shields.io/badge/try-online-blue.svg)](https://wandbox.org/permlink/tB8z0G3kMAIZkIca) The latest version of the single header can be downloaded directly using this link diff --git a/appveyor.yml b/appveyor.yml index f3a2c5bd..0fb159e6 100644 --- a/appveyor.yml +++ b/appveyor.yml @@ -27,11 +27,10 @@ matrix: init: - git config --global core.autocrlf input - # Set build version to git commit-hash - - ps: Update-AppveyorBuild -Version "$($env:APPVEYOR_REPO_BRANCH) - $($env:APPVEYOR_REPO_COMMIT)" -# fetch repository as zip archive -shallow_clone: true +install: + - ps: if (($env:CONFIGURATION) -eq "Debug" ) { python -m pip install codecov } + - ps: if (($env:CONFIGURATION) -eq "Debug" ) { .\misc\installOpenCppCoverage.ps1 } # Win32 and x64 are CMake-compatible solution platform names. # This allows us to pass %PLATFORM% to CMake -A. @@ -46,9 +45,12 @@ configuration: #Cmake will autodetect the compiler, but we set the arch before_build: - - python scripts/generateSingleHeader.py - set CXXFLAGS=%additional_flags% - - cmake -H. -BBuild -A%PLATFORM% -DUSE_WMAIN=%wmain% -DBUILD_EXAMPLES=ON + # Indirection because appveyor doesn't handle multiline batch scripts properly + # https://stackoverflow.com/questions/37627248/how-to-split-a-command-over-multiple-lines-in-appveyor-yml/37647169#37647169 + # https://help.appveyor.com/discussions/questions/3888-multi-line-cmd-or-powershell-warning-ignore + - cmd: .\misc\appveyorBuildConfigurationScript.bat + # build with MSBuild build: @@ -57,5 +59,5 @@ build: verbosity: normal # MSBuild verbosity level {quiet|minimal|normal|detailed} test_script: - - cd Build - - ctest -V -j 2 -C %CONFIGURATION% + - set CTEST_OUTPUT_ON_FAILURE=1 + - cmd: .\misc\appveyorTestRunScript.bat diff --git a/codecov.yml b/codecov.yml new file mode 100644 index 00000000..54226c2a --- /dev/null +++ b/codecov.yml @@ -0,0 +1,10 @@ +codecov: + branch: master + +coverage: + ignore: + - "projects/SelfTest" + - "**/catch_reporter_tap.hpp" + - "**/catch_reporter_automake.hpp" + - "**/catch_reporter_teamcity.hpp" + - "**/external/clara.hpp" diff --git a/docs/release-notes.md b/docs/release-notes.md index 4d053578..1a762dbd 100644 --- a/docs/release-notes.md +++ b/docs/release-notes.md @@ -87,6 +87,41 @@ # Older versions +## 1.11.x + +### 1.11.0 + +#### Fixes +* The original expression in `REQUIRE_FALSE( expr )` is now reporter properly as `!( expr )` (#1051) + * Previously the parentheses were missing and `x != y` would be expanded as `!x != x` +* `Approx::Margin` is now inclusive (#952) + * Previously it was meant and documented as inclusive, but the check itself wasn't + * This means that `REQUIRE( 0.25f == Approx( 0.0f ).margin( 0.25f ) )` passes, instead of fails +* `RandomNumberGenerator::result_type` is now unsigned (#1050) + +#### Improvements +* `__JETBRAINS_IDE__` macro handling is now CLion version specific (#1017) + * When CLion 2017.3 or newer is detected, `__COUNTER__` is used instead of +* TeamCity reporter now explicitly flushes output stream after each report (#1057) + * On some platforms, output from redirected streams would show up only after the tests finished running +* `ParseAndAddCatchTests` now can add test files as dependency to CMake configuration + * This means you do not have to manually rerun CMake configuration step to detect new tests + +## 1.10.x + +### 1.10.0 + +#### Fixes +* Evaluation layer has been rewritten (backported from Catch 2) + * The new layer is much simpler and fixes some issues (#981) +* Implemented workaround for VS 2017 raw string literal stringification bug (#995) +* Fixed interaction between `[!shouldfail]` and `[!mayfail]` tags and sections + * Previously sections with failing assertions would be marked as failed, not failed-but-ok + +#### Improvements +* Added [libidentify](https://github.com/janwilmans/LibIdentify) support +* Added "wait-for-keypress" option + ## 1.9.x ### 1.9.6 diff --git a/examples/210-Evt-EventListeners.cpp b/examples/210-Evt-EventListeners.cpp index a2103a0b..8ba360f5 100644 --- a/examples/210-Evt-EventListeners.cpp +++ b/examples/210-Evt-EventListeners.cpp @@ -295,12 +295,15 @@ void print( std::ostream& os, int const level, std::string const& title, Catch:: // 2. My listener and registration: // -const std::string dashed_line = +char const * dashed_line = "--------------------------------------------------------------------------"; struct MyListener : Catch::TestEventListenerBase { using TestEventListenerBase::TestEventListenerBase; // inherit constructor + + // Get rid of Wweak-tables + ~MyListener(); // The whole test run starting virtual void testRunStarting( Catch::TestRunInfo const& testRunInfo ) override { @@ -367,6 +370,10 @@ struct MyListener : Catch::TestEventListenerBase { CATCH_REGISTER_LISTENER( MyListener ) +// Get rid of Wweak-tables +MyListener::~MyListener() {} + + // ----------------------------------------------------------------------- // 3. Test cases: // diff --git a/include/internal/catch_assertionhandler.cpp b/include/internal/catch_assertionhandler.cpp index 8bef64c1..ba9ded90 100644 --- a/include/internal/catch_assertionhandler.cpp +++ b/include/internal/catch_assertionhandler.cpp @@ -13,8 +13,7 @@ #include "catch_debugger.h" #include "catch_interfaces_registry_hub.h" #include "catch_capture_matchers.h" - -#include +#include "catch_run_context.h" namespace Catch { @@ -54,87 +53,60 @@ namespace Catch { SourceLineInfo const& lineInfo, StringRef capturedExpression, ResultDisposition::Flags resultDisposition ) - : m_assertionInfo{ macroName, lineInfo, capturedExpression, resultDisposition } - { - getCurrentContext().getResultCapture()->assertionStarting( m_assertionInfo ); + : m_assertionInfo{ macroName, lineInfo, capturedExpression, resultDisposition }, + m_resultCapture( getResultCapture() ) + {} + + AssertionHandler::~AssertionHandler() { + if ( !m_completed ) + m_resultCapture.handleIncomplete( m_assertionInfo ); } - void AssertionHandler::handle( ITransientExpression const& expr ) { - - bool negated = isFalseTest( m_assertionInfo.resultDisposition ); - bool result = expr.getResult() != negated; - - if(result && !getCurrentContext().getConfig()->includeSuccessfulResults()) - { - getCurrentContext().getResultCapture()->assertionRun(); - getCurrentContext().getResultCapture()->assertionPassed(); - return; - } - - handle( result ? ResultWas::Ok : ResultWas::ExpressionFailed, &expr, negated ); + void AssertionHandler::handleExpr( ITransientExpression const& expr ) { + m_resultCapture.handleExpr( m_assertionInfo, expr, m_reaction ); } - void AssertionHandler::handle( ResultWas::OfType resultType ) { - handle( resultType, nullptr, false ); - } - void AssertionHandler::handle( ResultWas::OfType resultType, StringRef const& message ) { - AssertionResultData data( resultType, LazyExpression( false ) ); - data.message = message; - handle( data, nullptr ); - } - void AssertionHandler::handle( ResultWas::OfType resultType, ITransientExpression const* expr, bool negated ) { - AssertionResultData data( resultType, LazyExpression( negated ) ); - handle( data, expr ); - } - void AssertionHandler::handle( AssertionResultData const& resultData, ITransientExpression const* expr ) { - - getResultCapture().assertionRun(); - - AssertionResult assertionResult{ m_assertionInfo, resultData }; - assertionResult.m_resultData.lazyExpression.m_transientExpression = expr; - - getResultCapture().assertionEnded( assertionResult ); - - if( !assertionResult.isOk() ) { - m_shouldDebugBreak = getCurrentContext().getConfig()->shouldDebugBreak(); - m_shouldThrow = - getCurrentContext().getRunner()->aborting() || - (m_assertionInfo.resultDisposition & ResultDisposition::Normal); - } + void AssertionHandler::handleMessage(ResultWas::OfType resultType, StringRef const& message) { + m_resultCapture.handleMessage( m_assertionInfo, resultType, message, m_reaction ); } auto AssertionHandler::allowThrows() const -> bool { return getCurrentContext().getConfig()->allowThrows(); } - auto AssertionHandler::shouldDebugBreak() const -> bool { - return m_shouldDebugBreak; - } - void AssertionHandler::reactWithDebugBreak() const { - if (m_shouldDebugBreak) { - /////////////////////////////////////////////////////////////////// - // To inspect the state during test, you need to go one level up the callstack - // To go back to the test and change execution, jump over the reactWithoutDebugBreak() call - /////////////////////////////////////////////////////////////////// + void AssertionHandler::complete() { + setCompleted(); + if( m_reaction.shouldDebugBreak ) { + + // If you find your debugger stopping you here then go one level up on the + // call-stack for the code that caused it (typically a failed assertion) + + // (To go back to the test and change execution, jump over the throw, next) CATCH_BREAK_INTO_DEBUGGER(); } - reactWithoutDebugBreak(); - } - void AssertionHandler::reactWithoutDebugBreak() const { - if( m_shouldThrow ) + if( m_reaction.shouldThrow ) throw Catch::TestFailureException(); } - - void AssertionHandler::useActiveException() { - handle( ResultWas::ThrewException, Catch::translateActiveException() ); + void AssertionHandler::setCompleted() { + m_completed = true; } - void AssertionHandler::setExceptionGuard() { - assert( m_inExceptionGuard == false ); - m_inExceptionGuard = true; + void AssertionHandler::handleUnexpectedInflightException() { + m_resultCapture.handleUnexpectedInflightException( m_assertionInfo, Catch::translateActiveException(), m_reaction ); } - void AssertionHandler::unsetExceptionGuard() { - assert( m_inExceptionGuard == true ); - m_inExceptionGuard = false; + + void AssertionHandler::handleExceptionThrownAsExpected() { + m_resultCapture.handleNonExpr(m_assertionInfo, ResultWas::Ok, m_reaction); + } + void AssertionHandler::handleExceptionNotThrownAsExpected() { + m_resultCapture.handleNonExpr(m_assertionInfo, ResultWas::Ok, m_reaction); + } + + void AssertionHandler::handleUnexpectedExceptionNotThrown() { + m_resultCapture.handleUnexpectedExceptionNotThrown( m_assertionInfo, m_reaction ); + } + + void AssertionHandler::handleThrowingCallSkipped() { + m_resultCapture.handleNonExpr(m_assertionInfo, ResultWas::Ok, m_reaction); } // This is the overload that takes a string and infers the Equals matcher from it diff --git a/include/internal/catch_assertionhandler.h b/include/internal/catch_assertionhandler.h index bfd6aeba..bd785829 100644 --- a/include/internal/catch_assertionhandler.h +++ b/include/internal/catch_assertionhandler.h @@ -17,10 +17,13 @@ namespace Catch { struct TestFailureException{}; struct AssertionResultData; + struct IResultCapture; + class RunContext; class LazyExpression { friend class AssertionHandler; friend struct AssertionStats; + friend class RunContext; ITransientExpression const* m_transientExpression = nullptr; bool m_isNegated; @@ -34,11 +37,16 @@ namespace Catch { friend auto operator << ( std::ostream& os, LazyExpression const& lazyExpr ) -> std::ostream&; }; + struct AssertionReaction { + bool shouldDebugBreak = false; + bool shouldThrow = false; + }; + class AssertionHandler { AssertionInfo m_assertionInfo; - bool m_shouldDebugBreak = false; - bool m_shouldThrow = false; - bool m_inExceptionGuard = false; + AssertionReaction m_reaction; + bool m_completed = false; + IResultCapture& m_resultCapture; public: AssertionHandler @@ -54,24 +62,25 @@ namespace Catch { } } - void handle( ITransientExpression const& expr ); - template - void handle( ExprLhs const& expr ) { - handle( expr.makeUnaryExpr() ); + void handleExpr( ExprLhs const& expr ) { + handleExpr( expr.makeUnaryExpr() ); } - void handle( ResultWas::OfType resultType ); - void handle( ResultWas::OfType resultType, StringRef const& message ); - void handle( ResultWas::OfType resultType, ITransientExpression const* expr, bool negated ); - void handle( AssertionResultData const& resultData, ITransientExpression const* expr ); + void handleExpr( ITransientExpression const& expr ); - auto shouldDebugBreak() const -> bool; + void handleMessage(ResultWas::OfType resultType, StringRef const& message); + + void handleExceptionThrownAsExpected(); + void handleUnexpectedExceptionNotThrown(); + void handleExceptionNotThrownAsExpected(); + void handleThrowingCallSkipped(); + void handleUnexpectedInflightException(); + + void complete(); + void setCompleted(); + + // query auto allowThrows() const -> bool; - void reactWithDebugBreak() const; - void reactWithoutDebugBreak() const; - void useActiveException(); - void setExceptionGuard(); - void unsetExceptionGuard(); }; void handleExceptionMatchExpr( AssertionHandler& handler, std::string const& str, StringRef matcherString ); diff --git a/include/internal/catch_capture.hpp b/include/internal/catch_capture.hpp index 595336d4..e4600f7c 100644 --- a/include/internal/catch_capture.hpp +++ b/include/internal/catch_capture.hpp @@ -11,7 +11,6 @@ #include "catch_assertionhandler.h" #include "catch_message.h" #include "catch_interfaces_capture.h" -#include "catch_debugger.h" #if !defined(CATCH_CONFIG_DISABLE) @@ -22,48 +21,33 @@ #endif #if defined(CATCH_CONFIG_FAST_COMPILE) -/////////////////////////////////////////////////////////////////////////////// -// We can speedup compilation significantly by breaking into debugger lower in -// the callstack, because then we don't have to expand CATCH_BREAK_INTO_DEBUGGER -// macro in each assertion -#define INTERNAL_CATCH_REACT( handler ) \ - handler.reactWithDebugBreak(); /////////////////////////////////////////////////////////////////////////////// // Another way to speed-up compilation is to omit local try-catch for REQUIRE* // macros. -// This can potentially cause false negative, if the test code catches -// the exception before it propagates back up to the runner. -#define INTERNAL_CATCH_TRY( capturer ) capturer.setExceptionGuard(); -#define INTERNAL_CATCH_CATCH( capturer ) capturer.unsetExceptionGuard(); +#define INTERNAL_CATCH_TRY +#define INTERNAL_CATCH_CATCH( capturer ) #else // CATCH_CONFIG_FAST_COMPILE -/////////////////////////////////////////////////////////////////////////////// -// In the event of a failure works out if the debugger needs to be invoked -// and/or an exception thrown and takes appropriate action. -// This needs to be done as a macro so the debugger will stop in the user -// source code rather than in Catch library code -#define INTERNAL_CATCH_REACT( handler ) \ - if( handler.shouldDebugBreak() ) CATCH_BREAK_INTO_DEBUGGER(); \ - handler.reactWithoutDebugBreak(); - -#define INTERNAL_CATCH_TRY( capturer ) try -#define INTERNAL_CATCH_CATCH( capturer ) catch(...) { capturer.useActiveException(); } +#define INTERNAL_CATCH_TRY try +#define INTERNAL_CATCH_CATCH( handler ) catch(...) { handler.handleUnexpectedInflightException(); } #endif +#define INTERNAL_CATCH_REACT( handler ) handler.complete(); + /////////////////////////////////////////////////////////////////////////////// #define INTERNAL_CATCH_TEST( macroName, resultDisposition, ... ) \ do { \ Catch::AssertionHandler catchAssertionHandler( macroName, CATCH_INTERNAL_LINEINFO, CATCH_INTERNAL_STRINGIFY(__VA_ARGS__), resultDisposition ); \ - INTERNAL_CATCH_TRY( catchAssertionHandler ) { \ + INTERNAL_CATCH_TRY { \ CATCH_INTERNAL_SUPPRESS_PARENTHESES_WARNINGS \ - catchAssertionHandler.handle( Catch::Decomposer() <= __VA_ARGS__ ); \ + catchAssertionHandler.handleExpr( Catch::Decomposer() <= __VA_ARGS__ ); \ CATCH_INTERNAL_UNSUPPRESS_PARENTHESES_WARNINGS \ } INTERNAL_CATCH_CATCH( catchAssertionHandler ) \ INTERNAL_CATCH_REACT( catchAssertionHandler ) \ - } while( Catch::isTrue(false) && static_cast( !!(__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( !!(__VA_ARGS__) ) ) // the expression here is never evaluated at runtime but it forces the compiler to give it a look // The double negation silences MSVC's C4800 warning, the static_cast forces short-circuit evaluation if the type has overloaded &&. /////////////////////////////////////////////////////////////////////////////// @@ -82,13 +66,13 @@ Catch::AssertionHandler catchAssertionHandler( macroName, CATCH_INTERNAL_LINEINFO, CATCH_INTERNAL_STRINGIFY(__VA_ARGS__), resultDisposition ); \ try { \ static_cast(__VA_ARGS__); \ - catchAssertionHandler.handle( Catch::ResultWas::Ok ); \ + catchAssertionHandler.handleExceptionNotThrownAsExpected(); \ } \ catch( ... ) { \ - catchAssertionHandler.useActiveException(); \ + catchAssertionHandler.handleUnexpectedInflightException(); \ } \ INTERNAL_CATCH_REACT( catchAssertionHandler ) \ - } while( Catch::alwaysFalse() ) + } while( false ) /////////////////////////////////////////////////////////////////////////////// #define INTERNAL_CATCH_THROWS( macroName, resultDisposition, ... ) \ @@ -97,15 +81,15 @@ if( catchAssertionHandler.allowThrows() ) \ try { \ static_cast(__VA_ARGS__); \ - catchAssertionHandler.handle( Catch::ResultWas::DidntThrowException ); \ + catchAssertionHandler.handleUnexpectedExceptionNotThrown(); \ } \ catch( ... ) { \ - catchAssertionHandler.handle( Catch::ResultWas::Ok ); \ + catchAssertionHandler.handleExceptionThrownAsExpected(); \ } \ else \ - catchAssertionHandler.handle( Catch::ResultWas::Ok ); \ + catchAssertionHandler.handleThrowingCallSkipped(); \ INTERNAL_CATCH_REACT( catchAssertionHandler ) \ - } while( Catch::alwaysFalse() ) + } while( false ) /////////////////////////////////////////////////////////////////////////////// #define INTERNAL_CATCH_THROWS_AS( macroName, exceptionType, resultDisposition, expr ) \ @@ -114,31 +98,31 @@ if( catchAssertionHandler.allowThrows() ) \ try { \ static_cast(expr); \ - catchAssertionHandler.handle( Catch::ResultWas::DidntThrowException ); \ + catchAssertionHandler.handleUnexpectedExceptionNotThrown(); \ } \ catch( exceptionType const& ) { \ - catchAssertionHandler.handle( Catch::ResultWas::Ok ); \ + catchAssertionHandler.handleExceptionThrownAsExpected(); \ } \ catch( ... ) { \ - catchAssertionHandler.useActiveException(); \ + catchAssertionHandler.handleUnexpectedInflightException(); \ } \ else \ - catchAssertionHandler.handle( Catch::ResultWas::Ok ); \ + catchAssertionHandler.handleThrowingCallSkipped(); \ INTERNAL_CATCH_REACT( catchAssertionHandler ) \ - } while( Catch::alwaysFalse() ) + } while( false ) /////////////////////////////////////////////////////////////////////////////// #define INTERNAL_CATCH_MSG( macroName, messageType, resultDisposition, ... ) \ do { \ Catch::AssertionHandler catchAssertionHandler( macroName, CATCH_INTERNAL_LINEINFO, "", resultDisposition ); \ - catchAssertionHandler.handle( messageType, ( Catch::MessageStream() << __VA_ARGS__ + ::Catch::StreamEndStop() ).m_stream.str() ); \ + catchAssertionHandler.handleMessage( messageType, ( Catch::MessageStream() << __VA_ARGS__ + ::Catch::StreamEndStop() ).m_stream.str() ); \ INTERNAL_CATCH_REACT( catchAssertionHandler ) \ - } while( Catch::alwaysFalse() ) + } while( false ) /////////////////////////////////////////////////////////////////////////////// #define INTERNAL_CATCH_INFO( macroName, log ) \ - Catch::ScopedMessage INTERNAL_CATCH_UNIQUE_NAME( scopedMessage ) = Catch::MessageBuilder( macroName, CATCH_INTERNAL_LINEINFO, Catch::ResultWas::Info ) << log; + Catch::ScopedMessage INTERNAL_CATCH_UNIQUE_NAME( scopedMessage )( Catch::MessageBuilder( macroName, CATCH_INTERNAL_LINEINFO, Catch::ResultWas::Info ) << log ); /////////////////////////////////////////////////////////////////////////////// // Although this is matcher-based, it can be used with just a string @@ -148,15 +132,15 @@ if( catchAssertionHandler.allowThrows() ) \ try { \ static_cast(__VA_ARGS__); \ - catchAssertionHandler.handle( Catch::ResultWas::DidntThrowException ); \ + catchAssertionHandler.handleUnexpectedExceptionNotThrown(); \ } \ catch( ... ) { \ - handleExceptionMatchExpr( catchAssertionHandler, matcher, #matcher ); \ + Catch::handleExceptionMatchExpr( catchAssertionHandler, matcher, #matcher ); \ } \ else \ - catchAssertionHandler.handle( Catch::ResultWas::Ok ); \ + catchAssertionHandler.handleThrowingCallSkipped(); \ INTERNAL_CATCH_REACT( catchAssertionHandler ) \ - } while( Catch::alwaysFalse() ) + } while( false ) #endif // CATCH_CONFIG_DISABLE diff --git a/include/internal/catch_capture_matchers.cpp b/include/internal/catch_capture_matchers.cpp index 758177e9..7ef1597d 100644 --- a/include/internal/catch_capture_matchers.cpp +++ b/include/internal/catch_capture_matchers.cpp @@ -18,7 +18,7 @@ namespace Catch { void handleExceptionMatchExpr( AssertionHandler& handler, StringMatcher const& matcher, StringRef matcherString ) { std::string exceptionMessage = Catch::translateActiveException(); MatchExpr expr( exceptionMessage, matcher, matcherString ); - handler.handle( expr ); + handler.handleExpr( expr ); } } // namespace Catch diff --git a/include/internal/catch_capture_matchers.h b/include/internal/catch_capture_matchers.h index 77366f33..358bbc3b 100644 --- a/include/internal/catch_capture_matchers.h +++ b/include/internal/catch_capture_matchers.h @@ -21,18 +21,14 @@ namespace Catch { ArgT const& m_arg; MatcherT m_matcher; StringRef m_matcherString; - bool m_result; public: MatchExpr( ArgT const& arg, MatcherT const& matcher, StringRef matcherString ) - : m_arg( arg ), + : ITransientExpression{ true, matcher.match( arg ) }, + m_arg( arg ), m_matcher( matcher ), - m_matcherString( matcherString ), - m_result( matcher.match( arg ) ) + m_matcherString( matcherString ) {} - auto isBinaryExpression() const -> bool override { return true; } - auto getResult() const -> bool override { return m_result; } - void streamReconstructedExpression( std::ostream &os ) const override { auto matcherAsString = m_matcher.toString(); os << Catch::Detail::stringify( m_arg ) << ' '; @@ -59,11 +55,11 @@ namespace Catch { #define INTERNAL_CHECK_THAT( macroName, matcher, resultDisposition, arg ) \ do { \ Catch::AssertionHandler catchAssertionHandler( macroName, CATCH_INTERNAL_LINEINFO, CATCH_INTERNAL_STRINGIFY(arg) ", " CATCH_INTERNAL_STRINGIFY(matcher), resultDisposition ); \ - INTERNAL_CATCH_TRY( catchAssertionHandler ) { \ - catchAssertionHandler.handle( Catch::makeMatchExpr( arg, matcher, #matcher ) ); \ + INTERNAL_CATCH_TRY { \ + catchAssertionHandler.handleExpr( Catch::makeMatchExpr( arg, matcher, #matcher ) ); \ } INTERNAL_CATCH_CATCH( catchAssertionHandler ) \ INTERNAL_CATCH_REACT( catchAssertionHandler ) \ - } while( Catch::alwaysFalse() ) + } while( false ) /////////////////////////////////////////////////////////////////////////////// @@ -73,17 +69,17 @@ namespace Catch { if( catchAssertionHandler.allowThrows() ) \ try { \ static_cast(__VA_ARGS__ ); \ - catchAssertionHandler.handle( Catch::ResultWas::DidntThrowException ); \ + catchAssertionHandler.handleUnexpectedExceptionNotThrown(); \ } \ catch( exceptionType const& ex ) { \ - catchAssertionHandler.handle( Catch::makeMatchExpr( ex, matcher, #matcher ) ); \ + catchAssertionHandler.handleExpr( Catch::makeMatchExpr( ex, matcher, #matcher ) ); \ } \ catch( ... ) { \ - catchAssertionHandler.useActiveException(); \ + catchAssertionHandler.handleUnexpectedInflightException(); \ } \ else \ - catchAssertionHandler.handle( Catch::ResultWas::Ok ); \ + catchAssertionHandler.handleThrowingCallSkipped(); \ INTERNAL_CATCH_REACT( catchAssertionHandler ) \ - } while( Catch::alwaysFalse() ) + } while( false ) #endif // TWOBLUECUBES_CATCH_CAPTURE_MATCHERS_HPP_INCLUDED diff --git a/include/internal/catch_common.cpp b/include/internal/catch_common.cpp index 7e24afc7..c271146d 100644 --- a/include/internal/catch_common.cpp +++ b/include/internal/catch_common.cpp @@ -34,9 +34,6 @@ namespace Catch { return os; } - bool alwaysTrue() { return true; } - bool alwaysFalse() { return false; } - std::string StreamEndStop::operator+() const { return std::string(); } diff --git a/include/internal/catch_common.h b/include/internal/catch_common.h index 2c2962f8..b904da15 100644 --- a/include/internal/catch_common.h +++ b/include/internal/catch_common.h @@ -61,12 +61,6 @@ namespace Catch { std::ostream& operator << ( std::ostream& os, SourceLineInfo const& info ); - // This is just here to avoid compiler warnings with macro constants and boolean literals - inline bool isTrue( bool value ){ return value; } - - bool alwaysTrue(); - bool alwaysFalse(); - // Use this in variadic streaming macros to allow // >> +StreamEndStop // as well as diff --git a/include/internal/catch_debugger.h b/include/internal/catch_debugger.h index f842ff5b..6ca6a539 100644 --- a/include/internal/catch_debugger.h +++ b/include/internal/catch_debugger.h @@ -40,7 +40,7 @@ namespace Catch { #ifdef CATCH_TRAP #define CATCH_BREAK_INTO_DEBUGGER() if( Catch::isDebuggerActive() ) { CATCH_TRAP(); } #else - #define CATCH_BREAK_INTO_DEBUGGER() Catch::alwaysTrue(); + #define CATCH_BREAK_INTO_DEBUGGER() (void)0, 0 #endif #endif // TWOBLUECUBES_CATCH_DEBUGGER_H_INCLUDED diff --git a/include/internal/catch_decomposer.h b/include/internal/catch_decomposer.h index 25824c78..d663114b 100644 --- a/include/internal/catch_decomposer.h +++ b/include/internal/catch_decomposer.h @@ -24,27 +24,32 @@ namespace Catch { struct ITransientExpression { - virtual auto isBinaryExpression() const -> bool = 0; - virtual auto getResult() const -> bool = 0; + auto isBinaryExpression() const -> bool { return m_isBinaryExpression; } + auto getResult() const -> bool { return m_result; } virtual void streamReconstructedExpression( std::ostream &os ) const = 0; - // We don't actually need a virtual destructore, but many static analysers + ITransientExpression( bool isBinaryExpression, bool result ) + : m_isBinaryExpression( isBinaryExpression ), + m_result( result ) + {} + + // We don't actually need a virtual destructor, but many static analysers // complain if it's not here :-( virtual ~ITransientExpression(); + + bool m_isBinaryExpression; + bool m_result; + }; void formatReconstructedExpression( std::ostream &os, std::string const& lhs, StringRef op, std::string const& rhs ); template class BinaryExpr : public ITransientExpression { - bool m_result; LhsT m_lhs; StringRef m_op; RhsT m_rhs; - auto isBinaryExpression() const -> bool override { return true; } - auto getResult() const -> bool override { return m_result; } - void streamReconstructedExpression( std::ostream &os ) const override { formatReconstructedExpression ( os, Catch::Detail::stringify( m_lhs ), m_op, Catch::Detail::stringify( m_rhs ) ); @@ -52,7 +57,7 @@ namespace Catch { public: BinaryExpr( bool comparisonResult, LhsT lhs, StringRef op, RhsT rhs ) - : m_result( comparisonResult ), + : ITransientExpression{ true, comparisonResult }, m_lhs( lhs ), m_op( op ), m_rhs( rhs ) @@ -63,15 +68,15 @@ namespace Catch { class UnaryExpr : public ITransientExpression { LhsT m_lhs; - auto isBinaryExpression() const -> bool override { return false; } - auto getResult() const -> bool override { return m_lhs ? true : false; } - void streamReconstructedExpression( std::ostream &os ) const override { os << Catch::Detail::stringify( m_lhs ); } public: - UnaryExpr( LhsT lhs ) : m_lhs( lhs ) {} + explicit UnaryExpr( LhsT lhs ) + : ITransientExpression{ false, lhs ? true : false }, + m_lhs( lhs ) + {} }; @@ -103,43 +108,43 @@ namespace Catch { class ExprLhs { LhsT m_lhs; public: - ExprLhs( LhsT lhs ) : m_lhs( lhs ) {} + explicit ExprLhs( LhsT lhs ) : m_lhs( lhs ) {} template auto operator == ( RhsT const& rhs ) -> BinaryExpr const { - return BinaryExpr( compareEqual( m_lhs, rhs ), m_lhs, "==", rhs ); + return { compareEqual( m_lhs, rhs ), m_lhs, "==", rhs }; } auto operator == ( bool rhs ) -> BinaryExpr const { - return BinaryExpr( m_lhs == rhs, m_lhs, "==", rhs ); + return { m_lhs == rhs, m_lhs, "==", rhs }; } template auto operator != ( RhsT const& rhs ) -> BinaryExpr const { - return BinaryExpr( compareNotEqual( m_lhs, rhs ), m_lhs, "!=", rhs ); + return { compareNotEqual( m_lhs, rhs ), m_lhs, "!=", rhs }; } auto operator != ( bool rhs ) -> BinaryExpr const { - return BinaryExpr( m_lhs != rhs, m_lhs, "!=", rhs ); + return { m_lhs != rhs, m_lhs, "!=", rhs }; } template auto operator > ( RhsT const& rhs ) -> BinaryExpr const { - return BinaryExpr( m_lhs > rhs, m_lhs, ">", rhs ); + return { m_lhs > rhs, m_lhs, ">", rhs }; } template auto operator < ( RhsT const& rhs ) -> BinaryExpr const { - return BinaryExpr( m_lhs < rhs, m_lhs, "<", rhs ); + return { m_lhs < rhs, m_lhs, "<", rhs }; } template auto operator >= ( RhsT const& rhs ) -> BinaryExpr const { - return BinaryExpr( m_lhs >= rhs, m_lhs, ">=", rhs ); + return { m_lhs >= rhs, m_lhs, ">=", rhs }; } template auto operator <= ( RhsT const& rhs ) -> BinaryExpr const { - return BinaryExpr( m_lhs <= rhs, m_lhs, "<=", rhs ); + return { m_lhs <= rhs, m_lhs, "<=", rhs }; } auto makeUnaryExpr() const -> UnaryExpr { - return UnaryExpr( m_lhs ); + return UnaryExpr{ m_lhs }; } }; @@ -153,10 +158,11 @@ namespace Catch { struct Decomposer { template auto operator <= ( T const& lhs ) -> ExprLhs { - return ExprLhs( lhs ); + return ExprLhs{ lhs }; } + auto operator <=( bool value ) -> ExprLhs { - return ExprLhs( value ); + return ExprLhs{ value }; } }; diff --git a/include/internal/catch_fatal_condition.cpp b/include/internal/catch_fatal_condition.cpp index 39b63455..0fa09f26 100644 --- a/include/internal/catch_fatal_condition.cpp +++ b/include/internal/catch_fatal_condition.cpp @@ -12,6 +12,11 @@ #include "catch_context.h" #include "catch_interfaces_capture.h" +#if defined(__GNUC__) +# pragma GCC diagnostic push +# pragma GCC diagnostic ignored "-Wmissing-field-initializers" +#endif + namespace { // Report the error condition void reportFatal( char const * const message ) { @@ -174,3 +179,7 @@ namespace Catch { # endif // CATCH_CONFIG_POSIX_SIGNALS #endif // not Windows + +#if defined(__GNUC__) +# pragma GCC diagnostic pop +#endif diff --git a/include/internal/catch_interfaces_capture.h b/include/internal/catch_interfaces_capture.h index 5b370114..4d091f89 100644 --- a/include/internal/catch_interfaces_capture.h +++ b/include/internal/catch_interfaces_capture.h @@ -11,6 +11,7 @@ #include #include "catch_stringref.h" +#include "catch_result_type.h" namespace Catch { @@ -22,13 +23,14 @@ namespace Catch { struct Counts; struct BenchmarkInfo; struct BenchmarkStats; + struct AssertionReaction; + + struct ITransientExpression; struct IResultCapture { virtual ~IResultCapture(); - virtual void assertionStarting( AssertionInfo const& info ) = 0; - virtual void assertionEnded( AssertionResult const& result ) = 0; virtual bool sectionStarted( SectionInfo const& sectionInfo, Counts& assertions ) = 0; virtual void sectionEnded( SectionEndInfo const& endInfo ) = 0; @@ -40,16 +42,40 @@ namespace Catch { virtual void pushScopedMessage( MessageInfo const& message ) = 0; virtual void popScopedMessage( MessageInfo const& message ) = 0; - virtual std::string getCurrentTestName() const = 0; - virtual const AssertionResult* getLastResult() const = 0; - - virtual void exceptionEarlyReported() = 0; - virtual void handleFatalErrorCondition( StringRef message ) = 0; + virtual void handleExpr + ( AssertionInfo const& info, + ITransientExpression const& expr, + AssertionReaction& reaction ) = 0; + virtual void handleMessage + ( AssertionInfo const& info, + ResultWas::OfType resultType, + StringRef const& message, + AssertionReaction& reaction ) = 0; + virtual void handleUnexpectedExceptionNotThrown + ( AssertionInfo const& info, + AssertionReaction& reaction ) = 0; + virtual void handleUnexpectedInflightException + ( AssertionInfo const& info, + std::string const& message, + AssertionReaction& reaction ) = 0; + virtual void handleIncomplete + ( AssertionInfo const& info ) = 0; + virtual void handleNonExpr + ( AssertionInfo const &info, + ResultWas::OfType resultType, + AssertionReaction &reaction ) = 0; + + + virtual bool lastAssertionPassed() = 0; virtual void assertionPassed() = 0; - virtual void assertionRun() = 0; + + // Deprecated, do not use: + virtual std::string getCurrentTestName() const = 0; + virtual const AssertionResult* getLastResult() const = 0; + virtual void exceptionEarlyReported() = 0; }; IResultCapture& getResultCapture(); diff --git a/include/internal/catch_interfaces_exception.h b/include/internal/catch_interfaces_exception.h index 3473ff52..430701cb 100644 --- a/include/internal/catch_interfaces_exception.h +++ b/include/internal/catch_interfaces_exception.h @@ -73,7 +73,9 @@ namespace Catch { /////////////////////////////////////////////////////////////////////////////// #define INTERNAL_CATCH_TRANSLATE_EXCEPTION2( translatorName, signature ) \ static std::string translatorName( signature ); \ - namespace{ Catch::ExceptionTranslatorRegistrar INTERNAL_CATCH_UNIQUE_NAME( catch_internal_ExceptionRegistrar )( &translatorName ); }\ + CATCH_INTERNAL_SUPPRESS_GLOBALS_WARNINGS \ + namespace{ Catch::ExceptionTranslatorRegistrar INTERNAL_CATCH_UNIQUE_NAME( catch_internal_ExceptionRegistrar )( &translatorName ); } \ + CATCH_INTERNAL_UNSUPPRESS_GLOBALS_WARNINGS \ static std::string translatorName( signature ) #define INTERNAL_CATCH_TRANSLATE_EXCEPTION( signature ) INTERNAL_CATCH_TRANSLATE_EXCEPTION2( INTERNAL_CATCH_UNIQUE_NAME( catch_internal_ExceptionTranslator ), signature ) diff --git a/include/internal/catch_leak_detector.cpp b/include/internal/catch_leak_detector.cpp index 32109310..36aba6ab 100644 --- a/include/internal/catch_leak_detector.cpp +++ b/include/internal/catch_leak_detector.cpp @@ -8,11 +8,11 @@ #include "catch_leak_detector.h" -namespace Catch { - #ifdef CATCH_CONFIG_WINDOWS_CRTDBG #include +namespace Catch { + LeakDetector::LeakDetector() { int flag = _CrtSetDbgFlag(_CRTDBG_REPORT_FLAG); flag |= _CRTDBG_LEAK_CHECK_DF; @@ -23,11 +23,10 @@ namespace Catch { // Change this to leaking allocation's number to break there _CrtSetBreakAlloc(-1); } +} #else - LeakDetector::LeakDetector(){} + Catch::LeakDetector::LeakDetector() {} #endif - -} diff --git a/include/internal/catch_matchers_floating.cpp b/include/internal/catch_matchers_floating.cpp index 3d2e342a..c6d2b1dd 100644 --- a/include/internal/catch_matchers_floating.cpp +++ b/include/internal/catch_matchers_floating.cpp @@ -79,7 +79,11 @@ namespace Catch { namespace Matchers { namespace Floating { WithinAbsMatcher::WithinAbsMatcher(double target, double margin) - :m_target{ target }, m_margin{ margin } {} + :m_target{ target }, m_margin{ margin } { + if (m_margin < 0) { + throw std::domain_error("Allowed margin difference has to be >= 0"); + } + } // Performs equivalent check of std::fabs(lhs - rhs) <= margin // But without the subtraction to allow for INFINITY in comparison @@ -95,7 +99,7 @@ namespace Floating { WithinUlpsMatcher::WithinUlpsMatcher(double target, int ulps, FloatingPointKind baseType) :m_target{ target }, m_ulps{ ulps }, m_type{ baseType } { if (m_ulps < 0) { - throw std::domain_error("Expected ulp difference has to be >0"); + throw std::domain_error("Allowed ulp difference has to be >= 0"); } } diff --git a/include/internal/catch_matchers_vector.h b/include/internal/catch_matchers_vector.h index 84ffab87..833d7dc2 100644 --- a/include/internal/catch_matchers_vector.h +++ b/include/internal/catch_matchers_vector.h @@ -10,10 +10,33 @@ #include "catch_matchers.h" +#include + namespace Catch { namespace Matchers { namespace Vector { + namespace Detail { + template + 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 + bool contains(InputIterator first, InputIterator last, T const& item) { + for (; first != last; ++first) { + if (*first == item) { + return true; + } + } + return false; + } + } template struct ContainsElementMatcher : MatcherBase> { @@ -89,6 +112,46 @@ namespace Matchers { std::vector const& m_comparator; }; + template + struct UnorderedEqualsMatcher : MatcherBase> { + UnorderedEqualsMatcher(std::vector const& target) : m_target(target) {} + bool match(std::vector const& vec) const override { + // Note: This is a reimplementation of std::is_permutation, + // because I don't want to include 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 const& m_target; + }; + } // namespace Vector // The following functions create the actual matcher objects. @@ -109,6 +172,11 @@ namespace Matchers { return Vector::EqualsMatcher( comparator ); } + template + Vector::UnorderedEqualsMatcher UnorderedEquals(std::vector const& target) { + return Vector::UnorderedEqualsMatcher(target); + } + } // namespace Matchers } // namespace Catch diff --git a/include/internal/catch_message.cpp b/include/internal/catch_message.cpp index a66c5271..513c1776 100644 --- a/include/internal/catch_message.cpp +++ b/include/internal/catch_message.cpp @@ -49,11 +49,18 @@ namespace Catch { getResultCapture().pushScopedMessage( m_info ); } +#if defined(_MSC_VER) +#pragma warning(push) +#pragma warning(disable:4996) // std::uncaught_exception is deprecated in C++17 +#endif ScopedMessage::~ScopedMessage() { if ( !std::uncaught_exception() ){ getResultCapture().popScopedMessage(m_info); } } +#if defined(_MSC_VER) +#pragma warning(pop) +#endif } // end namespace Catch diff --git a/include/internal/catch_message.h b/include/internal/catch_message.h index 9000fbd9..0a512ed5 100644 --- a/include/internal/catch_message.h +++ b/include/internal/catch_message.h @@ -59,7 +59,7 @@ namespace Catch { class ScopedMessage { public: - ScopedMessage( MessageBuilder const& builder ); + explicit ScopedMessage( MessageBuilder const& builder ); ~ScopedMessage(); MessageInfo m_info; diff --git a/include/internal/catch_registry_hub.cpp b/include/internal/catch_registry_hub.cpp index ab5ad361..d98708c5 100644 --- a/include/internal/catch_registry_hub.cpp +++ b/include/internal/catch_registry_hub.cpp @@ -87,6 +87,7 @@ namespace Catch { delete getTheRegistryHub(); getTheRegistryHub() = nullptr; cleanUpContext(); + ReusableStringStream::cleanup(); } std::string translateActiveException() { return getRegistryHub().getExceptionTranslatorRegistry().translateActiveException(); diff --git a/include/internal/catch_reporter_registrars.hpp b/include/internal/catch_reporter_registrars.hpp index ce88fa34..943fba65 100644 --- a/include/internal/catch_reporter_registrars.hpp +++ b/include/internal/catch_reporter_registrars.hpp @@ -29,7 +29,7 @@ namespace Catch { public: - ReporterRegistrar( std::string const& name ) { + explicit ReporterRegistrar( std::string const& name ) { getMutableRegistryHub().registerReporter( name, std::make_shared() ); } }; diff --git a/include/internal/catch_run_context.cpp b/include/internal/catch_run_context.cpp index d6ab1576..2f07a6a0 100644 --- a/include/internal/catch_run_context.cpp +++ b/include/internal/catch_run_context.cpp @@ -10,37 +10,55 @@ namespace Catch { - StreamRedirect::StreamRedirect(std::ostream& stream, std::string& targetString) - : m_stream(stream), - m_prevBuf(stream.rdbuf()), - m_targetString(targetString) { - stream.rdbuf(m_oss.get().rdbuf()); - } + class RedirectedStream { + std::ostream& m_originalStream; + std::ostream& m_redirectionStream; + std::streambuf* m_prevBuf; - StreamRedirect::~StreamRedirect() { - m_targetString += m_oss.str(); - m_stream.rdbuf(m_prevBuf); - } + public: + RedirectedStream( std::ostream& originalStream, std::ostream& redirectionStream ) + : m_originalStream( originalStream ), + m_redirectionStream( redirectionStream ), + m_prevBuf( m_originalStream.rdbuf() ) + { + m_originalStream.rdbuf( m_redirectionStream.rdbuf() ); + } + ~RedirectedStream() { + m_originalStream.rdbuf( m_prevBuf ); + } + }; - StdErrRedirect::StdErrRedirect(std::string & targetString) - :m_cerrBuf(cerr().rdbuf()), m_clogBuf(clog().rdbuf()), - m_targetString(targetString) { - cerr().rdbuf(m_oss.get().rdbuf()); - clog().rdbuf(m_oss.get().rdbuf()); - } + class RedirectedStdOut { + ReusableStringStream m_rss; + RedirectedStream m_cout; + public: + RedirectedStdOut() : m_cout( Catch::cout(), m_rss.get() ) {} + auto str() const -> std::string { return m_rss.str(); } + }; + + // StdErr has two constituent streams in C++, std::cerr and std::clog + // This means that we need to redirect 2 streams into 1 to keep proper + // order of writes + class RedirectedStdErr { + ReusableStringStream m_rss; + RedirectedStream m_cerr; + RedirectedStream m_clog; + public: + RedirectedStdErr() + : m_cerr( Catch::cerr(), m_rss.get() ), + m_clog( Catch::clog(), m_rss.get() ) + {} + auto str() const -> std::string { return m_rss.str(); } + }; - StdErrRedirect::~StdErrRedirect() { - m_targetString += m_oss.str(); - cerr().rdbuf(m_cerrBuf); - clog().rdbuf(m_clogBuf); - } RunContext::RunContext(IConfigPtr const& _config, IStreamingReporterPtr&& reporter) : m_runInfo(_config->name()), m_context(getCurrentMutableContext()), m_config(_config), m_reporter(std::move(reporter)), - m_lastAssertionInfo{ "", SourceLineInfo("",0), "", ResultDisposition::Normal } + m_lastAssertionInfo{ "", SourceLineInfo("",0), "", ResultDisposition::Normal }, + m_includeSuccessfulResults( m_config->includeSuccessfulResults() ) { m_context.setRunner(this); m_context.setConfig(m_config); @@ -109,18 +127,20 @@ namespace Catch { return *m_reporter; } - void RunContext::assertionStarting(AssertionInfo const& info) { - m_reporter->assertionStarting( info ); - } void RunContext::assertionEnded(AssertionResult const & result) { if (result.getResultType() == ResultWas::Ok) { m_totals.assertions.passed++; + m_lastAssertionPassed = true; } else if (!result.isOk()) { + m_lastAssertionPassed = false; if( m_activeTestCase->getTestCaseInfo().okToFail() ) m_totals.assertions.failedButOk++; else m_totals.assertions.failed++; } + else { + m_lastAssertionPassed = true; + } // We have no use for the return value (whether messages should be cleared), because messages were made scoped // and should be let to clear themselves out. @@ -223,7 +243,7 @@ namespace Catch { tempResult.message = message; AssertionResult result(m_lastAssertionInfo, tempResult); - getResultCapture().assertionEnded(result); + assertionEnded(result); handleUnfinishedSections(); @@ -252,18 +272,15 @@ namespace Catch { } bool RunContext::lastAssertionPassed() { - return m_totals.assertions.passed == (m_prevPassed + 1); + return m_lastAssertionPassed; } void RunContext::assertionPassed() { + m_lastAssertionPassed = true; ++m_totals.assertions.passed; resetAssertionInfo(); } - void RunContext::assertionRun() { - m_prevPassed = m_totals.assertions.passed; - } - bool RunContext::aborting() const { return m_totals.assertions.failed == static_cast(m_config->abortAfter()); } @@ -282,10 +299,13 @@ namespace Catch { Timer timer; try { if (m_reporter->getPreferences().shouldRedirectStdOut) { - StreamRedirect coutRedir(cout(), redirectedCout); - StdErrRedirect errRedir(redirectedCerr); + RedirectedStdOut redirectedStdOut; + RedirectedStdErr redirectedStdErr; timer.start(); invokeActiveTestCase(); + redirectedCout += redirectedStdOut.str(); + redirectedCerr += redirectedStdErr.str(); + } else { timer.start(); invokeActiveTestCase(); @@ -296,12 +316,9 @@ namespace Catch { } catch (...) { // Under CATCH_CONFIG_FAST_COMPILE, unexpected exceptions under REQUIRE assertions // are reported without translation at the point of origin. - if (m_shouldReportUnexpected) { - AssertionHandler - ( m_lastAssertionInfo.macroName, - m_lastAssertionInfo.lineInfo, - m_lastAssertionInfo.capturedExpression, - m_lastAssertionInfo.resultDisposition ).useActiveException(); + if( m_shouldReportUnexpected ) { + AssertionReaction dummyReaction; + handleUnexpectedInflightException( m_lastAssertionInfo, translateActiveException(), dummyReaction ); } } m_testCaseTracker->close(); @@ -331,6 +348,113 @@ namespace Catch { m_unfinishedSections.clear(); } + void RunContext::handleExpr( + AssertionInfo const& info, + ITransientExpression const& expr, + AssertionReaction& reaction + ) { + m_reporter->assertionStarting( info ); + + bool negated = isFalseTest( info.resultDisposition ); + bool result = expr.getResult() != negated; + + if( result ) { + if (!m_includeSuccessfulResults) { + assertionPassed(); + } + else { + reportExpr(info, ResultWas::Ok, &expr, negated); + } + } + else { + reportExpr(info, ResultWas::ExpressionFailed, &expr, negated ); + populateReaction( reaction ); + } + } + void RunContext::reportExpr( + AssertionInfo const &info, + ResultWas::OfType resultType, + ITransientExpression const *expr, + bool negated ) { + + m_lastAssertionInfo = info; + AssertionResultData data( resultType, LazyExpression( negated ) ); + + AssertionResult assertionResult{ info, data }; + assertionResult.m_resultData.lazyExpression.m_transientExpression = expr; + + assertionEnded( assertionResult ); + } + + void RunContext::handleMessage( + AssertionInfo const& info, + ResultWas::OfType resultType, + StringRef const& message, + AssertionReaction& reaction + ) { + m_reporter->assertionStarting( info ); + + m_lastAssertionInfo = info; + + AssertionResultData data( resultType, LazyExpression( false ) ); + data.message = message; + AssertionResult assertionResult{ m_lastAssertionInfo, data }; + assertionEnded( assertionResult ); + if( !assertionResult.isOk() ) + populateReaction( reaction ); + } + void RunContext::handleUnexpectedExceptionNotThrown( + AssertionInfo const& info, + AssertionReaction& reaction + ) { + handleNonExpr(info, Catch::ResultWas::DidntThrowException, reaction); + } + + void RunContext::handleUnexpectedInflightException( + AssertionInfo const& info, + std::string const& message, + AssertionReaction& reaction + ) { + m_lastAssertionInfo = info; + + AssertionResultData data( ResultWas::ThrewException, LazyExpression( false ) ); + data.message = message; + AssertionResult assertionResult{ info, data }; + assertionEnded( assertionResult ); + populateReaction( reaction ); + } + + void RunContext::populateReaction( AssertionReaction& reaction ) { + reaction.shouldDebugBreak = m_config->shouldDebugBreak(); + reaction.shouldThrow = aborting() || (m_lastAssertionInfo.resultDisposition & ResultDisposition::Normal); + } + + void RunContext::handleIncomplete( + AssertionInfo const& info + ) { + m_lastAssertionInfo = info; + + AssertionResultData data( ResultWas::ThrewException, LazyExpression( false ) ); + data.message = "Exception translation was disabled by CATCH_CONFIG_FAST_COMPILE"; + AssertionResult assertionResult{ info, data }; + assertionEnded( assertionResult ); + } + void RunContext::handleNonExpr( + AssertionInfo const &info, + ResultWas::OfType resultType, + AssertionReaction &reaction + ) { + m_lastAssertionInfo = info; + + AssertionResultData data( resultType, LazyExpression( false ) ); + AssertionResult assertionResult{ info, data }; + assertionEnded( assertionResult ); + + if( !assertionResult.isOk() ) + populateReaction( reaction ); + } + + IResultCapture& getResultCapture() { if (auto* capture = getCurrentContext().getResultCapture()) return *capture; diff --git a/include/internal/catch_run_context.h b/include/internal/catch_run_context.h index 22c4d76a..d0007a36 100644 --- a/include/internal/catch_run_context.h +++ b/include/internal/catch_run_context.h @@ -28,33 +28,6 @@ namespace Catch { struct IMutableContext; - class StreamRedirect { - - public: - StreamRedirect(std::ostream& stream, std::string& targetString); - ~StreamRedirect(); - - private: - std::ostream& m_stream; - std::streambuf* m_prevBuf; - ReusableStringStream m_oss; - std::string& m_targetString; - }; - - // StdErr has two constituent streams in C++, std::cerr and std::clog - // This means that we need to redirect 2 streams into 1 to keep proper - // order of writes and cannot use StreamRedirect on its own - class StdErrRedirect { - public: - StdErrRedirect(std::string& targetString); - ~StdErrRedirect(); - private: - std::streambuf* m_cerrBuf; - std::streambuf* m_clogBuf; - ReusableStringStream m_oss; - std::string& m_targetString; - }; - /////////////////////////////////////////////////////////////////////////// class RunContext : public IResultCapture, public IRunner { @@ -63,35 +36,54 @@ namespace Catch { RunContext( RunContext const& ) = delete; RunContext& operator =( RunContext const& ) = delete; - explicit RunContext(IConfigPtr const& _config, IStreamingReporterPtr&& reporter); + explicit RunContext( IConfigPtr const& _config, IStreamingReporterPtr&& reporter ); - virtual ~RunContext(); + ~RunContext() override; - void testGroupStarting(std::string const& testSpec, std::size_t groupIndex, std::size_t groupsCount); - void testGroupEnded(std::string const& testSpec, Totals const& totals, std::size_t groupIndex, std::size_t groupsCount); + void testGroupStarting( std::string const& testSpec, std::size_t groupIndex, std::size_t groupsCount ); + void testGroupEnded( std::string const& testSpec, Totals const& totals, std::size_t groupIndex, std::size_t groupsCount ); Totals runTest(TestCase const& testCase); IConfigPtr config() const; IStreamingReporter& reporter() const; - private: // IResultCapture + public: // IResultCapture - - void assertionStarting(AssertionInfo const& info) override; - void assertionEnded(AssertionResult const& result) override; + // Assertion handlers + void handleExpr + ( AssertionInfo const& info, + ITransientExpression const& expr, + AssertionReaction& reaction ) override; + void handleMessage + ( AssertionInfo const& info, + ResultWas::OfType resultType, + StringRef const& message, + AssertionReaction& reaction ) override; + void handleUnexpectedExceptionNotThrown + ( AssertionInfo const& info, + AssertionReaction& reaction ) override; + void handleUnexpectedInflightException + ( AssertionInfo const& info, + std::string const& message, + AssertionReaction& reaction ) override; + void handleIncomplete + ( AssertionInfo const& info ) override; + void handleNonExpr + ( AssertionInfo const &info, + ResultWas::OfType resultType, + AssertionReaction &reaction ) override; bool sectionStarted( SectionInfo const& sectionInfo, Counts& assertions ) override; - bool testForMissingAssertions(Counts& assertions); - void sectionEnded(SectionEndInfo const& endInfo) override; - void sectionEndedEarly(SectionEndInfo const& endInfo) override; + void sectionEnded( SectionEndInfo const& endInfo ) override; + void sectionEndedEarly( SectionEndInfo const& endInfo ) override; void benchmarkStarting( BenchmarkInfo const& info ) override; void benchmarkEnded( BenchmarkStats const& stats ) override; - void pushScopedMessage(MessageInfo const& message) override; - void popScopedMessage(MessageInfo const& message) override; + void pushScopedMessage( MessageInfo const& message ) override; + void popScopedMessage( MessageInfo const& message ) override; std::string getCurrentTestName() const override; @@ -105,18 +97,26 @@ namespace Catch { void assertionPassed() override; - void assertionRun() override; - public: // !TBD We need to do this another way! bool aborting() const override; private: - void runCurrentTest(std::string& redirectedCout, std::string& redirectedCerr); + void runCurrentTest( std::string& redirectedCout, std::string& redirectedCerr ); void invokeActiveTestCase(); void resetAssertionInfo(); + bool testForMissingAssertions( Counts& assertions ); + + void assertionEnded( AssertionResult const& result ); + void reportExpr + ( AssertionInfo const &info, + ResultWas::OfType resultType, + ITransientExpression const *expr, + bool negated ); + + void populateReaction( AssertionReaction& reaction ); private: @@ -136,12 +136,11 @@ namespace Catch { std::vector m_unfinishedSections; std::vector m_activeSections; TrackerContext m_trackerContext; - std::size_t m_prevPassed = 0; + bool m_lastAssertionPassed = false; bool m_shouldReportUnexpected = true; + bool m_includeSuccessfulResults; }; - IResultCapture& getResultCapture(); - } // end namespace Catch #endif // TWOBLUECUBES_CATCH_RUNNER_IMPL_HPP_INCLUDED diff --git a/include/internal/catch_session.cpp b/include/internal/catch_session.cpp index 5a6a8434..b1c00d05 100644 --- a/include/internal/catch_session.cpp +++ b/include/internal/catch_session.cpp @@ -22,97 +22,91 @@ #include #include +namespace Catch { -namespace { - const int MaxExitCode = 255; - using Catch::IStreamingReporterPtr; - using Catch::IConfigPtr; - using Catch::Config; + namespace { + const int MaxExitCode = 255; - IStreamingReporterPtr createReporter(std::string const& reporterName, IConfigPtr const& config) { - auto reporter = Catch::getRegistryHub().getReporterRegistry().create(reporterName, config); - CATCH_ENFORCE(reporter, "No reporter registered with name: '" << reporterName << "'"); + IStreamingReporterPtr createReporter(std::string const& reporterName, IConfigPtr const& config) { + auto reporter = Catch::getRegistryHub().getReporterRegistry().create(reporterName, config); + CATCH_ENFORCE(reporter, "No reporter registered with name: '" << reporterName << "'"); - return reporter; - } + return reporter; + } #ifndef CATCH_CONFIG_DEFAULT_REPORTER #define CATCH_CONFIG_DEFAULT_REPORTER "console" #endif - IStreamingReporterPtr makeReporter(std::shared_ptr const& config) { - auto const& reporterNames = config->getReporterNames(); - if (reporterNames.empty()) - return createReporter(CATCH_CONFIG_DEFAULT_REPORTER, config); + IStreamingReporterPtr makeReporter(std::shared_ptr const& config) { + auto const& reporterNames = config->getReporterNames(); + if (reporterNames.empty()) + return createReporter(CATCH_CONFIG_DEFAULT_REPORTER, config); - IStreamingReporterPtr reporter; - for (auto const& name : reporterNames) - addReporter(reporter, createReporter(name, config)); - return reporter; - } + IStreamingReporterPtr reporter; + for (auto const& name : reporterNames) + addReporter(reporter, createReporter(name, config)); + return reporter; + } #undef CATCH_CONFIG_DEFAULT_REPORTER - void addListeners(IStreamingReporterPtr& reporters, IConfigPtr const& config) { - auto const& listeners = Catch::getRegistryHub().getReporterRegistry().getListeners(); - for (auto const& listener : listeners) - addReporter(reporters, listener->create(Catch::ReporterConfig(config))); - } - - - Catch::Totals runTests(std::shared_ptr const& config) { - using namespace Catch; - IStreamingReporterPtr reporter = makeReporter(config); - addListeners(reporter, config); - - RunContext context(config, std::move(reporter)); - - Totals totals; - - context.testGroupStarting(config->name(), 1, 1); - - TestSpec testSpec = config->testSpec(); - if (!testSpec.hasFilters()) - testSpec = TestSpecParser(ITagAliasRegistry::get()).parse("~[.]").testSpec(); // All not hidden tests - - auto const& allTestCases = getAllTestCasesSorted(*config); - for (auto const& testCase : allTestCases) { - if (!context.aborting() && matchTest(testCase, testSpec, *config)) - totals += context.runTest(testCase); - else - context.reporter().skipTest(testCase); + void addListeners(IStreamingReporterPtr& reporters, IConfigPtr const& config) { + auto const& listeners = Catch::getRegistryHub().getReporterRegistry().getListeners(); + for (auto const& listener : listeners) + addReporter(reporters, listener->create(Catch::ReporterConfig(config))); } - context.testGroupEnded(config->name(), totals, 1, 1); - return totals; - } - void applyFilenamesAsTags(Catch::IConfig const& config) { - using namespace Catch; - auto& tests = const_cast&>(getAllTestCasesSorted(config)); - for (auto& testCase : tests) { - auto tags = testCase.tags; + Catch::Totals runTests(std::shared_ptr const& config) { + IStreamingReporterPtr reporter = makeReporter(config); + addListeners(reporter, config); - std::string filename = testCase.lineInfo.file; - auto lastSlash = filename.find_last_of("\\/"); - if (lastSlash != std::string::npos) { - filename.erase(0, lastSlash); - filename[0] = '#'; + RunContext context(config, std::move(reporter)); + + Totals totals; + + context.testGroupStarting(config->name(), 1, 1); + + TestSpec testSpec = config->testSpec(); + if (!testSpec.hasFilters()) + testSpec = TestSpecParser(ITagAliasRegistry::get()).parse("~[.]").testSpec(); // All not hidden tests + + auto const& allTestCases = getAllTestCasesSorted(*config); + for (auto const& testCase : allTestCases) { + if (!context.aborting() && matchTest(testCase, testSpec, *config)) + totals += context.runTest(testCase); + else + context.reporter().skipTest(testCase); } - auto lastDot = filename.find_last_of('.'); - if (lastDot != std::string::npos) { - filename.erase(lastDot); - } - - tags.push_back(std::move(filename)); - setTags(testCase, tags); + context.testGroupEnded(config->name(), totals, 1, 1); + return totals; } - } -} + void applyFilenamesAsTags(Catch::IConfig const& config) { + auto& tests = const_cast&>(getAllTestCasesSorted(config)); + for (auto& testCase : tests) { + auto tags = testCase.tags; -namespace Catch { + std::string filename = testCase.lineInfo.file; + auto lastSlash = filename.find_last_of("\\/"); + if (lastSlash != std::string::npos) { + filename.erase(0, lastSlash); + filename[0] = '#'; + } + + auto lastDot = filename.find_last_of('.'); + if (lastDot != std::string::npos) { + filename.erase(lastDot); + } + + tags.push_back(std::move(filename)); + setTags(testCase, tags); + } + } + + } // anon namespace Session::Session() { static bool alreadyInstantiated = false; diff --git a/include/internal/catch_stream.cpp b/include/internal/catch_stream.cpp index 76bbc8f3..125fd471 100644 --- a/include/internal/catch_stream.cpp +++ b/include/internal/catch_stream.cpp @@ -145,6 +145,7 @@ namespace Catch { std::vector> m_streams; std::vector m_unused; std::ostringstream m_referenceStream; // Used for copy state/ flags from + static StringStreams* s_instance; auto add() -> std::size_t { if( m_unused.empty() ) { @@ -165,11 +166,21 @@ namespace Catch { // !TBD: put in TLS static auto instance() -> StringStreams& { - static StringStreams s_stringStreams; - return s_stringStreams; + if( !s_instance ) + s_instance = new StringStreams(); + return *s_instance; + } + static void cleanup() { + delete s_instance; + s_instance = nullptr; } }; + StringStreams* StringStreams::s_instance = nullptr; + + void ReusableStringStream::cleanup() { + StringStreams::cleanup(); + } ReusableStringStream::ReusableStringStream() : m_index( StringStreams::instance().add() ), diff --git a/include/internal/catch_stream.h b/include/internal/catch_stream.h index 2b41adbd..c5e78b22 100644 --- a/include/internal/catch_stream.h +++ b/include/internal/catch_stream.h @@ -43,6 +43,8 @@ namespace Catch { return *this; } auto get() -> std::ostream& { return *m_oss; } + + static void cleanup(); }; } diff --git a/include/internal/catch_suppress_warnings.h b/include/internal/catch_suppress_warnings.h index 65ed6a62..92801142 100644 --- a/include/internal/catch_suppress_warnings.h +++ b/include/internal/catch_suppress_warnings.h @@ -10,9 +10,6 @@ # pragma warning(push) # pragma warning(disable: 161 1682) # else // __ICC -# pragma clang diagnostic ignored "-Wglobal-constructors" -# pragma clang diagnostic ignored "-Wvariadic-macros" -# pragma clang diagnostic ignored "-Wc99-extensions" # pragma clang diagnostic ignored "-Wunused-variable" # pragma clang diagnostic push # pragma clang diagnostic ignored "-Wpadded" @@ -20,10 +17,8 @@ # pragma clang diagnostic ignored "-Wcovered-switch-default" # endif #elif defined __GNUC__ -# pragma GCC diagnostic ignored "-Wvariadic-macros" # pragma GCC diagnostic ignored "-Wunused-variable" # pragma GCC diagnostic ignored "-Wparentheses" - # pragma GCC diagnostic push # pragma GCC diagnostic ignored "-Wpadded" #endif diff --git a/include/internal/catch_tag_alias_autoregistrar.h b/include/internal/catch_tag_alias_autoregistrar.h index e9b961e4..32a5734e 100644 --- a/include/internal/catch_tag_alias_autoregistrar.h +++ b/include/internal/catch_tag_alias_autoregistrar.h @@ -17,6 +17,9 @@ namespace Catch { } // end namespace Catch -#define CATCH_REGISTER_TAG_ALIAS( alias, spec ) namespace{ Catch::RegistrarForTagAliases INTERNAL_CATCH_UNIQUE_NAME( AutoRegisterTagAlias )( alias, spec, CATCH_INTERNAL_LINEINFO ); } +#define CATCH_REGISTER_TAG_ALIAS( alias, spec ) \ + CATCH_INTERNAL_SUPPRESS_GLOBALS_WARNINGS \ + namespace{ Catch::RegistrarForTagAliases INTERNAL_CATCH_UNIQUE_NAME( AutoRegisterTagAlias )( alias, spec, CATCH_INTERNAL_LINEINFO ); } \ + CATCH_INTERNAL_UNSUPPRESS_GLOBALS_WARNINGS #endif // TWOBLUECUBES_CATCH_TAG_ALIAS_AUTOREGISTRAR_H_INCLUDED diff --git a/include/internal/catch_timer.cpp b/include/internal/catch_timer.cpp index a6f37e8e..b9ae6887 100644 --- a/include/internal/catch_timer.cpp +++ b/include/internal/catch_timer.cpp @@ -45,11 +45,11 @@ namespace Catch { void Timer::start() { m_nanoseconds = getCurrentNanosecondsSinceEpoch(); } - auto Timer::getElapsedNanoseconds() const -> unsigned int { - return static_cast(getCurrentNanosecondsSinceEpoch() - m_nanoseconds); + auto Timer::getElapsedNanoseconds() const -> uint64_t { + return getCurrentNanosecondsSinceEpoch() - m_nanoseconds; } - auto Timer::getElapsedMicroseconds() const -> unsigned int { - return static_cast(getElapsedNanoseconds()/1000); + auto Timer::getElapsedMicroseconds() const -> uint64_t { + return getElapsedNanoseconds()/1000; } auto Timer::getElapsedMilliseconds() const -> unsigned int { return static_cast(getElapsedMicroseconds()/1000); diff --git a/include/internal/catch_timer.h b/include/internal/catch_timer.h index 28e1f9db..74ab428e 100644 --- a/include/internal/catch_timer.h +++ b/include/internal/catch_timer.h @@ -19,8 +19,8 @@ namespace Catch { uint64_t m_nanoseconds = 0; public: void start(); - auto getElapsedNanoseconds() const -> unsigned int; - auto getElapsedMicroseconds() const -> unsigned int; + auto getElapsedNanoseconds() const -> uint64_t; + auto getElapsedMicroseconds() const -> uint64_t; auto getElapsedMilliseconds() const -> unsigned int; auto getElapsedSeconds() const -> double; }; diff --git a/include/reporters/catch_reporter_console.cpp b/include/reporters/catch_reporter_console.cpp index 74d2f19d..54b62cd8 100644 --- a/include/reporters/catch_reporter_console.cpp +++ b/include/reporters/catch_reporter_console.cpp @@ -210,7 +210,7 @@ class Duration { Unit m_units; public: - Duration(uint64_t inNanoseconds, Unit units = Unit::Auto) + explicit Duration(uint64_t inNanoseconds, Unit units = Unit::Auto) : m_inNanoseconds(inNanoseconds), m_units(units) { if (m_units == Unit::Auto) { @@ -273,9 +273,9 @@ class TablePrinter { bool m_isOpen = false; public: - TablePrinter(std::ostream& os, std::vector const& columnInfos) - : m_os(os), - m_columnInfos(columnInfos) {} + TablePrinter( std::ostream& os, std::vector columnInfos ) + : m_os( os ), + m_columnInfos( std::move( columnInfos ) ) {} auto columnInfos() const -> std::vector const& { return m_columnInfos; @@ -346,7 +346,8 @@ ConsoleReporter::ConsoleReporter(ReporterConfig const& config) { "elapsed ns", 14, ColumnInfo::Right }, { "average", 14, ColumnInfo::Right } })) {} -ConsoleReporter::~ConsoleReporter() {} +ConsoleReporter::~ConsoleReporter() = default; + std::string ConsoleReporter::getDescription() { return "Reports test results as plain lines of text"; } @@ -402,7 +403,7 @@ void ConsoleReporter::sectionEnded(SectionStats const& _sectionStats) { void ConsoleReporter::benchmarkStarting(BenchmarkInfo const& info) { lazyPrintWithoutClosingBenchmarkTable(); - auto nameCol = Column(info.name).width(m_tablePrinter->columnInfos()[0].width - 2); + auto nameCol = Column( info.name ).width( static_cast( m_tablePrinter->columnInfos()[0].width - 2 ) ); bool firstLine = true; for (auto line : nameCol) { @@ -528,10 +529,10 @@ void ConsoleReporter::printHeaderString(std::string const& _string, std::size_t struct SummaryColumn { - SummaryColumn(std::string const& _label, Colour::Code _colour) - : label(_label), - colour(_colour) {} - SummaryColumn addRow(std::size_t count) { + SummaryColumn( std::string _label, Colour::Code _colour ) + : label( std::move( _label ) ), + colour( _colour ) {} + SummaryColumn addRow( std::size_t count ) { ReusableStringStream rss; rss << count; std::string row = rss.str(); @@ -551,7 +552,7 @@ struct SummaryColumn { }; -void ConsoleReporter::printTotals(Totals const& totals) { +void ConsoleReporter::printTotals( Totals const& totals ) { if (totals.testCases.total() == 0) { stream << Colour(Colour::Warning) << "No tests ran\n"; } else if (totals.assertions.total() > 0 && totals.testCases.allPassed()) { diff --git a/include/reporters/catch_reporter_teamcity.hpp b/include/reporters/catch_reporter_teamcity.hpp index 96e35a84..dbd0db53 100644 --- a/include/reporters/catch_reporter_teamcity.hpp +++ b/include/reporters/catch_reporter_teamcity.hpp @@ -14,8 +14,6 @@ // file can be distributed as a single header that works with the main // Catch single header. -#include "../internal/catch_enforce.h" - #include #ifdef __clang__ @@ -99,12 +97,12 @@ namespace Catch { case ResultWas::Ok: case ResultWas::Info: case ResultWas::Warning: - CATCH_ERROR( "Internal error in TeamCity reporter" ); + throw std::domain_error( "Internal error in TeamCity reporter" ); // These cases are here to prevent compiler warnings case ResultWas::Unknown: case ResultWas::FailureBit: case ResultWas::Exception: - CATCH_ERROR( "Not implemented" ); + throw std::domain_error( "Not implemented" ); } if( assertionStats.infoMessages.size() == 1 ) msg << " with message:"; diff --git a/include/reporters/catch_reporter_xml.cpp b/include/reporters/catch_reporter_xml.cpp index 1ea61c0b..b721d449 100644 --- a/include/reporters/catch_reporter_xml.cpp +++ b/include/reporters/catch_reporter_xml.cpp @@ -26,7 +26,7 @@ namespace Catch { m_reporterPrefs.shouldRedirectStdOut = true; } - XmlReporter::~XmlReporter() {}; + XmlReporter::~XmlReporter() = default; std::string XmlReporter::getDescription() { return "Reports test results as an XML document"; @@ -95,10 +95,10 @@ namespace Catch { bool includeResults = m_config->includeSuccessfulResults() || !result.isOk(); - if( includeResults ) { + if( includeResults || result.getResultType() == ResultWas::Warning ) { // Print any info messages in tags. for( auto const& msg : assertionStats.infoMessages ) { - if( msg.type == ResultWas::Info ) { + if( msg.type == ResultWas::Info && includeResults ) { m_xml.scopedElement( "Info" ) .writeText( msg.message ); } else if ( msg.type == ResultWas::Warning ) { diff --git a/misc/CMakeLists.txt b/misc/CMakeLists.txt new file mode 100644 index 00000000..bf80846c --- /dev/null +++ b/misc/CMakeLists.txt @@ -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() diff --git a/misc/appveyorBuildConfigurationScript.bat b/misc/appveyorBuildConfigurationScript.bat new file mode 100644 index 00000000..c70da86d --- /dev/null +++ b/misc/appveyorBuildConfigurationScript.bat @@ -0,0 +1,13 @@ +@REM # In debug build, we want to +@REM # 1) Prebuild memcheck redirecter +@REM # 2) Regenerate single header include for examples +@REM # 3) Enable building examples +if "%CONFIGURATION%"=="Debug" ( + python scripts\generateSingleHeader.py + cmake -Hmisc -Bbuild-misc -A%PLATFORM% + cmake --build build-misc + cmake -H. -BBuild -A%PLATFORM% -DUSE_WMAIN=%wmain% -DBUILD_EXAMPLES=ON -DMEMORYCHECK_COMMAND=build-misc\Debug\CoverageHelper.exe -DMEMORYCHECK_COMMAND_OPTIONS=--sep-- -DMEMORYCHECK_TYPE=Valgrind +) +if "%CONFIGURATION%"=="Release" ( + cmake -H. -BBuild -A%PLATFORM% -DUSE_WMAIN=%wmain% +) diff --git a/misc/appveyorMergeCoverageScript.py b/misc/appveyorMergeCoverageScript.py new file mode 100644 index 00000000..a74e6e0e --- /dev/null +++ b/misc/appveyorMergeCoverageScript.py @@ -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) diff --git a/misc/appveyorTestRunScript.bat b/misc/appveyorTestRunScript.bat new file mode 100644 index 00000000..fdcc2ce0 --- /dev/null +++ b/misc/appveyorTestRunScript.bat @@ -0,0 +1,9 @@ +cd Build +if "%CONFIGURATION%"=="Debug" ( + ctest -j 2 -C %CONFIGURATION% -D ExperimentalMemCheck + python ..\misc\appveyorMergeCoverageScript.py + codecov --root .. --no-color --disable gcov -f cobertura.xml -t %CODECOV_TOKEN% +) +if "%CONFIGURATION%"=="Release" ( + ctest -j 2 -C %CONFIGURATION% +) diff --git a/misc/coverage-helper.cpp b/misc/coverage-helper.cpp new file mode 100644 index 00000000..9783d693 --- /dev/null +++ b/misc/coverage-helper.cpp @@ -0,0 +1,100 @@ +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + + +void create_empty_file(std::string const& path) { + std::ofstream ofs(path); + ofs << '\n'; +} + +const std::string separator = "--sep--"; +const std::string logfile_prefix = "--log-file="; + +bool starts_with(std::string const& str, std::string const& pref) { + return str.find(pref) == 0; +} + +int parse_log_file_arg(std::string const& arg) { + assert(starts_with(arg, logfile_prefix) && "Attempting to parse incorrect arg!"); + auto fname = arg.substr(logfile_prefix.size()); + create_empty_file(fname); + std::regex regex("MemoryChecker\\.(\\d+)\\.log", std::regex::icase); + std::smatch match; + if (std::regex_search(fname, match, regex)) { + return std::stoi(match[1]); + } else { + throw std::domain_error("Couldn't find desired expression in string: " + fname); + } +} + +std::string catch_path(std::string path) { + auto start = path.find("catch"); + // try capitalized instead + if (start == std::string::npos) { + start = path.find("Catch"); + } + if (start == std::string::npos) { + throw std::domain_error("Couldn't find Catch's base path"); + } + auto end = path.find_first_of("\\/", start); + return path.substr(0, end); +} + +std::string windowsify_path(std::string path) { + for (auto& c : path) { + if (c == '/') { + c = '\\'; + } + } + return path; +} + +void exec_cmd(std::string const& cmd, int log_num, std::string const& path) { + std::array buffer; +#if defined(_WIN32) + auto real_cmd = "OpenCppCoverage --export_type binary:cov-report" + std::to_string(log_num) + + ".bin --quiet " + "--sources " + path + " --cover_children -- " + cmd; + std::cout << "=== Marker ===: Cmd: " << real_cmd << '\n'; + std::shared_ptr pipe(_popen(real_cmd.c_str(), "r"), _pclose); +#else // Just for testing, in the real world we will always work under WIN32 + (void)log_num; (void)path; + std::shared_ptr pipe(popen(cmd.c_str(), "r"), pclose); +#endif + + if (!pipe) { + throw std::runtime_error("popen() failed!"); + } + while (!feof(pipe.get())) { + if (fgets(buffer.data(), 128, pipe.get()) != nullptr) { + std::cout << buffer.data(); + } + } +} + +// argv should be: +// [0]: our path +// [1]: "--log-file=" +// [2]: "--sep--" +// [3]+: the actual command + +int main(int argc, char** argv) { + std::vector args(argv, argv + argc); + auto sep = std::find(begin(args), end(args), separator); + assert(sep - begin(args) == 2 && "Structure differs from expected!"); + + auto num = parse_log_file_arg(args[1]); + + auto cmdline = std::accumulate(++sep, end(args), std::string{}, [] (const std::string& lhs, const std::string& rhs) { + return lhs + ' ' + rhs; + }); + + exec_cmd(cmdline, num, windowsify_path(catch_path(args[0]))); +} diff --git a/misc/installOpenCppCoverage.ps1 b/misc/installOpenCppCoverage.ps1 new file mode 100644 index 00000000..9dbffca7 --- /dev/null +++ b/misc/installOpenCppCoverage.ps1 @@ -0,0 +1,19 @@ +# Downloads are done from the oficial github release page links +$downloadUrl = "https://github.com/OpenCppCoverage/OpenCppCoverage/releases/download/release-0.9.6.1/OpenCppCoverageSetup-x64-0.9.6.1.exe" +$installerPath = [System.IO.Path]::Combine($Env:USERPROFILE, "Downloads", "OpenCppCoverageSetup.exe") + +if(-Not (Test-Path $installerPath)) { + Write-Host -ForegroundColor White ("Downloading OpenCppCoverage from: " + $downloadUrl) + Start-FileDownload $downloadUrl -FileName $installerPath +} + +Write-Host -ForegroundColor White "About to install OpenCppCoverage..." + +$installProcess = (Start-Process $installerPath -ArgumentList '/VERYSILENT' -PassThru -Wait) +if($installProcess.ExitCode -ne 0) { + throw [System.String]::Format("Failed to install OpenCppCoverage, ExitCode: {0}.", $installProcess.ExitCode) +} + +# Assume standard, boring, installation path of ".../Program Files/OpenCppCoverage" +$installPath = [System.IO.Path]::Combine(${Env:ProgramFiles}, "OpenCppCoverage") +$env:Path="$env:Path;$installPath" diff --git a/projects/SelfTest/Baselines/compact.sw.approved.txt b/projects/SelfTest/Baselines/compact.sw.approved.txt new file mode 100644 index 00000000..b205a13f --- /dev/null +++ b/projects/SelfTest/Baselines/compact.sw.approved.txt @@ -0,0 +1,1035 @@ +Misc.tests.cpp:: passed: with 1 message: 'yay' +Decomposition.tests.cpp:: passed: fptr == 0 for: 0 == 0 +Decomposition.tests.cpp:: passed: fptr == 0l for: 0 == 0 +Compilation.tests.cpp:: passed: y.v == 0 for: 0 == 0 +Compilation.tests.cpp:: passed: 0 == y.v for: 0 == 0 +Exception.tests.cpp:: failed: unexpected exception with message: 'answer := 42' with 1 message: 'expected exception' +Exception.tests.cpp:: failed: unexpected exception with message: 'answer := 42'; expression was: thisThrows() with 1 message: 'expected exception' +Exception.tests.cpp:: passed: thisThrows() with 1 message: 'answer := 42' +Compilation.tests.cpp:: passed: 42 == f for: 42 == {?} +Compilation.tests.cpp:: passed: a == t for: 3 == 3 +Compilation.tests.cpp:: passed: a == t for: 3 == 3 +Compilation.tests.cpp:: passed: throws_int(true) +Compilation.tests.cpp:: passed: throws_int(true), int +Compilation.tests.cpp:: passed: throws_int(false) +Compilation.tests.cpp:: passed: "aaa", Catch::EndsWith("aaa") for: "aaa" ends with: "aaa" +Compilation.tests.cpp:: passed: templated_tests(3) for: true +Misc.tests.cpp:: failed: f() == 0 for: 1 == 0 +Misc.tests.cpp:: passed: errno == 1 for: 1 == 1 +Compilation.tests.cpp:: passed: x == 4 for: {?} == 4 with 1 message: 'dummy := 0' +Misc.tests.cpp:: passed: with 1 message: 'Everything is OK' +Misc.tests.cpp:: passed: with 1 message: 'Everything is OK' +Misc.tests.cpp:: passed: with 1 message: 'Everything is OK' +Misc.tests.cpp:: passed: with 1 message: 'Everything is OK' +Misc.tests.cpp:: passed: with 1 message: 'Everything is OK' +Condition.tests.cpp:: failed: false != false +Condition.tests.cpp:: failed: true != true +Condition.tests.cpp:: failed: !true for: false +Condition.tests.cpp:: failed: !(true) for: !true +Condition.tests.cpp:: failed: !trueValue for: false +Condition.tests.cpp:: failed: !(trueValue) for: !true +Condition.tests.cpp:: failed: !(1 == 1) for: false +Condition.tests.cpp:: failed: !(1 == 1) +Condition.tests.cpp:: passed: false == false +Condition.tests.cpp:: passed: true == true +Condition.tests.cpp:: passed: !false for: true +Condition.tests.cpp:: passed: !(false) for: !false +Condition.tests.cpp:: passed: !falseValue for: true +Condition.tests.cpp:: passed: !(falseValue) for: !false +Condition.tests.cpp:: passed: !(1 == 2) for: true +Condition.tests.cpp:: passed: !(1 == 2) +Tricky.tests.cpp:: passed: is_true::value == true for: true == true +Tricky.tests.cpp:: passed: true == is_true::value for: true == true +Tricky.tests.cpp:: passed: is_true::value == false for: false == false +Tricky.tests.cpp:: passed: false == is_true::value for: false == false +Tricky.tests.cpp:: passed: !is_true::value for: true +Tricky.tests.cpp:: passed: !!is_true::value for: true +Tricky.tests.cpp:: passed: is_true::value for: true +Tricky.tests.cpp:: passed: !(is_true::value) for: !false +Class.tests.cpp:: failed: s == "world" for: "hello" == "world" +Class.tests.cpp:: passed: s == "hello" for: "hello" == "hello" +Class.tests.cpp:: failed: m_a == 2 for: 1 == 2 +Class.tests.cpp:: passed: m_a == 1 for: 1 == 1 +Misc.tests.cpp:: passed: with 1 message: 'that's not flying - that's failing in style' +Misc.tests.cpp:: failed: explicitly with 1 message: 'to infinity and beyond' +Tricky.tests.cpp:: failed: &o1 == &o2 for: 0x == 0x +Tricky.tests.cpp:: failed: o1 == o2 for: {?} == {?} +Approx.tests.cpp:: passed: 104.0 != Approx(100.0) for: 104.0 != Approx( 100.0 ) +Approx.tests.cpp:: passed: 104.0 == Approx(100.0).margin(5) for: 104.0 == Approx( 100.0 ) +Approx.tests.cpp:: passed: 104.0 == Approx(100.0).margin(4) for: 104.0 == Approx( 100.0 ) +Approx.tests.cpp:: passed: 104.0 != Approx(100.0).margin(3) for: 104.0 != Approx( 100.0 ) +Approx.tests.cpp:: passed: 100.3 != Approx(100.0) for: 100.3 != Approx( 100.0 ) +Approx.tests.cpp:: passed: 100.3 == Approx(100.0).margin(0.5) for: 100.3 == Approx( 100.0 ) +Tricky.tests.cpp:: passed: i++ == 7 for: 7 == 7 +Tricky.tests.cpp:: passed: i++ == 8 for: 8 == 8 +Exception.tests.cpp:: passed: 1 == 1 +Exception.tests.cpp:: failed: unexpected exception with message: 'unexpected exception'; expression was: {Unknown expression after the reported line} +VariadicMacros.tests.cpp:: passed: with 1 message: 'anonymous test case' +Approx.tests.cpp:: passed: Approx(0).margin(0) +Approx.tests.cpp:: passed: Approx(0).margin(1234656) +Approx.tests.cpp:: passed: Approx(0).margin(-2), std::domain_error +Approx.tests.cpp:: passed: Approx(0).epsilon(0) +Approx.tests.cpp:: passed: Approx(0).epsilon(1) +Approx.tests.cpp:: passed: Approx(0).epsilon(-0.001), std::domain_error +Approx.tests.cpp:: passed: Approx(0).epsilon(1.0001), std::domain_error +Approx.tests.cpp:: passed: 0.25f == Approx(0.0f).margin(0.25f) for: 0.25f == Approx( 0.0 ) +Approx.tests.cpp:: passed: 0.0f == Approx(0.25f).margin(0.25f) for: 0.0f == Approx( 0.25 ) +Approx.tests.cpp:: passed: 0.5f == Approx(0.25f).margin(0.25f) for: 0.5f == Approx( 0.25 ) +Approx.tests.cpp:: passed: 245.0f == Approx(245.25f).margin(0.25f) for: 245.0f == Approx( 245.25 ) +Approx.tests.cpp:: passed: 245.5f == Approx(245.25f).margin(0.25f) for: 245.5f == Approx( 245.25 ) +Approx.tests.cpp:: passed: divide( 22, 7 ) == Approx( 3.141 ).epsilon( 0.001 ) for: 3.1428571429 == Approx( 3.141 ) +Approx.tests.cpp:: passed: divide( 22, 7 ) != Approx( 3.141 ).epsilon( 0.0001 ) for: 3.1428571429 != Approx( 3.141 ) +Approx.tests.cpp:: passed: d != Approx( 1.231 ) for: 1.23 != Approx( 1.231 ) +Approx.tests.cpp:: passed: d == Approx( 1.231 ).epsilon( 0.1 ) for: 1.23 == Approx( 1.231 ) +Approx.tests.cpp:: passed: 1.23f == Approx( 1.23f ) for: 1.23f == Approx( 1.2300000191 ) +Approx.tests.cpp:: passed: 0.0f == Approx( 0.0f ) for: 0.0f == Approx( 0.0 ) +Approx.tests.cpp:: passed: 1 == Approx( 1 ) for: 1 == Approx( 1.0 ) +Approx.tests.cpp:: passed: 0 == Approx( 0 ) for: 0 == Approx( 0.0 ) +Approx.tests.cpp:: passed: 1.0f == Approx( 1 ) for: 1.0f == Approx( 1.0 ) +Approx.tests.cpp:: passed: 0 == Approx( dZero) for: 0 == Approx( 0.0 ) +Approx.tests.cpp:: passed: 0 == Approx( dSmall ).margin( 0.001 ) for: 0 == Approx( 0.00001 ) +Approx.tests.cpp:: passed: 1.234f == Approx( dMedium ) for: 1.234f == Approx( 1.234 ) +Approx.tests.cpp:: passed: dMedium == Approx( 1.234f ) for: 1.234 == Approx( 1.2339999676 ) +Tricky.tests.cpp:: passed: true +Tricky.tests.cpp:: passed: true +Tricky.tests.cpp:: passed: true +Tricky.tests.cpp:: passed: true +Tricky.tests.cpp:: passed: true +Tricky.tests.cpp:: passed: true +Approx.tests.cpp:: passed: INFINITY == Approx(INFINITY) for: inff == Approx( inf ) +Approx.tests.cpp:: passed: NAN != Approx(NAN) for: nanf != Approx( nan ) +Approx.tests.cpp:: passed: !(NAN == Approx(NAN)) for: !(nanf == Approx( nan )) +Tricky.tests.cpp:: passed: y.v == 0 for: 0 == 0 +Tricky.tests.cpp:: passed: 0 == y.v for: 0 == 0 +ToStringGeneral.tests.cpp:: passed: true with 1 message: 'i := 2' +ToStringGeneral.tests.cpp:: passed: true with 1 message: '3' +ToStringGeneral.tests.cpp:: passed: tab == '/t' for: '/t' == '/t' +ToStringGeneral.tests.cpp:: passed: newline == '/n' for: '/n' == '/n' +ToStringGeneral.tests.cpp:: passed: carr_return == '/r' for: '/r' == '/r' +ToStringGeneral.tests.cpp:: passed: form_feed == '/f' for: '/f' == '/f' +ToStringGeneral.tests.cpp:: passed: space == ' ' for: ' ' == ' ' +ToStringGeneral.tests.cpp:: passed: c == chars[i] for: 'a' == 'a' +ToStringGeneral.tests.cpp:: passed: c == chars[i] for: 'z' == 'z' +ToStringGeneral.tests.cpp:: passed: c == chars[i] for: 'A' == 'A' +ToStringGeneral.tests.cpp:: passed: c == chars[i] for: 'Z' == 'Z' +ToStringGeneral.tests.cpp:: passed: null_terminator == '/0' for: 0 == 0 +ToStringGeneral.tests.cpp:: passed: c == i for: 2 == 2 +ToStringGeneral.tests.cpp:: passed: c == i for: 3 == 3 +ToStringGeneral.tests.cpp:: passed: c == i for: 4 == 4 +ToStringGeneral.tests.cpp:: passed: c == i for: 5 == 5 +Tricky.tests.cpp:: passed: std::vector{constructor_throws{}, constructor_throws{}} +Tricky.tests.cpp:: passed: std::vector{constructor_throws{}, constructor_throws{}} +Tricky.tests.cpp:: passed: std::vector{1, 2, 3} == std::vector{1, 2, 3} +Tricky.tests.cpp:: passed: std::vector{1, 2, 3} == std::vector{1, 2, 3} +Tricky.tests.cpp:: passed: std::vector{1, 2} == std::vector{1, 2} for: { 1, 2 } == { 1, 2 } +Tricky.tests.cpp:: passed: std::vector{1, 2} == std::vector{1, 2} for: { 1, 2 } == { 1, 2 } +Tricky.tests.cpp:: passed: !(std::vector{1, 2} == std::vector{1, 2, 3}) for: !({ 1, 2 } == { 1, 2, 3 }) +Tricky.tests.cpp:: passed: !(std::vector{1, 2} == std::vector{1, 2, 3}) for: !({ 1, 2 } == { 1, 2, 3 }) +Tricky.tests.cpp:: passed: std::vector{1, 2} == std::vector{1, 2} for: { 1, 2 } == { 1, 2 } +Tricky.tests.cpp:: passed: std::vector{1, 2} == std::vector{1, 2} for: { 1, 2 } == { 1, 2 } +Tricky.tests.cpp:: passed: true +Tricky.tests.cpp:: passed: std::vector{1, 2} == std::vector{1, 2} for: { 1, 2 } == { 1, 2 } +Tricky.tests.cpp:: passed: a for: 0x +Tricky.tests.cpp:: passed: a == &foo for: 0x == 0x +Approx.tests.cpp:: passed: td == Approx(10.0) for: StrongDoubleTypedef(10) == Approx( 10.0 ) +Approx.tests.cpp:: passed: Approx(10.0) == td for: Approx( 10.0 ) == StrongDoubleTypedef(10) +Approx.tests.cpp:: passed: td != Approx(11.0) for: StrongDoubleTypedef(10) != Approx( 11.0 ) +Approx.tests.cpp:: passed: Approx(11.0) != td for: Approx( 11.0 ) != StrongDoubleTypedef(10) +Approx.tests.cpp:: passed: td <= Approx(10.0) for: StrongDoubleTypedef(10) <= Approx( 10.0 ) +Approx.tests.cpp:: passed: td <= Approx(11.0) for: StrongDoubleTypedef(10) <= Approx( 11.0 ) +Approx.tests.cpp:: passed: Approx(10.0) <= td for: Approx( 10.0 ) <= StrongDoubleTypedef(10) +Approx.tests.cpp:: passed: Approx(9.0) <= td for: Approx( 9.0 ) <= StrongDoubleTypedef(10) +Approx.tests.cpp:: passed: td >= Approx(9.0) for: StrongDoubleTypedef(10) >= Approx( 9.0 ) +Approx.tests.cpp:: passed: td >= Approx(td) for: StrongDoubleTypedef(10) >= Approx( 10.0 ) +Approx.tests.cpp:: passed: Approx(td) >= td for: Approx( 10.0 ) >= StrongDoubleTypedef(10) +Approx.tests.cpp:: passed: Approx(11.0) >= td for: Approx( 11.0 ) >= StrongDoubleTypedef(10) +Condition.tests.cpp:: passed: 54 == 6*9 for: 54 == 54 +Condition.tests.cpp:: passed: ( -1 > 2u ) for: true +Condition.tests.cpp:: passed: -1 > 2u for: -1 > 2 +Condition.tests.cpp:: passed: ( 2u < -1 ) for: true +Condition.tests.cpp:: passed: 2u < -1 for: 2 < -1 +Condition.tests.cpp:: passed: ( minInt > 2u ) for: true +Condition.tests.cpp:: passed: minInt > 2u for: -2147483648 > 2 +Condition.tests.cpp:: passed: i == 1 for: 1 == 1 +Condition.tests.cpp:: passed: ui == 2 for: 2 == 2 +Condition.tests.cpp:: passed: l == 3 for: 3 == 3 +Condition.tests.cpp:: passed: ul == 4 for: 4 == 4 +Condition.tests.cpp:: passed: c == 5 for: 5 == 5 +Condition.tests.cpp:: passed: uc == 6 for: 6 == 6 +Condition.tests.cpp:: passed: 1 == i for: 1 == 1 +Condition.tests.cpp:: passed: 2 == ui for: 2 == 2 +Condition.tests.cpp:: passed: 3 == l for: 3 == 3 +Condition.tests.cpp:: passed: 4 == ul for: 4 == 4 +Condition.tests.cpp:: passed: 5 == c for: 5 == 5 +Condition.tests.cpp:: passed: 6 == uc for: 6 == 6 +Condition.tests.cpp:: passed: (std::numeric_limits::max)() > ul for: 4294967295 (0x) > 4 +Matchers.tests.cpp:: failed: testStringForMatching(), Contains("not there", Catch::CaseSensitive::No) for: "this string contains 'abc' as a substring" contains: "not there" (case insensitive) +Matchers.tests.cpp:: failed: testStringForMatching(), Contains("STRING") for: "this string contains 'abc' as a substring" contains: "STRING" +Exception.tests.cpp:: failed: unexpected exception with message: 'custom exception - not std'; expression was: throwCustom() +Exception.tests.cpp:: failed: unexpected exception with message: 'custom exception - not std'; expression was: throwCustom(), std::exception +Exception.tests.cpp:: failed: unexpected exception with message: 'custom std exception' +Approx.tests.cpp:: passed: 101.000001 != Approx(100).epsilon(0.01) for: 101.000001 != Approx( 100.0 ) +Approx.tests.cpp:: passed: std::pow(10, -5) != Approx(std::pow(10, -7)) for: 0.00001 != Approx( 0.0000001 ) +Matchers.tests.cpp:: failed: testStringForMatching(), EndsWith("Substring") for: "this string contains 'abc' as a substring" ends with: "Substring" +Matchers.tests.cpp:: failed: testStringForMatching(), EndsWith("this", Catch::CaseSensitive::No) for: "this string contains 'abc' as a substring" ends with: "this" (case insensitive) +Approx.tests.cpp:: passed: 101.01 != Approx(100).epsilon(0.01) for: 101.01 != Approx( 100.0 ) +Condition.tests.cpp:: failed: data.int_seven == 6 for: 7 == 6 +Condition.tests.cpp:: failed: data.int_seven == 8 for: 7 == 8 +Condition.tests.cpp:: failed: data.int_seven == 0 for: 7 == 0 +Condition.tests.cpp:: failed: data.float_nine_point_one == Approx( 9.11f ) for: 9.1f == Approx( 9.1099996567 ) +Condition.tests.cpp:: failed: data.float_nine_point_one == Approx( 9.0f ) for: 9.1f == Approx( 9.0 ) +Condition.tests.cpp:: failed: data.float_nine_point_one == Approx( 1 ) for: 9.1f == Approx( 1.0 ) +Condition.tests.cpp:: failed: data.float_nine_point_one == Approx( 0 ) for: 9.1f == Approx( 0.0 ) +Condition.tests.cpp:: failed: data.double_pi == Approx( 3.1415 ) for: 3.1415926535 == Approx( 3.1415 ) +Condition.tests.cpp:: failed: data.str_hello == "goodbye" for: "hello" == "goodbye" +Condition.tests.cpp:: failed: data.str_hello == "hell" for: "hello" == "hell" +Condition.tests.cpp:: failed: data.str_hello == "hello1" for: "hello" == "hello1" +Condition.tests.cpp:: failed: data.str_hello.size() == 6 for: 5 == 6 +Condition.tests.cpp:: failed: x == Approx( 1.301 ) for: 1.3 == Approx( 1.301 ) +Condition.tests.cpp:: passed: data.int_seven == 7 for: 7 == 7 +Condition.tests.cpp:: passed: data.float_nine_point_one == Approx( 9.1f ) for: 9.1f == Approx( 9.1000003815 ) +Condition.tests.cpp:: passed: data.double_pi == Approx( 3.1415926535 ) for: 3.1415926535 == Approx( 3.1415926535 ) +Condition.tests.cpp:: passed: data.str_hello == "hello" for: "hello" == "hello" +Condition.tests.cpp:: passed: "hello" == data.str_hello for: "hello" == "hello" +Condition.tests.cpp:: passed: data.str_hello.size() == 5 for: 5 == 5 +Condition.tests.cpp:: passed: x == Approx( 1.3 ) for: 1.3 == Approx( 1.3 ) +Matchers.tests.cpp:: passed: testStringForMatching(), Equals("this string contains 'abc' as a substring") for: "this string contains 'abc' as a substring" equals: "this string contains 'abc' as a substring" +Matchers.tests.cpp:: passed: testStringForMatching(), Equals("this string contains 'ABC' as a substring", Catch::CaseSensitive::No) for: "this string contains 'abc' as a substring" equals: "this string contains 'abc' as a substring" (case insensitive) +Matchers.tests.cpp:: failed: testStringForMatching(), Equals("this string contains 'ABC' as a substring") for: "this string contains 'abc' as a substring" equals: "this string contains 'ABC' as a substring" +Matchers.tests.cpp:: failed: testStringForMatching(), Equals("something else", Catch::CaseSensitive::No) for: "this string contains 'abc' as a substring" equals: "something else" (case insensitive) +Matchers.tests.cpp:: failed: expected exception, got none; expression was: doesNotThrow(), SpecialException, ExceptionMatcher{1} +Matchers.tests.cpp:: failed: expected exception, got none; expression was: doesNotThrow(), SpecialException, ExceptionMatcher{1} +Matchers.tests.cpp:: failed: unexpected exception with message: 'Unknown exception'; expression was: throwsAsInt(1), SpecialException, ExceptionMatcher{1} +Matchers.tests.cpp:: failed: unexpected exception with message: 'Unknown exception'; expression was: throwsAsInt(1), SpecialException, ExceptionMatcher{1} +Matchers.tests.cpp:: failed: throws(3), SpecialException, ExceptionMatcher{1} for: {?} special exception has value of 1 +Matchers.tests.cpp:: failed: throws(4), SpecialException, ExceptionMatcher{1} for: {?} special exception has value of 1 +Matchers.tests.cpp:: passed: throws(1), SpecialException, ExceptionMatcher{1} for: {?} special exception has value of 1 +Matchers.tests.cpp:: passed: throws(2), SpecialException, ExceptionMatcher{2} for: {?} special exception has value of 2 +Exception.tests.cpp:: passed: thisThrows(), "expected exception" for: "expected exception" equals: "expected exception" +Exception.tests.cpp:: passed: thisThrows(), Equals( "expecteD Exception", Catch::CaseSensitive::No ) for: "expected exception" equals: "expected exception" (case insensitive) +Exception.tests.cpp:: passed: thisThrows(), StartsWith( "expected" ) for: "expected exception" starts with: "expected" +Exception.tests.cpp:: passed: thisThrows(), EndsWith( "exception" ) for: "expected exception" ends with: "exception" +Exception.tests.cpp:: passed: thisThrows(), Contains( "except" ) for: "expected exception" contains: "except" +Exception.tests.cpp:: passed: thisThrows(), Contains( "exCept", Catch::CaseSensitive::No ) for: "expected exception" contains: "except" (case insensitive) +Exception.tests.cpp:: failed: unexpected exception with message: 'expected exception'; expression was: thisThrows(), std::string +Exception.tests.cpp:: failed: expected exception, got none; expression was: thisDoesntThrow(), std::domain_error +Exception.tests.cpp:: failed: unexpected exception with message: 'expected exception'; expression was: thisThrows() +Message.tests.cpp:: failed: explicitly with 1 message: 'This is a failure' +Message.tests.cpp:: failed: explicitly +Message.tests.cpp:: failed: explicitly with 1 message: 'This is a failure' +Message.tests.cpp:: warning: 'This message appears in the output' +Misc.tests.cpp:: passed: Factorial(0) == 1 for: 1 == 1 +Misc.tests.cpp:: passed: Factorial(1) == 1 for: 1 == 1 +Misc.tests.cpp:: passed: Factorial(2) == 2 for: 2 == 2 +Misc.tests.cpp:: passed: Factorial(3) == 6 for: 6 == 6 +Misc.tests.cpp:: passed: Factorial(10) == 3628800 for: 3628800 (0x) == 3628800 (0x) +Matchers.tests.cpp:: passed: 1., WithinAbs(1., 0) for: 1.0 is within 0.0 of 1.0 +Matchers.tests.cpp:: passed: 0., WithinAbs(1., 1) for: 0.0 is within 1.0 of 1.0 +Matchers.tests.cpp:: passed: 0., !WithinAbs(1., 0.99) for: 0.0 not is within 0.99 of 1.0 +Matchers.tests.cpp:: passed: 0., !WithinAbs(1., 0.99) for: 0.0 not is within 0.99 of 1.0 +Matchers.tests.cpp:: passed: NAN, !WithinAbs(NAN, 0) for: nanf not is within 0.0 of nan +Matchers.tests.cpp:: passed: 1., WithinULP(1., 0) for: 1.0 is within 0 ULPs of 1.0 +Matchers.tests.cpp:: passed: std::nextafter(1., 2.), WithinULP(1., 1) for: 1.0 is within 1 ULPs of 1.0 +Matchers.tests.cpp:: passed: std::nextafter(1., 0.), WithinULP(1., 1) for: 1.0 is within 1 ULPs of 1.0 +Matchers.tests.cpp:: passed: std::nextafter(1., 2.), !WithinULP(1., 0) for: 1.0 not is within 0 ULPs of 1.0 +Matchers.tests.cpp:: passed: 1., WithinULP(1., 0) for: 1.0 is within 0 ULPs of 1.0 +Matchers.tests.cpp:: passed: -0., WithinULP(0., 0) for: -0.0 is within 0 ULPs of 0.0 +Matchers.tests.cpp:: passed: NAN, !WithinULP(NAN, 123) for: nanf not is within 123 ULPs of nanf +Matchers.tests.cpp:: passed: 1., WithinAbs(1., 0.5) || WithinULP(2., 1) for: 1.0 ( is within 0.5 of 1.0 or is within 1 ULPs of 2.0 ) +Matchers.tests.cpp:: passed: 1., WithinAbs(2., 0.5) || WithinULP(1., 0) for: 1.0 ( is within 0.5 of 2.0 or is within 0 ULPs of 1.0 ) +Matchers.tests.cpp:: passed: NAN, !(WithinAbs(NAN, 100) || WithinULP(NAN, 123)) for: nanf not ( is within 100.0 of nan or is within 123 ULPs of nanf ) +Matchers.tests.cpp:: passed: WithinAbs(1., 0.) +Matchers.tests.cpp:: passed: WithinAbs(1., -1.), std::domain_error +Matchers.tests.cpp:: passed: WithinULP(1., 0) +Matchers.tests.cpp:: passed: WithinULP(1., -1), std::domain_error +Matchers.tests.cpp:: passed: 1.f, WithinAbs(1.f, 0) for: 1.0f is within 0.0 of 1.0 +Matchers.tests.cpp:: passed: 0.f, WithinAbs(1.f, 1) for: 0.0f is within 1.0 of 1.0 +Matchers.tests.cpp:: passed: 0.f, !WithinAbs(1.f, 0.99f) for: 0.0f not is within 0.9900000095 of 1.0 +Matchers.tests.cpp:: passed: 0.f, !WithinAbs(1.f, 0.99f) for: 0.0f not is within 0.9900000095 of 1.0 +Matchers.tests.cpp:: passed: 0.f, WithinAbs(-0.f, 0) for: 0.0f is within 0.0 of -0.0 +Matchers.tests.cpp:: passed: NAN, !WithinAbs(NAN, 0) for: nanf not is within 0.0 of nan +Matchers.tests.cpp:: passed: 1.f, WithinULP(1.f, 0) for: 1.0f is within 0 ULPs of 1.0f +Matchers.tests.cpp:: passed: std::nextafter(1.f, 2.f), WithinULP(1.f, 1) for: 1.0f is within 1 ULPs of 1.0f +Matchers.tests.cpp:: passed: std::nextafter(1.f, 0.f), WithinULP(1.f, 1) for: 1.0f is within 1 ULPs of 1.0f +Matchers.tests.cpp:: passed: std::nextafter(1.f, 2.f), !WithinULP(1.f, 0) for: 1.0f not is within 0 ULPs of 1.0f +Matchers.tests.cpp:: passed: 1.f, WithinULP(1.f, 0) for: 1.0f is within 0 ULPs of 1.0f +Matchers.tests.cpp:: passed: -0.f, WithinULP(0.f, 0) for: -0.0f is within 0 ULPs of 0.0f +Matchers.tests.cpp:: passed: NAN, !WithinULP(NAN, 123) for: nanf not is within 123 ULPs of nanf +Matchers.tests.cpp:: passed: 1.f, WithinAbs(1.f, 0.5) || WithinULP(1.f, 1) for: 1.0f ( is within 0.5 of 1.0 or is within 1 ULPs of 1.0f ) +Matchers.tests.cpp:: passed: 1.f, WithinAbs(2.f, 0.5) || WithinULP(1.f, 0) for: 1.0f ( is within 0.5 of 2.0 or is within 0 ULPs of 1.0f ) +Matchers.tests.cpp:: passed: NAN, !(WithinAbs(NAN, 100) || WithinULP(NAN, 123)) for: nanf not ( is within 100.0 of nan or is within 123 ULPs of nanf ) +Matchers.tests.cpp:: passed: WithinAbs(1.f, 0.f) +Matchers.tests.cpp:: passed: WithinAbs(1.f, -1.f), std::domain_error +Matchers.tests.cpp:: passed: WithinULP(1.f, 0) +Matchers.tests.cpp:: passed: WithinULP(1.f, -1), std::domain_error +Approx.tests.cpp:: passed: d >= Approx( 1.22 ) for: 1.23 >= Approx( 1.22 ) +Approx.tests.cpp:: passed: d >= Approx( 1.23 ) for: 1.23 >= Approx( 1.23 ) +Approx.tests.cpp:: passed: !(d >= Approx( 1.24 )) for: !(1.23 >= Approx( 1.24 )) +Approx.tests.cpp:: passed: d >= Approx( 1.24 ).epsilon(0.1) for: 1.23 >= Approx( 1.24 ) +Message.tests.cpp:: warning: 'this is a message' with 1 message: 'this is a warning' +Message.tests.cpp:: failed: a == 1 for: 2 == 1 with 2 messages: 'this message should be logged' and 'so should this' +Message.tests.cpp:: passed: a == 2 for: 2 == 2 with 1 message: 'this message may be logged later' +Message.tests.cpp:: failed: a == 1 for: 2 == 1 with 2 messages: 'this message may be logged later' and 'this message should be logged' +Message.tests.cpp:: failed: a == 0 for: 2 == 0 with 3 messages: 'this message may be logged later' and 'this message should be logged' and 'and this, but later' +Message.tests.cpp:: passed: a == 2 for: 2 == 2 with 4 messages: 'this message may be logged later' and 'this message should be logged' and 'and this, but later' and 'but not this' +Message.tests.cpp:: passed: i < 10 for: 0 < 10 with 2 messages: 'current counter 0' and 'i := 0' +Message.tests.cpp:: passed: i < 10 for: 1 < 10 with 2 messages: 'current counter 1' and 'i := 1' +Message.tests.cpp:: passed: i < 10 for: 2 < 10 with 2 messages: 'current counter 2' and 'i := 2' +Message.tests.cpp:: passed: i < 10 for: 3 < 10 with 2 messages: 'current counter 3' and 'i := 3' +Message.tests.cpp:: passed: i < 10 for: 4 < 10 with 2 messages: 'current counter 4' and 'i := 4' +Message.tests.cpp:: passed: i < 10 for: 5 < 10 with 2 messages: 'current counter 5' and 'i := 5' +Message.tests.cpp:: passed: i < 10 for: 6 < 10 with 2 messages: 'current counter 6' and 'i := 6' +Message.tests.cpp:: passed: i < 10 for: 7 < 10 with 2 messages: 'current counter 7' and 'i := 7' +Message.tests.cpp:: passed: i < 10 for: 8 < 10 with 2 messages: 'current counter 8' and 'i := 8' +Message.tests.cpp:: passed: i < 10 for: 9 < 10 with 2 messages: 'current counter 9' and 'i := 9' +Message.tests.cpp:: failed: i < 10 for: 10 < 10 with 2 messages: 'current counter 10' and 'i := 10' +Condition.tests.cpp:: failed: data.int_seven != 7 for: 7 != 7 +Condition.tests.cpp:: failed: data.float_nine_point_one != Approx( 9.1f ) for: 9.1f != Approx( 9.1000003815 ) +Condition.tests.cpp:: failed: data.double_pi != Approx( 3.1415926535 ) for: 3.1415926535 != Approx( 3.1415926535 ) +Condition.tests.cpp:: failed: data.str_hello != "hello" for: "hello" != "hello" +Condition.tests.cpp:: failed: data.str_hello.size() != 5 for: 5 != 5 +Condition.tests.cpp:: passed: data.int_seven != 6 for: 7 != 6 +Condition.tests.cpp:: passed: data.int_seven != 8 for: 7 != 8 +Condition.tests.cpp:: passed: data.float_nine_point_one != Approx( 9.11f ) for: 9.1f != Approx( 9.1099996567 ) +Condition.tests.cpp:: passed: data.float_nine_point_one != Approx( 9.0f ) for: 9.1f != Approx( 9.0 ) +Condition.tests.cpp:: passed: data.float_nine_point_one != Approx( 1 ) for: 9.1f != Approx( 1.0 ) +Condition.tests.cpp:: passed: data.float_nine_point_one != Approx( 0 ) for: 9.1f != Approx( 0.0 ) +Condition.tests.cpp:: passed: data.double_pi != Approx( 3.1415 ) for: 3.1415926535 != Approx( 3.1415 ) +Condition.tests.cpp:: passed: data.str_hello != "goodbye" for: "hello" != "goodbye" +Condition.tests.cpp:: passed: data.str_hello != "hell" for: "hello" != "hell" +Condition.tests.cpp:: passed: data.str_hello != "hello1" for: "hello" != "hello1" +Condition.tests.cpp:: passed: data.str_hello.size() != 6 for: 5 != 6 +Approx.tests.cpp:: passed: d <= Approx( 1.24 ) for: 1.23 <= Approx( 1.24 ) +Approx.tests.cpp:: passed: d <= Approx( 1.23 ) for: 1.23 <= Approx( 1.23 ) +Approx.tests.cpp:: passed: !(d <= Approx( 1.22 )) for: !(1.23 <= Approx( 1.22 )) +Approx.tests.cpp:: passed: d <= Approx( 1.22 ).epsilon(0.1) for: 1.23 <= Approx( 1.22 ) +Misc.tests.cpp:: passed: with 1 message: 'was called' +Matchers.tests.cpp:: passed: testStringForMatching(), Contains("string") && Contains("abc") && Contains("substring") && Contains("contains") for: "this string contains 'abc' as a substring" ( contains: "string" and contains: "abc" and contains: "substring" and contains: "contains" ) +Matchers.tests.cpp:: passed: testStringForMatching(), Contains("string") || Contains("different") || Contains("random") for: "this string contains 'abc' as a substring" ( contains: "string" or contains: "different" or contains: "random" ) +Matchers.tests.cpp:: passed: testStringForMatching2(), Contains("string") || Contains("different") || Contains("random") for: "some completely different text that contains one common word" ( contains: "string" or contains: "different" or contains: "random" ) +Matchers.tests.cpp:: passed: testStringForMatching(), (Contains("string") || Contains("different")) && Contains("substring") for: "this string contains 'abc' as a substring" ( ( contains: "string" or contains: "different" ) and contains: "substring" ) +Matchers.tests.cpp:: failed: testStringForMatching(), (Contains("string") || Contains("different")) && Contains("random") for: "this string contains 'abc' as a substring" ( ( contains: "string" or contains: "different" ) and contains: "random" ) +Matchers.tests.cpp:: passed: testStringForMatching(), !Contains("different") for: "this string contains 'abc' as a substring" not contains: "different" +Matchers.tests.cpp:: failed: testStringForMatching(), !Contains("substring") for: "this string contains 'abc' as a substring" not contains: "substring" +Exception.tests.cpp:: passed: thisThrows(), "expected exception" for: "expected exception" equals: "expected exception" +Exception.tests.cpp:: failed: thisThrows(), "should fail" for: "expected exception" equals: "should fail" +Misc.tests.cpp:: warning: 'This one ran' +Exception.tests.cpp:: failed: unexpected exception with message: 'custom exception' +Tricky.tests.cpp:: passed: True for: {?} +Tricky.tests.cpp:: passed: !False for: true +Tricky.tests.cpp:: passed: !(False) for: !{?} +Condition.tests.cpp:: failed: data.int_seven > 7 for: 7 > 7 +Condition.tests.cpp:: failed: data.int_seven < 7 for: 7 < 7 +Condition.tests.cpp:: failed: data.int_seven > 8 for: 7 > 8 +Condition.tests.cpp:: failed: data.int_seven < 6 for: 7 < 6 +Condition.tests.cpp:: failed: data.int_seven < 0 for: 7 < 0 +Condition.tests.cpp:: failed: data.int_seven < -1 for: 7 < -1 +Condition.tests.cpp:: failed: data.int_seven >= 8 for: 7 >= 8 +Condition.tests.cpp:: failed: data.int_seven <= 6 for: 7 <= 6 +Condition.tests.cpp:: failed: data.float_nine_point_one < 9 for: 9.1f < 9 +Condition.tests.cpp:: failed: data.float_nine_point_one > 10 for: 9.1f > 10 +Condition.tests.cpp:: failed: data.float_nine_point_one > 9.2 for: 9.1f > 9.2 +Condition.tests.cpp:: failed: data.str_hello > "hello" for: "hello" > "hello" +Condition.tests.cpp:: failed: data.str_hello < "hello" for: "hello" < "hello" +Condition.tests.cpp:: failed: data.str_hello > "hellp" for: "hello" > "hellp" +Condition.tests.cpp:: failed: data.str_hello > "z" for: "hello" > "z" +Condition.tests.cpp:: failed: data.str_hello < "hellm" for: "hello" < "hellm" +Condition.tests.cpp:: failed: data.str_hello < "a" for: "hello" < "a" +Condition.tests.cpp:: failed: data.str_hello >= "z" for: "hello" >= "z" +Condition.tests.cpp:: failed: data.str_hello <= "a" for: "hello" <= "a" +Condition.tests.cpp:: passed: data.int_seven < 8 for: 7 < 8 +Condition.tests.cpp:: passed: data.int_seven > 6 for: 7 > 6 +Condition.tests.cpp:: passed: data.int_seven > 0 for: 7 > 0 +Condition.tests.cpp:: passed: data.int_seven > -1 for: 7 > -1 +Condition.tests.cpp:: passed: data.int_seven >= 7 for: 7 >= 7 +Condition.tests.cpp:: passed: data.int_seven >= 6 for: 7 >= 6 +Condition.tests.cpp:: passed: data.int_seven <= 7 for: 7 <= 7 +Condition.tests.cpp:: passed: data.int_seven <= 8 for: 7 <= 8 +Condition.tests.cpp:: passed: data.float_nine_point_one > 9 for: 9.1f > 9 +Condition.tests.cpp:: passed: data.float_nine_point_one < 10 for: 9.1f < 10 +Condition.tests.cpp:: passed: data.float_nine_point_one < 9.2 for: 9.1f < 9.2 +Condition.tests.cpp:: passed: data.str_hello <= "hello" for: "hello" <= "hello" +Condition.tests.cpp:: passed: data.str_hello >= "hello" for: "hello" >= "hello" +Condition.tests.cpp:: passed: data.str_hello < "hellp" for: "hello" < "hellp" +Condition.tests.cpp:: passed: data.str_hello < "zebra" for: "hello" < "zebra" +Condition.tests.cpp:: passed: data.str_hello > "hellm" for: "hello" > "hellm" +Condition.tests.cpp:: passed: data.str_hello > "a" for: "hello" > "a" +Message.tests.cpp:: failed: explicitly with 1 message: 'Message from section one' +Message.tests.cpp:: failed: explicitly with 1 message: 'Message from section two' +CmdLine.tests.cpp:: passed: spec.hasFilters() == false for: false == false +CmdLine.tests.cpp:: passed: spec.matches( tcA ) == false for: false == false +CmdLine.tests.cpp:: passed: spec.matches( tcB ) == false for: false == false +CmdLine.tests.cpp:: passed: spec.hasFilters() == false for: false == false +CmdLine.tests.cpp:: passed: spec.matches(tcA ) == false for: false == false +CmdLine.tests.cpp:: passed: spec.matches( tcB ) == false for: false == false +CmdLine.tests.cpp:: passed: spec.hasFilters() == false for: false == false +CmdLine.tests.cpp:: passed: spec.matches( tcA ) == false for: false == false +CmdLine.tests.cpp:: passed: spec.matches( tcB ) == false for: false == false +CmdLine.tests.cpp:: passed: spec.hasFilters() == true for: true == true +CmdLine.tests.cpp:: passed: spec.matches( tcA ) == false for: false == false +CmdLine.tests.cpp:: passed: spec.matches( tcB ) == true for: true == true +CmdLine.tests.cpp:: passed: spec.hasFilters() == true for: true == true +CmdLine.tests.cpp:: passed: spec.matches( tcA ) == false for: false == false +CmdLine.tests.cpp:: passed: spec.matches( tcB ) == true for: true == true +CmdLine.tests.cpp:: passed: spec.hasFilters() == true for: true == true +CmdLine.tests.cpp:: passed: spec.matches( tcA ) == false for: false == false +CmdLine.tests.cpp:: passed: spec.matches( tcB ) == true for: true == true +CmdLine.tests.cpp:: passed: spec.matches( tcC ) == false for: false == false +CmdLine.tests.cpp:: passed: spec.hasFilters() == true for: true == true +CmdLine.tests.cpp:: passed: spec.matches( tcA ) == false for: false == false +CmdLine.tests.cpp:: passed: spec.matches( tcB ) == false for: false == false +CmdLine.tests.cpp:: passed: spec.matches( tcC ) == true for: true == true +CmdLine.tests.cpp:: passed: spec.matches( tcD ) == false for: false == false +CmdLine.tests.cpp:: passed: parseTestSpec( "*a" ).matches( tcA ) == true for: true == true +CmdLine.tests.cpp:: passed: spec.hasFilters() == true for: true == true +CmdLine.tests.cpp:: passed: spec.matches( tcA ) == false for: false == false +CmdLine.tests.cpp:: passed: spec.matches( tcB ) == false for: false == false +CmdLine.tests.cpp:: passed: spec.matches( tcC ) == true for: true == true +CmdLine.tests.cpp:: passed: spec.matches( tcD ) == false for: false == false +CmdLine.tests.cpp:: passed: parseTestSpec( "a*" ).matches( tcA ) == true for: true == true +CmdLine.tests.cpp:: passed: spec.hasFilters() == true for: true == true +CmdLine.tests.cpp:: passed: spec.matches( tcA ) == false for: false == false +CmdLine.tests.cpp:: passed: spec.matches( tcB ) == false for: false == false +CmdLine.tests.cpp:: passed: spec.matches( tcC ) == true for: true == true +CmdLine.tests.cpp:: passed: spec.matches( tcD ) == true for: true == true +CmdLine.tests.cpp:: passed: parseTestSpec( "*a*" ).matches( tcA ) == true for: true == true +CmdLine.tests.cpp:: passed: spec.hasFilters() == true for: true == true +CmdLine.tests.cpp:: passed: spec.matches( tcA ) == true for: true == true +CmdLine.tests.cpp:: passed: spec.matches( tcB ) == false for: false == false +CmdLine.tests.cpp:: passed: spec.hasFilters() == true for: true == true +CmdLine.tests.cpp:: passed: spec.matches( tcA ) == true for: true == true +CmdLine.tests.cpp:: passed: spec.matches( tcB ) == false for: false == false +CmdLine.tests.cpp:: passed: spec.hasFilters() == true for: true == true +CmdLine.tests.cpp:: passed: spec.matches( tcA ) == true for: true == true +CmdLine.tests.cpp:: passed: spec.matches( tcB ) == false for: false == false +CmdLine.tests.cpp:: passed: spec.hasFilters() == true for: true == true +CmdLine.tests.cpp:: passed: spec.matches( tcA ) == false for: false == false +CmdLine.tests.cpp:: passed: spec.matches( tcB ) == false for: false == false +CmdLine.tests.cpp:: passed: spec.matches( tcC ) == true for: true == true +CmdLine.tests.cpp:: passed: spec.matches( tcD ) == true for: true == true +CmdLine.tests.cpp:: passed: spec.hasFilters() == true for: true == true +CmdLine.tests.cpp:: passed: spec.matches( tcA ) == true for: true == true +CmdLine.tests.cpp:: passed: spec.matches( tcB ) == true for: true == true +CmdLine.tests.cpp:: passed: spec.matches( tcC ) == true for: true == true +CmdLine.tests.cpp:: passed: spec.matches( tcD ) == true for: true == true +CmdLine.tests.cpp:: passed: spec.hasFilters() == true for: true == true +CmdLine.tests.cpp:: passed: spec.matches( tcA ) == false for: false == false +CmdLine.tests.cpp:: passed: spec.matches( tcB ) == true for: true == true +CmdLine.tests.cpp:: passed: spec.matches( tcC ) == false for: false == false +CmdLine.tests.cpp:: passed: spec.hasFilters() == true for: true == true +CmdLine.tests.cpp:: passed: spec.matches( tcA ) == false for: false == false +CmdLine.tests.cpp:: passed: spec.matches( tcB ) == true for: true == true +CmdLine.tests.cpp:: passed: spec.matches( tcC ) == true for: true == true +CmdLine.tests.cpp:: passed: spec.hasFilters() == true for: true == true +CmdLine.tests.cpp:: passed: spec.matches( tcA ) == false for: false == false +CmdLine.tests.cpp:: passed: spec.matches( tcB ) == false for: false == false +CmdLine.tests.cpp:: passed: spec.matches( tcC ) == true for: true == true +CmdLine.tests.cpp:: passed: spec.hasFilters() == true for: true == true +CmdLine.tests.cpp:: passed: spec.matches( tcA ) == false for: false == false +CmdLine.tests.cpp:: passed: spec.matches( tcB ) == false for: false == false +CmdLine.tests.cpp:: passed: spec.matches( tcC ) == true for: true == true +CmdLine.tests.cpp:: passed: spec.hasFilters() == true for: true == true +CmdLine.tests.cpp:: passed: spec.matches( tcA ) == false for: false == false +CmdLine.tests.cpp:: passed: spec.matches( tcB ) == false for: false == false +CmdLine.tests.cpp:: passed: spec.matches( tcC ) == true for: true == true +CmdLine.tests.cpp:: passed: spec.matches( tcD ) == false for: false == false +CmdLine.tests.cpp:: passed: spec.hasFilters() == true for: true == true +CmdLine.tests.cpp:: passed: spec.matches( tcA ) == true for: true == true +CmdLine.tests.cpp:: passed: spec.matches( tcB ) == false for: false == false +CmdLine.tests.cpp:: passed: spec.matches( tcC ) == true for: true == true +CmdLine.tests.cpp:: passed: spec.hasFilters() == true for: true == true +CmdLine.tests.cpp:: passed: spec.matches( tcA ) == false for: false == false +CmdLine.tests.cpp:: passed: spec.matches( tcB ) == true for: true == true +CmdLine.tests.cpp:: passed: spec.matches( tcC ) == false for: false == false +CmdLine.tests.cpp:: passed: spec.hasFilters() == true for: true == true +CmdLine.tests.cpp:: passed: spec.matches( tcA ) == false for: false == false +CmdLine.tests.cpp:: passed: spec.matches( tcB ) == false for: false == false +CmdLine.tests.cpp:: passed: spec.matches( tcC ) == false for: false == false +CmdLine.tests.cpp:: passed: spec.matches( tcD ) == true for: true == true +CmdLine.tests.cpp:: passed: spec.hasFilters() == true for: true == true +CmdLine.tests.cpp:: passed: spec.matches( tcA ) == false for: false == false +CmdLine.tests.cpp:: passed: spec.matches( tcB ) == false for: false == false +CmdLine.tests.cpp:: passed: spec.matches( tcC ) == false for: false == false +CmdLine.tests.cpp:: passed: spec.matches( tcD ) == true for: true == true +CmdLine.tests.cpp:: passed: spec.hasFilters() == true for: true == true +CmdLine.tests.cpp:: passed: spec.matches( tcA ) == true for: true == true +CmdLine.tests.cpp:: passed: spec.matches( tcB ) == false for: false == false +CmdLine.tests.cpp:: passed: spec.matches( tcC ) == true for: true == true +CmdLine.tests.cpp:: passed: spec.matches( tcD ) == true for: true == true +CmdLine.tests.cpp:: passed: spec.hasFilters() == true for: true == true +CmdLine.tests.cpp:: passed: spec.matches( tcA ) == true for: true == true +CmdLine.tests.cpp:: passed: spec.matches( tcB ) == true for: true == true +CmdLine.tests.cpp:: passed: spec.matches( tcC ) == false for: false == false +CmdLine.tests.cpp:: passed: spec.matches( tcD ) == false for: false == false +CmdLine.tests.cpp:: passed: spec.hasFilters() == true for: true == true +CmdLine.tests.cpp:: passed: spec.matches( tcA ) == true for: true == true +CmdLine.tests.cpp:: passed: spec.matches( tcB ) == true for: true == true +CmdLine.tests.cpp:: passed: spec.matches( tcC ) == true for: true == true +CmdLine.tests.cpp:: passed: spec.matches( tcD ) == false for: false == false +CmdLine.tests.cpp:: passed: spec.hasFilters() == true for: true == true +CmdLine.tests.cpp:: passed: spec.matches( tcA ) == true for: true == true +CmdLine.tests.cpp:: passed: spec.matches( tcB ) == true for: true == true +CmdLine.tests.cpp:: passed: spec.matches( tcC ) == true for: true == true +CmdLine.tests.cpp:: passed: spec.matches( tcD ) == false for: false == false +CmdLine.tests.cpp:: passed: spec.hasFilters() == true for: true == true +CmdLine.tests.cpp:: passed: spec.matches( tcA ) == false for: false == false +CmdLine.tests.cpp:: passed: spec.matches( tcB ) == false for: false == false +CmdLine.tests.cpp:: passed: spec.matches( tcC ) == true for: true == true +CmdLine.tests.cpp:: passed: spec.matches( tcD ) == false for: false == false +CmdLine.tests.cpp:: passed: spec.hasFilters() == false for: false == false +CmdLine.tests.cpp:: passed: spec.matches( tcA ) == false for: false == false +CmdLine.tests.cpp:: passed: spec.matches( tcB ) == false for: false == false +CmdLine.tests.cpp:: passed: spec.matches( tcC ) == false for: false == false +CmdLine.tests.cpp:: passed: spec.matches( tcD ) == false for: false == false +CmdLine.tests.cpp:: passed: spec.hasFilters() == false for: false == false +CmdLine.tests.cpp:: passed: spec.matches( tcA ) == false for: false == false +CmdLine.tests.cpp:: passed: spec.matches( tcB ) == false for: false == false +CmdLine.tests.cpp:: passed: spec.matches( tcC ) == false for: false == false +CmdLine.tests.cpp:: passed: spec.matches( tcD ) == false for: false == false +CmdLine.tests.cpp:: passed: spec.hasFilters() == true for: true == true +CmdLine.tests.cpp:: passed: spec.matches( tcA ) == false for: false == false +CmdLine.tests.cpp:: passed: spec.matches( tcB ) == false for: false == false +CmdLine.tests.cpp:: passed: spec.matches( tcC ) == false for: false == false +CmdLine.tests.cpp:: passed: spec.matches( tcD ) == true for: true == true +Tricky.tests.cpp:: passed: (std::pair( 1, 2 )) == aNicePair for: {?} == {?} +Condition.tests.cpp:: passed: p == 0 for: 0 == 0 +Condition.tests.cpp:: passed: p == pNULL for: 0 == 0 +Condition.tests.cpp:: passed: p != 0 for: 0x != 0 +Condition.tests.cpp:: passed: cp != 0 for: 0x != 0 +Condition.tests.cpp:: passed: cpc != 0 for: 0x != 0 +Condition.tests.cpp:: passed: returnsNull() == 0 for: {null string} == 0 +Condition.tests.cpp:: passed: returnsConstNull() == 0 for: {null string} == 0 +Condition.tests.cpp:: passed: 0 != p for: 0 != 0x +CmdLine.tests.cpp:: passed: result for: {?} +CmdLine.tests.cpp:: passed: config.processName == "" for: "" == "" +CmdLine.tests.cpp:: passed: result for: {?} +CmdLine.tests.cpp:: passed: config.processName == "test" for: "test" == "test" +CmdLine.tests.cpp:: passed: config.shouldDebugBreak == false for: false == false +CmdLine.tests.cpp:: passed: config.abortAfter == -1 for: -1 == -1 +CmdLine.tests.cpp:: passed: config.noThrow == false for: false == false +CmdLine.tests.cpp:: passed: config.reporterNames.empty() for: true +CmdLine.tests.cpp:: passed: result for: {?} +CmdLine.tests.cpp:: passed: cfg.testSpec().matches(fakeTestCase("notIncluded")) == false for: false == false +CmdLine.tests.cpp:: passed: cfg.testSpec().matches(fakeTestCase("test1")) for: true +CmdLine.tests.cpp:: passed: result for: {?} +CmdLine.tests.cpp:: passed: cfg.testSpec().matches(fakeTestCase("test1")) == false for: false == false +CmdLine.tests.cpp:: passed: cfg.testSpec().matches(fakeTestCase("alwaysIncluded")) for: true +CmdLine.tests.cpp:: passed: result for: {?} +CmdLine.tests.cpp:: passed: cfg.testSpec().matches(fakeTestCase("test1")) == false for: false == false +CmdLine.tests.cpp:: passed: cfg.testSpec().matches(fakeTestCase("alwaysIncluded")) for: true +CmdLine.tests.cpp:: passed: cli.parse({"test", "-r", "console"}) for: {?} +CmdLine.tests.cpp:: passed: config.reporterNames[0] == "console" for: "console" == "console" +CmdLine.tests.cpp:: passed: cli.parse({"test", "-r", "xml"}) for: {?} +CmdLine.tests.cpp:: passed: config.reporterNames[0] == "xml" for: "xml" == "xml" +CmdLine.tests.cpp:: passed: cli.parse({"test", "-r", "xml", "-r", "junit"}) for: {?} +CmdLine.tests.cpp:: passed: config.reporterNames.size() == 2 for: 2 == 2 +CmdLine.tests.cpp:: passed: config.reporterNames[0] == "xml" for: "xml" == "xml" +CmdLine.tests.cpp:: passed: config.reporterNames[1] == "junit" for: "junit" == "junit" +CmdLine.tests.cpp:: passed: cli.parse({"test", "--reporter", "junit"}) for: {?} +CmdLine.tests.cpp:: passed: config.reporterNames[0] == "junit" for: "junit" == "junit" +CmdLine.tests.cpp:: passed: cli.parse({"test", "-b"}) for: {?} +CmdLine.tests.cpp:: passed: config.shouldDebugBreak == true for: true == true +CmdLine.tests.cpp:: passed: cli.parse({"test", "--break"}) for: {?} +CmdLine.tests.cpp:: passed: config.shouldDebugBreak for: true +CmdLine.tests.cpp:: passed: cli.parse({"test", "-a"}) for: {?} +CmdLine.tests.cpp:: passed: config.abortAfter == 1 for: 1 == 1 +CmdLine.tests.cpp:: passed: cli.parse({"test", "-x", "2"}) for: {?} +CmdLine.tests.cpp:: passed: config.abortAfter == 2 for: 2 == 2 +CmdLine.tests.cpp:: passed: !result for: true +CmdLine.tests.cpp:: passed: result.errorMessage(), Contains("convert") && Contains("oops") for: "Unable to convert 'oops' to destination type" ( contains: "convert" and contains: "oops" ) +CmdLine.tests.cpp:: passed: cli.parse({"test", "-e"}) for: {?} +CmdLine.tests.cpp:: passed: config.noThrow for: true +CmdLine.tests.cpp:: passed: cli.parse({"test", "--nothrow"}) for: {?} +CmdLine.tests.cpp:: passed: config.noThrow for: true +CmdLine.tests.cpp:: passed: cli.parse({"test", "-o", "filename.ext"}) for: {?} +CmdLine.tests.cpp:: passed: config.outputFilename == "filename.ext" for: "filename.ext" == "filename.ext" +CmdLine.tests.cpp:: passed: cli.parse({"test", "--out", "filename.ext"}) for: {?} +CmdLine.tests.cpp:: passed: config.outputFilename == "filename.ext" for: "filename.ext" == "filename.ext" +CmdLine.tests.cpp:: passed: cli.parse({"test", "-abe"}) for: {?} +CmdLine.tests.cpp:: passed: config.abortAfter == 1 for: 1 == 1 +CmdLine.tests.cpp:: passed: config.shouldDebugBreak for: true +CmdLine.tests.cpp:: passed: config.noThrow == true for: true == true +CmdLine.tests.cpp:: passed: cli.parse({"test"}) for: {?} +CmdLine.tests.cpp:: passed: config.useColour == UseColour::Auto for: 0 == 0 +CmdLine.tests.cpp:: passed: cli.parse({"test", "--use-colour", "auto"}) for: {?} +CmdLine.tests.cpp:: passed: config.useColour == UseColour::Auto for: 0 == 0 +CmdLine.tests.cpp:: passed: cli.parse({"test", "--use-colour", "yes"}) for: {?} +CmdLine.tests.cpp:: passed: config.useColour == UseColour::Yes for: 1 == 1 +CmdLine.tests.cpp:: passed: cli.parse({"test", "--use-colour", "no"}) for: {?} +CmdLine.tests.cpp:: passed: config.useColour == UseColour::No for: 2 == 2 +CmdLine.tests.cpp:: passed: !result for: true +CmdLine.tests.cpp:: passed: result.errorMessage(), Contains( "colour mode must be one of" ) for: "colour mode must be one of: auto, yes or no. 'wrong' not recognised" contains: "colour mode must be one of" +Decomposition.tests.cpp:: failed: truthy(false) for: Hey, its truthy! +Matchers.tests.cpp:: failed: testStringForMatching(), Matches("this STRING contains 'abc' as a substring") for: "this string contains 'abc' as a substring" matches "this STRING contains 'abc' as a substring" case sensitively +Matchers.tests.cpp:: failed: testStringForMatching(), Matches("contains 'abc' as a substring") for: "this string contains 'abc' as a substring" matches "contains 'abc' as a substring" case sensitively +Matchers.tests.cpp:: failed: testStringForMatching(), Matches("this string contains 'abc' as a") for: "this string contains 'abc' as a substring" matches "this string contains 'abc' as a" case sensitively +Message.tests.cpp:: passed: with 1 message: 'this is a success' +Message.tests.cpp:: passed: +BDD.tests.cpp:: passed: before == 0 for: 0 == 0 +BDD.tests.cpp:: passed: after > before for: 1 > 0 +BDD.tests.cpp:: passed: itDoesThis() for: true +BDD.tests.cpp:: passed: itDoesThat() for: true +BDD.tests.cpp:: passed: with 1 message: 'boo!' +BDD.tests.cpp:: passed: v.size() == 0 for: 0 == 0 +BDD.tests.cpp:: passed: v.size() == 10 for: 10 == 10 +BDD.tests.cpp:: passed: v.capacity() >= 10 for: 10 >= 10 +BDD.tests.cpp:: passed: v.size() == 5 for: 5 == 5 +BDD.tests.cpp:: passed: v.capacity() >= 10 for: 10 >= 10 +BDD.tests.cpp:: passed: v.size() == 0 for: 0 == 0 +BDD.tests.cpp:: passed: v.capacity() >= 10 for: 10 >= 10 +BDD.tests.cpp:: passed: v.size() == 0 for: 0 == 0 +A string sent directly to stdout +A string sent directly to stderr +A string sent to stderr via clog +Approx.tests.cpp:: passed: d == Approx( 1.23 ) for: 1.23 == Approx( 1.23 ) +Approx.tests.cpp:: passed: d != Approx( 1.22 ) for: 1.23 != Approx( 1.22 ) +Approx.tests.cpp:: passed: d != Approx( 1.24 ) for: 1.23 != Approx( 1.24 ) +Approx.tests.cpp:: passed: Approx( d ) == 1.23 for: Approx( 1.23 ) == 1.23 +Approx.tests.cpp:: passed: Approx( d ) != 1.22 for: Approx( 1.23 ) != 1.22 +Approx.tests.cpp:: passed: Approx( d ) != 1.24 for: Approx( 1.23 ) != 1.24 +Approx.tests.cpp:: passed: INFINITY == Approx(INFINITY) for: inff == Approx( inf ) +Message from section one +Message from section two +Matchers.tests.cpp:: failed: testStringForMatching(), StartsWith("This String") for: "this string contains 'abc' as a substring" starts with: "This String" +Matchers.tests.cpp:: failed: testStringForMatching(), StartsWith("string", Catch::CaseSensitive::No) for: "this string contains 'abc' as a substring" starts with: "string" (case insensitive) +Matchers.tests.cpp:: passed: testStringForMatching(), Contains("string") for: "this string contains 'abc' as a substring" contains: "string" +Matchers.tests.cpp:: passed: testStringForMatching(), Contains("string", Catch::CaseSensitive::No) for: "this string contains 'abc' as a substring" contains: "string" (case insensitive) +Matchers.tests.cpp:: passed: testStringForMatching(), Contains("abc") for: "this string contains 'abc' as a substring" contains: "abc" +Matchers.tests.cpp:: passed: testStringForMatching(), Contains("aBC", Catch::CaseSensitive::No) for: "this string contains 'abc' as a substring" contains: "abc" (case insensitive) +Matchers.tests.cpp:: passed: testStringForMatching(), StartsWith("this") for: "this string contains 'abc' as a substring" starts with: "this" +Matchers.tests.cpp:: passed: testStringForMatching(), StartsWith("THIS", Catch::CaseSensitive::No) for: "this string contains 'abc' as a substring" starts with: "this" (case insensitive) +Matchers.tests.cpp:: passed: testStringForMatching(), EndsWith("substring") for: "this string contains 'abc' as a substring" ends with: "substring" +Matchers.tests.cpp:: passed: testStringForMatching(), EndsWith(" SuBsTrInG", Catch::CaseSensitive::No) for: "this string contains 'abc' as a substring" ends with: " substring" (case insensitive) +String.tests.cpp:: passed: empty.empty() for: true +String.tests.cpp:: passed: empty.size() == 0 for: 0 == 0 +String.tests.cpp:: passed: std::strcmp( empty.c_str(), "" ) == 0 for: 0 == 0 +String.tests.cpp:: passed: s.empty() == false for: false == false +String.tests.cpp:: passed: s.size() == 5 for: 5 == 5 +String.tests.cpp:: passed: isSubstring( s ) == false for: false == false +String.tests.cpp:: passed: std::strcmp( rawChars, "hello" ) == 0 for: 0 == 0 +String.tests.cpp:: passed: isOwned( s ) == false for: false == false +String.tests.cpp:: passed: s.c_str() == rawChars for: "hello" == "hello" +String.tests.cpp:: passed: isOwned( s ) == false for: false == false +String.tests.cpp:: passed: original == "original" +String.tests.cpp:: failed: isSubstring( original ) for: false +String.tests.cpp:: passed: ss.empty() == false for: false == false +String.tests.cpp:: passed: ss.size() == 5 for: 5 == 5 +String.tests.cpp:: passed: std::strcmp( ss.c_str(), "hello" ) == 0 for: 0 == 0 +String.tests.cpp:: passed: ss == "hello" for: hello == "hello" +String.tests.cpp:: passed: isSubstring( ss ) for: true +String.tests.cpp:: passed: isOwned( ss ) == false for: false == false +String.tests.cpp:: passed: rawChars == data( s ) for: "hello world!" == "hello world!" +String.tests.cpp:: passed: ss.c_str() != rawChars for: "hello" != "hello world!" +String.tests.cpp:: passed: isSubstring( ss ) == false for: false == false +String.tests.cpp:: passed: isOwned( ss ) for: true +String.tests.cpp:: passed: data( ss ) != data( s ) for: "hello" != "hello world!" +String.tests.cpp:: passed: ss.size() == 6 for: 6 == 6 +String.tests.cpp:: passed: std::strcmp( ss.c_str(), "world!" ) == 0 for: 0 == 0 +String.tests.cpp:: passed: s.c_str() == s2.c_str() for: "hello world!" == "hello world!" +String.tests.cpp:: passed: s.c_str() != ss.c_str() for: "hello world!" != "hello" +String.tests.cpp:: passed: StringRef("hello") == StringRef("hello") for: hello == hello +String.tests.cpp:: passed: StringRef("hello") != StringRef("cello") for: hello != cello +String.tests.cpp:: passed: sr == "a standard string" for: a standard string == "a standard string" +String.tests.cpp:: passed: sr.size() == stdStr.size() for: 17 == 17 +String.tests.cpp:: passed: sr == "a standard string" for: a standard string == "a standard string" +String.tests.cpp:: passed: sr.size() == stdStr.size() for: 17 == 17 +String.tests.cpp:: passed: sr == "a standard string" for: a standard string == "a standard string" +String.tests.cpp:: passed: sr.size() == stdStr.size() for: 17 == 17 +String.tests.cpp:: passed: stdStr == "a stringref" for: "a stringref" == "a stringref" +String.tests.cpp:: passed: stdStr.size() == sr.size() for: 11 == 11 +String.tests.cpp:: passed: stdStr == "a stringref" for: "a stringref" == "a stringref" +String.tests.cpp:: passed: stdStr.size() == sr.size() for: 11 == 11 +String.tests.cpp:: passed: stdStr == "a stringref" for: "a stringref" == "a stringref" +String.tests.cpp:: passed: stdStr.size() == sr.size() for: 11 == 11 +ToStringChrono.tests.cpp:: passed: minute == seconds for: 1 m == 60 s +ToStringChrono.tests.cpp:: passed: hour != seconds for: 1 h != 60 s +ToStringChrono.tests.cpp:: passed: micro != milli for: 1 us != 1 ms +ToStringChrono.tests.cpp:: passed: nano != micro for: 1 ns != 1 us +ToStringChrono.tests.cpp:: passed: half_minute != femto_second for: 1 [30/1]s != 1 fs +ToStringChrono.tests.cpp:: passed: pico_second != atto_second for: 1 ps != 1 as +ToStringChrono.tests.cpp:: passed: now != later for: {iso8601-timestamp} +!= +{iso8601-timestamp} +Misc.tests.cpp:: failed: s1 == s2 for: "if ($b == 10) { + $a = 20; +}" +== +"if ($b == 10) { + $a = 20; +} +" +TagAlias.tests.cpp:: passed: what, Contains( "[@zzz]" ) for: "error: tag alias, '[@zzz]' already registered. + First seen at: file:2 + Redefined at: file:10" contains: "[@zzz]" +TagAlias.tests.cpp:: passed: what, Contains( "file" ) for: "error: tag alias, '[@zzz]' already registered. + First seen at: file:2 + Redefined at: file:10" contains: "file" +TagAlias.tests.cpp:: passed: what, Contains( "2" ) for: "error: tag alias, '[@zzz]' already registered. + First seen at: file:2 + Redefined at: file:10" contains: "2" +TagAlias.tests.cpp:: passed: what, Contains( "10" ) for: "error: tag alias, '[@zzz]' already registered. + First seen at: file:2 + Redefined at: file:10" contains: "10" +TagAlias.tests.cpp:: passed: registry.add( "[no ampersat]", "", Catch::SourceLineInfo( "file", 3 ) ) +TagAlias.tests.cpp:: passed: registry.add( "[the @ is not at the start]", "", Catch::SourceLineInfo( "file", 3 ) ) +TagAlias.tests.cpp:: passed: registry.add( "@no square bracket at start]", "", Catch::SourceLineInfo( "file", 3 ) ) +TagAlias.tests.cpp:: passed: registry.add( "[@no square bracket at end", "", Catch::SourceLineInfo( "file", 3 ) ) +VariadicMacros.tests.cpp:: passed: with 1 message: 'no assertions' +Tricky.tests.cpp:: passed: 0x == bit30and31 for: 3221225472 (0x) == 3221225472 +Message.tests.cpp:: failed - but was ok: 1 == 2 +Misc.tests.cpp:: passed: with 1 message: 'oops!' +PartTracker.tests.cpp:: passed: testCase.isOpen() for: true +PartTracker.tests.cpp:: passed: s1.isOpen() for: true +PartTracker.tests.cpp:: passed: s1.isSuccessfullyCompleted() for: true +PartTracker.tests.cpp:: passed: testCase.isComplete() == false for: false == false +PartTracker.tests.cpp:: passed: ctx.completedCycle() for: true +PartTracker.tests.cpp:: passed: testCase.isSuccessfullyCompleted() for: true +PartTracker.tests.cpp:: passed: testCase.isOpen() for: true +PartTracker.tests.cpp:: passed: s1.isOpen() for: true +PartTracker.tests.cpp:: passed: s1.isComplete() for: true +PartTracker.tests.cpp:: passed: s1.isSuccessfullyCompleted() == false for: false == false +PartTracker.tests.cpp:: passed: testCase.isComplete() == false for: false == false +PartTracker.tests.cpp:: passed: ctx.completedCycle() for: true +PartTracker.tests.cpp:: passed: testCase.isSuccessfullyCompleted() == false for: false == false +PartTracker.tests.cpp:: passed: testCase2.isOpen() for: true +PartTracker.tests.cpp:: passed: s1b.isOpen() == false for: false == false +PartTracker.tests.cpp:: passed: ctx.completedCycle() for: true +PartTracker.tests.cpp:: passed: testCase.isComplete() for: true +PartTracker.tests.cpp:: passed: testCase.isSuccessfullyCompleted() for: true +PartTracker.tests.cpp:: passed: testCase.isOpen() for: true +PartTracker.tests.cpp:: passed: s1.isOpen() for: true +PartTracker.tests.cpp:: passed: s1.isComplete() for: true +PartTracker.tests.cpp:: passed: s1.isSuccessfullyCompleted() == false for: false == false +PartTracker.tests.cpp:: passed: testCase.isComplete() == false for: false == false +PartTracker.tests.cpp:: passed: ctx.completedCycle() for: true +PartTracker.tests.cpp:: passed: testCase.isSuccessfullyCompleted() == false for: false == false +PartTracker.tests.cpp:: passed: testCase2.isOpen() for: true +PartTracker.tests.cpp:: passed: s1b.isOpen() == false for: false == false +PartTracker.tests.cpp:: passed: s2.isOpen() for: true +PartTracker.tests.cpp:: passed: ctx.completedCycle() for: true +PartTracker.tests.cpp:: passed: testCase.isComplete() for: true +PartTracker.tests.cpp:: passed: testCase.isSuccessfullyCompleted() for: true +PartTracker.tests.cpp:: passed: testCase.isOpen() for: true +PartTracker.tests.cpp:: passed: s1.isOpen() for: true +PartTracker.tests.cpp:: passed: s2.isOpen() == false for: false == false +PartTracker.tests.cpp:: passed: testCase.isComplete() == false for: false == false +PartTracker.tests.cpp:: passed: testCase2.isOpen() for: true +PartTracker.tests.cpp:: passed: s1b.isOpen() == false for: false == false +PartTracker.tests.cpp:: passed: s2b.isOpen() for: true +PartTracker.tests.cpp:: passed: ctx.completedCycle() == false for: false == false +PartTracker.tests.cpp:: passed: ctx.completedCycle() for: true +PartTracker.tests.cpp:: passed: s2b.isSuccessfullyCompleted() for: true +PartTracker.tests.cpp:: passed: testCase2.isComplete() == false for: false == false +PartTracker.tests.cpp:: passed: testCase2.isSuccessfullyCompleted() for: true +PartTracker.tests.cpp:: passed: testCase.isOpen() for: true +PartTracker.tests.cpp:: passed: s1.isOpen() for: true +PartTracker.tests.cpp:: passed: s2.isOpen() == false for: false == false +PartTracker.tests.cpp:: passed: testCase.isComplete() == false for: false == false +PartTracker.tests.cpp:: passed: testCase2.isOpen() for: true +PartTracker.tests.cpp:: passed: s1b.isOpen() == false for: false == false +PartTracker.tests.cpp:: passed: s2b.isOpen() for: true +PartTracker.tests.cpp:: passed: ctx.completedCycle() == false for: false == false +PartTracker.tests.cpp:: passed: ctx.completedCycle() for: true +PartTracker.tests.cpp:: passed: s2b.isComplete() for: true +PartTracker.tests.cpp:: passed: s2b.isSuccessfullyCompleted() == false for: false == false +PartTracker.tests.cpp:: passed: testCase2.isSuccessfullyCompleted() == false for: false == false +PartTracker.tests.cpp:: passed: testCase3.isOpen() for: true +PartTracker.tests.cpp:: passed: s1c.isOpen() == false for: false == false +PartTracker.tests.cpp:: passed: s2c.isOpen() == false for: false == false +PartTracker.tests.cpp:: passed: testCase3.isSuccessfullyCompleted() for: true +PartTracker.tests.cpp:: passed: testCase.isOpen() for: true +PartTracker.tests.cpp:: passed: s1.isOpen() for: true +PartTracker.tests.cpp:: passed: s2.isOpen() for: true +PartTracker.tests.cpp:: passed: s2.isComplete() for: true +PartTracker.tests.cpp:: passed: s1.isComplete() == false for: false == false +PartTracker.tests.cpp:: passed: s1.isComplete() for: true +PartTracker.tests.cpp:: passed: testCase.isComplete() == false for: false == false +PartTracker.tests.cpp:: passed: testCase.isComplete() for: true +PartTracker.tests.cpp:: passed: testCase.isOpen() for: true +PartTracker.tests.cpp:: passed: s1.isOpen() for: true +PartTracker.tests.cpp:: passed: g1.isOpen() for: true +PartTracker.tests.cpp:: passed: g1.index() == 0 for: 0 == 0 +PartTracker.tests.cpp:: passed: g1.isComplete() == false for: false == false +PartTracker.tests.cpp:: passed: s1.isComplete() == false for: false == false +PartTracker.tests.cpp:: passed: s1.isComplete() == false for: false == false +PartTracker.tests.cpp:: passed: testCase.isSuccessfullyCompleted() == false for: false == false +PartTracker.tests.cpp:: passed: testCase2.isOpen() for: true +PartTracker.tests.cpp:: passed: s1b.isOpen() for: true +PartTracker.tests.cpp:: passed: g1b.isOpen() for: true +PartTracker.tests.cpp:: passed: g1b.index() == 1 for: 1 == 1 +PartTracker.tests.cpp:: passed: s1.isComplete() == false for: false == false +PartTracker.tests.cpp:: passed: s1b.isComplete() for: true +PartTracker.tests.cpp:: passed: g1b.isComplete() for: true +PartTracker.tests.cpp:: passed: testCase2.isComplete() for: true +PartTracker.tests.cpp:: passed: testCase.isOpen() for: true +PartTracker.tests.cpp:: passed: s1.isOpen() for: true +PartTracker.tests.cpp:: passed: g1.isOpen() for: true +PartTracker.tests.cpp:: passed: g1.index() == 0 for: 0 == 0 +PartTracker.tests.cpp:: passed: g1.isComplete() == false for: false == false +PartTracker.tests.cpp:: passed: s1.isComplete() == false for: false == false +PartTracker.tests.cpp:: passed: s2.isOpen() for: true +PartTracker.tests.cpp:: passed: s2.isComplete() for: true +PartTracker.tests.cpp:: passed: s1.isComplete() == false for: false == false +PartTracker.tests.cpp:: passed: testCase.isComplete() == false for: false == false +PartTracker.tests.cpp:: passed: testCase2.isOpen() for: true +PartTracker.tests.cpp:: passed: s1b.isOpen() for: true +PartTracker.tests.cpp:: passed: g1b.isOpen() for: true +PartTracker.tests.cpp:: passed: g1b.index() == 1 for: 1 == 1 +PartTracker.tests.cpp:: passed: s2b.isOpen() for: true +PartTracker.tests.cpp:: passed: s2b.isComplete() for: true +PartTracker.tests.cpp:: passed: g1b.isComplete() for: true +PartTracker.tests.cpp:: passed: s1b.isComplete() for: true +PartTracker.tests.cpp:: passed: testCase2.isComplete() for: true +PartTracker.tests.cpp:: passed: testCase.isOpen() for: true +PartTracker.tests.cpp:: passed: s1.isOpen() for: true +PartTracker.tests.cpp:: passed: g1.isOpen() for: true +PartTracker.tests.cpp:: passed: g1.index() == 0 for: 0 == 0 +PartTracker.tests.cpp:: passed: g1.isComplete() == false for: false == false +PartTracker.tests.cpp:: passed: s1.isComplete() == false for: false == false +PartTracker.tests.cpp:: passed: s2.isOpen() for: true +PartTracker.tests.cpp:: passed: s2.isComplete() for: true +PartTracker.tests.cpp:: passed: s2.isSuccessfullyCompleted() == false for: false == false +PartTracker.tests.cpp:: passed: s1.isComplete() == false for: false == false +PartTracker.tests.cpp:: passed: testCase.isComplete() == false for: false == false +PartTracker.tests.cpp:: passed: testCase2.isOpen() for: true +PartTracker.tests.cpp:: passed: s1b.isOpen() for: true +PartTracker.tests.cpp:: passed: g1b.isOpen() for: true +PartTracker.tests.cpp:: passed: g1b.index() == 0 for: 0 == 0 +PartTracker.tests.cpp:: passed: s2b.isOpen() == false for: false == false +PartTracker.tests.cpp:: passed: g1b.isComplete() == false for: false == false +PartTracker.tests.cpp:: passed: s1b.isComplete() == false for: false == false +PartTracker.tests.cpp:: passed: testCase2.isComplete() == false for: false == false +PartTracker.tests.cpp:: passed: testCase3.isOpen() for: true +PartTracker.tests.cpp:: passed: s1c.isOpen() for: true +PartTracker.tests.cpp:: passed: g1c.isOpen() for: true +PartTracker.tests.cpp:: passed: g1c.index() == 1 for: 1 == 1 +PartTracker.tests.cpp:: passed: s2c.isOpen() for: true +PartTracker.tests.cpp:: passed: s2c.isComplete() for: true +PartTracker.tests.cpp:: passed: g1c.isComplete() for: true +PartTracker.tests.cpp:: passed: s1c.isComplete() for: true +PartTracker.tests.cpp:: passed: testCase3.isComplete() for: true +Exception.tests.cpp:: failed: unexpected exception with message: '3.14' +Approx.tests.cpp:: passed: d == approx( 1.23 ) for: 1.23 == Approx( 1.23 ) +Approx.tests.cpp:: passed: d == approx( 1.22 ) for: 1.23 == Approx( 1.22 ) +Approx.tests.cpp:: passed: d == approx( 1.24 ) for: 1.23 == Approx( 1.24 ) +Approx.tests.cpp:: passed: d != approx( 1.25 ) for: 1.23 != Approx( 1.25 ) +Approx.tests.cpp:: passed: approx( d ) == 1.23 for: Approx( 1.23 ) == 1.23 +Approx.tests.cpp:: passed: approx( d ) == 1.22 for: Approx( 1.23 ) == 1.22 +Approx.tests.cpp:: passed: approx( d ) == 1.24 for: Approx( 1.23 ) == 1.24 +Approx.tests.cpp:: passed: approx( d ) != 1.25 for: Approx( 1.23 ) != 1.25 +VariadicMacros.tests.cpp:: passed: with 1 message: 'no assertions' +Matchers.tests.cpp:: passed: v, VectorContains(1) for: { 1, 2, 3 } Contains: 1 +Matchers.tests.cpp:: passed: v, VectorContains(2) for: { 1, 2, 3 } Contains: 2 +Matchers.tests.cpp:: passed: v, Contains(v2) for: { 1, 2, 3 } Contains: { 1, 2 } +Matchers.tests.cpp:: passed: v, Contains(v2) for: { 1, 2, 3 } Contains: { 1, 2, 3 } +Matchers.tests.cpp:: passed: v, Contains(empty) for: { 1, 2, 3 } Contains: { } +Matchers.tests.cpp:: passed: empty, Contains(empty) for: { } Contains: { } +Matchers.tests.cpp:: passed: v, VectorContains(1) && VectorContains(2) for: { 1, 2, 3 } ( Contains: 1 and Contains: 2 ) +Matchers.tests.cpp:: passed: v, Equals(v) for: { 1, 2, 3 } Equals: { 1, 2, 3 } +Matchers.tests.cpp:: passed: empty, Equals(empty) for: { } Equals: { } +Matchers.tests.cpp:: passed: v, Equals(v2) for: { 1, 2, 3 } Equals: { 1, 2, 3 } +Matchers.tests.cpp:: passed: v, UnorderedEquals(v) for: { 1, 2, 3 } UnorderedEquals: { 1, 2, 3 } +Matchers.tests.cpp:: passed: empty, UnorderedEquals(empty) for: { } UnorderedEquals: { } +Matchers.tests.cpp:: passed: permuted, UnorderedEquals(v) for: { 1, 3, 2 } UnorderedEquals: { 1, 2, 3 } +Matchers.tests.cpp:: passed: permuted, UnorderedEquals(v) for: { 2, 3, 1 } UnorderedEquals: { 1, 2, 3 } +Matchers.tests.cpp:: failed: v, VectorContains(-1) for: { 1, 2, 3 } Contains: -1 +Matchers.tests.cpp:: failed: empty, VectorContains(1) for: { } Contains: 1 +Matchers.tests.cpp:: failed: empty, Contains(v) for: { } Contains: { 1, 2, 3 } +Matchers.tests.cpp:: failed: v, Contains(v2) for: { 1, 2, 3 } Contains: { 1, 2, 4 } +Matchers.tests.cpp:: failed: v, Equals(v2) for: { 1, 2, 3 } Equals: { 1, 2 } +Matchers.tests.cpp:: failed: v2, Equals(v) for: { 1, 2 } Equals: { 1, 2, 3 } +Matchers.tests.cpp:: failed: empty, Equals(v) for: { } Equals: { 1, 2, 3 } +Matchers.tests.cpp:: failed: v, Equals(empty) for: { 1, 2, 3 } Equals: { } +Matchers.tests.cpp:: failed: v, UnorderedEquals(empty) for: { 1, 2, 3 } UnorderedEquals: { } +Matchers.tests.cpp:: failed: empty, UnorderedEquals(v) for: { } UnorderedEquals: { 1, 2, 3 } +Matchers.tests.cpp:: failed: permuted, UnorderedEquals(v) for: { 1, 3 } UnorderedEquals: { 1, 2, 3 } +Matchers.tests.cpp:: failed: permuted, UnorderedEquals(v) for: { 3, 1 } UnorderedEquals: { 1, 2, 3 } +Exception.tests.cpp:: passed: thisThrows(), std::domain_error +Exception.tests.cpp:: passed: thisDoesntThrow() +Exception.tests.cpp:: passed: thisThrows() +Exception.tests.cpp:: failed: unexpected exception with message: 'unexpected exception' +Exception.tests.cpp:: failed: unexpected exception with message: 'expected exception'; expression was: thisThrows() == 0 +Exception.tests.cpp:: failed: unexpected exception with message: 'expected exception'; expression was: thisThrows() == 0 +Exception.tests.cpp:: failed: unexpected exception with message: 'expected exception'; expression was: thisThrows() == 0 +Exception.tests.cpp:: failed: unexpected exception with message: 'unexpected exception' +Tricky.tests.cpp:: warning: 'Uncomment the code in this test to check that it gives a sensible compiler error' +Tricky.tests.cpp:: warning: 'Uncomment the code in this test to check that it gives a sensible compiler error' +Tricky.tests.cpp:: passed: +Tricky.tests.cpp:: passed: +Tricky.tests.cpp:: passed: +Tricky.tests.cpp:: passed: +Xml.tests.cpp:: passed: encode( "normal string" ) == "normal string" for: "normal string" == "normal string" +Xml.tests.cpp:: passed: encode( "" ) == "" for: "" == "" +Xml.tests.cpp:: passed: encode( "smith & jones" ) == "smith & jones" for: "smith & jones" == "smith & jones" +Xml.tests.cpp:: passed: encode( "smith < jones" ) == "smith < jones" for: "smith < jones" == "smith < jones" +Xml.tests.cpp:: passed: encode( "smith > jones" ) == "smith > jones" for: "smith > jones" == "smith > jones" +Xml.tests.cpp:: passed: encode( "smith ]]> jones" ) == "smith ]]> jones" for: "smith ]]> jones" +== +"smith ]]> jones" +Xml.tests.cpp:: passed: encode( stringWithQuotes ) == stringWithQuotes for: "don't "quote" me on that" +== +"don't "quote" me on that" +Xml.tests.cpp:: passed: encode( stringWithQuotes, Catch::XmlEncode::ForAttributes ) == "don't "quote" me on that" for: "don't "quote" me on that" +== +"don't "quote" me on that" +Xml.tests.cpp:: passed: encode( "[/x01]" ) == "[//x01]" for: "[/x01]" == "[/x01]" +Xml.tests.cpp:: passed: encode( "[/x7F]" ) == "[//x7F]" for: "[/x7F]" == "[/x7F]" +Misc.tests.cpp:: passed: x == 0 for: 0 == 0 +Tricky.tests.cpp:: passed: obj.prop != 0 for: 0x != 0 +Misc.tests.cpp:: passed: flag for: true +Misc.tests.cpp:: passed: testCheckedElse( true ) for: true +Misc.tests.cpp:: failed: flag for: false +Misc.tests.cpp:: failed: testCheckedElse( false ) for: false +Misc.tests.cpp:: passed: flag for: true +Misc.tests.cpp:: passed: testCheckedIf( true ) for: true +Misc.tests.cpp:: failed: flag for: false +Misc.tests.cpp:: failed: testCheckedIf( false ) for: false +Condition.tests.cpp:: passed: unsigned_char_var == 1 for: 1 == 1 +Condition.tests.cpp:: passed: unsigned_short_var == 1 for: 1 == 1 +Condition.tests.cpp:: passed: unsigned_int_var == 1 for: 1 == 1 +Condition.tests.cpp:: passed: unsigned_long_var == 1 for: 1 == 1 +Condition.tests.cpp:: passed: long_var == unsigned_char_var for: 1 == 1 +Condition.tests.cpp:: passed: long_var == unsigned_short_var for: 1 == 1 +Condition.tests.cpp:: passed: long_var == unsigned_int_var for: 1 == 1 +Condition.tests.cpp:: passed: long_var == unsigned_long_var for: 1 == 1 +Misc.tests.cpp:: passed: +Misc.tests.cpp:: passed: +Misc.tests.cpp:: passed: +loose text artifact +Message.tests.cpp:: failed: explicitly with 1 message: 'Previous info should not be seen' +Misc.tests.cpp:: passed: l == std::numeric_limits::max() for: 9223372036854775807 (0x) +== +9223372036854775807 (0x) +Misc.tests.cpp:: failed: b > a for: 0 > 1 +Misc.tests.cpp:: failed: ( fib[i] % 2 ) == 0 for: 1 == 0 with 1 message: 'Testing if fib[0] (1) is even' +Misc.tests.cpp:: failed: ( fib[i] % 2 ) == 0 for: 1 == 0 with 1 message: 'Testing if fib[1] (1) is even' +Misc.tests.cpp:: passed: ( fib[i] % 2 ) == 0 for: 0 == 0 with 1 message: 'Testing if fib[2] (2) is even' +Misc.tests.cpp:: failed: ( fib[i] % 2 ) == 0 for: 1 == 0 with 1 message: 'Testing if fib[3] (3) is even' +Misc.tests.cpp:: failed: ( fib[i] % 2 ) == 0 for: 1 == 0 with 1 message: 'Testing if fib[4] (5) is even' +Misc.tests.cpp:: passed: ( fib[i] % 2 ) == 0 for: 0 == 0 with 1 message: 'Testing if fib[5] (8) is even' +Misc.tests.cpp:: failed: ( fib[i] % 2 ) == 0 for: 1 == 0 with 1 message: 'Testing if fib[6] (13) is even' +Misc.tests.cpp:: failed: ( fib[i] % 2 ) == 0 for: 1 == 0 with 1 message: 'Testing if fib[7] (21) is even' +Misc.tests.cpp:: failed: a == b for: 1 == 2 +Misc.tests.cpp:: passed: a != b for: 1 != 2 +Misc.tests.cpp:: passed: a < b for: 1 < 2 +Misc.tests.cpp:: passed: a != b for: 1 != 2 +Misc.tests.cpp:: passed: b != a for: 2 != 1 +Misc.tests.cpp:: passed: a != b for: 1 != 2 +Tricky.tests.cpp:: passed: s == "7" for: "7" == "7" +Tricky.tests.cpp:: passed: ti == typeid(int) for: {?} == {?} +Misc.tests.cpp:: passed: +Misc.tests.cpp:: passed: makeString( false ) != static_cast(0) for: "valid string" != {null string} +Misc.tests.cpp:: passed: makeString( true ) == static_cast(0) for: {null string} == {null string} +Tricky.tests.cpp:: passed: ptr.get() == 0 for: 0 == 0 +ToStringPair.tests.cpp:: passed: ::Catch::Detail::stringify( pair ) == "{ { 42, /"Arthur/" }, { /"Ford/", 24 } }" for: "{ { 42, "Arthur" }, { "Ford", 24 } }" +== +"{ { 42, "Arthur" }, { "Ford", 24 } }" +Tricky.tests.cpp:: passed: p == 0 for: 0 == 0 +Misc.tests.cpp:: passed: a != b for: 1 != 2 +Misc.tests.cpp:: passed: b != a for: 2 != 1 +Misc.tests.cpp:: passed: a != b for: 1 != 2 +String.tests.cpp:: passed: Catch::replaceInPlace( letters, "b", "z" ) for: true +String.tests.cpp:: passed: letters == "azcdefcg" for: "azcdefcg" == "azcdefcg" +String.tests.cpp:: passed: Catch::replaceInPlace( letters, "c", "z" ) for: true +String.tests.cpp:: passed: letters == "abzdefzg" for: "abzdefzg" == "abzdefzg" +String.tests.cpp:: passed: Catch::replaceInPlace( letters, "a", "z" ) for: true +String.tests.cpp:: passed: letters == "zbcdefcg" for: "zbcdefcg" == "zbcdefcg" +String.tests.cpp:: passed: Catch::replaceInPlace( letters, "g", "z" ) for: true +String.tests.cpp:: passed: letters == "abcdefcz" for: "abcdefcz" == "abcdefcz" +String.tests.cpp:: passed: Catch::replaceInPlace( letters, letters, "replaced" ) for: true +String.tests.cpp:: passed: letters == "replaced" for: "replaced" == "replaced" +String.tests.cpp:: passed: !(Catch::replaceInPlace( letters, "x", "z" )) for: !false +String.tests.cpp:: passed: letters == letters for: "abcdefcg" == "abcdefcg" +String.tests.cpp:: passed: Catch::replaceInPlace( s, "'", "|'" ) for: true +String.tests.cpp:: passed: s == "didn|'t" for: "didn|'t" == "didn|'t" +Misc.tests.cpp:: failed: false with 1 message: '3' +Message.tests.cpp:: failed: false with 2 messages: 'hi' and 'i := 7' +ToStringPair.tests.cpp:: passed: ::Catch::Detail::stringify(value) == "{ 34, /"xyzzy/" }" for: "{ 34, "xyzzy" }" == "{ 34, "xyzzy" }" +ToStringPair.tests.cpp:: passed: ::Catch::Detail::stringify( value ) == "{ 34, /"xyzzy/" }" for: "{ 34, "xyzzy" }" == "{ 34, "xyzzy" }" +ToStringPair.tests.cpp:: passed: ::Catch::Detail::stringify( pr ) == "{ { /"green/", 55 } }" for: "{ { "green", 55 } }" +== +"{ { "green", 55 } }" +Tricky.tests.cpp:: failed: std::string( "first" ) == "second" for: "first" == "second" +ToStringWhich.tests.cpp:: passed: ::Catch::Detail::stringify( item ) == "StringMaker" for: "StringMaker" +== +"StringMaker" +ToStringWhich.tests.cpp:: passed: ::Catch::Detail::stringify( item ) == "StringMaker" for: "StringMaker" +== +"StringMaker" +ToStringWhich.tests.cpp:: passed: ::Catch::Detail::stringify( item ) == "operator<<( has_operator )" for: "operator<<( has_operator )" +== +"operator<<( has_operator )" +Misc.tests.cpp:: passed: result == "/"wide load/"" for: ""wide load"" == ""wide load"" +Misc.tests.cpp:: passed: result == "/"wide load/"" for: ""wide load"" == ""wide load"" +Misc.tests.cpp:: passed: result == "/"wide load/"" for: ""wide load"" == ""wide load"" +Misc.tests.cpp:: passed: result == "/"wide load/"" for: ""wide load"" == ""wide load"" +ToStringWhich.tests.cpp:: passed: ::Catch::Detail::stringify( v ) == "{ StringMaker }" for: "{ StringMaker }" +== +"{ StringMaker }" +ToStringWhich.tests.cpp:: passed: ::Catch::Detail::stringify( v ) == "{ StringMaker }" for: "{ StringMaker }" +== +"{ StringMaker }" +ToStringWhich.tests.cpp:: passed: ::Catch::Detail::stringify( v ) == "{ operator<<( has_operator ) }" for: "{ operator<<( has_operator ) }" +== +"{ operator<<( has_operator ) }" +EnumToString.tests.cpp:: passed: ::Catch::Detail::stringify(e0) == "E2/V0" for: "E2/V0" == "E2/V0" +EnumToString.tests.cpp:: passed: ::Catch::Detail::stringify(e1) == "E2/V1" for: "E2/V1" == "E2/V1" +EnumToString.tests.cpp:: passed: ::Catch::Detail::stringify(e3) == "Unknown enum value 10" for: "Unknown enum value 10" +== +"Unknown enum value 10" +EnumToString.tests.cpp:: failed: ::Catch::Detail::stringify(e0) == "0" for: "{?}" == "0" +EnumToString.tests.cpp:: failed: ::Catch::Detail::stringify(e1) == "1" for: "{?}" == "1" +EnumToString.tests.cpp:: passed: ::Catch::Detail::stringify(e0) == "E2{0}" for: "E2{0}" == "E2{0}" +EnumToString.tests.cpp:: passed: ::Catch::Detail::stringify(e1) == "E2{1}" for: "E2{1}" == "E2{1}" +EnumToString.tests.cpp:: passed: ::Catch::Detail::stringify(e0) == "0" for: "0" == "0" +EnumToString.tests.cpp:: passed: ::Catch::Detail::stringify(e1) == "1" for: "1" == "1" +ToStringTuple.tests.cpp:: passed: "{ }" == ::Catch::Detail::stringify(type{}) for: "{ }" == "{ }" +ToStringTuple.tests.cpp:: passed: "{ }" == ::Catch::Detail::stringify(value) for: "{ }" == "{ }" +ToStringTuple.tests.cpp:: passed: "1.2f" == ::Catch::Detail::stringify(float(1.2)) for: "1.2f" == "1.2f" +ToStringTuple.tests.cpp:: passed: "{ 1.2f, 0 }" == ::Catch::Detail::stringify(type{1.2f,0}) for: "{ 1.2f, 0 }" == "{ 1.2f, 0 }" +ToStringTuple.tests.cpp:: passed: "{ 0 }" == ::Catch::Detail::stringify(type{0}) for: "{ 0 }" == "{ 0 }" +ToStringTuple.tests.cpp:: passed: "{ 0, 42, /"Catch me/" }" == ::Catch::Detail::stringify(value) for: "{ 0, 42, "Catch me" }" +== +"{ 0, 42, "Catch me" }" +ToStringTuple.tests.cpp:: passed: "{ /"hello/", /"world/" }" == ::Catch::Detail::stringify(type{"hello","world"}) for: "{ "hello", "world" }" +== +"{ "hello", "world" }" +ToStringTuple.tests.cpp:: passed: "{ { 42 }, { }, 1.2f }" == ::Catch::Detail::stringify(value) for: "{ { 42 }, { }, 1.2f }" +== +"{ { 42 }, { }, 1.2f }" +ToStringVector.tests.cpp:: passed: ::Catch::Detail::stringify(v) == "{ }" for: "{ }" == "{ }" +ToStringVector.tests.cpp:: passed: ::Catch::Detail::stringify(v) == "{ { /"hello/" }, { /"world/" } }" for: "{ { "hello" }, { "world" } }" +== +"{ { "hello" }, { "world" } }" +ToStringVector.tests.cpp:: passed: ::Catch::Detail::stringify(vv) == "{ }" for: "{ }" == "{ }" +ToStringVector.tests.cpp:: passed: ::Catch::Detail::stringify(vv) == "{ 42 }" for: "{ 42 }" == "{ 42 }" +ToStringVector.tests.cpp:: passed: ::Catch::Detail::stringify(vv) == "{ 42, 250 }" for: "{ 42, 250 }" == "{ 42, 250 }" +ToStringVector.tests.cpp:: passed: ::Catch::Detail::stringify(vv) == "{ }" for: "{ }" == "{ }" +ToStringVector.tests.cpp:: passed: ::Catch::Detail::stringify(vv) == "{ 42 }" for: "{ 42 }" == "{ 42 }" +ToStringVector.tests.cpp:: passed: ::Catch::Detail::stringify(vv) == "{ 42, 250 }" for: "{ 42, 250 }" == "{ 42, 250 }" +ToStringVector.tests.cpp:: passed: ::Catch::Detail::stringify(vv) == "{ }" for: "{ }" == "{ }" +ToStringVector.tests.cpp:: passed: ::Catch::Detail::stringify(vv) == "{ /"hello/" }" for: "{ "hello" }" == "{ "hello" }" +ToStringVector.tests.cpp:: passed: ::Catch::Detail::stringify(vv) == "{ /"hello/", /"world/" }" for: "{ "hello", "world" }" +== +"{ "hello", "world" }" +Misc.tests.cpp:: passed: v.size() == 5 for: 5 == 5 +Misc.tests.cpp:: passed: v.capacity() >= 5 for: 5 >= 5 +Misc.tests.cpp:: passed: v.size() == 10 for: 10 == 10 +Misc.tests.cpp:: passed: v.capacity() >= 10 for: 10 >= 10 +Misc.tests.cpp:: passed: v.size() == 5 for: 5 == 5 +Misc.tests.cpp:: passed: v.capacity() >= 5 for: 5 >= 5 +Misc.tests.cpp:: passed: v.size() == 0 for: 0 == 0 +Misc.tests.cpp:: passed: v.capacity() >= 5 for: 5 >= 5 +Misc.tests.cpp:: passed: v.capacity() == 0 for: 0 == 0 +Misc.tests.cpp:: passed: v.size() == 5 for: 5 == 5 +Misc.tests.cpp:: passed: v.capacity() >= 5 for: 5 >= 5 +Misc.tests.cpp:: passed: v.size() == 5 for: 5 == 5 +Misc.tests.cpp:: passed: v.capacity() >= 10 for: 10 >= 10 +Misc.tests.cpp:: passed: v.size() == 5 for: 5 == 5 +Misc.tests.cpp:: passed: v.capacity() >= 5 for: 5 >= 5 +Misc.tests.cpp:: passed: v.size() == 5 for: 5 == 5 +Misc.tests.cpp:: passed: v.capacity() >= 5 for: 5 >= 5 +Misc.tests.cpp:: passed: +Misc.tests.cpp:: passed: +Failed 50 test cases, failed 110 assertions. + diff --git a/projects/SelfTest/Baselines/console.std.approved.txt b/projects/SelfTest/Baselines/console.std.approved.txt index 13846d4c..d8087755 100644 --- a/projects/SelfTest/Baselines/console.std.approved.txt +++ b/projects/SelfTest/Baselines/console.std.approved.txt @@ -700,6 +700,7 @@ with expansion: A string sent directly to stdout A string sent directly to stderr +A string sent to stderr via clog Message from section one Message from section two ------------------------------------------------------------------------------- @@ -808,6 +809,33 @@ Matchers.tests.cpp:: FAILED: with expansion: { 1, 2, 3 } Equals: { } +------------------------------------------------------------------------------- +Vector matchers that fail + UnorderedEquals +------------------------------------------------------------------------------- +Matchers.tests.cpp: +............................................................................... + +Matchers.tests.cpp:: FAILED: + CHECK_THAT( v, UnorderedEquals(empty) ) +with expansion: + { 1, 2, 3 } UnorderedEquals: { } + +Matchers.tests.cpp:: FAILED: + CHECK_THAT( empty, UnorderedEquals(v) ) +with expansion: + { } UnorderedEquals: { 1, 2, 3 } + +Matchers.tests.cpp:: FAILED: + CHECK_THAT( permuted, UnorderedEquals(v) ) +with expansion: + { 1, 3 } UnorderedEquals: { 1, 2, 3 } + +Matchers.tests.cpp:: FAILED: + CHECK_THAT( permuted, UnorderedEquals(v) ) +with expansion: + { 3, 1 } UnorderedEquals: { 1, 2, 3 } + ------------------------------------------------------------------------------- When unchecked exceptions are thrown directly they are always failures ------------------------------------------------------------------------------- @@ -1052,6 +1080,6 @@ with expansion: "{?}" == "1" =============================================================================== -test cases: 189 | 137 passed | 48 failed | 4 failed as expected -assertions: 952 | 828 passed | 103 failed | 21 failed as expected +test cases: 191 | 139 passed | 48 failed | 4 failed as expected +assertions: 971 | 843 passed | 107 failed | 21 failed as expected diff --git a/projects/SelfTest/Baselines/console.sw.approved.txt b/projects/SelfTest/Baselines/console.sw.approved.txt index 6eac35ed..c755c1f0 100644 --- a/projects/SelfTest/Baselines/console.sw.approved.txt +++ b/projects/SelfTest/Baselines/console.sw.approved.txt @@ -762,9 +762,7 @@ Tricky.tests.cpp: Tricky.tests.cpp:: PASSED: - REQUIRE( Catch::alwaysTrue() ) -with expansion: - true + REQUIRE( true ) ------------------------------------------------------------------------------- Assertions then sections @@ -775,9 +773,7 @@ Tricky.tests.cpp: Tricky.tests.cpp:: PASSED: - REQUIRE( Catch::alwaysTrue() ) -with expansion: - true + REQUIRE( true ) ------------------------------------------------------------------------------- Assertions then sections @@ -789,9 +785,7 @@ Tricky.tests.cpp: Tricky.tests.cpp:: PASSED: - REQUIRE( Catch::alwaysTrue() ) -with expansion: - true + REQUIRE( true ) ------------------------------------------------------------------------------- Assertions then sections @@ -801,9 +795,7 @@ Tricky.tests.cpp: Tricky.tests.cpp:: PASSED: - REQUIRE( Catch::alwaysTrue() ) -with expansion: - true + REQUIRE( true ) ------------------------------------------------------------------------------- Assertions then sections @@ -814,9 +806,7 @@ Tricky.tests.cpp: Tricky.tests.cpp:: PASSED: - REQUIRE( Catch::alwaysTrue() ) -with expansion: - true + REQUIRE( true ) ------------------------------------------------------------------------------- Assertions then sections @@ -828,9 +818,7 @@ Tricky.tests.cpp: Tricky.tests.cpp:: PASSED: - REQUIRE( Catch::alwaysTrue() ) -with expansion: - true + REQUIRE( true ) ------------------------------------------------------------------------------- Assorted miscellaneous tests @@ -1153,13 +1141,13 @@ with expansion: Approx.tests.cpp:: PASSED: - REQUIRE( td >= Approx(10.0) ) + REQUIRE( td >= Approx(td) ) with expansion: StrongDoubleTypedef(10) >= Approx( 10.0 ) Approx.tests.cpp:: PASSED: - REQUIRE( Approx(10.0) >= td ) + REQUIRE( Approx(td) >= td ) with expansion: Approx( 10.0 ) >= StrongDoubleTypedef(10) @@ -1884,6 +1872,29 @@ PASSED: with expansion: nanf not ( is within 100.0 of nan or is within 123 ULPs of nanf ) +------------------------------------------------------------------------------- +Floating point matchers: double + Constructor validation +------------------------------------------------------------------------------- +Matchers.tests.cpp: +............................................................................... + +Matchers.tests.cpp:: +PASSED: + REQUIRE_NOTHROW( WithinAbs(1., 0.) ) + +Matchers.tests.cpp:: +PASSED: + REQUIRE_THROWS_AS( WithinAbs(1., -1.), std::domain_error ) + +Matchers.tests.cpp:: +PASSED: + REQUIRE_NOTHROW( WithinULP(1., 0) ) + +Matchers.tests.cpp:: +PASSED: + REQUIRE_THROWS_AS( WithinULP(1., -1), std::domain_error ) + ------------------------------------------------------------------------------- Floating point matchers: float Margin @@ -2001,6 +2012,29 @@ PASSED: with expansion: nanf not ( is within 100.0 of nan or is within 123 ULPs of nanf ) +------------------------------------------------------------------------------- +Floating point matchers: float + Constructor validation +------------------------------------------------------------------------------- +Matchers.tests.cpp: +............................................................................... + +Matchers.tests.cpp:: +PASSED: + REQUIRE_NOTHROW( WithinAbs(1.f, 0.f) ) + +Matchers.tests.cpp:: +PASSED: + REQUIRE_THROWS_AS( WithinAbs(1.f, -1.f), std::domain_error ) + +Matchers.tests.cpp:: +PASSED: + REQUIRE_NOTHROW( WithinULP(1.f, 0) ) + +Matchers.tests.cpp:: +PASSED: + REQUIRE_THROWS_AS( WithinULP(1.f, -1), std::domain_error ) + ------------------------------------------------------------------------------- Greater-than inequalities with different epsilons ------------------------------------------------------------------------------- @@ -4585,6 +4619,7 @@ with expansion: A string sent directly to stdout A string sent directly to stderr +A string sent to stderr via clog ------------------------------------------------------------------------------- Some simple comparisons between doubles ------------------------------------------------------------------------------- @@ -5139,6 +5174,12 @@ PASSED: with expansion: 1 [30/1]s != 1 fs +ToStringChrono.tests.cpp:: +PASSED: + REQUIRE( pico_second != atto_second ) +with expansion: + 1 ps != 1 as + ------------------------------------------------------------------------------- Stringifying std::chrono::time_point ------------------------------------------------------------------------------- @@ -6439,6 +6480,37 @@ PASSED: with expansion: { 1, 2, 3 } Equals: { 1, 2, 3 } +------------------------------------------------------------------------------- +Vector matchers + UnorderedEquals +------------------------------------------------------------------------------- +Matchers.tests.cpp: +............................................................................... + +Matchers.tests.cpp:: +PASSED: + CHECK_THAT( v, UnorderedEquals(v) ) +with expansion: + { 1, 2, 3 } UnorderedEquals: { 1, 2, 3 } + +Matchers.tests.cpp:: +PASSED: + CHECK_THAT( empty, UnorderedEquals(empty) ) +with expansion: + { } UnorderedEquals: { } + +Matchers.tests.cpp:: +PASSED: + REQUIRE_THAT( permuted, UnorderedEquals(v) ) +with expansion: + { 1, 3, 2 } UnorderedEquals: { 1, 2, 3 } + +Matchers.tests.cpp:: +PASSED: + REQUIRE_THAT( permuted, UnorderedEquals(v) ) +with expansion: + { 2, 3, 1 } UnorderedEquals: { 1, 2, 3 } + ------------------------------------------------------------------------------- Vector matchers that fail Contains (element) @@ -6500,6 +6572,33 @@ Matchers.tests.cpp:: FAILED: with expansion: { 1, 2, 3 } Equals: { } +------------------------------------------------------------------------------- +Vector matchers that fail + UnorderedEquals +------------------------------------------------------------------------------- +Matchers.tests.cpp: +............................................................................... + +Matchers.tests.cpp:: FAILED: + CHECK_THAT( v, UnorderedEquals(empty) ) +with expansion: + { 1, 2, 3 } UnorderedEquals: { } + +Matchers.tests.cpp:: FAILED: + CHECK_THAT( empty, UnorderedEquals(v) ) +with expansion: + { } UnorderedEquals: { 1, 2, 3 } + +Matchers.tests.cpp:: FAILED: + CHECK_THAT( permuted, UnorderedEquals(v) ) +with expansion: + { 1, 3 } UnorderedEquals: { 1, 2, 3 } + +Matchers.tests.cpp:: FAILED: + CHECK_THAT( permuted, UnorderedEquals(v) ) +with expansion: + { 3, 1 } UnorderedEquals: { 1, 2, 3 } + ------------------------------------------------------------------------------- When checked exceptions are thrown they can be expected or unexpected ------------------------------------------------------------------------------- @@ -7529,7 +7628,7 @@ with expansion: ""wide load"" == ""wide load"" ------------------------------------------------------------------------------- -toString( vectors ) ------------------------------------------------------------------------------- ToStringWhich.tests.cpp: ............................................................................... @@ -7542,6 +7641,34 @@ with expansion: == "{ StringMaker }" +------------------------------------------------------------------------------- +toString( vectors ) +------------------------------------------------------------------------------- +ToStringWhich.tests.cpp: +............................................................................... + +ToStringWhich.tests.cpp:: +PASSED: + REQUIRE( ::Catch::Detail::stringify( v ) == "{ StringMaker }" ) +with expansion: + "{ StringMaker }" + == + "{ StringMaker }" + +------------------------------------------------------------------------------- +toString( vectors ) +------------------------------------------------------------------------------- +ToStringWhich.tests.cpp: +............................................................................... + +ToStringWhich.tests.cpp:: +PASSED: + REQUIRE( ::Catch::Detail::stringify( v ) == "{ operator<<( has_operator ) }" ) +with expansion: + "{ operator<<( has_operator ) }" + == + "{ operator<<( has_operator ) }" + ------------------------------------------------------------------------------- toString(enum class w/operator<<) ------------------------------------------------------------------------------- @@ -7987,6 +8114,6 @@ Misc.tests.cpp:: PASSED: =============================================================================== -test cases: 189 | 135 passed | 50 failed | 4 failed as expected -assertions: 951 | 824 passed | 106 failed | 21 failed as expected +test cases: 191 | 137 passed | 50 failed | 4 failed as expected +assertions: 970 | 839 passed | 110 failed | 21 failed as expected diff --git a/projects/SelfTest/Baselines/junit.sw.approved.txt b/projects/SelfTest/Baselines/junit.sw.approved.txt index 610e3f8e..11baab4d 100644 --- a/projects/SelfTest/Baselines/junit.sw.approved.txt +++ b/projects/SelfTest/Baselines/junit.sw.approved.txt @@ -1,7 +1,7 @@ - + @@ -276,9 +276,11 @@ Message.tests.cpp: + + @@ -517,6 +519,7 @@ A string sent directly to stdout A string sent directly to stderr +A string sent to stderr via clog @@ -605,6 +608,7 @@ Exception.tests.cpp: + Matchers.tests.cpp: @@ -632,6 +636,20 @@ Matchers.tests.cpp: Matchers.tests.cpp: +Matchers.tests.cpp: + + + + +Matchers.tests.cpp: + + +Matchers.tests.cpp: + + +Matchers.tests.cpp: + + Matchers.tests.cpp: @@ -796,7 +814,9 @@ Tricky.tests.cpp: - + + + @@ -833,6 +853,7 @@ Message from section two A string sent directly to stderr +A string sent to stderr via clog diff --git a/projects/SelfTest/Baselines/xml.sw.approved.txt b/projects/SelfTest/Baselines/xml.sw.approved.txt index d194c96e..99274e0f 100644 --- a/projects/SelfTest/Baselines/xml.sw.approved.txt +++ b/projects/SelfTest/Baselines/xml.sw.approved.txt @@ -819,7 +819,7 @@ - Catch::alwaysTrue() + true true @@ -828,7 +828,7 @@
- Catch::alwaysTrue() + true true @@ -837,7 +837,7 @@
- Catch::alwaysTrue() + true true @@ -849,7 +849,7 @@
- Catch::alwaysTrue() + true true @@ -858,7 +858,7 @@
- Catch::alwaysTrue() + true true @@ -867,7 +867,7 @@
- Catch::alwaysTrue() + true true @@ -1273,7 +1273,7 @@ - td >= Approx(10.0) + td >= Approx(td) StrongDoubleTypedef(10) >= Approx( 10.0 ) @@ -1281,7 +1281,7 @@ - Approx(10.0) >= td + Approx(td) >= td Approx( 10.0 ) >= StrongDoubleTypedef(10) @@ -2142,6 +2142,41 @@
+
+ + + WithinAbs(1., 0.) + + + WithinAbs(1., 0.) + + + + + WithinAbs(1., -1.), std::domain_error + + + WithinAbs(1., -1.), std::domain_error + + + + + WithinULP(1., 0) + + + WithinULP(1., 0) + + + + + WithinULP(1., -1), std::domain_error + + + WithinULP(1., -1), std::domain_error + + + +
@@ -2282,6 +2317,41 @@
+
+ + + WithinAbs(1.f, 0.f) + + + WithinAbs(1.f, 0.f) + + + + + WithinAbs(1.f, -1.f), std::domain_error + + + WithinAbs(1.f, -1.f), std::domain_error + + + + + WithinULP(1.f, 0) + + + WithinULP(1.f, 0) + + + + + WithinULP(1.f, -1), std::domain_error + + + WithinULP(1.f, -1), std::domain_error + + + +
@@ -5260,6 +5330,7 @@ A string sent directly to stdout A string sent directly to stderr +A string sent to stderr via clog @@ -5878,6 +5949,14 @@ Message from section two 1 [30/1]s != 1 fs
+ + + pico_second != atto_second + + + 1 ps != 1 as + + @@ -7298,6 +7377,41 @@ Message from section two
+
+ + + v, UnorderedEquals(v) + + + { 1, 2, 3 } UnorderedEquals: { 1, 2, 3 } + + + + + empty, UnorderedEquals(empty) + + + { } UnorderedEquals: { } + + + + + permuted, UnorderedEquals(v) + + + { 1, 3, 2 } UnorderedEquals: { 1, 2, 3 } + + + + + permuted, UnorderedEquals(v) + + + { 2, 3, 1 } UnorderedEquals: { 1, 2, 3 } + + + +
@@ -7374,6 +7488,41 @@ Message from section two +
+ + + v, UnorderedEquals(empty) + + + { 1, 2, 3 } UnorderedEquals: { } + + + + + empty, UnorderedEquals(v) + + + { } UnorderedEquals: { 1, 2, 3 } + + + + + permuted, UnorderedEquals(v) + + + { 1, 3 } UnorderedEquals: { 1, 2, 3 } + + + + + permuted, UnorderedEquals(v) + + + { 3, 1 } UnorderedEquals: { 1, 2, 3 } + + + +
@@ -8409,7 +8558,7 @@ loose text artifact - + ::Catch::Detail::stringify( v ) == "{ StringMaker<has_maker> }" @@ -8422,6 +8571,32 @@ loose text artifact + + + + ::Catch::Detail::stringify( v ) == "{ StringMaker<has_maker_and_operator> }" + + + "{ StringMaker<has_maker_and_operator> }" +== +"{ StringMaker<has_maker_and_operator> }" + + + + + + + + ::Catch::Detail::stringify( v ) == "{ operator<<( has_operator ) }" + + + "{ operator<<( has_operator ) }" +== +"{ operator<<( has_operator ) }" + + + + @@ -8863,7 +9038,7 @@ loose text artifact - + - + diff --git a/projects/SelfTest/UsageTests/Approx.tests.cpp b/projects/SelfTest/UsageTests/Approx.tests.cpp index b3974971..5930075d 100644 --- a/projects/SelfTest/UsageTests/Approx.tests.cpp +++ b/projects/SelfTest/UsageTests/Approx.tests.cpp @@ -189,8 +189,8 @@ TEST_CASE( "Comparison with explicitly convertible types", "[Approx]" ) REQUIRE(Approx(9.0) <= td); REQUIRE(td >= Approx(9.0)); - REQUIRE(td >= Approx(10.0)); - REQUIRE(Approx(10.0) >= td); + REQUIRE(td >= Approx(td)); + REQUIRE(Approx(td) >= td); REQUIRE(Approx(11.0) >= td); } diff --git a/projects/SelfTest/UsageTests/Condition.tests.cpp b/projects/SelfTest/UsageTests/Condition.tests.cpp index 975fd400..9aed5e2c 100644 --- a/projects/SelfTest/UsageTests/Condition.tests.cpp +++ b/projects/SelfTest/UsageTests/Condition.tests.cpp @@ -8,7 +8,10 @@ #ifdef __clang__ # pragma clang diagnostic push # pragma clang diagnostic ignored "-Wpadded" -# pragma clang diagnostic ignored "-Wdouble-promotion" +// Wdouble-promotion is not supported until 3.8 +# if (__clang_major__ > 3) || (__clang_major__ == 3 && __clang_minor__ > 7) +# pragma clang diagnostic ignored "-Wdouble-promotion" +# endif #endif #include "catch.hpp" diff --git a/projects/SelfTest/UsageTests/Exception.tests.cpp b/projects/SelfTest/UsageTests/Exception.tests.cpp index 1100114a..d67ef34c 100644 --- a/projects/SelfTest/UsageTests/Exception.tests.cpp +++ b/projects/SelfTest/UsageTests/Exception.tests.cpp @@ -12,11 +12,12 @@ #include #ifdef _MSC_VER -#pragma warning(disable:4702) // Unreachable code -- MSVC 19 (VS 2015) sees right through the indirection +#pragma warning(disable:4702) // Unreachable code -- uncoditional throws and so on #endif #ifdef __clang__ #pragma clang diagnostic push #pragma clang diagnostic ignored "-Wweak-vtables" +#pragma clang diagnostic ignored "-Wmissing-noreturn" #endif namespace { namespace ExceptionTests { @@ -24,9 +25,8 @@ namespace { namespace ExceptionTests { #ifndef EXCEPTION_TEST_HELPERS_INCLUDED // Don't compile this more than once per TU #define EXCEPTION_TEST_HELPERS_INCLUDED -inline int thisThrows() { - if( Catch::alwaysTrue() ) - throw std::domain_error( "expected exception" ); +int thisThrows() { + throw std::domain_error( "expected exception" ); return 1; } @@ -36,8 +36,8 @@ int thisDoesntThrow() { class CustomException { public: - CustomException( const std::string& msg ) - : m_msg( msg ) + explicit CustomException( const std::string& msg ) + : m_msg( msg ) {} std::string getMessage() const { @@ -50,10 +50,10 @@ private: class CustomStdException : public std::exception { public: - CustomStdException( const std::string& msg ) - : m_msg( msg ) + explicit CustomStdException( const std::string& msg ) + : m_msg( msg ) {} - ~CustomStdException() noexcept {} + ~CustomStdException() noexcept override {} std::string getMessage() const { return m_msg; @@ -63,9 +63,8 @@ private: std::string m_msg; }; -inline void throwCustom() { - if( Catch::alwaysTrue() ) - throw CustomException( "custom exception - not std" ); +[[noreturn]] void throwCustom() { + throw CustomException( "custom exception - not std" ); } #endif @@ -83,20 +82,17 @@ TEST_CASE( "Expected exceptions that don't throw or unexpected exceptions fail t } TEST_CASE( "When unchecked exceptions are thrown directly they are always failures", "[.][failing][!throws]" ) { - if( Catch::alwaysTrue() ) - throw std::domain_error( "unexpected exception" ); + throw std::domain_error( "unexpected exception" ); } TEST_CASE( "An unchecked exception reports the line of the last assertion", "[.][failing][!throws]" ) { CHECK( 1 == 1 ); - if( Catch::alwaysTrue() ) - throw std::domain_error( "unexpected exception" ); + throw std::domain_error( "unexpected exception" ); } TEST_CASE( "When unchecked exceptions are thrown from sections they are always failures", "[.][failing][!throws]" ) { SECTION( "section name" ) { - if( Catch::alwaysTrue() ) - throw std::domain_error( "unexpected exception" ); + throw std::domain_error("unexpected exception"); } } @@ -139,13 +135,11 @@ CATCH_TRANSLATE_EXCEPTION( double& ex ) { } TEST_CASE("Non-std exceptions can be translated", "[.][failing][!throws]" ) { - if( Catch::alwaysTrue() ) - throw CustomException( "custom exception" ); + throw CustomException( "custom exception" ); } TEST_CASE("Custom std-exceptions can be custom translated", "[.][failing][!throws]" ) { - if( Catch::alwaysTrue() ) - throw CustomException( "custom std exception" ); + throw CustomException( "custom std exception" ); } TEST_CASE( "Custom exceptions can be translated when testing for nothrow", "[.][failing][!throws]" ) { @@ -157,8 +151,7 @@ TEST_CASE( "Custom exceptions can be translated when testing for throwing as som } TEST_CASE( "Unexpected exceptions can be translated", "[.][failing][!throws]" ) { - if( Catch::alwaysTrue() ) - throw double( 3.14 ); + throw double( 3.14 ); } #ifndef CATCH_CONFIG_DISABLE_MATCHERS diff --git a/projects/SelfTest/UsageTests/Matchers.tests.cpp b/projects/SelfTest/UsageTests/Matchers.tests.cpp index 34f167ab..3c20caa1 100644 --- a/projects/SelfTest/UsageTests/Matchers.tests.cpp +++ b/projects/SelfTest/UsageTests/Matchers.tests.cpp @@ -9,6 +9,7 @@ #include "catch.hpp" #include +#include #ifdef __clang__ #pragma clang diagnostic push @@ -216,6 +217,17 @@ namespace { namespace MatchersTests { v2.push_back(3); CHECK_THAT(v, Equals(v2)); } + SECTION("UnorderedEquals") { + CHECK_THAT(v, UnorderedEquals(v)); + CHECK_THAT(empty, UnorderedEquals(empty)); + + auto permuted = v; + std::next_permutation(begin(permuted), end(permuted)); + REQUIRE_THAT(permuted, UnorderedEquals(v)); + + std::reverse(begin(permuted), end(permuted)); + REQUIRE_THAT(permuted, UnorderedEquals(v)); + } } TEST_CASE("Vector matchers that fail", "[matchers][vector][.][failing]") { @@ -247,6 +259,18 @@ namespace { namespace MatchersTests { CHECK_THAT(empty, Equals(v)); CHECK_THAT(v, Equals(empty)); } + SECTION("UnorderedEquals") { + CHECK_THAT(v, UnorderedEquals(empty)); + CHECK_THAT(empty, UnorderedEquals(v)); + + auto permuted = v; + std::next_permutation(begin(permuted), end(permuted)); + permuted.pop_back(); + CHECK_THAT(permuted, UnorderedEquals(v)); + + std::reverse(begin(permuted), end(permuted)); + CHECK_THAT(permuted, UnorderedEquals(v)); + } } TEST_CASE("Exception matchers that succeed", "[matchers][exceptions][!throws]") { @@ -298,6 +322,13 @@ namespace { namespace MatchersTests { REQUIRE_THAT(NAN, !(WithinAbs(NAN, 100) || WithinULP(NAN, 123))); } + SECTION("Constructor validation") { + REQUIRE_NOTHROW(WithinAbs(1.f, 0.f)); + REQUIRE_THROWS_AS(WithinAbs(1.f, -1.f), std::domain_error); + + REQUIRE_NOTHROW(WithinULP(1.f, 0)); + REQUIRE_THROWS_AS(WithinULP(1.f, -1), std::domain_error); + } } TEST_CASE("Floating point matchers: double", "[matchers][floating-point]") { @@ -328,6 +359,13 @@ namespace { namespace MatchersTests { REQUIRE_THAT(NAN, !(WithinAbs(NAN, 100) || WithinULP(NAN, 123))); } + SECTION("Constructor validation") { + REQUIRE_NOTHROW(WithinAbs(1., 0.)); + REQUIRE_THROWS_AS(WithinAbs(1., -1.), std::domain_error); + + REQUIRE_NOTHROW(WithinULP(1., 0)); + REQUIRE_THROWS_AS(WithinULP(1., -1), std::domain_error); + } } } } // namespace MatchersTests diff --git a/projects/SelfTest/UsageTests/Misc.tests.cpp b/projects/SelfTest/UsageTests/Misc.tests.cpp index 6a6f1da0..757a99b5 100644 --- a/projects/SelfTest/UsageTests/Misc.tests.cpp +++ b/projects/SelfTest/UsageTests/Misc.tests.cpp @@ -57,7 +57,9 @@ struct AutoTestReg { REGISTER_TEST_CASE( manuallyRegisteredTestFunction, "ManuallyRegistered" ); } }; +CATCH_INTERNAL_SUPPRESS_GLOBALS_WARNINGS static AutoTestReg autoTestReg; +CATCH_INTERNAL_UNSUPPRESS_GLOBALS_WARNINGS #endif @@ -146,8 +148,8 @@ TEST_CASE( "looped tests", "[.][failing]" ) { TEST_CASE( "Sends stuff to stdout and stderr", "[.]" ) { std::cout << "A string sent directly to stdout" << std::endl; - std::cerr << "A string sent directly to stderr" << std::endl; + std::clog << "A string sent to stderr via clog" << std::endl; } TEST_CASE( "null strings" ) { @@ -301,13 +303,13 @@ TEST_CASE( "toString on const wchar_t pointer returns the string contents", "[to } TEST_CASE( "toString on wchar_t const pointer returns the string contents", "[toString]" ) { - wchar_t * const s = const_cast( L"wide load" ); + auto const s = const_cast( L"wide load" ); std::string result = ::Catch::Detail::stringify( s ); CHECK( result == "\"wide load\"" ); } TEST_CASE( "toString on wchar_t returns the string contents", "[toString]" ) { - wchar_t * s = const_cast( L"wide load" ); + auto s = const_cast( L"wide load" ); std::string result = ::Catch::Detail::stringify( s ); CHECK( result == "\"wide load\"" ); } diff --git a/projects/SelfTest/UsageTests/ToStringChrono.tests.cpp b/projects/SelfTest/UsageTests/ToStringChrono.tests.cpp index 97d0c1cb..c2c0829f 100644 --- a/projects/SelfTest/UsageTests/ToStringChrono.tests.cpp +++ b/projects/SelfTest/UsageTests/ToStringChrono.tests.cpp @@ -20,8 +20,11 @@ TEST_CASE("Stringifying std::chrono::duration helpers", "[toString][chrono]") { TEST_CASE("Stringifying std::chrono::duration with weird ratios", "[toString][chrono]") { std::chrono::duration> half_minute(1); + std::chrono::duration> pico_second(1); std::chrono::duration> femto_second(1); + std::chrono::duration> atto_second(1); REQUIRE(half_minute != femto_second); + REQUIRE(pico_second != atto_second); } TEST_CASE("Stringifying std::chrono::time_point", "[toString][chrono]") { diff --git a/projects/SelfTest/UsageTests/ToStringWhich.tests.cpp b/projects/SelfTest/UsageTests/ToStringWhich.tests.cpp index fc658a7a..2bf467cb 100644 --- a/projects/SelfTest/UsageTests/ToStringWhich.tests.cpp +++ b/projects/SelfTest/UsageTests/ToStringWhich.tests.cpp @@ -55,19 +55,19 @@ TEST_CASE( "stringify( has_maker_and_toString )", "[.][toString]" ) { // Vectors... // Don't run this in approval tests as it is sensitive to two phase lookup differences -TEST_CASE( "toString( vectors )", "[toString]" ) { std::vector v(1); REQUIRE( ::Catch::Detail::stringify( v ) == "{ operator<<( has_operator ) }" ); } -TEST_CASE( "toString( vectors )", "[toString]" ) { std::vector v(1); REQUIRE( ::Catch::Detail::stringify( v ) == "{ StringMaker }" ); } // Don't run this in approval tests as it is sensitive to two phase lookup differences -TEST_CASE( "toString( vectors )", "[toString]" ) { std::vector v(1); - REQUIRE( ::Catch::Detail::stringify( v ) == "{ StringMaker }" ); + REQUIRE( ::Catch::Detail::stringify( v ) == "{ StringMaker }" ); } diff --git a/projects/SelfTest/UsageTests/Tricky.tests.cpp b/projects/SelfTest/UsageTests/Tricky.tests.cpp index 79d2c63c..1c352ce9 100644 --- a/projects/SelfTest/UsageTests/Tricky.tests.cpp +++ b/projects/SelfTest/UsageTests/Tricky.tests.cpp @@ -298,19 +298,19 @@ TEST_CASE( "Assertions then sections", "[Tricky]" ) // This was causing a failure due to the way the console reporter was handling // the current section - REQUIRE( Catch::alwaysTrue() ); + REQUIRE( true ); SECTION( "A section" ) { - REQUIRE( Catch::alwaysTrue() ); + REQUIRE( true ); SECTION( "Another section" ) { - REQUIRE( Catch::alwaysTrue() ); + REQUIRE( true ); } SECTION( "Another other section" ) { - REQUIRE( Catch::alwaysTrue() ); + REQUIRE( true ); } } } diff --git a/scripts/approvalTests.py b/scripts/approvalTests.py index 92a40a0e..e16f7140 100755 --- a/scripts/approvalTests.py +++ b/scripts/approvalTests.py @@ -167,6 +167,8 @@ def approve(baseName, args): print("Running approvals against executable:") print(" " + cmdPath) + +### Keep default reporters here # Standard console reporter approve("console.std", ["~[!nonportable]~[!benchmark]~[approvals]", "--order", "lex"]) # console reporter, include passes, warn about No Assertions @@ -177,6 +179,8 @@ approve("console.swa4", ["~[!nonportable]~[!benchmark]~[approvals]", "-s", "-w", approve("junit.sw", ["~[!nonportable]~[!benchmark]~[approvals]", "-s", "-w", "NoAssertions", "-r", "junit", "--order", "lex"]) # xml reporter, include passes, warn about No Assertions approve("xml.sw", ["~[!nonportable]~[!benchmark]~[approvals]", "-s", "-w", "NoAssertions", "-r", "xml", "--order", "lex"]) +# compact reporter, include passes, warn about No Assertions +approve('compact.sw', ['~[!nonportable]~[!benchmark]~[approvals]', '-s', '-w', 'NoAssertions', '-r', 'compact', '--order', 'lex']) if overallResult != 0: print("If these differences are expected, run approve.py to approve new baselines.")