diff --git a/.travis.yml b/.travis.yml index 6195c1e8..bc196146 100644 --- a/.travis.yml +++ b/.travis.yml @@ -5,19 +5,6 @@ matrix: include: # 1/ Linux Clang Builds - - os: linux - compiler: clang - addons: &clang34 - apt: - sources: ['llvm-toolchain-precise', 'ubuntu-toolchain-r-test'] - packages: ['clang'] - env: COMPILER='clang++' BUILD_TYPE='Release' - - - os: linux - compiler: clang - addons: *clang34 - env: COMPILER='clang++' BUILD_TYPE='Debug' - - os: linux compiler: clang addons: &clang35 @@ -75,34 +62,6 @@ matrix: # 2/ Linux GCC Builds - - os: linux - compiler: gcc - addons: &gcc44 - apt: - sources: ['ubuntu-toolchain-r-test'] - packages: ['g++-4.4'] - env: COMPILER='g++-4.4' BUILD_TYPE='Release' - - - os: linux - compiler: gcc - addons: *gcc44 - env: COMPILER='g++-4.4' BUILD_TYPE='Debug' - - - - os: linux - compiler: gcc - addons: &gcc47 - apt: - sources: ['ubuntu-toolchain-r-test'] - packages: ['g++-4.7'] - env: COMPILER='g++-4.7' BUILD_TYPE='Release' - - - os: linux - compiler: gcc - addons: *gcc47 - env: COMPILER='g++-4.7' BUILD_TYPE='Debug' - - - os: linux compiler: gcc addons: &gcc48 @@ -191,7 +150,7 @@ matrix: addons: *gcc6 env: COMPILER='g++-6' BUILD_TYPE='Debug' CPP14=1 - # 4b/ Linux C++14 Clang builds +# # 4b/ Linux C++14 Clang builds # - os: linux # compiler: clang # addons: *clang38 diff --git a/CMakeLists.txt b/CMakeLists.txt index 6e608fa3..41d2f44a 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -9,14 +9,15 @@ set(CATCH_DIR ${CMAKE_CURRENT_SOURCE_DIR}) set(SELF_TEST_DIR ${CATCH_DIR}/projects/SelfTest) set(BENCHMARK_DIR ${CATCH_DIR}/projects/Benchmark) set(HEADER_DIR ${CATCH_DIR}/include) +set(CATCH_VERSION_NUMBER 2.0.1) -if(USE_CPP11) - ## We can't turn this on by default, since it breaks on travis - message(STATUS "Enabling C++11") - set(CMAKE_CXX_FLAGS "-std=c++11 ${CMAKE_CXX_FLAGS}") -elseif(USE_CPP14) +if(USE_CPP14) message(STATUS "Enabling C++14") set(CMAKE_CXX_FLAGS "-std=c++14 ${CMAKE_CXX_FLAGS}") +else() + message(STATUS "Enabling C++11") + set(CMAKE_CXX_FLAGS "-std=c++11 ${CMAKE_CXX_FLAGS}") + endif() if(USE_WMAIN) @@ -55,6 +56,7 @@ endfunction() set(TEST_SOURCES ${SELF_TEST_DIR}/ApproxTests.cpp ${SELF_TEST_DIR}/BDDTests.cpp + ${SELF_TEST_DIR}/Benchmark.tests.cpp ${SELF_TEST_DIR}/ClassTests.cpp ${SELF_TEST_DIR}/CmdLineTests.cpp ${SELF_TEST_DIR}/CompilationTests.cpp @@ -62,12 +64,12 @@ set(TEST_SOURCES ${SELF_TEST_DIR}/DecompositionTests.cpp ${SELF_TEST_DIR}/EnumToString.cpp ${SELF_TEST_DIR}/ExceptionTests.cpp - ${SELF_TEST_DIR}/GeneratorTests.cpp ${SELF_TEST_DIR}/MessageTests.cpp ${SELF_TEST_DIR}/MiscTests.cpp ${SELF_TEST_DIR}/PartTrackerTests.cpp ${SELF_TEST_DIR}/TagAliasTests.cpp ${SELF_TEST_DIR}/TestMain.cpp + ${SELF_TEST_DIR}/ToStringChrono.cpp ${SELF_TEST_DIR}/ToStringGeneralTests.cpp ${SELF_TEST_DIR}/ToStringPair.cpp ${SELF_TEST_DIR}/ToStringTuple.cpp @@ -76,157 +78,195 @@ set(TEST_SOURCES ${SELF_TEST_DIR}/TrickyTests.cpp ${SELF_TEST_DIR}/VariadicMacrosTests.cpp ${SELF_TEST_DIR}/MatchersTests.cpp + ${SELF_TEST_DIR}/StringRef.tests.cpp ) CheckFileList(TEST_SOURCES ${SELF_TEST_DIR}) # A set of impl files that just #include a single header # Please keep these ordered alphabetically -set(IMPL_SOURCES - ${SELF_TEST_DIR}/SurrogateCpps/catch_common.cpp +set(SURROGATE_SOURCES ${SELF_TEST_DIR}/SurrogateCpps/catch_console_colour.cpp ${SELF_TEST_DIR}/SurrogateCpps/catch_debugger.cpp - ${SELF_TEST_DIR}/SurrogateCpps/catch_interfaces_capture.cpp - ${SELF_TEST_DIR}/SurrogateCpps/catch_interfaces_config.cpp - ${SELF_TEST_DIR}/SurrogateCpps/catch_interfaces_exception.cpp - ${SELF_TEST_DIR}/SurrogateCpps/catch_interfaces_generators.cpp - ${SELF_TEST_DIR}/SurrogateCpps/catch_interfaces_registry_hub.cpp ${SELF_TEST_DIR}/SurrogateCpps/catch_interfaces_reporter.cpp - ${SELF_TEST_DIR}/SurrogateCpps/catch_interfaces_runner.cpp - ${SELF_TEST_DIR}/SurrogateCpps/catch_interfaces_testcase.cpp - ${SELF_TEST_DIR}/SurrogateCpps/catch_message.cpp ${SELF_TEST_DIR}/SurrogateCpps/catch_option.cpp - ${SELF_TEST_DIR}/SurrogateCpps/catch_ptr.cpp ${SELF_TEST_DIR}/SurrogateCpps/catch_stream.cpp - ${SELF_TEST_DIR}/SurrogateCpps/catch_streambuf.cpp + ${SELF_TEST_DIR}/SurrogateCpps/catch_test_case_tracker.cpp ${SELF_TEST_DIR}/SurrogateCpps/catch_test_spec.cpp ${SELF_TEST_DIR}/SurrogateCpps/catch_xmlwriter.cpp - ${SELF_TEST_DIR}/SurrogateCpps/catch_test_case_tracker.cpp ) -CheckFileList(IMPL_SOURCES ${SELF_TEST_DIR}/SurrogateCpps) +CheckFileList(SURROGATE_SOURCES ${SELF_TEST_DIR}/SurrogateCpps) # Please keep these ordered alphabetically set(TOP_LEVEL_HEADERS ${HEADER_DIR}/catch.hpp - ${HEADER_DIR}/catch_session.hpp ${HEADER_DIR}/catch_with_main.hpp ) CheckFileList(TOP_LEVEL_HEADERS ${HEADER_DIR}) # Please keep these ordered alphabetically set(EXTERNAL_HEADERS - ${HEADER_DIR}/external/clara.h - ${HEADER_DIR}/external/tbc_text_format.h + ${HEADER_DIR}/external/clara.hpp ) CheckFileList(EXTERNAL_HEADERS ${HEADER_DIR}/external) # Please keep these ordered alphabetically set(INTERNAL_HEADERS - ${HEADER_DIR}/internal/catch_approx.hpp + ${HEADER_DIR}/internal/catch_approx.h + ${HEADER_DIR}/internal/catch_assertionhandler.h + ${HEADER_DIR}/internal/catch_assertioninfo.h ${HEADER_DIR}/internal/catch_assertionresult.h - ${HEADER_DIR}/internal/catch_assertionresult.hpp ${HEADER_DIR}/internal/catch_capture.hpp + ${HEADER_DIR}/internal/catch_capture_matchers.h ${HEADER_DIR}/internal/catch_clara.h - ${HEADER_DIR}/internal/catch_commandline.hpp + ${HEADER_DIR}/internal/catch_commandline.h ${HEADER_DIR}/internal/catch_common.h - ${HEADER_DIR}/internal/catch_common.hpp ${HEADER_DIR}/internal/catch_compiler_capabilities.h ${HEADER_DIR}/internal/catch_config.hpp - ${HEADER_DIR}/internal/catch_console_colour.hpp - ${HEADER_DIR}/internal/catch_console_colour_impl.hpp + ${HEADER_DIR}/internal/catch_console_colour.h ${HEADER_DIR}/internal/catch_context.h - ${HEADER_DIR}/internal/catch_context_impl.hpp + ${HEADER_DIR}/internal/catch_debug_console.h ${HEADER_DIR}/internal/catch_debugger.h - ${HEADER_DIR}/internal/catch_debugger.hpp + ${HEADER_DIR}/internal/catch_decomposer.h ${HEADER_DIR}/internal/catch_default_main.hpp - ${HEADER_DIR}/internal/catch_errno_guard.hpp - ${HEADER_DIR}/internal/catch_evaluate.hpp - ${HEADER_DIR}/internal/catch_exception_translator_registry.hpp - ${HEADER_DIR}/internal/catch_expression_lhs.hpp - ${HEADER_DIR}/internal/catch_fatal_condition.hpp - ${HEADER_DIR}/internal/catch_generators.hpp - ${HEADER_DIR}/internal/catch_generators_impl.hpp + ${HEADER_DIR}/internal/catch_enforce.h + ${HEADER_DIR}/internal/catch_errno_guard.h + ${HEADER_DIR}/internal/catch_exception_translator_registry.h + ${HEADER_DIR}/internal/catch_external_interfaces.h + ${HEADER_DIR}/internal/catch_fatal_condition.h ${HEADER_DIR}/internal/catch_impl.hpp ${HEADER_DIR}/internal/catch_interfaces_capture.h ${HEADER_DIR}/internal/catch_interfaces_config.h ${HEADER_DIR}/internal/catch_interfaces_exception.h - ${HEADER_DIR}/internal/catch_interfaces_generators.h ${HEADER_DIR}/internal/catch_interfaces_registry_hub.h ${HEADER_DIR}/internal/catch_interfaces_reporter.h ${HEADER_DIR}/internal/catch_interfaces_runner.h ${HEADER_DIR}/internal/catch_interfaces_tag_alias_registry.h ${HEADER_DIR}/internal/catch_interfaces_testcase.h - ${HEADER_DIR}/internal/catch_legacy_reporter_adapter.h - ${HEADER_DIR}/internal/catch_legacy_reporter_adapter.hpp - ${HEADER_DIR}/internal/catch_list.hpp - ${HEADER_DIR}/internal/catch_matchers.hpp + ${HEADER_DIR}/internal/catch_leak_detector.h + ${HEADER_DIR}/internal/catch_list.h + ${HEADER_DIR}/internal/catch_matchers.h ${HEADER_DIR}/internal/catch_matchers_string.h - ${HEADER_DIR}/internal/catch_matchers_string.hpp ${HEADER_DIR}/internal/catch_matchers_vector.h ${HEADER_DIR}/internal/catch_message.h - ${HEADER_DIR}/internal/catch_message.hpp - ${HEADER_DIR}/internal/catch_notimplemented_exception.h - ${HEADER_DIR}/internal/catch_notimplemented_exception.hpp ${HEADER_DIR}/internal/catch_objc.hpp ${HEADER_DIR}/internal/catch_objc_arc.hpp ${HEADER_DIR}/internal/catch_option.hpp ${HEADER_DIR}/internal/catch_platform.h - ${HEADER_DIR}/internal/catch_ptr.hpp + ${HEADER_DIR}/internal/catch_random_number_generator.h ${HEADER_DIR}/internal/catch_reenable_warnings.h - ${HEADER_DIR}/internal/catch_registry_hub.hpp ${HEADER_DIR}/internal/catch_reporter_registrars.hpp - ${HEADER_DIR}/internal/catch_reporter_registry.hpp - ${HEADER_DIR}/internal/catch_result_builder.h - ${HEADER_DIR}/internal/catch_result_builder.hpp + ${HEADER_DIR}/internal/catch_reporter_registry.h ${HEADER_DIR}/internal/catch_result_type.h - ${HEADER_DIR}/internal/catch_run_context.hpp + ${HEADER_DIR}/internal/catch_run_context.h + ${HEADER_DIR}/internal/catch_benchmark.h ${HEADER_DIR}/internal/catch_section.h - ${HEADER_DIR}/internal/catch_section.hpp ${HEADER_DIR}/internal/catch_section_info.h - ${HEADER_DIR}/internal/catch_section_info.hpp + ${HEADER_DIR}/internal/catch_session.h + ${HEADER_DIR}/internal/catch_startup_exception_registry.h ${HEADER_DIR}/internal/catch_stream.h - ${HEADER_DIR}/internal/catch_stream.hpp ${HEADER_DIR}/internal/catch_streambuf.h + ${HEADER_DIR}/internal/catch_stringref.h + ${HEADER_DIR}/internal/catch_string_manip.h ${HEADER_DIR}/internal/catch_suppress_warnings.h ${HEADER_DIR}/internal/catch_tag_alias.h + ${HEADER_DIR}/internal/catch_tag_alias_autoregistrar.h ${HEADER_DIR}/internal/catch_tag_alias_registry.h - ${HEADER_DIR}/internal/catch_tag_alias_registry.hpp ${HEADER_DIR}/internal/catch_test_case_info.h - ${HEADER_DIR}/internal/catch_test_case_info.hpp - ${HEADER_DIR}/internal/catch_test_case_registry_impl.hpp - ${HEADER_DIR}/internal/catch_test_case_tracker.hpp - ${HEADER_DIR}/internal/catch_test_registry.hpp - ${HEADER_DIR}/internal/catch_test_spec.hpp - ${HEADER_DIR}/internal/catch_test_spec_parser.hpp + ${HEADER_DIR}/internal/catch_test_case_registry_impl.h + ${HEADER_DIR}/internal/catch_test_case_tracker.h + ${HEADER_DIR}/internal/catch_test_registry.h + ${HEADER_DIR}/internal/catch_test_spec.h + ${HEADER_DIR}/internal/catch_test_spec_parser.h ${HEADER_DIR}/internal/catch_text.h ${HEADER_DIR}/internal/catch_timer.h - ${HEADER_DIR}/internal/catch_timer.hpp ${HEADER_DIR}/internal/catch_tostring.h - ${HEADER_DIR}/internal/catch_tostring.hpp - ${HEADER_DIR}/internal/catch_totals.hpp - ${HEADER_DIR}/internal/catch_type_traits.hpp + ${HEADER_DIR}/internal/catch_totals.h ${HEADER_DIR}/internal/catch_version.h - ${HEADER_DIR}/internal/catch_version.hpp - ${HEADER_DIR}/internal/catch_wildcard_pattern.hpp + ${HEADER_DIR}/internal/catch_wildcard_pattern.h ${HEADER_DIR}/internal/catch_windows_h_proxy.h - ${HEADER_DIR}/internal/catch_xmlwriter.hpp + ${HEADER_DIR}/internal/catch_xmlwriter.h ) -CheckFileList(INTERNAL_HEADERS ${HEADER_DIR}/internal) +set(IMPL_SOURCES + ${HEADER_DIR}/internal/catch_approx.cpp + ${HEADER_DIR}/internal/catch_assertionhandler.cpp + ${HEADER_DIR}/internal/catch_assertionresult.cpp + ${HEADER_DIR}/internal/catch_benchmark.cpp + ${HEADER_DIR}/internal/catch_capture_matchers.cpp + ${HEADER_DIR}/internal/catch_commandline.cpp + ${HEADER_DIR}/internal/catch_common.cpp + ${HEADER_DIR}/internal/catch_config.cpp + ${HEADER_DIR}/internal/catch_console_colour.cpp + ${HEADER_DIR}/internal/catch_context.cpp + ${HEADER_DIR}/internal/catch_debug_console.cpp + ${HEADER_DIR}/internal/catch_debugger.cpp + ${HEADER_DIR}/internal/catch_decomposer.cpp + ${HEADER_DIR}/internal/catch_errno_guard.cpp + ${HEADER_DIR}/internal/catch_exception_translator_registry.cpp + ${HEADER_DIR}/internal/catch_fatal_condition.cpp + ${HEADER_DIR}/internal/catch_interfaces_capture.cpp + ${HEADER_DIR}/internal/catch_interfaces_config.cpp + ${HEADER_DIR}/internal/catch_interfaces_exception.cpp + ${HEADER_DIR}/internal/catch_interfaces_registry_hub.cpp + ${HEADER_DIR}/internal/catch_interfaces_runner.cpp + ${HEADER_DIR}/internal/catch_interfaces_testcase.cpp + ${HEADER_DIR}/internal/catch_list.cpp + ${HEADER_DIR}/internal/catch_leak_detector.cpp + ${HEADER_DIR}/internal/catch_matchers.cpp + ${HEADER_DIR}/internal/catch_matchers_string.cpp + ${HEADER_DIR}/internal/catch_message.cpp + ${HEADER_DIR}/internal/catch_registry_hub.cpp + ${HEADER_DIR}/internal/catch_interfaces_reporter.cpp + ${HEADER_DIR}/internal/catch_random_number_generator.cpp + ${HEADER_DIR}/internal/catch_reporter_registry.cpp + ${HEADER_DIR}/internal/catch_result_type.cpp + ${HEADER_DIR}/internal/catch_run_context.cpp + ${HEADER_DIR}/internal/catch_section.cpp + ${HEADER_DIR}/internal/catch_section_info.cpp + ${HEADER_DIR}/internal/catch_session.cpp + ${HEADER_DIR}/internal/catch_startup_exception_registry.cpp + ${HEADER_DIR}/internal/catch_stream.cpp + ${HEADER_DIR}/internal/catch_streambuf.cpp + ${HEADER_DIR}/internal/catch_stringref.cpp + ${HEADER_DIR}/internal/catch_string_manip.cpp + ${HEADER_DIR}/internal/catch_tag_alias.cpp + ${HEADER_DIR}/internal/catch_tag_alias_autoregistrar.cpp + ${HEADER_DIR}/internal/catch_tag_alias_registry.cpp + ${HEADER_DIR}/internal/catch_test_case_info.cpp + ${HEADER_DIR}/internal/catch_test_case_registry_impl.cpp + ${HEADER_DIR}/internal/catch_test_case_tracker.cpp + ${HEADER_DIR}/internal/catch_test_registry.cpp + ${HEADER_DIR}/internal/catch_test_spec.cpp + ${HEADER_DIR}/internal/catch_test_spec_parser.cpp + ${HEADER_DIR}/internal/catch_timer.cpp + ${HEADER_DIR}/internal/catch_tostring.cpp + ${HEADER_DIR}/internal/catch_totals.cpp + ${HEADER_DIR}/internal/catch_version.cpp + ${HEADER_DIR}/internal/catch_wildcard_pattern.cpp + ${HEADER_DIR}/internal/catch_xmlwriter.cpp + ) +set(INTERNAL_FILES ${IMPL_SOURCES} ${INTERNAL_HEADERS}) +CheckFileList(INTERNAL_FILES ${HEADER_DIR}/internal) # Please keep these ordered alphabetically set(REPORTER_HEADERS ${HEADER_DIR}/reporters/catch_reporter_automake.hpp ${HEADER_DIR}/reporters/catch_reporter_bases.hpp - ${HEADER_DIR}/reporters/catch_reporter_compact.hpp - ${HEADER_DIR}/reporters/catch_reporter_console.hpp - ${HEADER_DIR}/reporters/catch_reporter_junit.hpp - ${HEADER_DIR}/reporters/catch_reporter_multi.hpp + ${HEADER_DIR}/reporters/catch_reporter_multi.h ${HEADER_DIR}/reporters/catch_reporter_tap.hpp ${HEADER_DIR}/reporters/catch_reporter_teamcity.hpp - ${HEADER_DIR}/reporters/catch_reporter_xml.hpp ) -CheckFileList(REPORTER_HEADERS ${HEADER_DIR}/reporters) +set(REPORTER_SOURCES + ${HEADER_DIR}/reporters/catch_reporter_bases.cpp + ${HEADER_DIR}/reporters/catch_reporter_compact.cpp + ${HEADER_DIR}/reporters/catch_reporter_console.cpp + ${HEADER_DIR}/reporters/catch_reporter_junit.cpp + ${HEADER_DIR}/reporters/catch_reporter_multi.cpp + ${HEADER_DIR}/reporters/catch_reporter_xml.cpp + ) +set(REPORTER_FILES ${REPORTER_HEADERS} ${REPORTER_SOURCES}) +CheckFileList(REPORTER_FILES ${HEADER_DIR}/reporters) # Specify the headers, too, so CLion recognises them as project files set(HEADERS @@ -236,17 +276,9 @@ set(HEADERS ${REPORTER_HEADERS} ) - -set(BENCH_SOURCES - ${BENCHMARK_DIR}/BenchMain.cpp - ${BENCHMARK_DIR}/StringificationBench.cpp - ) -CheckFileList(BENCH_SOURCES ${BENCHMARK_DIR}) - # Provide some groupings for IDEs SOURCE_GROUP("Tests" FILES ${TEST_SOURCES}) -SOURCE_GROUP("Surrogates" FILES ${IMPL_SOURCES}) -SOURCE_GROUP("Benchmarks" FILES ${BENCH_SOURCES}) +SOURCE_GROUP("Surrogates" FILES ${SURROGATE_SOURCES}) # configure the executable include_directories(${HEADER_DIR}) @@ -254,17 +286,19 @@ include_directories(${HEADER_DIR}) # Projects consuming Catch via ExternalProject_Add might want to use install step # without building all of our selftests. if (NOT NO_SELFTEST) - add_executable(SelfTest ${TEST_SOURCES} ${IMPL_SOURCES} ${HEADERS}) - add_executable(Benchmark ${BENCH_SOURCES} ${HEADERS}) + add_executable(SelfTest ${TEST_SOURCES} ${IMPL_SOURCES} ${REPORTER_SOURCES} ${SURROGATE_SOURCES} ${HEADERS}) # Add desired warnings if ( CMAKE_CXX_COMPILER_ID MATCHES "Clang|AppleClang|GNU" ) - target_compile_options( SelfTest PRIVATE -Wall -Wextra ) - target_compile_options( Benchmark PRIVATE -Wall -Wextra ) + target_compile_options( SelfTest PRIVATE -Wall -Wextra -Wunreachable-code ) + endif() + # Clang specific warning go here + if ( CMAKE_CXX_COMPILER_ID MATCHES "Clang" ) + # Actually keep these + target_compile_options( SelfTest PRIVATE -Wweak-vtables -Wexit-time-destructors -Wglobal-constructors -Wmissing-noreturn ) endif() if ( CMAKE_CXX_COMPILER_ID MATCHES "MSVC" ) - target_compile_options( SelfTest PRIVATE /W4 /w44265 /WX ) - target_compile_options( Benchmark PRIVATE /W4 ) + target_compile_options( SelfTest PRIVATE /W4 /w44265 /WX /w44061 /w44062 ) endif() @@ -278,7 +312,24 @@ if (NOT NO_SELFTEST) add_test(NAME ListTags COMMAND SelfTest --list-tags) set_tests_properties(ListTags PROPERTIES PASS_REGULAR_EXPRESSION "[0-9]+ tags") + # 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") endif() # !NO_SELFTEST install(DIRECTORY "single_include/" DESTINATION "include/catch") + +## Provide some pkg-config integration +# Don't bother on Windows +if(NOT WIN32 OR NOT CMAKE_HOST_SYSTEM_NAME MATCHES Windows) + + set(PKGCONFIG_INSTALL_DIR + "${CMAKE_INSTALL_PREFIX}/share/pkgconfig" + CACHE PATH "Path where catch.pc is installed" + ) + + configure_file(${CMAKE_CURRENT_SOURCE_DIR}/catch.pc.in ${CMAKE_CURRENT_BINARY_DIR}/catch.pc @ONLY) + install(FILES ${CMAKE_CURRENT_BINARY_DIR}/catch.pc DESTINATION ${PKGCONFIG_INSTALL_DIR}) + +endif() diff --git a/README.md b/README.md index b1eaf22c..0d1b9649 100644 --- a/README.md +++ b/README.md @@ -1,28 +1,28 @@ + ![catch logo](catch-logo-small.png) [![Github Releases](https://img.shields.io/github/release/philsquared/catch.svg)](https://github.com/philsquared/catch/releases) -[![Build Status](https://travis-ci.org/philsquared/Catch.svg?branch=master)](https://travis-ci.org/philsquared/Catch) -[![Build status](https://ci.appveyor.com/api/projects/status/hrtk60hv6tw6fght/branch/master?svg=true)](https://ci.appveyor.com/project/philsquared/catch/branch/master) -[![Try online](https://img.shields.io/badge/try-online-blue.svg)](https://wandbox.org/permlink/EyEbEIfp8CnnjguW) +[![Build Status](https://travis-ci.org/philsquared/Catch.svg?branch=catch2)](https://travis-ci.org/philsquared/Catch?branch=catch2) +[![Build status](https://ci.appveyor.com/api/projects/status/hrtk60hv6tw6fght/branch/catch2?svg=true)](https://ci.appveyor.com/project/philsquared/catch/branch/catch2) -The latest, single header, version can be downloaded directly using this link +The latest version of the single header can be downloaded directly using this link ## What's the Catch? -Catch stands for C++ Automated Test Cases in Headers and is a multi-paradigm automated test framework for C++ and Objective-C (and, maybe, C). It is implemented entirely in a set of header files, but is packaged up as a single header for extra convenience. +Catch stands for C++ Automated Test Cases in Headers and is a +multi-paradigm test framework for C++. which also supports Objective-C +and, maybe, C. +It is primarily distributed as a single header file, although certain +extensions may require additional headers. ## How to use it This documentation comprises these three parts: -* [Why do we need yet another C++ Test Framework?](docs/why-catch.md) -* [Tutorial](docs/tutorial.md) - getting started -* [Reference section](docs/Readme.md) - all the details +* [Why do we need yet another C++ Test Framework?](docs/why-catch.md#top) +* [Tutorial](docs/tutorial.md#top) - getting started +* [Reference section](docs/Readme.md#top) - all the details ## More * Issues and bugs can be raised on the [Issue tracker on GitHub](https://github.com/philsquared/Catch/issues) * For discussion or questions please use [the dedicated Google Groups forum](https://groups.google.com/forum/?fromgroups#!forum/catch-forum) -* See [who else is using Catch](docs/opensource-users.md) - -## Help us out -We're currently running [a survey](https://www.surveymonkey.co.uk/r/TLLYQJW) to help us shape the future of Catch. -Please take a few moments to fill it out (there's only ten questions). +* See [who else is using Catch](docs/opensource-users.md#top) diff --git a/appveyor.yml b/appveyor.yml index d09c1dd5..40bb68a8 100644 --- a/appveyor.yml +++ b/appveyor.yml @@ -4,7 +4,6 @@ version: "{build}" os: - Visual Studio 2017 - Visual Studio 2015 - - Visual Studio 2013 environment: matrix: diff --git a/catch.pc.in b/catch.pc.in new file mode 100644 index 00000000..4a7b2545 --- /dev/null +++ b/catch.pc.in @@ -0,0 +1,9 @@ +prefix=@CMAKE_INSTALL_PREFIX@ +exec_prefix=${prefix} + +Name: Catch +Description: Testing library for C++ +Requires: +Version: @CATCH_VERSION_NUMBER@ +Libs: +Cflags: -I${prefix}/@INCLUDE_INSTALL_DIR@/include diff --git a/conanfile.py b/conanfile.py index 02699974..b4993b71 100644 --- a/conanfile.py +++ b/conanfile.py @@ -4,7 +4,7 @@ from conans import ConanFile class CatchConan(ConanFile): name = "Catch" - version = "1.11.0" + version = "2.0.1" description = "A modern, C++-native, header-only, framework for unit-tests, TDD and BDD" author = "philsquared" generators = "cmake" diff --git a/docs/Readme.md b/docs/Readme.md index 36048ff8..e7815671 100644 --- a/docs/Readme.md +++ b/docs/Readme.md @@ -1,30 +1,33 @@ -To get the most out of Catch, start with the [tutorial](tutorial.md). + +# Reference + +To get the most out of Catch, start with the [tutorial](tutorial.md#top). Once you're up and running consider the following reference material. Writing tests: -* [Assertion macros](assertions.md) -* [Matchers](matchers.md) -* [Logging macros](logging.md) -* [Test cases and sections](test-cases-and-sections.md) -* [Test fixtures](test-fixtures.md) -* [Reporters](reporters.md) -* [Event Listeners](event-listeners.md) +* [Assertion macros](assertions.md#top) +* [Matchers](matchers.md#top) +* [Logging macros](logging.md#top) +* [Test cases and sections](test-cases-and-sections.md#top) +* [Test fixtures](test-fixtures.md#top) +* [Reporters](reporters.md#top) +* [Event Listeners](event-listeners.md#top) Fine tuning: -* [Supplying your own main()](own-main.md) -* [Compile-time configuration](configuration.md) -* [String Conversions](tostring.md) +* [Supplying your own main()](own-main.md#top) +* [Compile-time configuration](configuration.md#top) +* [String Conversions](tostring.md#top) Running: -* [Command line](command-line.md) -* [CI and Build system integration](build-systems.md) +* [Command line](command-line.md#top) +* [CI and Build system integration](build-systems.md#top) FAQ: -* [Why are my tests slow to compile?](slow-compiles.md) -* [Known limitations](limitations.md) +* [Why are my tests slow to compile?](slow-compiles.md#top) +* [Known limitations](limitations.md#top) Other: -* [Why Catch?](why-catch.md) -* [Open Source Projects using Catch](opensource-users.md) -* [Contributing](contributing.md) -* [Release Notes](release-notes.md) +* [Why Catch?](why-catch.md#top) +* [Open Source Projects using Catch](opensource-users.md#top) +* [Contributing](contributing.md#top) +* [Release Notes](release-notes.md#top) diff --git a/docs/assertions.md b/docs/assertions.md index f5df0c97..93f88c41 100644 --- a/docs/assertions.md +++ b/docs/assertions.md @@ -1,5 +1,12 @@ + # Assertion Macros +**Contents**
+[Natural Expressions](#natural-expressions)
+[Exceptions](#exceptions)
+[Matcher expressions](#matcher-expressions)
+[Thread Safety](#thread-safety)
+ Most test frameworks have a large collection of assertion macros to capture all possible conditional forms (```_EQUALS```, ```_NOTEQUALS```, ```_GREATER_THAN``` etc). Catch is different. Because it decomposes natural C-style conditional expressions most of these forms are reduced to one or two that you will use all the time. That said there are a rich set of auxilliary macros as well. We'll describe all of these here. @@ -57,7 +64,7 @@ This way `Approx` is constructed with reasonable defaults, covering most simple * __epsilon__ - epsilon serves to set the percentage by which a result can be erroneous, before it is rejected. By default set to `std::numeric_limits::epsilon()*100`. * __margin__ - margin serves to set the the absolute value by which a result can be erroneous before it is rejected. By default set to `0.0`. -* __scale__ - scale serves to adjust the base for comparison used by epsilon. By default set to `1.0`. +* __scale__ - scale serves to adjust the epsilon's multiplicator. By default set to `0.0`. #### epsilon example ```cpp @@ -77,7 +84,12 @@ Approx target = Approx(100).margin(5); ``` #### scale -Scale can be useful if the computation leading to the result worked on different scale, than is used by the results (and thus expected errors are on a different scale than would be expected based on the results alone), i.e. an additional not scaling difference will be accepted, because | current - target | < eps_rel + eps_abs (while eps_rel = max(current, target) * epsilon, eps_abs = epsilon * scale) will be checked. +Scale can be useful if the computation leading to the result worked +on different scale than is used by the results. Since allowed difference +between Approx's value and compared value is based primarily on Approx's value +(the allowed difference is computed as +`(Approx::scale + Approx::value) * epsilon`), the resulting comparison could +need rescaling to be correct. ## Exceptions @@ -95,7 +107,7 @@ Expects that an exception (of any type) is be thrown during evaluation of the ex * **REQUIRE_THROWS_AS(** _expression_, _exception type_ **)** and * **CHECK_THROWS_AS(** _expression_, _exception type_ **)** -Expects that an exception of the _specified type_ is thrown during evaluation of the expression. Note that the _exception type_ is used verbatim and you should include (const) reference. +Expects that an exception of the _specified type_ is thrown during evaluation of the expression. Note that the _exception type_ is extended with `const&` and you should not include it yourself. * **REQUIRE_THROWS_WITH(** _expression_, _string or string matcher_ **)** and * **CHECK_THROWS_WITH(** _expression_, _string or string matcher_ **)** @@ -108,8 +120,13 @@ REQUIRE_THROWS_WITH( openThePodBayDoors(), Contains( "afraid" ) && Contains( "ca REQUIRE_THROWS_WITH( dismantleHal(), "My mind is going" ); ``` +* **REQUIRE_THROWS_MATCHES(** _expression_, _exception type_, _matcher for given exception type_ **)** and +* **CHECK_THROWS_MATCHES(** _expression_, _exception type_, _matcher for given exception type_ **)** -Please note that the `THROW` family of assertions expects to be passed a single expression, not a statement or series of statements. If you want to check a more complicated sequence of operations, you can use a C++11 lambda function. +Expects that exception of _exception type_ is thrown and it matches provided matcher (see next section for Matchers). + + +_Please note that the `THROW` family of assertions expects to be passed a single expression, not a statement or series of statements. If you want to check a more complicated sequence of operations, you can use a C++11 lambda function._ ```cpp REQUIRE_NOTHROW([&](){ @@ -122,9 +139,11 @@ REQUIRE_NOTHROW([&](){ }()); ``` + + ## Matcher expressions -To support Matchers a slightly different form is used. Matchers have [their own documentation](matchers.md). +To support Matchers a slightly different form is used. Matchers have [their own documentation](matchers.md#top). * **REQUIRE_THAT(** _lhs_, _matcher expression_ **)** and * **CHECK_THAT(** _lhs_, _matcher expression_ **)** @@ -138,4 +157,4 @@ For more details, along with workarounds, see the section on [the limitations pa --- -[Home](Readme.md) +[Home](Readme.md#top) diff --git a/docs/build-systems.md b/docs/build-systems.md index 5dddc09d..20711d37 100644 --- a/docs/build-systems.md +++ b/docs/build-systems.md @@ -1,14 +1,15 @@ + # CI and build system integration Build Systems may refer to low-level tools, like CMake, or larger systems that run on servers, like Jenkins or TeamCity. This page will talk about both. -# Continuous Integration systems +## Continuous Integration systems Probably the most important aspect to using Catch with a build server is the use of different reporters. Catch comes bundled with three reporters that should cover the majority of build servers out there - although adding more for better integration with some is always a possibility (currently we also offer TeamCity, TAP and Automake reporters). Two of these reporters are built in (XML and JUnit) and the third (TeamCity) is included as a separate header. It's possible that the other two may be split out in the future too - as that would make the core of Catch smaller for those that don't need them. -## XML Reporter +### XML Reporter ```-r xml``` The XML Reporter writes in an XML format that is specific to Catch. @@ -17,7 +18,7 @@ The advantage of this format is that it corresponds well to the way Catch works The disadvantage is that, being specific to Catch, no existing build servers understand the format natively. It can be used as input to an XSLT transformation that could convert it to, say, HTML - although this loses the streaming advantage, of course. -## JUnit Reporter +### JUnit Reporter ```-r junit``` The JUnit Reporter writes in an XML format that mimics the JUnit ANT schema. @@ -52,9 +53,9 @@ The Automake Reporter writes out the [meta tags](https://www.gnu.org/software/au Because of the incremental nature of Catch's test suites and ability to run specific tests, our implementation of TAP reporter writes out the number of tests in a suite last. -# Low-level tools +## Low-level tools -## CMake +### CMake In general we recommend "vendoring" Catch's single-include releases inside your own repository. If you do this, the following example shows a minimal CMake project: ```CMake @@ -140,4 +141,4 @@ If you are using GCOV tool to get testing coverage of your code, and are not sur --- -[Home](Readme.md) +[Home](Readme.md#top) diff --git a/docs/command-line.md b/docs/command-line.md index a0a18be3..8afcb7d4 100644 --- a/docs/command-line.md +++ b/docs/command-line.md @@ -1,3 +1,30 @@ + +# Command line + +**Contents**
+[Specifying which tests to run](#specifying-which-tests-to-run)
+[Choosing a reporter to use](#choosing-a-reporter-to-use)
+[Breaking into the debugger](#breaking-into-the-debugger)
+[Showing results for successful tests](#showing-results-for-successful-tests)
+[Aborting after a certain number of failures](#aborting-after-a-certain-number-of-failures)
+[Listing available tests, tags or reporters](#listing-available-tests-tags-or-reporters)
+[Sending output to a file](#sending-output-to-a-file)
+[Naming a test run](#naming-a-test-run)
+[Eliding assertions expected to throw](#eliding-assertions-expected-to-throw)
+[Make whitespace visible](#make-whitespace-visible)
+[Warnings](#warnings)
+[Reporting timings](#reporting-timings)
+[Load test names to run from a file](#load-test-names-to-run-from-a-file)
+[Just test names](#just-test-names)
+[Specify the order test cases are run](#specify-the-order-test-cases-are-run)
+[Specify a seed for the Random Number Generator](#specify-a-seed-for-the-random-number-generator)
+[Identify framework and version according to the libIdentify standard](#identify-framework-and-version-according-to-the-libidentify-standard)
+[Wait for key before continuing](#wait-for-key-before-continuing)
+[Specify multiples of clock resolution to run benchmarks for](#specify-multiples-of-clock-resolution-to-run-benchmarks-for)
+[Usage](#usage)
+[Specify the section to run](#specify-the-section-to-run)
+[Filenames as tags](#filenames-as-tags)
+ Catch works quite nicely without any command line options at all - but for those times when you want greater control the following options are available. Click one of the followings links to take you straight to that option - or scroll on to browse the available options. @@ -29,6 +56,7 @@ Click one of the followings links to take you straight to that option - or scrol ` --rng-seed`
` --libidentify`
` --wait-for-keypress`
+ ` --benchmark-resolution-multiple`

@@ -228,6 +256,13 @@ See [The LibIdentify repo for more information and examples](https://github.com/ Will cause the executable to print a message and wait until the return/ enter key is pressed before continuing - either before running any tests, after running all tests - or both, depending on the argument. + +## Specify multiples of clock resolution to run benchmarks for +
--benchmark-resolution-multiple <multiplier>
+ +When running benchmarks the clock resolution is estimated. Benchmarks are then run for exponentially increasing +numbers of iterations until some multiple of the estimated resolution is exceed. By default that multiple is 100, but +it can be overriden here. ## Usage @@ -290,4 +325,4 @@ So, for example, tests within the file `~\Dev\MyProject\Ferrets.cpp` would be t --- -[Home](Readme.md) +[Home](Readme.md#top) diff --git a/docs/commercial-users.md b/docs/commercial-users.md index 424eb22c..e4d789f1 100644 --- a/docs/commercial-users.md +++ b/docs/commercial-users.md @@ -1,6 +1,7 @@ + # Commercial users of Catch -As well as [Open Source](opensource-users.md) users Catch is widely used within proprietary code bases too. +As well as [Open Source](opensource-users.md#top) users Catch is widely used within proprietary code bases too. Many organisations like to keep this information internal, and that's fine, but if you're more open it would be great if we could list the names of as many organisations as possible that use Catch somewhere in their codebase. diff --git a/docs/configuration.md b/docs/configuration.md index 26a727df..3e59e763 100644 --- a/docs/configuration.md +++ b/docs/configuration.md @@ -1,22 +1,43 @@ + +# Compile-time configuration + +**Contents**
+[main()/ implementation](#main-implementation)
+[Prefixing Catch macros](#prefixing-catch-macros)
+[Terminal colour](#terminal-colour)
+[Console width](#console-width)
+[stdout](#stdout)
+[Other toggles](#other-toggles)
+[Windows header clutter](#windows-header-clutter)
+[Enabling stringification](#enabling-stringification)
+ Catch is designed to "just work" as much as possible. For most people the only configuration needed is telling Catch which source file should host all the implementation code (```CATCH_CONFIG_MAIN```). Nonetheless there are still some occasions where finer control is needed. For these occasions Catch exposes a set of macros for configuring how it is built. -# main()/ implementation +## main()/ implementation CATCH_CONFIG_MAIN // Designates this as implementation file and defines main() CATCH_CONFIG_RUNNER // Designates this as implementation file Although Catch is header only it still, internally, maintains a distinction between interface headers and headers that contain implementation. Only one source file in your test project should compile the implementation headers and this is controlled through the use of one of these macros - one of these identifiers should be defined before including Catch in *exactly one implementation file in your project*. -# Prefixing Catch macros +# Reporter / Listener interfaces + + CATCH_CONFIG_EXTERNAL_INTERFACES // Brings in neccessary headers for Reporter/Listener implementation + +Brings in various parts of Catch that are required for user defined Reporters and Listeners. This means that new Reporters and Listeners can be defined in this file as well as in the main file. + +Implied by both `CATCH_CONFIG_MAIN` and `CATCH_CONFIG_RUNNER`. + +## Prefixing Catch macros CATCH_CONFIG_PREFIX_ALL To keep test code clean and uncluttered Catch uses short macro names (e.g. ```TEST_CASE``` and ```REQUIRE```). Occasionally these may conflict with identifiers from platform headers or the system under test. In this case the above identifier can be defined. This will cause all the Catch user macros to be prefixed with ```CATCH_``` (e.g. ```CATCH_TEST_CASE``` and ```CATCH_REQUIRE```). -# Terminal colour +## Terminal colour CATCH_CONFIG_COLOUR_NONE // completely disables all text colouring CATCH_CONFIG_COLOUR_WINDOWS // forces the Win32 console API to be used @@ -32,14 +53,14 @@ Note that when ANSI colour codes are used "unistd.h" must be includable - along Typically you should place the ```#define``` before #including "catch.hpp" in your main source file - but if you prefer you can define it for your whole project by whatever your IDE or build system provides for you to do so. -# Console width +## Console width CATCH_CONFIG_CONSOLE_WIDTH = x // where x is a number Catch formats output intended for the console to fit within a fixed number of characters. This is especially important as indentation is used extensively and uncontrolled line wraps break this. By default a console width of 80 is assumed but this can be controlled by defining the above identifier to be a different value. -# stdout +## stdout CATCH_CONFIG_NOSTDOUT @@ -51,63 +72,18 @@ Catch does not use ```std::cout```, ```std::cerr``` and ```std::clog``` directly This can be useful on certain platforms that do not provide the standard iostreams, such as certain embedded systems. -# Default reporter - CATCH_CONFIG_DEFAULT_REPORTER -The default reporter (reporter used when no reporters are explicitly specified) can be overriden during compilation time by using the above macro. Note that desired value of the macro is a C string and quotes have to be escaped during compilation: `clang++ test.cpp -DCATCH_CONFIG_DEFAULT_REPORTER=\"xml\"`. - -# C++ conformance toggles - - CATCH_CONFIG_CPP11_NULLPTR // nullptr is supported? - CATCH_CONFIG_CPP11_NOEXCEPT // noexcept is supported? - CATCH_CONFIG_CPP11_GENERATED_METHODS // delete and default keywords for methods - CATCH_CONFIG_CPP11_IS_ENUM // std::is_enum is supported? - CATCH_CONFIG_CPP11_TUPLE // std::tuple is supported - CATCH_CONFIG_VARIADIC_MACROS // Usually pre-C++11 compiler extensions are sufficient - CATCH_CONFIG_CPP11_LONG_LONG // generates overloads for the long long type - CATCH_CONFIG_CPP11_OVERRIDE // CATCH_OVERRIDE expands to override (for virtual function implementations) - CATCH_CONFIG_CPP11_UNIQUE_PTR // Use std::unique_ptr instead of std::auto_ptr - CATCH_CONFIG_CPP11_SHUFFLE // Use std::shuffle instead of std::random_shuffle - CATCH_CONFIG_CPP11_TYPE_TRAITS // Use std::enable_if and - CATCH_CONFIG_CPP11_STREAM_INSERTABLE_CHECK // Use C++11 expression SFINAE to check if class can be inserted to std::ostream - -Catch has some basic compiler detection that will attempt to select the appropriate mix of these macros. However being incomplete - and often without access to the respective compilers - this detection tends to be conservative. -So overriding control is given to the user. If a compiler supports a feature (and Catch does not already detect it) then one or more of these may be defined to enable it (or suppress it, in some cases). If you do do this please raise an issue, specifying your compiler version (ideally with an idea of how to detect it) and stating that it has such support. -You may also suppress any of these features by using the `_NO_` form, e.g. `CATCH_CONFIG_CPP11_NO_NULLPTR`. - -All C++11 support can be disabled with `CATCH_CONFIG_NO_CPP11` - -## `CATCH_CONFIG_CPP11_STREAM_INSERTABLE_CHECK` - -This flag is off by default, but allows you to resolve problems caused by types with private base class that are streamable, but the classes themselves are not. Without it, the following code will cause a compilation error: -```cpp -#define CATCH_CONFIG_MAIN -#include -struct A {}; -std::ostream &operator<< (std::ostream &o, const A &v) { return o << 0; } - -struct B : private A { - bool operator==(int){ return true;} -}; - -B f (); -std::ostream g (); - -TEST_CASE ("Error in streamable check") { - B x; - REQUIRE (x == 4); -} -``` - -# Other toggles +## Other toggles CATCH_CONFIG_COUNTER // Use __COUNTER__ to generate unique names for test cases CATCH_CONFIG_WINDOWS_SEH // Enable SEH handling on Windows CATCH_CONFIG_FAST_COMPILE // Sacrifices some (rather minor) features for compilation speed + CATCH_CONFIG_DISABLE_MATCHERS // Do not compile Matchers in this compilation unit CATCH_CONFIG_POSIX_SIGNALS // Enable handling POSIX signals CATCH_CONFIG_WINDOWS_CRTDBG // Enable leak checking using Windows's CRT Debug Heap CATCH_CONFIG_DISABLE_STRINGIFICATION // Disable stringifying the original expression + CATCH_CONFIG_DISABLE // Disables assertions and test case registration Currently Catch enables `CATCH_CONFIG_WINDOWS_SEH` only when compiled with MSVC, because some versions of MinGW do not have the necessary Win32 API support. @@ -115,26 +91,48 @@ Currently Catch enables `CATCH_CONFIG_WINDOWS_SEH` only when compiled with MSVC, `CATCH_CONFIG_WINDOWS_CRTDBG` is off by default. If enabled, Windows's CRT is used to check for memory leaks, and displays them after the tests finish running. -Just as with the C++11 conformance toggles, these toggles can be disabled by using `_NO_` form of the toggle, e.g. `CATCH_CONFIG_NO_WINDOWS_SEH`. +These toggles can be disabled by using `_NO_` form of the toggle, e.g. `CATCH_CONFIG_NO_WINDOWS_SEH`. -## `CATCH_CONFIG_FAST_COMPILE` +### `CATCH_CONFIG_FAST_COMPILE` Defining this flag speeds up compilation of test files by ~20%, by making 2 changes: * The `-b` (`--break`) flag no longer makes Catch break into debugger in the same stack frame as the failed test, but rather in a stack frame *below*. -* The `REQUIRE` family of macros (`REQUIRE`, `REQUIRE_FALSE` and `REQUIRE_THAT`) no longer uses local try-catch block. This disables exception translation, but should not lead to false negatives. +* Non-exception family of macros ({`REQUIRE`,`CHECK`}{`_`,`_FALSE`, `_FALSE`}, no longer use local try-cache block. This disables exception translation, but should not lead to false negatives. `CATCH_CONFIG_FAST_COMPILE` has to be either defined, or not defined, in all translation units that are linked into single test binary, or the behaviour of setting `-b` flag and throwing unexpected exceptions will be unpredictable. -## `CATCH_CONFIG_DISABLE_STRINGIFICATION` -This toggle enables a workaround for VS 2017 bug. For details see -[known limitations](limitations.md#visual-studio-2017----raw-string-literal-in-assert-fails-to-compile). +### `CATCH_CONFIG_DISABLE_MATCHERS` +When `CATCH_CONFIG_DISABLE_MATCHERS` is defined, all mentions of Catch's Matchers are ifdef-ed away from the translation unit. Doing so will speed up compilation of that TU. -# Windows header clutter +_Note: If you define `CATCH_CONFIG_DISABLE_MATCHERS` in the same file as Catch's main is implemented, your test executable will fail to link if you use Matchers anywhere._ + +### `CATCH_CONFIG_DISABLE_STRINGIFICATION` +This toggle enables a workaround for VS 2017 bug. For details see [known limitations](limitations.md#visual-studio-2017----raw-string-literal-in-assert-fails-to-compile). + +### `CATCH_CONFIG_DISABLE` +This toggle removes most of Catch from given file. This means that `TEST_CASE`s are not registered and assertions are turned into no-ops. Useful for keeping tests within implementation files (ie for functions with internal linkage), instead of in external files. + +This feature is considered experimental and might change at any point. + +_Inspired by Doctest's `DOCTEST_CONFIG_DISABLE`_ + +## Windows header clutter On Windows Catch includes `windows.h`. To minimize global namespace clutter in the implementation file, it defines `NOMINMAX` and `WIN32_LEAN_AND_MEAN` before including it. You can control this behaviour via two macros: CATCH_CONFIG_NO_NOMINMAX // Stops Catch from using NOMINMAX macro CATCH_CONFIG_NO_WIN32_LEAN_AND_MEAN // Stops Catch from using WIN32_LEAN_AND_MEAN macro + +## Enabling stringification + +By default, Catch does not stringify some types from the standard library. This is done to avoid dragging in various standard library headers by default. However, Catch does contain these and can be configured to provide them, using these macros: + + CATCH_CONFIG_ENABLE_PAIR_STRINGMAKER // Provide StringMaker specialization for std::pair + CATCH_CONFIG_ENABLE_TUPLE_STRINGMAKER // Provide StringMaker specialization for std::tuple + CATCH_CONFIG_ENABLE_CHRONO_STRINGMAKER // Provide StringMaker specialization for std::chrono::duration, std::chrono::timepoint + CATCH_CONFIG_ENABLE_ALL_STRINGMAKERS // Defines all of the above + + --- -[Home](Readme.md) +[Home](Readme.md#top) diff --git a/docs/contributing.md b/docs/contributing.md index 1bcef99b..b6150375 100644 --- a/docs/contributing.md +++ b/docs/contributing.md @@ -1,3 +1,4 @@ + # Contributing to Catch So you want to contribute something to Catch? That's great! Whether it's a bug fix, a new feature, support for @@ -57,4 +58,4 @@ be fairly self-explanatory. --- -[Home](Readme.md) \ No newline at end of file +[Home](Readme.md#top) diff --git a/docs/event-listeners.md b/docs/event-listeners.md index ab057444..b9331b49 100644 --- a/docs/event-listeners.md +++ b/docs/event-listeners.md @@ -1,3 +1,4 @@ + # Event Listeners A `Listener` is a class you can register with Catch that will then be passed events, @@ -12,10 +13,11 @@ so you are not forced to implement events you're not interested in. ## Implementing a Listener +Simply derive a class from `Catch::TestEventListenerBase` and implement the methods you are interested in, either in +the main source file (i.e. the one that defines `CATCH_CONFIG_MAIN` or `CATCH_CONFIG_RUNNER`), or in a +file that defines `CATCH_CONFIG_EXTERNAL_INTERFACES`. -In your main source file (i.e. the one that has the `#define` for `CATCH_CONFIG_MAIN` or `CATCH_CONFIG_RUNNER`), -simply derive a class from `Catch::TestEventListenerBase` and implement the methods you are interested in. -Then register it using `INTERNAL_CATCH_REGISTER_LISTENER`. +Then register it using `CATCH_REGISTER_LISTENER`. For example: @@ -70,4 +72,4 @@ just look in the source code to see what fields are available. --- -[Home](Readme.md) +[Home](Readme.md#top) diff --git a/docs/limitations.md b/docs/limitations.md index 4897ec39..c6ef640e 100644 --- a/docs/limitations.md +++ b/docs/limitations.md @@ -1,7 +1,22 @@ + # Known limitations Catch has some known limitations, that we are not planning to change. Some of these are caused by our desire to support C++98 compilers, some of these are caused by our desire to keep Catch crossplatform, some exist because their priority is seen as low compared to the development effort they would need and some other yet are compiler/runtime bugs. +## Implementation limits +### Sections nested in loops + +If you are using `SECTION`s inside loops, you have to create them with different name per loop's iteration. The recommended way to do so is to incorporate the loop's counter into section's name, like so +```cpp +TEST_CASE( "Looped section" ) { + for (char i = '0'; i < '5'; ++i) { + SECTION(std::string("Looped section ") + i) { + SUCCEED( "Everything is OK" ); + } + } +} +``` + ## Features This section outlines some missing features, what is their status and their possible workarounds. diff --git a/docs/logging.md b/docs/logging.md index afbfd224..7ae54aab 100644 --- a/docs/logging.md +++ b/docs/logging.md @@ -1,3 +1,4 @@ + # Logging macros Additional messages can be logged during a test case. Note that the messages are scoped and thus will not be reported if failure occurs in scope preceding the message declaration. An example: @@ -79,4 +80,4 @@ These macros are now deprecated and are just aliases for INFO and CAPTURE (which --- -[Home](Readme.md) \ No newline at end of file +[Home](Readme.md#top) diff --git a/docs/matchers.md b/docs/matchers.md index 5daf68d7..647ec64f 100644 --- a/docs/matchers.md +++ b/docs/matchers.md @@ -1,3 +1,4 @@ + # Matchers Matchers are an alternative way to do assertions which are easily extensible and composable. @@ -101,4 +102,4 @@ with expansion: --- -[Home](Readme.md) \ No newline at end of file +[Home](Readme.md#top) diff --git a/docs/opensource-users.md b/docs/opensource-users.md index 4353da2e..c7171ed4 100644 --- a/docs/opensource-users.md +++ b/docs/opensource-users.md @@ -1,3 +1,4 @@ + # Open Source projects using Catch Catch is great for open source. With its [liberal license](../LICENSE.txt) and single-header, dependency-free, distribution @@ -83,4 +84,4 @@ Standardese aims to be a nextgen Doxygen --- -[Home](Readme.md) +[Home](Readme.md#top) diff --git a/docs/own-main.md b/docs/own-main.md index 67e551f3..fbaff591 100644 --- a/docs/own-main.md +++ b/docs/own-main.md @@ -1,3 +1,4 @@ + # Supplying main() yourself The easiest way to use Catch is to let it supply ```main()``` for you and handle configuring itself from the command line. @@ -16,15 +17,14 @@ If you just need to have code that executes before and/ or after Catch this is t #define CATCH_CONFIG_RUNNER #include "catch.hpp" -int main( int argc, char* argv[] ) -{ +int main( int argc, char* argv[] ) { // global setup... int result = Catch::Session().run( argc, argv ); // global clean-up... - return ( result < 0xff ? result : 0xff ); + return result; } ``` @@ -39,23 +39,24 @@ If you still want Catch to process the command line, but you want to programatic int main( int argc, char* argv[] ) { Catch::Session session; // There must be exactly one instance - + // writing to session.configData() here sets defaults // this is the preferred way to set them - + int returnCode = session.applyCommandLine( argc, argv ); if( returnCode != 0 ) // Indicates a command line error - return returnCode; - + return returnCode; + // writing to session.configData() or session.Config() here // overrides command line args // only do this if you know you need to int numFailed = session.run(); - // Note that on unices only the lower 8 bits are usually used, clamping - // the return value to 255 prevents false negative when some multiple - // of 256 tests has failed - return ( numFailed < 0xff ? numFailed : 0xff ); + + // numFailed is clamped to 255 as some unices only use the lower 8 bits. + // This clamping has already been applied, so just return it here + // You can also do any post run clean-up here + return numFailed; } ``` @@ -65,8 +66,45 @@ To take full control of the config simply omit the call to ```applyCommandLine() ## Adding your own command line options -Catch embeds a powerful command line parser which you can also use to parse your own options out. This capability is still in active development but will be documented here when it is ready. +Catch embeds a powerful command line parser called [Clara](https://github.com/philsquared/Clara). +As of Catch2 (and Clara 1.0) Clara allows you to write _composable_ option and argument parsers, +so extending Catch's own command line options is now easy. + +```c++ +#define CATCH_CONFIG_RUNNER +#include "catch.hpp" + +int main( int argc, char* argv[] ) +{ + Catch::Session session; // There must be exactly one instance + + int height = 0; // Some user variable you want to be able to set + + // Build a new parser on top of Catch's + auto cli + = session.cli() // Get Catch's composite command line parser + | Opt( height, "height" ) // bind variable to a new option, with a hint string + ["-g"]["--height"] // the option names it will respond to + ("how high?"); // description string for the help output + + // Now pass the new composite back to Catch so it uses that + session.cli( cli ); + + // Let Catch (using Clara) parse the command line + int returnCode = session.applyCommandLine( argc, argv ); + if( returnCode != 0 ) // Indicates a command line error + return returnCode; + + // if set on the command line then 'height' is now set at this point + if( height > 0 ) + std::cout << "height: " << height << std::endl; + + return session.run(); +} +``` + +See the [Clara documentation](https://github.com/philsquared/Clara/blob/master/README.md) for more details. --- -[Home](Readme.md) +[Home](Readme.md#top) diff --git a/docs/release-notes.md b/docs/release-notes.md index 3661c6f6..f51f68fb 100644 --- a/docs/release-notes.md +++ b/docs/release-notes.md @@ -1,70 +1,106 @@ -# 1.11.0 + +# 2.0.1 -### 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 +## Breaking changes +* Removed C++98 support +* Removed legacy reporter support +* Removed legacy generator support + * Generator support will come back later, reworked +* Removed `Catch::toString` support + * The new stringification machinery uses `Catch::StringMaker` specializations first and `operator<<` overloads second. +* Removed legacy `SCOPED_MSG` and `SCOPED_INFO` macros +* Removed `INTERNAL_CATCH_REGISTER_REPORTER` + * `CATCH_REGISTER_REPORTER` should be used to register reporters +* Removed legacy `[hide]` tag + * `[.]`, `[.foo]` and `[!hide]` are still supported +* Output into debugger is now colourized +* `*_THROWS_AS(expr, exception_type)` now unconditionally appends `const&` to the exception type. +* `CATCH_CONFIG_FAST_COMPILE` now affects the `CHECK_` family of assertions as well as `REQUIRE_` family of assertions + * This is most noticeable in `CHECK(throws())`, which would previously report failure, properly stringify the exception and continue. Now it will report failure and stop executing current section. +* Removed deprecated matcher utility functions `Not`, `AllOf` and `AnyOf`. + * They are superseded by operators `!`, `&&` and `||`, which are natural and do not have limited arity +* Removed support for non-const comparison operators + * Non-const comparison operators are an abomination that should not exist + * They were breaking support for comparing function to function pointer +* `std::pair` and `std::tuple` are no longer stringified by default + * This is done to avoid dragging in `` and `` headers in common path + * Their stringification can be enabled per-file via new configuration macros +* `Approx` is subtly different and hopefully behaves more as users would expect + * `Approx::scale` defaults to `0.0` + * `Approx::epsilon` no longer applies to the larger of the two compared values, but only to the `Approx`'s value + * `INFINITY == Approx(INFINITY)` returns true -# 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 +## Improvements +* Reporters and Listeners can be defined in files different from the main file + * The file has to define `CATCH_CONFIG_EXTERNAL_INTERFACES` before including catch.hpp. +* Errors that happen during set up before main are now caught and properly reported once main is entered + * If you are providing your own main, you can access and use these as well. +* New assertion macros, *_THROWS_WITH(expr, exception_type, matcher) are provided + * As the arguments suggest, these allow you to assert that an expression throws desired type of exception and pass the exception to a matcher. +* JUnit reporter no longer has significantly different output for test cases with and without sections +* Most assertions now support expressions containing commas (ie `REQUIRE(foo() == std::vector{1, 2, 3});`) +* Catch now contains experimental micro benchmarking support + * See `projects/SelfTest/Benchmark.tests.cpp` for examples + * The support being experiment means that it can be changed without prior notice +* Catch uses new CLI parsing library (Clara) + * Users can now easily add new command line options to the final executable + * This also leads to some changes in `Catch::Session` interface +* All parts of matchers can be removed from a TU by defining `CATCH_CONFIG_DISABLE_MATCHERS` + * This can be used to somewhat speed up compilation times +* An experimental implementation of `CATCH_CONFIG_DISABLE` has been added + * Inspired by Doctest's `DOCTEST_CONFIG_DISABLE` + * Useful for implementing tests in source files + * ie for functions in anonymous namespaces + * Removes all assertions + * Prevents `TEST_CASE` registrations + * Exception translators are not registered + * Reporters are not registered + * Listeners are not registered +* Reporters/Listeners are now notified of fatal errors + * This means specific signals or structured exceptions + * The Reporter/Listener interface provides default, empty, implementation to preserve backward compatibility +* Stringification of `std::chrono::duration` and `std::chrono::time_point` is now supported + * Needs to be enabled by a per-file compile time configuration option +* Add `pkg-config` support to CMake install command -# 1.9.7 - -### Fixes -* Various warnings from clang-tidy, Resharper-C++ and PVS Studio have been addressed (#957) -* Dynamically generated sections are now properly reported (#963) -* Writes to `std::clog` are redirected for reporters (#989) - * Previously only `std::cerr` writes were redirected - * Interleaved writes to `std::cerr` and `std::clog` are combined properly -* Assertions failed before signals/structured exceptions fails test case are properly reported as failed (#990) - -### Improvements -* Catch's runtime overhead has been decreased further (#940) -* Added support for IBM i ILE c++ compiler (#976) - * This means that AS/400 is now supported. -* The default reporter can be configured at compile time (#978) - * That is, the reporter used if no reporter is explicitly specified - -### Other -* `ParseAndAddCatchTests` cmake script has couple new customization options +## Fixes +* Don't use console colour if running in XCode +* Explicit constructor in reporter base class +* Swept out `-Wweak-vtables`, `-Wexit-time-destructors`, `-Wglobal-constructors` warnings +* Compilation for Universal Windows Platform (UWP) is supported + * SEH handling and colorized output are disabled when compiling for UWP +* Implemented a workaround for `std::uncaught_exception` issues in libcxxrt + * These issues caused incorrect section traversals + * The workaround is only partial, user's test can still trigger the issue by using `throw;` to rethrow an exception +* Suppressed C4061 warning under MSVC +## Internal changes +* The development version now uses .cpp files instead of header files containing implementation. + * This makes partial rebuilds much faster during development +* The expression decomposition layer has been rewritten +* The evaluation layer has been rewritten +* New library (TextFlow) is used for formatting text to output -# 1.9.6 -### Improvements +# Older versions + +## 1.9.x + +### 1.9.6 + +#### Improvements * Catch's runtime overhead has been significantly decreased (#937, #939) * Added `--list-extra-info` cli option (#934). * It lists all tests together with extra information, ie filename, line number and description. -# 1.9.5 +### 1.9.5 -### Fixes +#### Fixes * Truthy expressions are now reconstructed properly, not as booleans (#914) * Various warnings are no longer erroneously suppressed in test files (files that include `catch.hpp`, but do not define `CATCH_CONFIG_MAIN` or `CATCH_CONFIG_RUNNER`) (#871) * Catch no longer fails to link when main is compiled as C++, but linked against Objective-C (#855) @@ -72,35 +108,35 @@ * Previously any GCC with minor version less than 3 would be incorrectly classified as not supporting `__COUNTER__`. * Suppressed C4996 warning caused by upcoming updated to MSVC 2017, marking `std::uncaught_exception` as deprecated. (#927) -### Improvements +#### Improvements * CMake integration script now incorporates debug messages and registers tests in an improved way (#911) * Various documentation improvements -# 1.9.4 +### 1.9.4 -### Fixes +#### Fixes * `CATCH_FAIL` macro no longer causes compilation error without variadic macro support * `INFO` messages are no longer cleared after being reported once -### Improvements and minor changes +#### Improvements and minor changes * Catch now uses `wmain` when compiled under Windows and `UNICODE` is defined. * Note that Catch still officially supports only ASCII -# 1.9.3 +### 1.9.3 -### Fixes +#### Fixes * Completed the fix for (lack of) uint64_t in earlier Visual Studios -# 1.9.2 +### 1.9.2 -### Improvements and minor changes +#### Improvements and minor changes * All of `Approx`'s member functions now accept strong typedefs in C++11 mode (#888) * Previously `Approx::scale`, `Approx::epsilon`, `Approx::margin` and `Approx::operator()` didn't. -### Fixes +#### Fixes * POSIX signals are now disabled by default under QNX (#889) * QNX does not support current enough (2001) POSIX specification * JUnit no longer counts exceptions as failures if given test case is marked as ok to fail. @@ -108,22 +144,22 @@ * Catch no longer attempts to define `uint64_t` on windows (#862) * This was causing trouble when compiled under Cygwin -### Other +#### Other * Catch is now compiled under MSVC 2017 using `std:c++latest` (C++17 mode) in CI * We now provide cmake script that autoregisters Catch tests into ctest. * See `contrib` folder. -# 1.9.1 +### 1.9.1 -### Fixes +#### Fixes * Unexpected exceptions are no longer ignored by default (#885, #887) -# 1.9.0 +### 1.9.0 -### Improvements and minor changes +#### Improvements and minor changes * Catch no longer attempts to ensure the exception type passed by user in `REQUIRE_THROWS_AS` is a constant reference. * It was causing trouble when `REQUIRE_THROWS_AS` was used inside templated functions * This actually reverts changes made in v1.7.2 @@ -137,7 +173,7 @@ * When Catch is compiled using C++11, `Approx` is now constructible with anything that can be explicitly converted to `double`. * Captured messages are now printed on unexpected exceptions -### Fixes: +#### Fixes: * Clang's `-Wexit-time-destructors` should be suppressed for Catch's internals * GCC's `-Wparentheses` is now suppressed for all TU's that include `catch.hpp`. * This is functionally a revert of changes made in 1.8.0, where we tried using `_Pragma` based suppression. This should have kept the suppression local to Catch's assertions, but bugs in GCC's handling of `_Pragma`s in C++ mode meant that it did not always work. @@ -146,16 +182,18 @@ * [Details can be found in documentation](configuration.md#catch_config_cpp11_stream_insertable_check) -### Other notes: +#### Other notes: * We have added VS 2017 to our CI * Work on Catch 2 should start soon -# 1.8.2 +## 1.8.x + +### 1.8.2 -### Improvements and minor changes +#### Improvements and minor changes * TAP reporter now behaves as if `-s` was always set * This should be more consistent with the protocol desired behaviour. * Compact reporter now obeys `-d yes` argument (#780) @@ -165,11 +203,11 @@ * This means it reports `INFO` messages on success, if output on success (`-s`) is enabled. * Previously it only reported `INFO` messages on failure. * `CAPTURE(expr)` now stringifies `expr` in the same way assertion macros do (#639) -* Listeners are now finally [documented](event-listeners.md). +* Listeners are now finally [documented](event-listeners.md#top). * Listeners provide a way to hook into events generated by running your tests, including start and end of run, every test case, every section and every assertion. -### Fixes: +#### Fixes: * Catch no longer attempts to reconstruct expression that led to a fatal error (#810) * This fixes possible signal/SEH loop when processing expressions, where the signal was triggered by expression decomposition. * Fixed (C4265) missing virtual destructor warning in Matchers (#844) @@ -186,25 +224,25 @@ * Regression in Objective-C bindings (Matchers) fixed (#854) -### Other notes: +#### Other notes: * We have added VS 2013 and 2015 to our CI * Catch Classic (1.x.x) now contains its own, forked, version of Clara (the argument parser). -# 1.8.1 +### 1.8.1 -### Fixes +#### Fixes Cygwin issue with `gettimeofday` - `#define` was not early enough -# 1.8.0 +### 1.8.0 -### New features/ minor changes +#### New features/ minor changes * Matchers have new, simpler (and documented) interface. * Catch provides string and vector matchers. - * For details see [Matchers documentation](matchers.md). + * For details see [Matchers documentation](matchers.md#top). * Changed console reporter test duration reporting format (#322) * Old format: `Some simple comparisons between doubles completed in 0.000123s` * New format: `xxx.123s: Some simple comparisons between doubles` _(There will always be exactly 3 decimal places)_ @@ -215,12 +253,12 @@ Cygwin issue with `gettimeofday` - `#define` was not early enough * _More changes are coming_ * Added [TAP (Test Anything Protocol)](https://testanything.org/) and [Automake](https://www.gnu.org/software/automake/manual/html_node/Log-files-generation-and-test-results-recording.html#Log-files-generation-and-test-results-recording) reporters. * These are not present in the default single-include header and need to be downloaded from GitHub separately. - * For details see [documentation about integrating with build systems](build-systems.md). + * For details see [documentation about integrating with build systems](build-systems.md#top). * XML reporter now reports filename as part of the `Section` and `TestCase` tags. * `Approx` now supports an optional margin of absolute error - * It has also received [new documentation](assertions.md). + * It has also received [new documentation](assertions.md#top). -### Fixes +#### Fixes * Silenced C4312 ("conversion from int to 'ClassName *") warnings in the evaluate layer. * Fixed C4512 ("assignment operator could not be generated") warnings under VS2013. * Cygwin compatibility fixes @@ -231,13 +269,15 @@ Cygwin issue with `gettimeofday` - `#define` was not early enough * Otherwise it is supressed for the whole TU * Fixed test spec parser issue (with escapes in multiple names) -### Other +#### Other * Various documentation fixes and improvements -# 1.7.2 +## 1.7.x -### Fixes and minor improvements +### 1.7.2 + +#### Fixes and minor improvements Xml: (technically the first two are breaking changes but are also fixes and arguably break few if any people) @@ -256,26 +296,26 @@ Other: * Silenced a few more warnings in different circumstances * Travis improvements -# 1.7.1 +### 1.7.1 -### Fixes: +#### Fixes: * Fixed inconsistency in defining `NOMINMAX` and `WIN32_LEAN_AND_MEAN` inside `catch.hpp`. * Fixed SEH-related compilation error under older MinGW compilers, by making Windows SEH handling opt-in for compilers other than MSVC. - * For specifics, look into the [documentation](configuration.md). + * For specifics, look into the [documentation](configuration.md#top). * Fixed compilation error under MinGW caused by improper compiler detection. * Fixed XML reporter sometimes leaving an empty output file when a test ends with signal/structured exception. * Fixed XML reporter not reporting captured stdout/stderr. * Fixed possible infinite recursion in Windows SEH. * Fixed possible compilation error caused by Catch's operator overloads being ambiguous in regards to user-defined templated operators. -## 1.7.0 +### 1.7.0 -### Features/ Changes: +#### Features/ Changes: * Catch now runs significantly faster for passing tests * Microbenchmark focused on Catch's overhead went from ~3.4s to ~0.7s. * Real world test using [JSON for Modern C++](https://github.com/nlohmann/json)'s test suite went from ~6m 25s to ~4m 14s. * Catch can now run specific sections within test cases. - * For now the support is only basic (no wildcards or tags), for details see the [documentation](command-line.md). + * For now the support is only basic (no wildcards or tags), for details see the [documentation](command-line.md#top). * Catch now supports SEH on Windows as well as signals on Linux. * After receiving a signal, Catch reports failing assertion and then passes the signal onto the previous handler. * Approx can be used to compare values against strong typedefs (available in C++11 mode only). @@ -284,61 +324,63 @@ Other: * Certain characters (space, tab, etc) are now pretty printed. * This means that a `char c = ' '; REQUIRE(c == '\t');` would be printed as `' ' == '\t'`, instead of ` == 9`. -### Fixes: +#### Fixes: * Text formatting no longer attempts to access out-of-bounds characters under certain conditions. * THROW family of assertions no longer trigger `-Wunused-value` on expressions containing explicit cast. * Breaking into debugger under OS X works again and no longer required `DEBUG` to be defined. * Compilation no longer breaks under certain compiler if a lambda is used inside assertion macro. -### Other: +#### Other: * Catch's CMakeLists now defines install command. * Catch's CMakeLists now generates projects with warnings enabled. -## 1.6.1 +## 1.6.x -### Features/ Changes: +### 1.6.1 + +#### Features/ Changes: * Catch now supports breaking into debugger on Linux -### Fixes: +#### Fixes: * Generators no longer leak memory (generators are still unsupported in general) * JUnit reporter now reports UTC timestamps, instead of "tbd" * `CHECK_THAT` macro is now properly defined as `CATCH_CHECK_THAT` when using `CATCH_` prefixed macros -### Other: +#### Other: * Types with overloaded `&&` operator are no longer evaluated twice when used in an assertion macro. * The use of `__COUNTER__` is supressed when Catch is parsed by CLion * This change is not active when compiling a binary * Approval tests can now be run on Windows * CMake will now warn if a file is present in the `include` folder but not is not enumerated as part of the project * Catch now defines `NOMINMAX` and `WIN32_LEAN_AND_MEAN` before including `windows.h` - * This can be disabled if needed, see [documentation](configuration.md) for details. + * This can be disabled if needed, see [documentation](configuration.md#top) for details. -## 1.6.0 +### 1.6.0 -### Cmake/ projects: +#### Cmake/ projects: * Moved CMakeLists.txt to root, made it friendlier for CLion and generating XCode and VS projects, and removed the manually maintained XCode and VS projects. -### Features/ Changes: +#### Features/ Changes: * Approx now supports `>=` and `<=` * Can now use `\` to escape chars in test names on command line * Standardize C++11 feature toggles -### Fixes: +#### Fixes: * Blue shell colour * Missing argument to `CATCH_CHECK_THROWS` * Don't encode extended ASCII in XML * use `std::shuffle` on more compilers (fixes deprecation warning/error) * Use `__COUNTER__` more consistently (where available) -### Other: +#### Other: * Tweaks and changes to scripts - particularly for Approval test - to make them more portable -# Older versions +# Even Older versions Release notes were not maintained prior to v1.6.0, but you should be able to work them out from the Git history --- -[Home](Readme.md) +[Home](Readme.md#top) diff --git a/docs/release-process.md b/docs/release-process.md index 8e93a942..34002ae6 100644 --- a/docs/release-process.md +++ b/docs/release-process.md @@ -1,3 +1,4 @@ + # How to release When enough changes have accumulated, it is time to release new version of Catch. This document describes the proces in doing so, that no steps are forgotten. Note that all referenced scripts can be found in the `scripts/` directory. @@ -16,7 +17,7 @@ There should be no differences, as Approval tests should be updated when changes ### Incrementing version number -Catch uses a variant of [semantic versioning](http://semver.org/), with breaking API changes (and thus major version increments) being very rare. Thus, the release will usually increment the patch version, when it only contains couple of bugfixes, or minor version, when it contains new functionality, or larger changes in implementation of current functionality. +Catch uses a variant of [semantic versioning](http://semver.org/), with breaking API changes (and thus major version increments) being very rare. Thus, the release will usually increment the patch version, when it only contains couple of bugfixes, or minor version, when it contains new functionality, or larger changes in implementation of current functionality. After deciding which part of version number should be incremented, you can use one of the `*Release.py` scripts to perform the required changes to Catch. @@ -33,7 +34,7 @@ Once a release is ready, release notes need to be written. They should summarize ### Commit and push update to GitHub -After version number is incremented, single-include header is regenerated and release notes are updated, changes should be commited and pushed to GitHub. +After version number is incremented, single-include header is regenerated and release notes are updated, changes should be commited and pushed to GitHub. ### Release on GitHub diff --git a/docs/reporters.md b/docs/reporters.md index 703234b1..78e78ee9 100644 --- a/docs/reporters.md +++ b/docs/reporters.md @@ -1,3 +1,4 @@ + # Reporters Catch has a modular reporting system and comes bundled with a handful of useful reporters built in. @@ -17,12 +18,12 @@ There are four reporters built in to the single include: * `console` writes as lines of text, formatted to a typical terminal width, with colours if a capable terminal is detected. * `compact` similar to `console` but optimised for minimal output - each entry on one line -* `junit` writes xml that corresponds to Ant's [junitreport](http://help.catchsoftware.com/display/ET/JUnit+Format) target. Useful for build systems that understand Junit. If you are using Jenkins with Catch 1.x, you can improve quality of output by applying changes in [#923](https://github.com/philsquared/Catch/pull/923). +* `junit` writes xml that corresponds to Ant's [junitreport](http://help.catchsoftware.com/display/ET/JUnit+Format) target. Useful for build systems that understand Junit. Because of the way the junit format is structured the run must complete before anything is written. * `xml` writes an xml format tailored to Catch. Unlike `junit` this is a streaming format so results are delivered progressively. There are a few additional reporters, for specific build systems, in the Catch repository (in `include\reporters`) which you can `#include` in your project if you would like to make use of them. -Do this in one source file - typically the same one you have `CATCH_CONFIG_MAIN` or `CATCH_CONFIG_RUNNER`. +Do this in one source file - the same one you have `CATCH_CONFIG_MAIN` or `CATCH_CONFIG_RUNNER`. * `teamcity` writes the native, streaming, format that [TeamCity](https://www.jetbrains.com/teamcity/) understands. Use this when building as part of a TeamCity build to see results as they happen. @@ -42,4 +43,4 @@ but do keep in mind upcoming changes (these will be minor, simplifying, changes --- -[Home](Readme.md) \ No newline at end of file +[Home](Readme.md#top) diff --git a/docs/slow-compiles.md b/docs/slow-compiles.md index a12d0867..0853b661 100644 --- a/docs/slow-compiles.md +++ b/docs/slow-compiles.md @@ -1,5 +1,12 @@ + # Why do my tests take so long to compile? +**Contents**
+[Short answer](#short-answer)
+[Long answer](#long-answer)
+[Practical example](#practical-example)
+[Other possible solutions](#other-possible-solutions)
+ Several people have reported that test code written with Catch takes much longer to compile than they would expect. Why is that? Catch is implemented entirely in headers. There is a little overhead due to this - but not as much as you might think - and you can minimise it simply by organising your test code as follows: @@ -18,7 +25,7 @@ Because Catch is implemented *entirely* in headers you might think that the whol As a result the main source file *does* compile the whole of Catch every time! So it makes sense to dedicate this file to *only* ```#define```-ing the identifier and ```#include```-ing Catch (and implementing the runner code, if you're doing that). Keep all your test cases in other files. This way you won't pay the recompilation cost for the whole of Catch ## Practical example -Assume you have the `Factorial` function from the [tutorial](tutorial.md) in `factorial.cpp` (with forward declaration in `factorial.h`) and want to test it and keep the compile times down when adding new tests. Then you should have 2 files, `tests-main.cpp` and `tests-factorial.cpp`: +Assume you have the `Factorial` function from the [tutorial](tutorial.md#top) in `factorial.cpp` (with forward declaration in `factorial.h`) and want to test it and keep the compile times down when adding new tests. Then you should have 2 files, `tests-main.cpp` and `tests-factorial.cpp`: ```cpp // tests-main.cpp @@ -61,4 +68,4 @@ You can also opt to sacrifice some features in order to speed-up Catch's compila --- -[Home](Readme.md) \ No newline at end of file +[Home](Readme.md#top) diff --git a/docs/test-cases-and-sections.md b/docs/test-cases-and-sections.md index 529531d0..68944f51 100644 --- a/docs/test-cases-and-sections.md +++ b/docs/test-cases-and-sections.md @@ -1,3 +1,4 @@ + # Test cases and sections While Catch fully supports the traditional, xUnit, style of class-based fixtures containing test case methods this is not the preferred style. @@ -11,7 +12,7 @@ Test cases and sections are very easy to use in practice: _test name_ and _section name_ are free form, quoted, strings. The optional _tags_ argument is a quoted string containing one or more tags enclosed in square brackets. Tags are discussed below. Test names must be unique within the Catch executable. -For examples see the [Tutorial](tutorial.md) +For examples see the [Tutorial](tutorial.md#top) ## Tags @@ -34,7 +35,7 @@ Tag names are not case sensitive and can contain any ASCII characters. This mean All tag names beginning with non-alphanumeric characters are reserved by Catch. Catch defines a number of "special" tags, which have meaning to the test runner itself. These special tags all begin with a symbol character. Following is a list of currently defined special tags and their meanings. -* `[!hide]` or `[.]` (or, for legacy reasons, `[hide]`) - causes test cases to be skipped from the default list (i.e. when no test cases have been explicitly selected through tag expressions or name wildcards). The hide tag is often combined with another, user, tag (for example `[.][integration]` - so all integration tests are excluded from the default run but can be run by passing `[integration]` on the command line). As a short-cut you can combine these by simply prefixing your user tag with a `.` - e.g. `[.integration]`. Because the hide tag has evolved to have several forms, all forms are added as tags if you use one of them. +* `[!hide]` or `[.]` - causes test cases to be skipped from the default list (i.e. when no test cases have been explicitly selected through tag expressions or name wildcards). The hide tag is often combined with another, user, tag (for example `[.][integration]` - so all integration tests are excluded from the default run but can be run by passing `[integration]` on the command line). As a short-cut you can combine these by simply prefixing your user tag with a `.` - e.g. `[.integration]`. Because the hide tag has evolved to have several forms, all forms are added as tags if you use one of them. * `[!throws]` - lets Catch know that this test is likely to throw an exception even if successful. This causes the test to be excluded when running with `-e` or `--nothrow`. @@ -48,6 +49,8 @@ All tag names beginning with non-alphanumeric characters are reserved by Catch. * `[@]` - tag aliases all begin with `@` (see below). +* `[!benchmark]` - this test case is actually a benchmark. This is an experimental feature, and currently has no documentation. If you want to try it out, look at `projects/SelfTest/Benchmark.tests.cpp` for details. + ## Tag aliases Between tag expressions and wildcarded test names (as well as combinations of the two) quite complex patterns can be constructed to direct which test cases are run. If a complex pattern is used often it is convenient to be able to create an alias for the expression. This can be done, in code, using the following form: @@ -85,4 +88,4 @@ Other than the additional prefixes and the formatting in the console reporter th --- -[Home](Readme.md) +[Home](Readme.md#top) diff --git a/docs/test-fixtures.md b/docs/test-fixtures.md index fc546b39..1dc7baba 100644 --- a/docs/test-fixtures.md +++ b/docs/test-fixtures.md @@ -1,3 +1,6 @@ + +# Test fixtures + Although Catch allows you to group tests together as sections within a test case, it can still be convenient, sometimes, to group them using a more traditional test fixture. Catch fully supports this too. You define the test fixture as a simple structure: ```c++ @@ -29,4 +32,4 @@ The two test cases here will create uniquely-named derived classes of UniqueTest --- -[Home](Readme.md) +[Home](Readme.md#top) diff --git a/docs/tostring.md b/docs/tostring.md index b0c8d556..cfca0533 100644 --- a/docs/tostring.md +++ b/docs/tostring.md @@ -1,7 +1,8 @@ + # String conversions Catch needs to be able to convert types you use in assertions and logging expressions into strings (for logging and reporting purposes). -Most built-in or std types are supported out of the box but there are three ways that you can tell Catch how to convert your own types (or other, third-party types) into strings. +Most built-in or std types are supported out of the box but there are two ways that you can tell Catch how to convert your own types (or other, third-party types) into strings. ## operator << overload for std::ostream @@ -16,34 +17,19 @@ std::ostream& operator << ( std::ostream& os, T const& value ) { (where ```T``` is your type and ```convertMyTypeToString``` is where you'll write whatever code is necessary to make your type printable - it doesn't have to be in another function). -You should put this function in the same namespace as your type and it has to be declared before including Catch's header. - -## Catch::toString overload - -If you don't want to provide an ```operator <<``` overload, or you want to convert your type differently for testing purposes, you can provide an overload for ```Catch::toString()``` for your type. - -``` -namespace Catch { - std::string toString( T const& value ) { - return convertMyTypeToString( value ); - } -} -``` - -Again ```T``` is your type and ```convertMyTypeToString``` is where you'll write whatever code is necessary to make your type printable. Note that the function must be in the Catch namespace, which itself must be in the global namespace and must be declared _before_ Catch's header is included. - -**Please note that overloading `Catch::toString` is currently considered legacy and will not be supported in the next major version of Catch.** +You should put this function in the same namespace as your type and have it declared before including Catch's header. ## Catch::StringMaker specialisation -Another way of telling Catch how to convert a type to string is specialising `Catch::StringMaker` template. This allows you to have separate way of stringifying types for Catch, than you have for writing it to a stream and also doesn't require you to declare it before including Catch's header. +If you don't want to provide an ```operator <<``` overload, or you want to convert your type differently for testing purposes, you can provide a specialization for `Catch::StringMaker`: ``` namespace Catch { - template<> struct StringMaker { + template<> + struct StringMaker { static std::string convert( T const& value ) { - return convertMyTypeToString( value ); - } - }; + return convertMyTypeToString( value ); + } + }; } ``` @@ -59,4 +45,4 @@ CATCH_TRANSLATE_EXCEPTION( MyType& ex ) { --- -[Home](Readme.md) +[Home](Readme.md#top) diff --git a/docs/tutorial.md b/docs/tutorial.md index 600ba3ba..1b55bcf1 100644 --- a/docs/tutorial.md +++ b/docs/tutorial.md @@ -1,4 +1,16 @@ -# Getting Catch + +# Tutorial + +**Contents**
+[Getting Catch](#getting-catch)
+[Where to put it?](#where-to-put-it)
+[Writing tests](#writing-tests)
+[Test cases and sections](#test-cases-and-sections)
+[BDD-Style](#bdd-style)
+[Scaling up](#scaling-up)
+[Next steps](#next-steps)
+ +## Getting Catch The simplest way to get Catch is to download the latest [single header version](https://raw.githubusercontent.com/philsquared/Catch/master/single_include/catch.hpp). The single header is generated by merging a set of individual headers but it is still just normal source code in a header file. @@ -11,7 +23,7 @@ Catch is header only. All you need to do is drop the file(s) somewhere reachable The rest of this tutorial will assume that the Catch single-include header (or the include folder) is available unqualified - but you may need to prefix it with a folder name if necessary. -# Writing tests +## Writing tests Let's start with a really simple example. Say you have written a function to calculate factorials and now you want to test it (let's leave aside TDD for now). @@ -39,7 +51,7 @@ TEST_CASE( "Factorials are computed", "[factorial]" ) { } ``` -This will compile to a complete executable which responds to [command line arguments](command-line.md). If you just run it with no arguments it will execute all test cases (in this case there is just one), report any failures, report a summary of how many tests passed and failed and return the number of failed tests (useful for if you just want a yes/ no answer to: "did it work"). +This will compile to a complete executable which responds to [command line arguments](command-line.md#top). If you just run it with no arguments it will execute all test cases (in this case there is just one), report any failures, report a summary of how many tests passed and failed and return the number of failed tests (useful for if you just want a yes/ no answer to: "did it work"). If you run this as written it will pass. Everything is good. Right? Well, there is still a bug here. In fact the first version of this tutorial I posted here genuinely had the bug in! So it's not completely contrived (thanks to Daryle Walker (```@CTMacUser```) for pointing this out). @@ -82,12 +94,12 @@ Now all the tests pass. Of course there are still more issues to deal with. For example we'll hit problems when the return value starts to exceed the range of an unsigned int. With factorials that can happen quite quickly. You might want to add tests for such cases and decide how to handle them. We'll stop short of doing that here. -## What did we do here? +### What did we do here? Although this was a simple test it's been enough to demonstrate a few things about how Catch is used. Let's take moment to consider those before we move on. -1. All we did was ```#define``` one identifier and ```#include``` one header and we got everything - even an implementation of ```main()``` that will [respond to command line arguments](command-line.md). You can only use that ```#define``` in one implementation file, for (hopefully) obvious reasons. Once you have more than one file with unit tests in you'll just ```#include "catch.hpp"``` and go. Usually it's a good idea to have a dedicated implementation file that just has ```#define CATCH_CONFIG_MAIN``` and ```#include "catch.hpp"```. You can also provide your own implementation of main and drive Catch yourself (see [Supplying-your-own-main()](own-main.md)). -2. We introduce test cases with the ```TEST_CASE``` macro. This macro takes one or two arguments - a free form test name and, optionally, one or more tags (for more see Test cases and Sections, ). The test name must be unique. You can run sets of tests by specifying a wildcarded test name or a tag expression. See the [command line docs](command-line.md) for more information on running tests. +1. All we did was ```#define``` one identifier and ```#include``` one header and we got everything - even an implementation of ```main()``` that will [respond to command line arguments](command-line.md#top). You can only use that ```#define``` in one implementation file, for (hopefully) obvious reasons. Once you have more than one file with unit tests in you'll just ```#include "catch.hpp"``` and go. Usually it's a good idea to have a dedicated implementation file that just has ```#define CATCH_CONFIG_MAIN``` and ```#include "catch.hpp"```. You can also provide your own implementation of main and drive Catch yourself (see [Supplying-your-own-main()](own-main.md#top)). +2. We introduce test cases with the ```TEST_CASE``` macro. This macro takes one or two arguments - a free form test name and, optionally, one or more tags (for more see Test cases and Sections, ). The test name must be unique. You can run sets of tests by specifying a wildcarded test name or a tag expression. See the [command line docs](command-line.md#top) for more information on running tests. 3. The name and tags arguments are just strings. We haven't had to declare a function or method - or explicitly register the test case anywhere. Behind the scenes a function with a generated name is defined for you, and automatically registered using static registry classes. By abstracting the function name away we can name our tests without the constraints of identifier names. 4. We write our individual test assertions using the ```REQUIRE``` macro. Rather than a separate macro for each type of condition we express the condition naturally using C/C++ syntax. Behind the scenes a simple set of expression templates captures the left-hand-side and right-hand-side of the expression so we can display the values in our test report. As we'll see later there _are_ other assertion macros - but because of this technique the number of them is drastically reduced. @@ -161,7 +173,7 @@ Sections can be nested to an arbitrary depth (limited only by your stack size). ## BDD-Style -If you name your test cases and sections appropriately you can achieve a BDD-style specification structure. This became such a useful way of working that first class support has been added to Catch. Scenarios can be specified using ```SCENARIO```, ```GIVEN```, ```WHEN``` and ```THEN``` macros, which map on to ```TEST_CASE```s and ```SECTION```s, respectively. For more details see [Test cases and sections](test-cases-and-sections.md). +If you name your test cases and sections appropriately you can achieve a BDD-style specification structure. This became such a useful way of working that first class support has been added to Catch. Scenarios can be specified using ```SCENARIO```, ```GIVEN```, ```WHEN``` and ```THEN``` macros, which map on to ```TEST_CASE```s and ```SECTION```s, respectively. For more details see [Test cases and sections](test-cases-and-sections.md#top). The vector example can be adjusted to use these macros like so: @@ -224,7 +236,7 @@ Scenario: vectors can be sized and resized To keep the tutorial simple we put all our code in a single file. This is fine to get started - and makes jumping into Catch even quicker and easier. As you write more real-world tests, though, this is not really the best approach. -The requirement is that the following block of code ([or equivalent](own-main.md)): +The requirement is that the following block of code ([or equivalent](own-main.md#top)): ```c++ #define CATCH_CONFIG_MAIN @@ -233,7 +245,7 @@ The requirement is that the following block of code ([or equivalent](own-main.md appears in _exactly one_ source file. Use as many additional cpp files (or whatever you call your implementation files) as you need for your tests, partitioned however makes most sense for your way of working. Each additional file need only ```#include "catch.hpp"``` - do not repeat the ```#define```! -In fact it is usually a good idea to put the block with the ```#define``` [in its own source file](slow-compiles.md). +In fact it is usually a good idea to put the block with the ```#define``` [in its own source file](slow-compiles.md#top). Do not write your tests in header files! @@ -242,8 +254,8 @@ Do not write your tests in header files! This has been a brief introduction to get you up and running with Catch, and to point out some of the key differences between Catch and other frameworks you may already be familiar with. This will get you going quite far already and you are now in a position to dive in and write some tests. -Of course there is more to learn - most of which you should be able to page-fault in as you go. Please see the ever-growing [Reference section](Readme.md) for what's available. +Of course there is more to learn - most of which you should be able to page-fault in as you go. Please see the ever-growing [Reference section](Readme.md#top) for what's available. --- -[Home](Readme.md) +[Home](Readme.md#top) diff --git a/docs/why-catch.md b/docs/why-catch.md index b77ca2c2..45f58a6a 100644 --- a/docs/why-catch.md +++ b/docs/why-catch.md @@ -1,46 +1,46 @@ + # Why do we need yet another C++ test framework? -Good question. For C++ there are quite a number of established frameworks, including (but not limited to), [CppUnit](http://sourceforge.net/apps/mediawiki/cppunit/index.php?title=Main_Page), [Google Test](http://code.google.com/p/googletest/), [Boost.Test](http://www.boost.org/doc/libs/1_49_0/libs/test/doc/html/index.html), [Aeryn](https://launchpad.net/aeryn), [Cute](http://r2.ifs.hsr.ch/cute), [Fructose](http://fructose.sourceforge.net/) and [many, many more](http://en.wikipedia.org/wiki/List_of_unit_testing_frameworks#C.2B.2B). Even for Objective-C there are a few, including OCUnit - which now comes bundled with XCode. +Good question. For C++ there are quite a number of established frameworks, +including (but not limited to), +[Google Test](http://code.google.com/p/googletest/), +[Boost.Test](http://www.boost.org/doc/libs/1_49_0/libs/test/doc/html/index.html), +[CppUnit](http://sourceforge.net/apps/mediawiki/cppunit/index.php?title=Main_Page), +[Cute](http://r2.ifs.hsr.ch/cute), +[many, many more](http://en.wikipedia.org/wiki/List_of_unit_testing_frameworks#C.2B.2B). So what does Catch bring to the party that differentiates it from these? Apart from a Catchy name, of course. ## Key Features -* Really easy to get started. Just download catch.hpp, #include it and you're away. -* No external dependencies. As long as you can compile C++98 and have a C++ standard library available. -* Write test cases as, self-registering, functions or methods. -* Divide test cases into sections, each of which is run in isolation (eliminates the need for fixtures!) +* Quick and Really easy to get started. Just download catch.hpp, `#include` it and you're away. +* No external dependencies. As long as you can compile C++11 and have a C++ standard library available. +* Write test cases as, self-registering, functions (or methods, if you prefer). +* Divide test cases into sections, each of which is run in isolation (eliminates the need for fixtures). * Use BDD-style Given-When-Then sections as well as traditional unit test cases. * Only one core assertion macro for comparisons. Standard C/C++ operators are used for the comparison - yet the full expression is decomposed and lhs and rhs values are logged. +* Tests are named using free-form strings - no more couching names in legal identifiers. ## Other core features -* Tests are named using free-form strings - no more couching names in legal identifiers. * Tests can be tagged for easily running ad-hoc groups of tests. * Failures can (optionally) break into the debugger on Windows and Mac. * Output is through modular reporter objects. Basic textual and XML reporters are included. Custom reporters can easily be added. * JUnit xml output is supported for integration with third-party tools, such as CI servers. -* A default main() function is provided (in a header), but you can supply your own for complete control (e.g. integration into your own test runner GUI). +* A default main() function is provided, but you can supply your own for complete control (e.g. integration into your own test runner GUI). * A command line parser is provided and can still be used if you choose to provided your own main() function. * Catch can test itself. * Alternative assertion macro(s) report failures but don't abort the test case * Floating point tolerance comparisons are built in using an expressive Approx() syntax. * Internal and friendly macros are isolated so name clashes can be managed -* Support for Matchers (early stages) - -## Objective-C-specific features - -* Automatically detects if you are using it from an Objective-C project -* Works with and without ARC with no additional configuration -* Implement test fixtures using Obj-C classes too (like OCUnit) -* Additional built in matchers that work with Obj-C types (e.g. string matchers) +* Matchers ## Who else is using Catch? -See the list of [open source projects using Catch](opensource-users.md). +See the list of [open source projects using Catch](opensource-users.md#top). -See the [tutorial](tutorial.md) to get more of a taste of using CATCH in practice +See the [tutorial](tutorial.md#top) to get more of a taste of using Catch in practice --- -[Home](Readme.md) \ No newline at end of file +[Home](Readme.md#top) diff --git a/include/catch.hpp b/include/catch.hpp index f2d9f3da..118337c9 100644 --- a/include/catch.hpp +++ b/include/catch.hpp @@ -19,8 +19,14 @@ #if defined(CATCH_CONFIG_MAIN) || defined(CATCH_CONFIG_RUNNER) # define CATCH_IMPL +# define CATCH_CONFIG_EXTERNAL_INTERFACES +# if defined(CATCH_CONFIG_DISABLE_MATCHERS) +# undef CATCH_CONFIG_DISABLE_MATCHERS +# endif #endif +#include "internal/catch_platform.h" + #ifdef CATCH_IMPL # ifndef CLARA_CONFIG_MAIN # define CLARA_CONFIG_MAIN_NOT_DEFINED @@ -28,18 +34,19 @@ # endif #endif -#include "internal/catch_notimplemented_exception.h" -#include "internal/catch_context.h" -#include "internal/catch_test_registry.hpp" +#include "internal/catch_tag_alias_autoregistrar.h" +#include "internal/catch_test_registry.h" #include "internal/catch_capture.hpp" #include "internal/catch_section.h" -#include "internal/catch_generators.hpp" +#include "internal/catch_benchmark.h" #include "internal/catch_interfaces_exception.h" -#include "internal/catch_approx.hpp" -#include "internal/catch_matchers_string.h" -#include "internal/catch_matchers_vector.h" +#include "internal/catch_approx.h" #include "internal/catch_compiler_capabilities.h" -#include "internal/catch_interfaces_tag_alias_registry.h" +#include "internal/catch_string_manip.h" + +#ifndef CATCH_CONFIG_DISABLE_MATCHERS +#include "internal/catch_capture_matchers.h" +#endif // These files are included here so the single_include script doesn't put them // in the conditionally compiled sections @@ -50,30 +57,11 @@ #include "internal/catch_objc.hpp" #endif -#ifdef CATCH_IMPL - -// !TBD: Move the leak detector code into a separate header -#ifdef CATCH_CONFIG_WINDOWS_CRTDBG -#include -class LeakDetector { -public: - LeakDetector() { - int flag = _CrtSetDbgFlag(_CRTDBG_REPORT_FLAG); - flag |= _CRTDBG_LEAK_CHECK_DF; - flag |= _CRTDBG_ALLOC_MEM_DF; - _CrtSetDbgFlag(flag); - _CrtSetReportMode(_CRT_WARN, _CRTDBG_MODE_FILE | _CRTDBG_MODE_DEBUG); - _CrtSetReportFile(_CRT_WARN, _CRTDBG_FILE_STDERR); - // Change this to leaking allocation's number to break there - _CrtSetBreakAlloc(-1); - } -}; -#else -class LeakDetector {}; +#ifdef CATCH_CONFIG_EXTERNAL_INTERFACES +#include "internal/catch_external_interfaces.h" #endif -LeakDetector leakDetector; - +#ifdef CATCH_IMPL #include "internal/catch_impl.hpp" #endif @@ -86,134 +74,105 @@ LeakDetector leakDetector; # undef CLARA_CONFIG_MAIN #endif +#if !defined(CATCH_CONFIG_DISABLE) ////// - // If this config identifier is defined then all CATCH macros are prefixed with CATCH_ #ifdef CATCH_CONFIG_PREFIX_ALL -#if defined(CATCH_CONFIG_FAST_COMPILE) -#define CATCH_REQUIRE( expr ) INTERNAL_CATCH_TEST_NO_TRY( "CATCH_REQUIRE", Catch::ResultDisposition::Normal, expr ) -#define CATCH_REQUIRE_FALSE( expr ) INTERNAL_CATCH_TEST_NO_TRY( "CATCH_REQUIRE_FALSE", Catch::ResultDisposition::Normal | Catch::ResultDisposition::FalseTest, expr ) -#else -#define CATCH_REQUIRE( expr ) INTERNAL_CATCH_TEST( "CATCH_REQUIRE", Catch::ResultDisposition::Normal, expr ) -#define CATCH_REQUIRE_FALSE( expr ) INTERNAL_CATCH_TEST( "CATCH_REQUIRE_FALSE", Catch::ResultDisposition::Normal | Catch::ResultDisposition::FalseTest, expr ) -#endif +#define CATCH_REQUIRE( ... ) INTERNAL_CATCH_TEST( "CATCH_REQUIRE", Catch::ResultDisposition::Normal, __VA_ARGS__ ) +#define CATCH_REQUIRE_FALSE( ... ) INTERNAL_CATCH_TEST( "CATCH_REQUIRE_FALSE", Catch::ResultDisposition::Normal | Catch::ResultDisposition::FalseTest, __VA_ARGS__ ) -#define CATCH_REQUIRE_THROWS( expr ) INTERNAL_CATCH_THROWS( "CATCH_REQUIRE_THROWS", Catch::ResultDisposition::Normal, "", expr ) +#define CATCH_REQUIRE_THROWS( ... ) INTERNAL_CATCH_THROWS( "CATCH_REQUIRE_THROWS", Catch::ResultDisposition::Normal, "", __VA_ARGS__ ) #define CATCH_REQUIRE_THROWS_AS( expr, exceptionType ) INTERNAL_CATCH_THROWS_AS( "CATCH_REQUIRE_THROWS_AS", exceptionType, Catch::ResultDisposition::Normal, expr ) -#define CATCH_REQUIRE_THROWS_WITH( expr, matcher ) INTERNAL_CATCH_THROWS( "CATCH_REQUIRE_THROWS_WITH", Catch::ResultDisposition::Normal, matcher, expr ) -#define CATCH_REQUIRE_NOTHROW( expr ) INTERNAL_CATCH_NO_THROW( "CATCH_REQUIRE_NOTHROW", Catch::ResultDisposition::Normal, expr ) +#define CATCH_REQUIRE_THROWS_WITH( expr, matcher ) INTERNAL_CATCH_THROWS_STR_MATCHES( "CATCH_REQUIRE_THROWS_WITH", Catch::ResultDisposition::Normal, matcher, expr ) +#if !defined(CATCH_CONFIG_DISABLE_MATCHERS) +#define CATCH_REQUIRE_THROWS_MATCHES( expr, exceptionType, matcher ) INTERNAL_CATCH_THROWS_MATCHES( "CATCH_REQUIRE_THROWS_MATCHES", exceptionType, Catch::ResultDisposition::Normal, matcher, expr ) +#endif// CATCH_CONFIG_DISABLE_MATCHERS +#define CATCH_REQUIRE_NOTHROW( ... ) INTERNAL_CATCH_NO_THROW( "CATCH_REQUIRE_NOTHROW", Catch::ResultDisposition::Normal, __VA_ARGS__ ) -#define CATCH_CHECK( expr ) INTERNAL_CATCH_TEST( "CATCH_CHECK", Catch::ResultDisposition::ContinueOnFailure, expr ) -#define CATCH_CHECK_FALSE( expr ) INTERNAL_CATCH_TEST( "CATCH_CHECK_FALSE", Catch::ResultDisposition::ContinueOnFailure | Catch::ResultDisposition::FalseTest, expr ) -#define CATCH_CHECKED_IF( expr ) INTERNAL_CATCH_IF( "CATCH_CHECKED_IF", Catch::ResultDisposition::ContinueOnFailure, expr ) -#define CATCH_CHECKED_ELSE( expr ) INTERNAL_CATCH_ELSE( "CATCH_CHECKED_ELSE", Catch::ResultDisposition::ContinueOnFailure, expr ) -#define CATCH_CHECK_NOFAIL( expr ) INTERNAL_CATCH_TEST( "CATCH_CHECK_NOFAIL", Catch::ResultDisposition::ContinueOnFailure | Catch::ResultDisposition::SuppressFail, expr ) +#define CATCH_CHECK( ... ) INTERNAL_CATCH_TEST( "CATCH_CHECK", Catch::ResultDisposition::ContinueOnFailure, __VA_ARGS__ ) +#define CATCH_CHECK_FALSE( ... ) INTERNAL_CATCH_TEST( "CATCH_CHECK_FALSE", Catch::ResultDisposition::ContinueOnFailure | Catch::ResultDisposition::FalseTest, __VA_ARGS__ ) +#define CATCH_CHECKED_IF( ... ) INTERNAL_CATCH_IF( "CATCH_CHECKED_IF", Catch::ResultDisposition::ContinueOnFailure, __VA_ARGS__ ) +#define CATCH_CHECKED_ELSE( ... ) INTERNAL_CATCH_ELSE( "CATCH_CHECKED_ELSE", Catch::ResultDisposition::ContinueOnFailure, __VA_ARGS__ ) +#define CATCH_CHECK_NOFAIL( ... ) INTERNAL_CATCH_TEST( "CATCH_CHECK_NOFAIL", Catch::ResultDisposition::ContinueOnFailure | Catch::ResultDisposition::SuppressFail, __VA_ARGS__ ) -#define CATCH_CHECK_THROWS( expr ) INTERNAL_CATCH_THROWS( "CATCH_CHECK_THROWS", Catch::ResultDisposition::ContinueOnFailure, "", expr ) +#define CATCH_CHECK_THROWS( ... ) INTERNAL_CATCH_THROWS( "CATCH_CHECK_THROWS", Catch::ResultDisposition::ContinueOnFailure, "", __VA_ARGS__ ) #define CATCH_CHECK_THROWS_AS( expr, exceptionType ) INTERNAL_CATCH_THROWS_AS( "CATCH_CHECK_THROWS_AS", exceptionType, Catch::ResultDisposition::ContinueOnFailure, expr ) -#define CATCH_CHECK_THROWS_WITH( expr, matcher ) INTERNAL_CATCH_THROWS( "CATCH_CHECK_THROWS_WITH", Catch::ResultDisposition::ContinueOnFailure, matcher, expr ) -#define CATCH_CHECK_NOTHROW( expr ) INTERNAL_CATCH_NO_THROW( "CATCH_CHECK_NOTHROW", Catch::ResultDisposition::ContinueOnFailure, expr ) +#define CATCH_CHECK_THROWS_WITH( expr, matcher ) INTERNAL_CATCH_THROWS_STR_MATCHES( "CATCH_CHECK_THROWS_WITH", Catch::ResultDisposition::ContinueOnFailure, matcher, expr ) +#if !defined(CATCH_CONFIG_DISABLE_MATCHERS) +#define CATCH_CHECK_THROWS_MATCHES( expr, exceptionType, matcher ) INTERNAL_CATCH_THROWS_MATCHES( "CATCH_CHECK_THROWS_MATCHES", exceptionType, Catch::ResultDisposition::ContinueOnFailure, matcher, expr ) +#endif // CATCH_CONFIG_DISABLE_MATCHERS +#define CATCH_CHECK_NOTHROW( ... ) INTERNAL_CATCH_NO_THROW( "CATCH_CHECK_NOTHROW", Catch::ResultDisposition::ContinueOnFailure, __VA_ARGS__ ) +#if !defined(CATCH_CONFIG_DISABLE_MATCHERS) #define CATCH_CHECK_THAT( arg, matcher ) INTERNAL_CHECK_THAT( "CATCH_CHECK_THAT", matcher, Catch::ResultDisposition::ContinueOnFailure, arg ) -#if defined(CATCH_CONFIG_FAST_COMPILE) -#define CATCH_REQUIRE_THAT( arg, matcher ) INTERNAL_CHECK_THAT_NO_TRY( "CATCH_REQUIRE_THAT", matcher, Catch::ResultDisposition::Normal, arg ) -#else #define CATCH_REQUIRE_THAT( arg, matcher ) INTERNAL_CHECK_THAT( "CATCH_REQUIRE_THAT", matcher, Catch::ResultDisposition::Normal, arg ) -#endif - +#endif // CATCH_CONFIG_DISABLE_MATCHERS #define CATCH_INFO( msg ) INTERNAL_CATCH_INFO( "CATCH_INFO", msg ) #define CATCH_WARN( msg ) INTERNAL_CATCH_MSG( "CATCH_WARN", Catch::ResultWas::Warning, Catch::ResultDisposition::ContinueOnFailure, msg ) -#define CATCH_SCOPED_INFO( msg ) INTERNAL_CATCH_INFO( "CATCH_INFO", msg ) -#define CATCH_CAPTURE( msg ) INTERNAL_CATCH_INFO( "CATCH_CAPTURE", #msg " := " << Catch::toString(msg) ) -#define CATCH_SCOPED_CAPTURE( msg ) INTERNAL_CATCH_INFO( "CATCH_CAPTURE", #msg " := " << Catch::toString(msg) ) +#define CATCH_CAPTURE( msg ) INTERNAL_CATCH_INFO( "CATCH_CAPTURE", #msg " := " << ::Catch::Detail::stringify(msg) ) -#ifdef CATCH_CONFIG_VARIADIC_MACROS - #define CATCH_TEST_CASE( ... ) INTERNAL_CATCH_TESTCASE( __VA_ARGS__ ) - #define CATCH_TEST_CASE_METHOD( className, ... ) INTERNAL_CATCH_TEST_CASE_METHOD( className, __VA_ARGS__ ) - #define CATCH_METHOD_AS_TEST_CASE( method, ... ) INTERNAL_CATCH_METHOD_AS_TEST_CASE( method, __VA_ARGS__ ) - #define CATCH_REGISTER_TEST_CASE( Function, ... ) INTERNAL_CATCH_REGISTER_TESTCASE( Function, __VA_ARGS__ ) - #define CATCH_SECTION( ... ) INTERNAL_CATCH_SECTION( __VA_ARGS__ ) - #define CATCH_FAIL( ... ) INTERNAL_CATCH_MSG( "CATCH_FAIL", Catch::ResultWas::ExplicitFailure, Catch::ResultDisposition::Normal, __VA_ARGS__ ) - #define CATCH_FAIL_CHECK( ... ) INTERNAL_CATCH_MSG( "CATCH_FAIL_CHECK", Catch::ResultWas::ExplicitFailure, Catch::ResultDisposition::ContinueOnFailure, __VA_ARGS__ ) - #define CATCH_SUCCEED( ... ) INTERNAL_CATCH_MSG( "CATCH_SUCCEED", Catch::ResultWas::Ok, Catch::ResultDisposition::ContinueOnFailure, __VA_ARGS__ ) -#else - #define CATCH_TEST_CASE( name, description ) INTERNAL_CATCH_TESTCASE( name, description ) - #define CATCH_TEST_CASE_METHOD( className, name, description ) INTERNAL_CATCH_TEST_CASE_METHOD( className, name, description ) - #define CATCH_METHOD_AS_TEST_CASE( method, name, description ) INTERNAL_CATCH_METHOD_AS_TEST_CASE( method, name, description ) - #define CATCH_REGISTER_TEST_CASE( function, name, description ) INTERNAL_CATCH_REGISTER_TESTCASE( function, name, description ) - #define CATCH_SECTION( name, description ) INTERNAL_CATCH_SECTION( name, description ) - #define CATCH_FAIL( msg ) INTERNAL_CATCH_MSG( "CATCH_FAIL", Catch::ResultWas::ExplicitFailure, Catch::ResultDisposition::Normal, msg ) - #define CATCH_FAIL_CHECK( msg ) INTERNAL_CATCH_MSG( "CATCH_FAIL_CHECK", Catch::ResultWas::ExplicitFailure, Catch::ResultDisposition::ContinueOnFailure, msg ) - #define CATCH_SUCCEED( msg ) INTERNAL_CATCH_MSG( "CATCH_SUCCEED", Catch::ResultWas::Ok, Catch::ResultDisposition::ContinueOnFailure, msg ) -#endif -#define CATCH_ANON_TEST_CASE() INTERNAL_CATCH_TESTCASE( "", "" ) +#define CATCH_TEST_CASE( ... ) INTERNAL_CATCH_TESTCASE( __VA_ARGS__ ) +#define CATCH_TEST_CASE_METHOD( className, ... ) INTERNAL_CATCH_TEST_CASE_METHOD( className, __VA_ARGS__ ) +#define CATCH_METHOD_AS_TEST_CASE( method, ... ) INTERNAL_CATCH_METHOD_AS_TEST_CASE( method, __VA_ARGS__ ) +#define CATCH_REGISTER_TEST_CASE( Function, ... ) INTERNAL_CATCH_REGISTER_TESTCASE( Function, __VA_ARGS__ ) +#define CATCH_SECTION( ... ) INTERNAL_CATCH_SECTION( __VA_ARGS__ ) +#define CATCH_FAIL( ... ) INTERNAL_CATCH_MSG( "CATCH_FAIL", Catch::ResultWas::ExplicitFailure, Catch::ResultDisposition::Normal, __VA_ARGS__ ) +#define CATCH_FAIL_CHECK( ... ) INTERNAL_CATCH_MSG( "CATCH_FAIL_CHECK", Catch::ResultWas::ExplicitFailure, Catch::ResultDisposition::ContinueOnFailure, __VA_ARGS__ ) +#define CATCH_SUCCEED( ... ) INTERNAL_CATCH_MSG( "CATCH_SUCCEED", Catch::ResultWas::Ok, Catch::ResultDisposition::ContinueOnFailure, __VA_ARGS__ ) -#define CATCH_REGISTER_REPORTER( name, reporterType ) INTERNAL_CATCH_REGISTER_REPORTER( name, reporterType ) -#define CATCH_REGISTER_LEGACY_REPORTER( name, reporterType ) INTERNAL_CATCH_REGISTER_LEGACY_REPORTER( name, reporterType ) - -#define CATCH_GENERATE( expr) INTERNAL_CATCH_GENERATE( expr ) +#define CATCH_ANON_TEST_CASE() INTERNAL_CATCH_TESTCASE() // "BDD-style" convenience wrappers -#ifdef CATCH_CONFIG_VARIADIC_MACROS #define CATCH_SCENARIO( ... ) CATCH_TEST_CASE( "Scenario: " __VA_ARGS__ ) #define CATCH_SCENARIO_METHOD( className, ... ) INTERNAL_CATCH_TEST_CASE_METHOD( className, "Scenario: " __VA_ARGS__ ) -#else -#define CATCH_SCENARIO( name, tags ) CATCH_TEST_CASE( "Scenario: " name, tags ) -#define CATCH_SCENARIO_METHOD( className, name, tags ) INTERNAL_CATCH_TEST_CASE_METHOD( className, "Scenario: " name, tags ) -#endif -#define CATCH_GIVEN( desc ) CATCH_SECTION( std::string( "Given: ") + desc, "" ) -#define CATCH_WHEN( desc ) CATCH_SECTION( std::string( " When: ") + desc, "" ) -#define CATCH_AND_WHEN( desc ) CATCH_SECTION( std::string( " And: ") + desc, "" ) -#define CATCH_THEN( desc ) CATCH_SECTION( std::string( " Then: ") + desc, "" ) -#define CATCH_AND_THEN( desc ) CATCH_SECTION( std::string( " And: ") + desc, "" ) +#define CATCH_GIVEN( desc ) CATCH_SECTION( std::string( "Given: ") + desc ) +#define CATCH_WHEN( desc ) CATCH_SECTION( std::string( " When: ") + desc ) +#define CATCH_AND_WHEN( desc ) CATCH_SECTION( std::string( " And: ") + desc ) +#define CATCH_THEN( desc ) CATCH_SECTION( std::string( " Then: ") + desc ) +#define CATCH_AND_THEN( desc ) CATCH_SECTION( std::string( " And: ") + desc ) // If CATCH_CONFIG_PREFIX_ALL is not defined then the CATCH_ prefix is not required #else -#if defined(CATCH_CONFIG_FAST_COMPILE) -#define REQUIRE( expr ) INTERNAL_CATCH_TEST_NO_TRY( "REQUIRE", Catch::ResultDisposition::Normal, expr ) -#define REQUIRE_FALSE( expr ) INTERNAL_CATCH_TEST_NO_TRY( "REQUIRE_FALSE", Catch::ResultDisposition::Normal | Catch::ResultDisposition::FalseTest, expr ) +#define REQUIRE( ... ) INTERNAL_CATCH_TEST( "REQUIRE", Catch::ResultDisposition::Normal, __VA_ARGS__ ) +#define REQUIRE_FALSE( ... ) INTERNAL_CATCH_TEST( "REQUIRE_FALSE", Catch::ResultDisposition::Normal | Catch::ResultDisposition::FalseTest, __VA_ARGS__ ) -#else -#define REQUIRE( expr ) INTERNAL_CATCH_TEST( "REQUIRE", Catch::ResultDisposition::Normal, expr ) -#define REQUIRE_FALSE( expr ) INTERNAL_CATCH_TEST( "REQUIRE_FALSE", Catch::ResultDisposition::Normal | Catch::ResultDisposition::FalseTest, expr ) -#endif - -#define REQUIRE_THROWS( expr ) INTERNAL_CATCH_THROWS( "REQUIRE_THROWS", Catch::ResultDisposition::Normal, "", expr ) +#define REQUIRE_THROWS( ... ) INTERNAL_CATCH_THROWS( "REQUIRE_THROWS", Catch::ResultDisposition::Normal, __VA_ARGS__ ) #define REQUIRE_THROWS_AS( expr, exceptionType ) INTERNAL_CATCH_THROWS_AS( "REQUIRE_THROWS_AS", exceptionType, Catch::ResultDisposition::Normal, expr ) -#define REQUIRE_THROWS_WITH( expr, matcher ) INTERNAL_CATCH_THROWS( "REQUIRE_THROWS_WITH", Catch::ResultDisposition::Normal, matcher, expr ) -#define REQUIRE_NOTHROW( expr ) INTERNAL_CATCH_NO_THROW( "REQUIRE_NOTHROW", Catch::ResultDisposition::Normal, expr ) +#define REQUIRE_THROWS_WITH( expr, matcher ) INTERNAL_CATCH_THROWS_STR_MATCHES( "REQUIRE_THROWS_WITH", Catch::ResultDisposition::Normal, matcher, expr ) +#if !defined(CATCH_CONFIG_DISABLE_MATCHERS) +#define REQUIRE_THROWS_MATCHES( expr, exceptionType, matcher ) INTERNAL_CATCH_THROWS_MATCHES( "REQUIRE_THROWS_MATCHES", exceptionType, Catch::ResultDisposition::Normal, matcher, expr ) +#endif // CATCH_CONFIG_DISABLE_MATCHERS +#define REQUIRE_NOTHROW( ... ) INTERNAL_CATCH_NO_THROW( "REQUIRE_NOTHROW", Catch::ResultDisposition::Normal, __VA_ARGS__ ) -#define CHECK( expr ) INTERNAL_CATCH_TEST( "CHECK", Catch::ResultDisposition::ContinueOnFailure, expr ) -#define CHECK_FALSE( expr ) INTERNAL_CATCH_TEST( "CHECK_FALSE", Catch::ResultDisposition::ContinueOnFailure | Catch::ResultDisposition::FalseTest, expr ) -#define CHECKED_IF( expr ) INTERNAL_CATCH_IF( "CHECKED_IF", Catch::ResultDisposition::ContinueOnFailure, expr ) -#define CHECKED_ELSE( expr ) INTERNAL_CATCH_ELSE( "CHECKED_ELSE", Catch::ResultDisposition::ContinueOnFailure, expr ) -#define CHECK_NOFAIL( expr ) INTERNAL_CATCH_TEST( "CHECK_NOFAIL", Catch::ResultDisposition::ContinueOnFailure | Catch::ResultDisposition::SuppressFail, expr ) +#define CHECK( ... ) INTERNAL_CATCH_TEST( "CHECK", Catch::ResultDisposition::ContinueOnFailure, __VA_ARGS__ ) +#define CHECK_FALSE( ... ) INTERNAL_CATCH_TEST( "CHECK_FALSE", Catch::ResultDisposition::ContinueOnFailure | Catch::ResultDisposition::FalseTest, __VA_ARGS__ ) +#define CHECKED_IF( ... ) INTERNAL_CATCH_IF( "CHECKED_IF", Catch::ResultDisposition::ContinueOnFailure, __VA_ARGS__ ) +#define CHECKED_ELSE( ... ) INTERNAL_CATCH_ELSE( "CHECKED_ELSE", Catch::ResultDisposition::ContinueOnFailure, __VA_ARGS__ ) +#define CHECK_NOFAIL( ... ) INTERNAL_CATCH_TEST( "CHECK_NOFAIL", Catch::ResultDisposition::ContinueOnFailure | Catch::ResultDisposition::SuppressFail, __VA_ARGS__ ) -#define CHECK_THROWS( expr ) INTERNAL_CATCH_THROWS( "CHECK_THROWS", Catch::ResultDisposition::ContinueOnFailure, "", expr ) +#define CHECK_THROWS( ... ) INTERNAL_CATCH_THROWS( "CHECK_THROWS", Catch::ResultDisposition::ContinueOnFailure, __VA_ARGS__ ) #define CHECK_THROWS_AS( expr, exceptionType ) INTERNAL_CATCH_THROWS_AS( "CHECK_THROWS_AS", exceptionType, Catch::ResultDisposition::ContinueOnFailure, expr ) -#define CHECK_THROWS_WITH( expr, matcher ) INTERNAL_CATCH_THROWS( "CHECK_THROWS_WITH", Catch::ResultDisposition::ContinueOnFailure, matcher, expr ) -#define CHECK_NOTHROW( expr ) INTERNAL_CATCH_NO_THROW( "CHECK_NOTHROW", Catch::ResultDisposition::ContinueOnFailure, expr ) +#define CHECK_THROWS_WITH( expr, matcher ) INTERNAL_CATCH_THROWS_STR_MATCHES( "CHECK_THROWS_WITH", Catch::ResultDisposition::ContinueOnFailure, matcher, expr ) +#if !defined(CATCH_CONFIG_DISABLE_MATCHERS) +#define CHECK_THROWS_MATCHES( expr, exceptionType, matcher ) INTERNAL_CATCH_THROWS_MATCHES( "CHECK_THROWS_MATCHES", exceptionType, Catch::ResultDisposition::ContinueOnFailure, matcher, expr ) +#endif // CATCH_CONFIG_DISABLE_MATCHERS +#define CHECK_NOTHROW( ... ) INTERNAL_CATCH_NO_THROW( "CHECK_NOTHROW", Catch::ResultDisposition::ContinueOnFailure, __VA_ARGS__ ) + +#if !defined(CATCH_CONFIG_DISABLE_MATCHERS) #define CHECK_THAT( arg, matcher ) INTERNAL_CHECK_THAT( "CHECK_THAT", matcher, Catch::ResultDisposition::ContinueOnFailure, arg ) -#if defined(CATCH_CONFIG_FAST_COMPILE) -#define REQUIRE_THAT( arg, matcher ) INTERNAL_CHECK_THAT_NO_TRY( "REQUIRE_THAT", matcher, Catch::ResultDisposition::Normal, arg ) -#else #define REQUIRE_THAT( arg, matcher ) INTERNAL_CHECK_THAT( "REQUIRE_THAT", matcher, Catch::ResultDisposition::Normal, arg ) -#endif - +#endif // CATCH_CONFIG_DISABLE_MATCHERS #define INFO( msg ) INTERNAL_CATCH_INFO( "INFO", msg ) #define WARN( msg ) INTERNAL_CATCH_MSG( "WARN", Catch::ResultWas::Warning, Catch::ResultDisposition::ContinueOnFailure, msg ) -#define SCOPED_INFO( msg ) INTERNAL_CATCH_INFO( "INFO", msg ) -#define CAPTURE( msg ) INTERNAL_CATCH_INFO( "CAPTURE", #msg " := " << Catch::toString(msg) ) -#define SCOPED_CAPTURE( msg ) INTERNAL_CATCH_INFO( "CAPTURE", #msg " := " << Catch::toString(msg) ) +#define CAPTURE( msg ) INTERNAL_CATCH_INFO( "CAPTURE", #msg " := " << ::Catch::Detail::stringify(msg) ) -#ifdef CATCH_CONFIG_VARIADIC_MACROS #define TEST_CASE( ... ) INTERNAL_CATCH_TESTCASE( __VA_ARGS__ ) #define TEST_CASE_METHOD( className, ... ) INTERNAL_CATCH_TEST_CASE_METHOD( className, __VA_ARGS__ ) #define METHOD_AS_TEST_CASE( method, ... ) INTERNAL_CATCH_METHOD_AS_TEST_CASE( method, __VA_ARGS__ ) @@ -222,43 +181,152 @@ LeakDetector leakDetector; #define FAIL( ... ) INTERNAL_CATCH_MSG( "FAIL", Catch::ResultWas::ExplicitFailure, Catch::ResultDisposition::Normal, __VA_ARGS__ ) #define FAIL_CHECK( ... ) INTERNAL_CATCH_MSG( "FAIL_CHECK", Catch::ResultWas::ExplicitFailure, Catch::ResultDisposition::ContinueOnFailure, __VA_ARGS__ ) #define SUCCEED( ... ) INTERNAL_CATCH_MSG( "SUCCEED", Catch::ResultWas::Ok, Catch::ResultDisposition::ContinueOnFailure, __VA_ARGS__ ) -#else -#define TEST_CASE( name, description ) INTERNAL_CATCH_TESTCASE( name, description ) - #define TEST_CASE_METHOD( className, name, description ) INTERNAL_CATCH_TEST_CASE_METHOD( className, name, description ) - #define METHOD_AS_TEST_CASE( method, name, description ) INTERNAL_CATCH_METHOD_AS_TEST_CASE( method, name, description ) - #define REGISTER_TEST_CASE( method, name, description ) INTERNAL_CATCH_REGISTER_TESTCASE( method, name, description ) - #define SECTION( name, description ) INTERNAL_CATCH_SECTION( name, description ) - #define FAIL( msg ) INTERNAL_CATCH_MSG( "FAIL", Catch::ResultWas::ExplicitFailure, Catch::ResultDisposition::Normal, msg ) - #define FAIL_CHECK( msg ) INTERNAL_CATCH_MSG( "FAIL_CHECK", Catch::ResultWas::ExplicitFailure, Catch::ResultDisposition::ContinueOnFailure, msg ) - #define SUCCEED( msg ) INTERNAL_CATCH_MSG( "SUCCEED", Catch::ResultWas::Ok, Catch::ResultDisposition::ContinueOnFailure, msg ) -#endif -#define ANON_TEST_CASE() INTERNAL_CATCH_TESTCASE( "", "" ) - -#define REGISTER_REPORTER( name, reporterType ) INTERNAL_CATCH_REGISTER_REPORTER( name, reporterType ) -#define REGISTER_LEGACY_REPORTER( name, reporterType ) INTERNAL_CATCH_REGISTER_LEGACY_REPORTER( name, reporterType ) - -#define GENERATE( expr) INTERNAL_CATCH_GENERATE( expr ) +#define ANON_TEST_CASE() INTERNAL_CATCH_TESTCASE() #endif #define CATCH_TRANSLATE_EXCEPTION( signature ) INTERNAL_CATCH_TRANSLATE_EXCEPTION( signature ) // "BDD-style" convenience wrappers -#ifdef CATCH_CONFIG_VARIADIC_MACROS #define SCENARIO( ... ) TEST_CASE( "Scenario: " __VA_ARGS__ ) #define SCENARIO_METHOD( className, ... ) INTERNAL_CATCH_TEST_CASE_METHOD( className, "Scenario: " __VA_ARGS__ ) -#else -#define SCENARIO( name, tags ) TEST_CASE( "Scenario: " name, tags ) -#define SCENARIO_METHOD( className, name, tags ) INTERNAL_CATCH_TEST_CASE_METHOD( className, "Scenario: " name, tags ) -#endif -#define GIVEN( desc ) SECTION( std::string(" Given: ") + desc, "" ) -#define WHEN( desc ) SECTION( std::string(" When: ") + desc, "" ) -#define AND_WHEN( desc ) SECTION( std::string("And when: ") + desc, "" ) -#define THEN( desc ) SECTION( std::string(" Then: ") + desc, "" ) -#define AND_THEN( desc ) SECTION( std::string(" And: ") + desc, "" ) + +#define GIVEN( desc ) SECTION( std::string(" Given: ") + desc ) +#define WHEN( desc ) SECTION( std::string(" When: ") + desc ) +#define AND_WHEN( desc ) SECTION( std::string("And when: ") + desc ) +#define THEN( desc ) SECTION( std::string(" Then: ") + desc ) +#define AND_THEN( desc ) SECTION( std::string(" And: ") + desc ) using Catch::Detail::Approx; +#else +////// +// If this config identifier is defined then all CATCH macros are prefixed with CATCH_ +#ifdef CATCH_CONFIG_PREFIX_ALL + +#define CATCH_REQUIRE( ... ) (void)(0) +#define CATCH_REQUIRE_FALSE( ... ) (void)(0) + +#define CATCH_REQUIRE_THROWS( ... ) (void)(0) +#define CATCH_REQUIRE_THROWS_AS( expr, exceptionType ) (void)(0) +#define CATCH_REQUIRE_THROWS_WITH( expr, matcher ) (void)(0) +#if !defined(CATCH_CONFIG_DISABLE_MATCHERS) +#define CATCH_REQUIRE_THROWS_MATCHES( expr, exceptionType, matcher ) (void)(0) +#endif// CATCH_CONFIG_DISABLE_MATCHERS +#define CATCH_REQUIRE_NOTHROW( ... ) (void)(0) + +#define CATCH_CHECK( ... ) (void)(0) +#define CATCH_CHECK_FALSE( ... ) (void)(0) +#define CATCH_CHECKED_IF( ... ) if (__VA_ARGS__) +#define CATCH_CHECKED_ELSE( ... ) if (!(__VA_ARGS__)) +#define CATCH_CHECK_NOFAIL( ... ) (void)(0) + +#define CATCH_CHECK_THROWS( ... ) (void)(0) +#define CATCH_CHECK_THROWS_AS( expr, exceptionType ) (void)(0) +#define CATCH_CHECK_THROWS_WITH( expr, matcher ) (void)(0) +#if !defined(CATCH_CONFIG_DISABLE_MATCHERS) +#define CATCH_CHECK_THROWS_MATCHES( expr, exceptionType, matcher ) (void)(0) +#endif // CATCH_CONFIG_DISABLE_MATCHERS +#define CATCH_CHECK_NOTHROW( ... ) (void)(0) + +#if !defined(CATCH_CONFIG_DISABLE_MATCHERS) +#define CATCH_CHECK_THAT( arg, matcher ) (void)(0) + +#define CATCH_REQUIRE_THAT( arg, matcher ) (void)(0) +#endif // CATCH_CONFIG_DISABLE_MATCHERS + +#define CATCH_INFO( msg ) (void)(0) +#define CATCH_WARN( msg ) (void)(0) +#define CATCH_CAPTURE( msg ) (void)(0) + +#define CATCH_TEST_CASE( ... ) INTERNAL_CATCH_TESTCASE_NO_REGISTRATION(INTERNAL_CATCH_UNIQUE_NAME( ____C_A_T_C_H____T_E_S_T____ )) +#define CATCH_TEST_CASE_METHOD( className, ... ) INTERNAL_CATCH_TESTCASE_NO_REGISTRATION(INTERNAL_CATCH_UNIQUE_NAME( ____C_A_T_C_H____T_E_S_T____ )) +#define CATCH_METHOD_AS_TEST_CASE( method, ... ) +#define CATCH_REGISTER_TEST_CASE( Function, ... ) (void)(0) +#define CATCH_SECTION( ... ) +#define CATCH_FAIL( ... ) (void)(0) +#define CATCH_FAIL_CHECK( ... ) (void)(0) +#define CATCH_SUCCEED( ... ) (void)(0) + +#define CATCH_ANON_TEST_CASE() INTERNAL_CATCH_TESTCASE_NO_REGISTRATION(INTERNAL_CATCH_UNIQUE_NAME( ____C_A_T_C_H____T_E_S_T____ )) + +// "BDD-style" convenience wrappers +#define CATCH_SCENARIO( ... ) INTERNAL_CATCH_TESTCASE_NO_REGISTRATION(INTERNAL_CATCH_UNIQUE_NAME( ____C_A_T_C_H____T_E_S_T____ )) +#define CATCH_SCENARIO_METHOD( className, ... ) INTERNAL_CATCH_TESTCASE_METHOD_NO_REGISTRATION(INTERNAL_CATCH_UNIQUE_NAME( ____C_A_T_C_H____T_E_S_T____ ), className ) +#define CATCH_GIVEN( desc ) +#define CATCH_WHEN( desc ) +#define CATCH_AND_WHEN( desc ) +#define CATCH_THEN( desc ) +#define CATCH_AND_THEN( desc ) + +// If CATCH_CONFIG_PREFIX_ALL is not defined then the CATCH_ prefix is not required +#else + +#define REQUIRE( ... ) (void)(0) +#define REQUIRE_FALSE( ... ) (void)(0) + +#define REQUIRE_THROWS( ... ) (void)(0) +#define REQUIRE_THROWS_AS( expr, exceptionType ) (void)(0) +#define REQUIRE_THROWS_WITH( expr, matcher ) (void)(0) +#if !defined(CATCH_CONFIG_DISABLE_MATCHERS) +#define REQUIRE_THROWS_MATCHES( expr, exceptionType, matcher ) (void)(0) +#endif // CATCH_CONFIG_DISABLE_MATCHERS +#define REQUIRE_NOTHROW( ... ) (void)(0) + +#define CHECK( ... ) (void)(0) +#define CHECK_FALSE( ... ) (void)(0) +#define CHECKED_IF( ... ) if (__VA_ARGS__) +#define CHECKED_ELSE( ... ) if (!(__VA_ARGS__)) +#define CHECK_NOFAIL( ... ) (void)(0) + +#define CHECK_THROWS( ... ) (void)(0) +#define CHECK_THROWS_AS( expr, exceptionType ) (void)(0) +#define CHECK_THROWS_WITH( expr, matcher ) (void)(0) +#if !defined(CATCH_CONFIG_DISABLE_MATCHERS) +#define CHECK_THROWS_MATCHES( expr, exceptionType, matcher ) (void)(0) +#endif // CATCH_CONFIG_DISABLE_MATCHERS +#define CHECK_NOTHROW( ... ) (void)(0) + + +#if !defined(CATCH_CONFIG_DISABLE_MATCHERS) +#define CHECK_THAT( arg, matcher ) (void)(0) + +#define REQUIRE_THAT( arg, matcher ) (void)(0) +#endif // CATCH_CONFIG_DISABLE_MATCHERS + +#define INFO( msg ) (void)(0) +#define WARN( msg ) (void)(0) +#define CAPTURE( msg ) (void)(0) + +#define TEST_CASE( ... ) INTERNAL_CATCH_TESTCASE_NO_REGISTRATION(INTERNAL_CATCH_UNIQUE_NAME( ____C_A_T_C_H____T_E_S_T____ )) +#define TEST_CASE_METHOD( className, ... ) INTERNAL_CATCH_TESTCASE_NO_REGISTRATION(INTERNAL_CATCH_UNIQUE_NAME( ____C_A_T_C_H____T_E_S_T____ )) +#define METHOD_AS_TEST_CASE( method, ... ) +#define REGISTER_TEST_CASE( Function, ... ) (void)(0) +#define SECTION( ... ) +#define FAIL( ... ) (void)(0) +#define FAIL_CHECK( ... ) (void)(0) +#define SUCCEED( ... ) (void)(0) +#define ANON_TEST_CASE() INTERNAL_CATCH_TESTCASE_NO_REGISTRATION(INTERNAL_CATCH_UNIQUE_NAME( ____C_A_T_C_H____T_E_S_T____ )) + +#endif + +#define CATCH_TRANSLATE_EXCEPTION( signature ) INTERNAL_CATCH_TRANSLATE_EXCEPTION_NO_REG( INTERNAL_CATCH_UNIQUE_NAME( catch_internal_ExceptionTranslator ), signature ) + +// "BDD-style" convenience wrappers +#define SCENARIO( ... ) INTERNAL_CATCH_TESTCASE_NO_REGISTRATION(INTERNAL_CATCH_UNIQUE_NAME( ____C_A_T_C_H____T_E_S_T____ ) ) +#define SCENARIO_METHOD( className, ... ) INTERNAL_CATCH_TESTCASE_METHOD_NO_REGISTRATION(INTERNAL_CATCH_UNIQUE_NAME( ____C_A_T_C_H____T_E_S_T____ ), className ) + +#define GIVEN( desc ) +#define WHEN( desc ) +#define AND_WHEN( desc ) +#define THEN( desc ) +#define AND_THEN( desc ) + +using Catch::Detail::Approx; + + +#endif + #include "internal/catch_reenable_warnings.h" #endif // TWOBLUECUBES_CATCH_HPP_INCLUDED diff --git a/include/external/clara.h b/include/external/clara.h deleted file mode 100644 index a641dda5..00000000 --- a/include/external/clara.h +++ /dev/null @@ -1,1054 +0,0 @@ -/* - * Created by Phil on 25/05/2013. - * Copyright 2013 Two Blue Cubes Ltd. All rights reserved. - * - * Distributed under the Boost Software License, Version 1.0. (See accompanying - * file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) - */ - -// Version 0.0.2.4 - -// Only use header guard if we are not using an outer namespace -#if !defined(TWOBLUECUBES_CLARA_H_INCLUDED) || defined(STITCH_CLARA_OPEN_NAMESPACE) - -#ifndef STITCH_CLARA_OPEN_NAMESPACE -#define TWOBLUECUBES_CLARA_H_INCLUDED -#define STITCH_CLARA_OPEN_NAMESPACE -#define STITCH_CLARA_CLOSE_NAMESPACE -#else -#define STITCH_CLARA_CLOSE_NAMESPACE } -#endif - - -#define STITCH_TBC_TEXT_FORMAT_OPEN_NAMESPACE STITCH_CLARA_OPEN_NAMESPACE - -// ----------- #included from tbc_text_format.h ----------- - -/* - * Created by Phil on 18/4/2013. - * Copyright 2013 Two Blue Cubes Ltd. All rights reserved. - * - * Distributed under the Boost Software License, Version 1.0. (See accompanying - * file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) - */ -// Only use header guard if we are not using an outer namespace -#if !defined(TBC_TEXT_FORMAT_H_INCLUDED) || defined(STITCH_TBC_TEXT_FORMAT_OUTER_NAMESPACE) -#ifndef STITCH_TBC_TEXT_FORMAT_OUTER_NAMESPACE -#define TBC_TEXT_FORMAT_H_INCLUDED -#endif - -#include -#include -#include -#include -#include - -// Use optional outer namespace -#ifdef STITCH_TBC_TEXT_FORMAT_OUTER_NAMESPACE -namespace STITCH_TBC_TEXT_FORMAT_OUTER_NAMESPACE { -#endif - -namespace Tbc { - -#ifdef TBC_TEXT_FORMAT_CONSOLE_WIDTH - const unsigned int consoleWidth = TBC_TEXT_FORMAT_CONSOLE_WIDTH; -#else - const unsigned int consoleWidth = 80; -#endif - - struct TextAttributes { - TextAttributes() - : initialIndent( std::string::npos ), - indent( 0 ), - width( consoleWidth-1 ), - tabChar( '\t' ) - {} - - TextAttributes& setInitialIndent( std::size_t _value ) { initialIndent = _value; return *this; } - TextAttributes& setIndent( std::size_t _value ) { indent = _value; return *this; } - TextAttributes& setWidth( std::size_t _value ) { width = _value; return *this; } - TextAttributes& setTabChar( char _value ) { tabChar = _value; return *this; } - - std::size_t initialIndent; // indent of first line, or npos - std::size_t indent; // indent of subsequent lines, or all if initialIndent is npos - std::size_t width; // maximum width of text, including indent. Longer text will wrap - char tabChar; // If this char is seen the indent is changed to current pos - }; - - class Text { - public: - Text( std::string const& _str, TextAttributes const& _attr = TextAttributes() ) - : attr( _attr ) - { - std::string wrappableChars = " [({.,/|\\-"; - std::size_t indent = _attr.initialIndent != std::string::npos - ? _attr.initialIndent - : _attr.indent; - std::string remainder = _str; - - while( !remainder.empty() ) { - if( lines.size() >= 1000 ) { - lines.push_back( "... message truncated due to excessive size" ); - return; - } - std::size_t tabPos = std::string::npos; - std::size_t width = (std::min)( remainder.size(), _attr.width - indent ); - std::size_t pos = remainder.find_first_of( '\n' ); - if( pos <= width ) { - width = pos; - } - pos = remainder.find_last_of( _attr.tabChar, width ); - if( pos != std::string::npos ) { - tabPos = pos; - if( remainder[width] == '\n' ) - width--; - remainder = remainder.substr( 0, tabPos ) + remainder.substr( tabPos+1 ); - } - - if( width == remainder.size() ) { - spliceLine( indent, remainder, width ); - } - else if( remainder[width] == '\n' ) { - spliceLine( indent, remainder, width ); - if( width <= 1 || remainder.size() != 1 ) - remainder = remainder.substr( 1 ); - indent = _attr.indent; - } - else { - pos = remainder.find_last_of( wrappableChars, width ); - if( pos != std::string::npos && pos > 0 ) { - spliceLine( indent, remainder, pos ); - if( remainder[0] == ' ' ) - remainder = remainder.substr( 1 ); - } - else { - spliceLine( indent, remainder, width-1 ); - lines.back() += "-"; - } - if( lines.size() == 1 ) - indent = _attr.indent; - if( tabPos != std::string::npos ) - indent += tabPos; - } - } - } - - void spliceLine( std::size_t _indent, std::string& _remainder, std::size_t _pos ) { - lines.push_back( std::string( _indent, ' ' ) + _remainder.substr( 0, _pos ) ); - _remainder = _remainder.substr( _pos ); - } - - typedef std::vector::const_iterator const_iterator; - - const_iterator begin() const { return lines.begin(); } - const_iterator end() const { return lines.end(); } - std::string const& last() const { return lines.back(); } - std::size_t size() const { return lines.size(); } - std::string const& operator[]( std::size_t _index ) const { return lines[_index]; } - std::string toString() const { - std::ostringstream oss; - oss << *this; - return oss.str(); - } - - friend std::ostream& operator << ( std::ostream& _stream, Text const& _text ) { - for( Text::const_iterator it = _text.begin(), itEnd = _text.end(); - it != itEnd; ++it ) { - if( it != _text.begin() ) - _stream << "\n"; - _stream << *it; - } - return _stream; - } - - private: - std::string str; - TextAttributes attr; - std::vector lines; - }; - -} // end namespace Tbc - -#ifdef STITCH_TBC_TEXT_FORMAT_OUTER_NAMESPACE -} // end outer namespace -#endif - -#endif // TBC_TEXT_FORMAT_H_INCLUDED - -// ----------- end of #include from tbc_text_format.h ----------- -// ........... back in clara.h - -#undef STITCH_TBC_TEXT_FORMAT_OPEN_NAMESPACE - - -// ----------- #included from clara_compilers.h ----------- - -/* - * Created by Phil on 10/02/2016. - * Copyright 2016 Two Blue Cubes Ltd. All rights reserved. - * - * Distributed under the Boost Software License, Version 1.0. (See accompanying - * file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) - */ -#ifndef TWOBLUECUBES_CLARA_COMPILERS_H_INCLUDED -#define TWOBLUECUBES_CLARA_COMPILERS_H_INCLUDED - -// Detect a number of compiler features - mostly C++11/14 conformance - by compiler -// The following features are defined: -// -// CLARA_CONFIG_CPP11_NULLPTR : is nullptr supported? -// CLARA_CONFIG_CPP11_NOEXCEPT : is noexcept supported? -// CLARA_CONFIG_CPP11_GENERATED_METHODS : The delete and default keywords for compiler generated methods -// CLARA_CONFIG_CPP11_OVERRIDE : is override supported? -// CLARA_CONFIG_CPP11_UNIQUE_PTR : is unique_ptr supported (otherwise use auto_ptr) - -// CLARA_CONFIG_CPP11_OR_GREATER : Is C++11 supported? - -// CLARA_CONFIG_VARIADIC_MACROS : are variadic macros supported? - -// In general each macro has a _NO_ form -// (e.g. CLARA_CONFIG_CPP11_NO_NULLPTR) which disables the feature. -// Many features, at point of detection, define an _INTERNAL_ macro, so they -// can be combined, en-mass, with the _NO_ forms later. - -// All the C++11 features can be disabled with CLARA_CONFIG_NO_CPP11 - -#ifdef __clang__ - -#if __has_feature(cxx_nullptr) -#define CLARA_INTERNAL_CONFIG_CPP11_NULLPTR -#endif - -#if __has_feature(cxx_noexcept) -#define CLARA_INTERNAL_CONFIG_CPP11_NOEXCEPT -#endif - -#endif // __clang__ - -//////////////////////////////////////////////////////////////////////////////// -// GCC -#ifdef __GNUC__ - -#if __GNUC__ == 4 && __GNUC_MINOR__ >= 6 && defined(__GXX_EXPERIMENTAL_CXX0X__) -#define CLARA_INTERNAL_CONFIG_CPP11_NULLPTR -#endif - -// - otherwise more recent versions define __cplusplus >= 201103L -// and will get picked up below - -#endif // __GNUC__ - -//////////////////////////////////////////////////////////////////////////////// -// Visual C++ -#ifdef _MSC_VER - -#if (_MSC_VER >= 1600) -#define CLARA_INTERNAL_CONFIG_CPP11_NULLPTR -#define CLARA_INTERNAL_CONFIG_CPP11_UNIQUE_PTR -#endif - -#if (_MSC_VER >= 1900 ) // (VC++ 13 (VS2015)) -#define CLARA_INTERNAL_CONFIG_CPP11_NOEXCEPT -#define CLARA_INTERNAL_CONFIG_CPP11_GENERATED_METHODS -#endif - -#endif // _MSC_VER - - -//////////////////////////////////////////////////////////////////////////////// -// C++ language feature support - -// catch all support for C++11 -#if defined(__cplusplus) && __cplusplus >= 201103L - -#define CLARA_CPP11_OR_GREATER - -#if !defined(CLARA_INTERNAL_CONFIG_CPP11_NULLPTR) -#define CLARA_INTERNAL_CONFIG_CPP11_NULLPTR -#endif - -#ifndef CLARA_INTERNAL_CONFIG_CPP11_NOEXCEPT -#define CLARA_INTERNAL_CONFIG_CPP11_NOEXCEPT -#endif - -#ifndef CLARA_INTERNAL_CONFIG_CPP11_GENERATED_METHODS -#define CLARA_INTERNAL_CONFIG_CPP11_GENERATED_METHODS -#endif - -#if !defined(CLARA_INTERNAL_CONFIG_CPP11_OVERRIDE) -#define CLARA_INTERNAL_CONFIG_CPP11_OVERRIDE -#endif -#if !defined(CLARA_INTERNAL_CONFIG_CPP11_UNIQUE_PTR) -#define CLARA_INTERNAL_CONFIG_CPP11_UNIQUE_PTR -#endif - - -#endif // __cplusplus >= 201103L - -// Now set the actual defines based on the above + anything the user has configured -#if defined(CLARA_INTERNAL_CONFIG_CPP11_NULLPTR) && !defined(CLARA_CONFIG_CPP11_NO_NULLPTR) && !defined(CLARA_CONFIG_CPP11_NULLPTR) && !defined(CLARA_CONFIG_NO_CPP11) -#define CLARA_CONFIG_CPP11_NULLPTR -#endif -#if defined(CLARA_INTERNAL_CONFIG_CPP11_NOEXCEPT) && !defined(CLARA_CONFIG_CPP11_NO_NOEXCEPT) && !defined(CLARA_CONFIG_CPP11_NOEXCEPT) && !defined(CLARA_CONFIG_NO_CPP11) -#define CLARA_CONFIG_CPP11_NOEXCEPT -#endif -#if defined(CLARA_INTERNAL_CONFIG_CPP11_GENERATED_METHODS) && !defined(CLARA_CONFIG_CPP11_NO_GENERATED_METHODS) && !defined(CLARA_CONFIG_CPP11_GENERATED_METHODS) && !defined(CLARA_CONFIG_NO_CPP11) -#define CLARA_CONFIG_CPP11_GENERATED_METHODS -#endif -#if defined(CLARA_INTERNAL_CONFIG_CPP11_OVERRIDE) && !defined(CLARA_CONFIG_NO_OVERRIDE) && !defined(CLARA_CONFIG_CPP11_OVERRIDE) && !defined(CLARA_CONFIG_NO_CPP11) -#define CLARA_CONFIG_CPP11_OVERRIDE -#endif -#if defined(CLARA_INTERNAL_CONFIG_CPP11_UNIQUE_PTR) && !defined(CLARA_CONFIG_NO_UNIQUE_PTR) && !defined(CLARA_CONFIG_CPP11_UNIQUE_PTR) && !defined(CLARA_CONFIG_NO_CPP11) -#define CLARA_CONFIG_CPP11_UNIQUE_PTR -#endif - - -// noexcept support: -#if defined(CLARA_CONFIG_CPP11_NOEXCEPT) && !defined(CLARA_NOEXCEPT) -#define CLARA_NOEXCEPT noexcept -# define CLARA_NOEXCEPT_IS(x) noexcept(x) -#else -#define CLARA_NOEXCEPT throw() -# define CLARA_NOEXCEPT_IS(x) -#endif - -// nullptr support -#ifdef CLARA_CONFIG_CPP11_NULLPTR -#define CLARA_NULL nullptr -#else -#define CLARA_NULL NULL -#endif - -// override support -#ifdef CLARA_CONFIG_CPP11_OVERRIDE -#define CLARA_OVERRIDE override -#else -#define CLARA_OVERRIDE -#endif - -// unique_ptr support -#ifdef CLARA_CONFIG_CPP11_UNIQUE_PTR -# define CLARA_AUTO_PTR( T ) std::unique_ptr -#else -# define CLARA_AUTO_PTR( T ) std::auto_ptr -#endif - -#endif // TWOBLUECUBES_CLARA_COMPILERS_H_INCLUDED - - -// ----------- end of #include from clara_compilers.h ----------- -// ........... back in clara.h - - -#include -#include -#include - -#if defined(WIN32) || defined(__WIN32__) || defined(_WIN32) || defined(_MSC_VER) -#define CLARA_PLATFORM_WINDOWS -#endif - - -// Use optional outer namespace -#ifdef STITCH_CLARA_OPEN_NAMESPACE -STITCH_CLARA_OPEN_NAMESPACE -#endif - -namespace Clara { - - struct UnpositionalTag {}; - - extern UnpositionalTag _; - -#ifdef CLARA_CONFIG_MAIN - UnpositionalTag _; -#endif - - namespace Detail { - -#ifdef CLARA_CONSOLE_WIDTH - const unsigned int consoleWidth = CLARA_CONFIG_CONSOLE_WIDTH; -#else - const unsigned int consoleWidth = 80; -#endif - - using namespace Tbc; - - inline bool startsWith( std::string const& str, std::string const& prefix ) { - return str.size() >= prefix.size() && str.substr( 0, prefix.size() ) == prefix; - } - - template struct RemoveConstRef{ typedef T type; }; - template struct RemoveConstRef{ typedef T type; }; - template struct RemoveConstRef{ typedef T type; }; - template struct RemoveConstRef{ typedef T type; }; - - template struct IsBool { static const bool value = false; }; - template<> struct IsBool { static const bool value = true; }; - - template - void convertInto( std::string const& _source, T& _dest ) { - std::stringstream ss; - ss << _source; - ss >> _dest; - if( ss.fail() ) - throw std::runtime_error( "Unable to convert " + _source + " to destination type" ); - } - inline void convertInto( std::string const& _source, std::string& _dest ) { - _dest = _source; - } - char toLowerCh(char c) { - return static_cast( std::tolower( c ) ); - } - inline void convertInto( std::string const& _source, bool& _dest ) { - std::string sourceLC = _source; - std::transform( sourceLC.begin(), sourceLC.end(), sourceLC.begin(), toLowerCh ); - if( sourceLC == "y" || sourceLC == "1" || sourceLC == "true" || sourceLC == "yes" || sourceLC == "on" ) - _dest = true; - else if( sourceLC == "n" || sourceLC == "0" || sourceLC == "false" || sourceLC == "no" || sourceLC == "off" ) - _dest = false; - else - throw std::runtime_error( "Expected a boolean value but did not recognise:\n '" + _source + "'" ); - } - - - template - struct IArgFunction { - virtual ~IArgFunction() {} -#ifdef CLARA_CONFIG_CPP11_GENERATED_METHODS - IArgFunction() = default; - IArgFunction( IArgFunction const& ) = default; -#endif - virtual void set( ConfigT& config, std::string const& value ) const = 0; - virtual bool takesArg() const = 0; - virtual IArgFunction* clone() const = 0; - }; - - template - class BoundArgFunction { - public: - BoundArgFunction() : functionObj( CLARA_NULL ) {} - BoundArgFunction( IArgFunction* _functionObj ) : functionObj( _functionObj ) {} - BoundArgFunction( BoundArgFunction const& other ) : functionObj( other.functionObj ? other.functionObj->clone() : CLARA_NULL ) {} - BoundArgFunction& operator = ( BoundArgFunction const& other ) { - IArgFunction* newFunctionObj = other.functionObj ? other.functionObj->clone() : CLARA_NULL; - delete functionObj; - functionObj = newFunctionObj; - return *this; - } - ~BoundArgFunction() { delete functionObj; } - - void set( ConfigT& config, std::string const& value ) const { - functionObj->set( config, value ); - } - bool takesArg() const { return functionObj->takesArg(); } - - bool isSet() const { - return functionObj != CLARA_NULL; - } - private: - IArgFunction* functionObj; - }; - - - template - struct NullBinder : IArgFunction{ - virtual void set( C&, std::string const& ) const {} - virtual bool takesArg() const { return true; } - virtual IArgFunction* clone() const { return new NullBinder( *this ); } - }; - - template - struct BoundDataMember : IArgFunction{ - BoundDataMember( M C::* _member ) : member( _member ) {} - virtual void set( C& p, std::string const& stringValue ) const { - convertInto( stringValue, p.*member ); - } - virtual bool takesArg() const { return !IsBool::value; } - virtual IArgFunction* clone() const { return new BoundDataMember( *this ); } - M C::* member; - }; - template - struct BoundUnaryMethod : IArgFunction{ - BoundUnaryMethod( void (C::*_member)( M ) ) : member( _member ) {} - virtual void set( C& p, std::string const& stringValue ) const { - typename RemoveConstRef::type value; - convertInto( stringValue, value ); - (p.*member)( value ); - } - virtual bool takesArg() const { return !IsBool::value; } - virtual IArgFunction* clone() const { return new BoundUnaryMethod( *this ); } - void (C::*member)( M ); - }; - template - struct BoundNullaryMethod : IArgFunction{ - BoundNullaryMethod( void (C::*_member)() ) : member( _member ) {} - virtual void set( C& p, std::string const& stringValue ) const { - bool value; - convertInto( stringValue, value ); - if( value ) - (p.*member)(); - } - virtual bool takesArg() const { return false; } - virtual IArgFunction* clone() const { return new BoundNullaryMethod( *this ); } - void (C::*member)(); - }; - - template - struct BoundUnaryFunction : IArgFunction{ - BoundUnaryFunction( void (*_function)( C& ) ) : function( _function ) {} - virtual void set( C& obj, std::string const& stringValue ) const { - bool value; - convertInto( stringValue, value ); - if( value ) - function( obj ); - } - virtual bool takesArg() const { return false; } - virtual IArgFunction* clone() const { return new BoundUnaryFunction( *this ); } - void (*function)( C& ); - }; - - template - struct BoundBinaryFunction : IArgFunction{ - BoundBinaryFunction( void (*_function)( C&, T ) ) : function( _function ) {} - virtual void set( C& obj, std::string const& stringValue ) const { - typename RemoveConstRef::type value; - convertInto( stringValue, value ); - function( obj, value ); - } - virtual bool takesArg() const { return !IsBool::value; } - virtual IArgFunction* clone() const { return new BoundBinaryFunction( *this ); } - void (*function)( C&, T ); - }; - - } // namespace Detail - - inline std::vector argsToVector( int argc, char const* const* const argv ) { - std::vector args( static_cast( argc ) ); - for( std::size_t i = 0; i < static_cast( argc ); ++i ) - args[i] = argv[i]; - - return args; - } - - class Parser { - enum Mode { None, MaybeShortOpt, SlashOpt, ShortOpt, LongOpt, Positional }; - Mode mode; - std::size_t from; - bool inQuotes; - public: - - struct Token { - enum Type { Positional, ShortOpt, LongOpt }; - Token( Type _type, std::string const& _data ) : type( _type ), data( _data ) {} - Type type; - std::string data; - }; - - Parser() : mode( None ), from( 0 ), inQuotes( false ){} - - void parseIntoTokens( std::vector const& args, std::vector& tokens ) { - const std::string doubleDash = "--"; - for( std::size_t i = 1; i < args.size() && args[i] != doubleDash; ++i ) - parseIntoTokens( args[i], tokens); - } - - void parseIntoTokens( std::string const& arg, std::vector& tokens ) { - for( std::size_t i = 0; i < arg.size(); ++i ) { - char c = arg[i]; - if( c == '"' ) - inQuotes = !inQuotes; - mode = handleMode( i, c, arg, tokens ); - } - mode = handleMode( arg.size(), '\0', arg, tokens ); - } - Mode handleMode( std::size_t i, char c, std::string const& arg, std::vector& tokens ) { - switch( mode ) { - case None: return handleNone( i, c ); - case MaybeShortOpt: return handleMaybeShortOpt( i, c ); - case ShortOpt: - case LongOpt: - case SlashOpt: return handleOpt( i, c, arg, tokens ); - case Positional: return handlePositional( i, c, arg, tokens ); - default: throw std::logic_error( "Unknown mode" ); - } - } - - Mode handleNone( std::size_t i, char c ) { - if( inQuotes ) { - from = i; - return Positional; - } - switch( c ) { - case '-': return MaybeShortOpt; -#ifdef CLARA_PLATFORM_WINDOWS - case '/': from = i+1; return SlashOpt; -#endif - default: from = i; return Positional; - } - } - Mode handleMaybeShortOpt( std::size_t i, char c ) { - switch( c ) { - case '-': from = i+1; return LongOpt; - default: from = i; return ShortOpt; - } - } - - Mode handleOpt( std::size_t i, char c, std::string const& arg, std::vector& tokens ) { - if( std::string( ":=\0", 3 ).find( c ) == std::string::npos ) - return mode; - - std::string optName = arg.substr( from, i-from ); - if( mode == ShortOpt ) - for( std::size_t j = 0; j < optName.size(); ++j ) - tokens.push_back( Token( Token::ShortOpt, optName.substr( j, 1 ) ) ); - else if( mode == SlashOpt && optName.size() == 1 ) - tokens.push_back( Token( Token::ShortOpt, optName ) ); - else - tokens.push_back( Token( Token::LongOpt, optName ) ); - return None; - } - Mode handlePositional( std::size_t i, char c, std::string const& arg, std::vector& tokens ) { - if( inQuotes || std::string( "\0", 1 ).find( c ) == std::string::npos ) - return mode; - - std::string data = arg.substr( from, i-from ); - tokens.push_back( Token( Token::Positional, data ) ); - return None; - } - }; - - template - struct CommonArgProperties { - CommonArgProperties() {} - CommonArgProperties( Detail::BoundArgFunction const& _boundField ) : boundField( _boundField ) {} - - Detail::BoundArgFunction boundField; - std::string description; - std::string detail; - std::string placeholder; // Only value if boundField takes an arg - - bool takesArg() const { - return !placeholder.empty(); - } - void validate() const { - if( !boundField.isSet() ) - throw std::logic_error( "option not bound" ); - } - }; - struct OptionArgProperties { - std::vector shortNames; - std::string longName; - - bool hasShortName( std::string const& shortName ) const { - return std::find( shortNames.begin(), shortNames.end(), shortName ) != shortNames.end(); - } - bool hasLongName( std::string const& _longName ) const { - return _longName == longName; - } - }; - struct PositionalArgProperties { - PositionalArgProperties() : position( -1 ) {} - int position; // -1 means non-positional (floating) - - bool isFixedPositional() const { - return position != -1; - } - }; - - template - class CommandLine { - - struct Arg : CommonArgProperties, OptionArgProperties, PositionalArgProperties { - Arg() {} - Arg( Detail::BoundArgFunction const& _boundField ) : CommonArgProperties( _boundField ) {} - - using CommonArgProperties::placeholder; // !TBD - - std::string dbgName() const { - if( !longName.empty() ) - return "--" + longName; - if( !shortNames.empty() ) - return "-" + shortNames[0]; - return "positional args"; - } - std::string commands() const { - std::ostringstream oss; - bool first = true; - std::vector::const_iterator it = shortNames.begin(), itEnd = shortNames.end(); - for(; it != itEnd; ++it ) { - if( first ) - first = false; - else - oss << ", "; - oss << "-" << *it; - } - if( !longName.empty() ) { - if( !first ) - oss << ", "; - oss << "--" << longName; - } - if( !placeholder.empty() ) - oss << " <" << placeholder << ">"; - return oss.str(); - } - }; - - typedef CLARA_AUTO_PTR( Arg ) ArgAutoPtr; - - friend void addOptName( Arg& arg, std::string const& optName ) - { - if( optName.empty() ) - return; - if( Detail::startsWith( optName, "--" ) ) { - if( !arg.longName.empty() ) - throw std::logic_error( "Only one long opt may be specified. '" - + arg.longName - + "' already specified, now attempting to add '" - + optName + "'" ); - arg.longName = optName.substr( 2 ); - } - else if( Detail::startsWith( optName, "-" ) ) - arg.shortNames.push_back( optName.substr( 1 ) ); - else - throw std::logic_error( "option must begin with - or --. Option was: '" + optName + "'" ); - } - friend void setPositionalArg( Arg& arg, int position ) - { - arg.position = position; - } - - - class ArgBuilder { - public: - ArgBuilder( Arg* arg ) : m_arg( arg ) {} - - // Bind a non-boolean data member (requires placeholder string) - template - void bind( M C::* field, std::string const& placeholder ) { - m_arg->boundField = new Detail::BoundDataMember( field ); - m_arg->placeholder = placeholder; - } - // Bind a boolean data member (no placeholder required) - template - void bind( bool C::* field ) { - m_arg->boundField = new Detail::BoundDataMember( field ); - } - - // Bind a method taking a single, non-boolean argument (requires a placeholder string) - template - void bind( void (C::* unaryMethod)( M ), std::string const& placeholder ) { - m_arg->boundField = new Detail::BoundUnaryMethod( unaryMethod ); - m_arg->placeholder = placeholder; - } - - // Bind a method taking a single, boolean argument (no placeholder string required) - template - void bind( void (C::* unaryMethod)( bool ) ) { - m_arg->boundField = new Detail::BoundUnaryMethod( unaryMethod ); - } - - // Bind a method that takes no arguments (will be called if opt is present) - template - void bind( void (C::* nullaryMethod)() ) { - m_arg->boundField = new Detail::BoundNullaryMethod( nullaryMethod ); - } - - // Bind a free function taking a single argument - the object to operate on (no placeholder string required) - template - void bind( void (* unaryFunction)( C& ) ) { - m_arg->boundField = new Detail::BoundUnaryFunction( unaryFunction ); - } - - // Bind a free function taking a single argument - the object to operate on (requires a placeholder string) - template - void bind( void (* binaryFunction)( C&, T ), std::string const& placeholder ) { - m_arg->boundField = new Detail::BoundBinaryFunction( binaryFunction ); - m_arg->placeholder = placeholder; - } - - ArgBuilder& describe( std::string const& description ) { - m_arg->description = description; - return *this; - } - ArgBuilder& detail( std::string const& detail ) { - m_arg->detail = detail; - return *this; - } - - protected: - Arg* m_arg; - }; - - class OptBuilder : public ArgBuilder { - public: - OptBuilder( Arg* arg ) : ArgBuilder( arg ) {} - OptBuilder( OptBuilder& other ) : ArgBuilder( other ) {} - - OptBuilder& operator[]( std::string const& optName ) { - addOptName( *ArgBuilder::m_arg, optName ); - return *this; - } - }; - - public: - - CommandLine() - : m_boundProcessName( new Detail::NullBinder() ), - m_highestSpecifiedArgPosition( 0 ), - m_throwOnUnrecognisedTokens( false ) - {} - CommandLine( CommandLine const& other ) - : m_boundProcessName( other.m_boundProcessName ), - m_options ( other.m_options ), - m_positionalArgs( other.m_positionalArgs ), - m_highestSpecifiedArgPosition( other.m_highestSpecifiedArgPosition ), - m_throwOnUnrecognisedTokens( other.m_throwOnUnrecognisedTokens ) - { - if( other.m_floatingArg.get() ) - m_floatingArg.reset( new Arg( *other.m_floatingArg ) ); - } - - CommandLine& setThrowOnUnrecognisedTokens( bool shouldThrow = true ) { - m_throwOnUnrecognisedTokens = shouldThrow; - return *this; - } - - - OptBuilder operator[]( std::string const& optName ) { - m_options.push_back( Arg() ); - addOptName( m_options.back(), optName ); - OptBuilder builder( &m_options.back() ); - return builder; - } - - ArgBuilder operator[]( int position ) { - m_positionalArgs.insert( std::make_pair( position, Arg() ) ); - if( position > m_highestSpecifiedArgPosition ) - m_highestSpecifiedArgPosition = position; - setPositionalArg( m_positionalArgs[position], position ); - ArgBuilder builder( &m_positionalArgs[position] ); - return builder; - } - - // Invoke this with the _ instance - ArgBuilder operator[]( UnpositionalTag ) { - if( m_floatingArg.get() ) - throw std::logic_error( "Only one unpositional argument can be added" ); - m_floatingArg.reset( new Arg() ); - ArgBuilder builder( m_floatingArg.get() ); - return builder; - } - - template - void bindProcessName( M C::* field ) { - m_boundProcessName = new Detail::BoundDataMember( field ); - } - template - void bindProcessName( void (C::*_unaryMethod)( M ) ) { - m_boundProcessName = new Detail::BoundUnaryMethod( _unaryMethod ); - } - - void optUsage( std::ostream& os, std::size_t indent = 0, std::size_t width = Detail::consoleWidth ) const { - typename std::vector::const_iterator itBegin = m_options.begin(), itEnd = m_options.end(), it; - std::size_t maxWidth = 0; - for( it = itBegin; it != itEnd; ++it ) - maxWidth = (std::max)( maxWidth, it->commands().size() ); - - for( it = itBegin; it != itEnd; ++it ) { - Detail::Text usage( it->commands(), Detail::TextAttributes() - .setWidth( maxWidth+indent ) - .setIndent( indent ) ); - Detail::Text desc( it->description, Detail::TextAttributes() - .setWidth( width - maxWidth - 3 ) ); - - for( std::size_t i = 0; i < (std::max)( usage.size(), desc.size() ); ++i ) { - std::string usageCol = i < usage.size() ? usage[i] : ""; - os << usageCol; - - if( i < desc.size() && !desc[i].empty() ) - os << std::string( indent + 2 + maxWidth - usageCol.size(), ' ' ) - << desc[i]; - os << "\n"; - } - } - } - std::string optUsage() const { - std::ostringstream oss; - optUsage( oss ); - return oss.str(); - } - - void argSynopsis( std::ostream& os ) const { - for( int i = 1; i <= m_highestSpecifiedArgPosition; ++i ) { - if( i > 1 ) - os << " "; - typename std::map::const_iterator it = m_positionalArgs.find( i ); - if( it != m_positionalArgs.end() ) - os << "<" << it->second.placeholder << ">"; - else if( m_floatingArg.get() ) - os << "<" << m_floatingArg->placeholder << ">"; - else - throw std::logic_error( "non consecutive positional arguments with no floating args" ); - } - // !TBD No indication of mandatory args - if( m_floatingArg.get() ) { - if( m_highestSpecifiedArgPosition > 1 ) - os << " "; - os << "[<" << m_floatingArg->placeholder << "> ...]"; - } - } - std::string argSynopsis() const { - std::ostringstream oss; - argSynopsis( oss ); - return oss.str(); - } - - void usage( std::ostream& os, std::string const& procName ) const { - validate(); - os << "usage:\n " << procName << " "; - argSynopsis( os ); - if( !m_options.empty() ) { - os << " [options]\n\nwhere options are: \n"; - optUsage( os, 2 ); - } - os << "\n"; - } - std::string usage( std::string const& procName ) const { - std::ostringstream oss; - usage( oss, procName ); - return oss.str(); - } - - ConfigT parse( std::vector const& args ) const { - ConfigT config; - parseInto( args, config ); - return config; - } - - std::vector parseInto( std::vector const& args, ConfigT& config ) const { - std::string processName = args.empty() ? std::string() : args[0]; - std::size_t lastSlash = processName.find_last_of( "/\\" ); - if( lastSlash != std::string::npos ) - processName = processName.substr( lastSlash+1 ); - m_boundProcessName.set( config, processName ); - std::vector tokens; - Parser parser; - parser.parseIntoTokens( args, tokens ); - return populate( tokens, config ); - } - - std::vector populate( std::vector const& tokens, ConfigT& config ) const { - validate(); - std::vector unusedTokens = populateOptions( tokens, config ); - unusedTokens = populateFixedArgs( unusedTokens, config ); - unusedTokens = populateFloatingArgs( unusedTokens, config ); - return unusedTokens; - } - - std::vector populateOptions( std::vector const& tokens, ConfigT& config ) const { - std::vector unusedTokens; - std::vector errors; - for( std::size_t i = 0; i < tokens.size(); ++i ) { - Parser::Token const& token = tokens[i]; - typename std::vector::const_iterator it = m_options.begin(), itEnd = m_options.end(); - for(; it != itEnd; ++it ) { - Arg const& arg = *it; - - try { - if( ( token.type == Parser::Token::ShortOpt && arg.hasShortName( token.data ) ) || - ( token.type == Parser::Token::LongOpt && arg.hasLongName( token.data ) ) ) { - if( arg.takesArg() ) { - if( i == tokens.size()-1 || tokens[i+1].type != Parser::Token::Positional ) - errors.push_back( "Expected argument to option: " + token.data ); - else - arg.boundField.set( config, tokens[++i].data ); - } - else { - arg.boundField.set( config, "true" ); - } - break; - } - } - catch( std::exception& ex ) { - errors.push_back( std::string( ex.what() ) + "\n- while parsing: (" + arg.commands() + ")" ); - } - } - if( it == itEnd ) { - if( token.type == Parser::Token::Positional || !m_throwOnUnrecognisedTokens ) - unusedTokens.push_back( token ); - else if( errors.empty() && m_throwOnUnrecognisedTokens ) - errors.push_back( "unrecognised option: " + token.data ); - } - } - if( !errors.empty() ) { - std::ostringstream oss; - for( std::vector::const_iterator it = errors.begin(), itEnd = errors.end(); - it != itEnd; - ++it ) { - if( it != errors.begin() ) - oss << "\n"; - oss << *it; - } - throw std::runtime_error( oss.str() ); - } - return unusedTokens; - } - std::vector populateFixedArgs( std::vector const& tokens, ConfigT& config ) const { - std::vector unusedTokens; - int position = 1; - for( std::size_t i = 0; i < tokens.size(); ++i ) { - Parser::Token const& token = tokens[i]; - typename std::map::const_iterator it = m_positionalArgs.find( position ); - if( it != m_positionalArgs.end() ) - it->second.boundField.set( config, token.data ); - else - unusedTokens.push_back( token ); - if( token.type == Parser::Token::Positional ) - position++; - } - return unusedTokens; - } - std::vector populateFloatingArgs( std::vector const& tokens, ConfigT& config ) const { - if( !m_floatingArg.get() ) - return tokens; - std::vector unusedTokens; - for( std::size_t i = 0; i < tokens.size(); ++i ) { - Parser::Token const& token = tokens[i]; - if( token.type == Parser::Token::Positional ) - m_floatingArg->boundField.set( config, token.data ); - else - unusedTokens.push_back( token ); - } - return unusedTokens; - } - - void validate() const - { - if( m_options.empty() && m_positionalArgs.empty() && !m_floatingArg.get() ) - throw std::logic_error( "No options or arguments specified" ); - - for( typename std::vector::const_iterator it = m_options.begin(), - itEnd = m_options.end(); - it != itEnd; ++it ) - it->validate(); - } - - private: - Detail::BoundArgFunction m_boundProcessName; - std::vector m_options; - std::map m_positionalArgs; - ArgAutoPtr m_floatingArg; - int m_highestSpecifiedArgPosition; - bool m_throwOnUnrecognisedTokens; - }; - -} // end namespace Clara - - -STITCH_CLARA_CLOSE_NAMESPACE -#undef STITCH_CLARA_OPEN_NAMESPACE -#undef STITCH_CLARA_CLOSE_NAMESPACE - - -#endif // TWOBLUECUBES_CLARA_H_INCLUDED diff --git a/include/external/clara.hpp b/include/external/clara.hpp new file mode 100644 index 00000000..88a5e9fe --- /dev/null +++ b/include/external/clara.hpp @@ -0,0 +1,1232 @@ +// v1.0-develop.2 +// See https://github.com/philsquared/Clara + +#ifndef CATCH_CLARA_HPP_INCLUDED +#define CATCH_CLARA_HPP_INCLUDED + +#ifndef CATCH_CLARA_CONFIG_CONSOLE_WIDTH +#define CATCH_CLARA_CONFIG_CONSOLE_WIDTH 80 +#endif + +#ifndef CATCH_CLARA_TEXTFLOW_CONFIG_CONSOLE_WIDTH +#define CATCH_CLARA_TEXTFLOW_CONFIG_CONSOLE_WIDTH CATCH_CLARA_CONFIG_CONSOLE_WIDTH +#endif + +// ----------- #included from clara_textflow.hpp ----------- + +// TextFlowCpp +// +// A single-header library for wrapping and laying out basic text, by Phil Nash +// +// This work is licensed under the BSD 2-Clause license. +// See the accompanying LICENSE file, or the one at https://opensource.org/licenses/BSD-2-Clause +// +// This project is hosted at https://github.com/philsquared/textflowcpp + +#ifndef CATCH_CLARA_TEXTFLOW_HPP_INCLUDED +#define CATCH_CLARA_TEXTFLOW_HPP_INCLUDED + +#include +#include +#include +#include + +#ifndef CATCH_CLARA_TEXTFLOW_CONFIG_CONSOLE_WIDTH +#define CATCH_CLARA_TEXTFLOW_CONFIG_CONSOLE_WIDTH 80 +#endif + + +namespace Catch { namespace clara { namespace TextFlow { + + inline auto isWhitespace( char c ) -> bool { + static std::string chars = " \t\n\r"; + return chars.find( c ) != std::string::npos; + } + inline auto isBreakableBefore( char c ) -> bool { + static std::string chars = "[({<|"; + return chars.find( c ) != std::string::npos; + } + inline auto isBreakableAfter( char c ) -> bool { + static std::string chars = "])}>.,:;*+-=&/\\"; + return chars.find( c ) != std::string::npos; + } + + class Columns; + + class Column { + std::vector m_strings; + size_t m_width = CATCH_CLARA_TEXTFLOW_CONFIG_CONSOLE_WIDTH; + size_t m_indent = 0; + size_t m_initialIndent = std::string::npos; + + public: + class iterator { + friend Column; + + Column const& m_column; + size_t m_stringIndex = 0; + size_t m_pos = 0; + + size_t m_len = 0; + size_t m_end = 0; + bool m_suffix = false; + + iterator( Column const& column, size_t stringIndex ) + : m_column( column ), + m_stringIndex( stringIndex ) + {} + + auto line() const -> std::string const& { return m_column.m_strings[m_stringIndex]; } + + auto isBoundary( size_t at ) const -> bool { + assert( at > 0 ); + assert( at <= line().size() ); + + return at == line().size() || + ( isWhitespace( line()[at] ) && !isWhitespace( line()[at-1] ) ) || + isBreakableBefore( line()[at] ) || + isBreakableAfter( line()[at-1] ); + } + + void calcLength() { + assert( m_stringIndex < m_column.m_strings.size() ); + + m_suffix = false; + auto width = m_column.m_width-indent(); + m_end = m_pos; + while( m_end < line().size() && line()[m_end] != '\n' ) + ++m_end; + + if( m_end < m_pos + width ) { + m_len = m_end - m_pos; + } + else { + size_t len = width; + while (len > 0 && !isBoundary(m_pos + len)) + --len; + while (len > 0 && isWhitespace( line()[m_pos + len - 1] )) + --len; + + if (len > 0) { + m_len = len; + } else { + m_suffix = true; + m_len = width - 1; + } + } + } + + auto indent() const -> size_t { + auto initial = m_pos == 0 && m_stringIndex == 0 ? m_column.m_initialIndent : std::string::npos; + return initial == std::string::npos ? m_column.m_indent : initial; + } + + auto addIndentAndSuffix(std::string const &plain) const -> std::string { + return std::string( indent(), ' ' ) + (m_suffix ? plain + "-" : plain); + } + + public: + explicit iterator( Column const& column ) : m_column( column ) { + assert( m_column.m_width > m_column.m_indent ); + assert( m_column.m_initialIndent == std::string::npos || m_column.m_width > m_column.m_initialIndent ); + calcLength(); + if( m_len == 0 ) + m_stringIndex++; // Empty string + } + + auto operator *() const -> std::string { + assert( m_stringIndex < m_column.m_strings.size() ); + assert( m_pos <= m_end ); + if( m_pos + m_column.m_width < m_end ) + return addIndentAndSuffix(line().substr(m_pos, m_len)); + else + return addIndentAndSuffix(line().substr(m_pos, m_end - m_pos)); + } + + auto operator ++() -> iterator& { + m_pos += m_len; + if( m_pos < line().size() && line()[m_pos] == '\n' ) + m_pos += 1; + else + while( m_pos < line().size() && isWhitespace( line()[m_pos] ) ) + ++m_pos; + + if( m_pos == line().size() ) { + m_pos = 0; + ++m_stringIndex; + } + if( m_stringIndex < m_column.m_strings.size() ) + calcLength(); + return *this; + } + auto operator ++(int) -> iterator { + iterator prev( *this ); + operator++(); + return prev; + } + + auto operator ==( iterator const& other ) const -> bool { + return + m_pos == other.m_pos && + m_stringIndex == other.m_stringIndex && + &m_column == &other.m_column; + } + auto operator !=( iterator const& other ) const -> bool { + return !operator==( other ); + } + }; + using const_iterator = iterator; + + explicit Column( std::string const& text ) { m_strings.push_back( text ); } + + auto width( size_t newWidth ) -> Column& { + assert( newWidth > 0 ); + m_width = newWidth; + return *this; + } + auto indent( size_t newIndent ) -> Column& { + m_indent = newIndent; + return *this; + } + auto initialIndent( size_t newIndent ) -> Column& { + m_initialIndent = newIndent; + return *this; + } + + auto width() const -> size_t { return m_width; } + auto begin() const -> iterator { return iterator( *this ); } + auto end() const -> iterator { return { *this, m_strings.size() }; } + + inline friend std::ostream& operator << ( std::ostream& os, Column const& col ) { + bool first = true; + for( auto line : col ) { + if( first ) + first = false; + else + os << "\n"; + os << line; + } + return os; + } + + auto operator + ( Column const& other ) -> Columns; + + auto toString() const -> std::string { + std::ostringstream oss; + oss << *this; + return oss.str(); + } + }; + + class Spacer : public Column { + + public: + explicit Spacer( size_t spaceWidth ) : Column( "" ) { + width( spaceWidth ); + } + }; + + class Columns { + std::vector m_columns; + + public: + + class iterator { + friend Columns; + struct EndTag {}; + + std::vector const& m_columns; + std::vector m_iterators; + size_t m_activeIterators; + + iterator( Columns const& columns, EndTag ) + : m_columns( columns.m_columns ), + m_activeIterators( 0 ) + { + m_iterators.reserve( m_columns.size() ); + + for( auto const& col : m_columns ) + m_iterators.push_back( col.end() ); + } + + public: + explicit iterator( Columns const& columns ) + : m_columns( columns.m_columns ), + m_activeIterators( m_columns.size() ) + { + m_iterators.reserve( m_columns.size() ); + + for( auto const& col : m_columns ) + m_iterators.push_back( col.begin() ); + } + + auto operator ==( iterator const& other ) const -> bool { + return m_iterators == other.m_iterators; + } + auto operator !=( iterator const& other ) const -> bool { + return m_iterators != other.m_iterators; + } + auto operator *() const -> std::string { + std::string row, padding; + + for( size_t i = 0; i < m_columns.size(); ++i ) { + auto width = m_columns[i].width(); + if( m_iterators[i] != m_columns[i].end() ) { + std::string col = *m_iterators[i]; + row += padding + col; + if( col.size() < width ) + padding = std::string( width - col.size(), ' ' ); + else + padding = ""; + } + else { + padding += std::string( width, ' ' ); + } + } + return row; + } + auto operator ++() -> iterator& { + for( size_t i = 0; i < m_columns.size(); ++i ) { + if (m_iterators[i] != m_columns[i].end()) + ++m_iterators[i]; + } + return *this; + } + auto operator ++(int) -> iterator { + iterator prev( *this ); + operator++(); + return prev; + } + }; + using const_iterator = iterator; + + auto begin() const -> iterator { return iterator( *this ); } + auto end() const -> iterator { return { *this, iterator::EndTag() }; } + + auto operator += ( Column const& col ) -> Columns& { + m_columns.push_back( col ); + return *this; + } + auto operator + ( Column const& col ) -> Columns { + Columns combined = *this; + combined += col; + return combined; + } + + inline friend std::ostream& operator << ( std::ostream& os, Columns const& cols ) { + + bool first = true; + for( auto line : cols ) { + if( first ) + first = false; + else + os << "\n"; + os << line; + } + return os; + } + + auto toString() const -> std::string { + std::ostringstream oss; + oss << *this; + return oss.str(); + } + }; + + inline auto Column::operator + ( Column const& other ) -> Columns { + Columns cols; + cols += *this; + cols += other; + return cols; + } +}}} // namespace Catch::clara::TextFlow + +#endif // CATCH_CLARA_TEXTFLOW_HPP_INCLUDED + +// ----------- end of #include from clara_textflow.hpp ----------- +// ........... back in clara.hpp + + +#include +#include +#include + +#if !defined(CATCH_PLATFORM_WINDOWS) && ( defined(WIN32) || defined(__WIN32__) || defined(_WIN32) || defined(_MSC_VER) ) +#define CATCH_PLATFORM_WINDOWS +#endif + +namespace Catch { namespace clara { +namespace detail { + + // Traits for extracting arg and return type of lambdas (for single argument lambdas) + template + struct UnaryLambdaTraits : UnaryLambdaTraits {}; + + template + struct UnaryLambdaTraits { + static const bool isValid = false; + }; + + template + struct UnaryLambdaTraits { + static const bool isValid = true; + using ArgType = typename std::remove_const::type>::type;; + using ReturnType = ReturnT; + }; + + class TokenStream; + + // Transport for raw args (copied from main args, or supplied via init list for testing) + class Args { + friend TokenStream; + std::string m_exeName; + std::vector m_args; + + public: + Args( int argc, char *argv[] ) { + m_exeName = argv[0]; + for( int i = 1; i < argc; ++i ) + m_args.push_back( argv[i] ); + } + + Args( std::initializer_list args ) + : m_exeName( *args.begin() ), + m_args( args.begin()+1, args.end() ) + {} + + auto exeName() const -> std::string { + return m_exeName; + } + }; + + // Wraps a token coming from a token stream. These may not directly correspond to strings as a single string + // may encode an option + its argument if the : or = form is used + enum class TokenType { + Option, Argument + }; + struct Token { + TokenType type; + std::string token; + }; + + inline auto isOptPrefix( char c ) -> bool { + return c == '-' +#ifdef CATCH_PLATFORM_WINDOWS + || c == '/' +#endif + ; + } + + // Abstracts iterators into args as a stream of tokens, with option arguments uniformly handled + class TokenStream { + using Iterator = std::vector::const_iterator; + Iterator it; + Iterator itEnd; + std::vector m_tokenBuffer; + + void loadBuffer() { + m_tokenBuffer.resize( 0 ); + + // Skip any empty strings + while( it != itEnd && it->empty() ) + ++it; + + if( it != itEnd ) { + auto const &next = *it; + if( isOptPrefix( next[0] ) ) { + auto delimiterPos = next.find_first_of( " :=" ); + if( delimiterPos != std::string::npos ) { + m_tokenBuffer.push_back( { TokenType::Option, next.substr( 0, delimiterPos ) } ); + m_tokenBuffer.push_back( { TokenType::Argument, next.substr( delimiterPos + 1 ) } ); + } else { + if( next[1] != '-' && next.size() > 2 ) { + std::string opt = "- "; + for( size_t i = 1; i < next.size(); ++i ) { + opt[1] = next[i]; + m_tokenBuffer.push_back( { TokenType::Option, opt } ); + } + } else { + m_tokenBuffer.push_back( { TokenType::Option, next } ); + } + } + } else { + m_tokenBuffer.push_back( { TokenType::Argument, next } ); + } + } + } + + public: + explicit TokenStream( Args const &args ) : TokenStream( args.m_args.begin(), args.m_args.end() ) {} + + TokenStream( Iterator it, Iterator itEnd ) : it( it ), itEnd( itEnd ) { + loadBuffer(); + } + + explicit operator bool() const { + return !m_tokenBuffer.empty() || it != itEnd; + } + + auto count() const -> size_t { return m_tokenBuffer.size() + (itEnd - it); } + + auto operator*() const -> Token { + assert( !m_tokenBuffer.empty() ); + return m_tokenBuffer.front(); + } + + auto operator->() const -> Token const * { + assert( !m_tokenBuffer.empty() ); + return &m_tokenBuffer.front(); + } + + auto operator++() -> TokenStream & { + if( m_tokenBuffer.size() >= 2 ) { + m_tokenBuffer.erase( m_tokenBuffer.begin() ); + } else { + if( it != itEnd ) + ++it; + loadBuffer(); + } + return *this; + } + }; + + + class ResultBase { + public: + enum Type { + Ok, LogicError, RuntimeError + }; + + protected: + ResultBase( Type type ) : m_type( type ) {} + virtual ~ResultBase() = default; + + virtual void enforceOk() const = 0; + + Type m_type; + }; + + template + class ResultValueBase : public ResultBase { + public: + auto value() const -> T const & { + enforceOk(); + return m_value; + } + + protected: + ResultValueBase( Type type ) : ResultBase( type ) {} + + ResultValueBase( ResultValueBase const &other ) : ResultBase( other ) { + if( m_type == ResultBase::Ok ) + new( &m_value ) T( other.m_value ); + } + + ResultValueBase( Type, T const &value ) : ResultBase( Ok ) { + new( &m_value ) T( value ); + } + + auto operator=( ResultValueBase const &other ) -> ResultValueBase & { + if( m_type == ResultBase::Ok ) + m_value.~T(); + ResultBase::operator=(other); + if( m_type == ResultBase::Ok ) + new( &m_value ) T( other.m_value ); + return *this; + } + + ~ResultValueBase() { + if( m_type == Ok ) + m_value.~T(); + } + + union { + T m_value; + }; + }; + + template<> + class ResultValueBase : public ResultBase { + protected: + using ResultBase::ResultBase; + }; + + template + class BasicResult : public ResultValueBase { + public: + template + explicit BasicResult( BasicResult const &other ) + : ResultValueBase( other.type() ), + m_errorMessage( other.errorMessage() ) + { + assert( type() != ResultBase::Ok ); + } + + template + static auto ok( U const &value ) -> BasicResult { return { ResultBase::Ok, value }; } + static auto ok() -> BasicResult { return { ResultBase::Ok }; } + static auto logicError( std::string const &message ) -> BasicResult { return { ResultBase::LogicError, message }; } + static auto runtimeError( std::string const &message ) -> BasicResult { return { ResultBase::RuntimeError, message }; } + + explicit operator bool() const { return m_type == ResultBase::Ok; } + auto type() const -> ResultBase::Type { return m_type; } + auto errorMessage() const -> std::string { return m_errorMessage; } + + protected: + virtual void enforceOk() const { + // !TBD: If no exceptions, std::terminate here or something + switch( m_type ) { + case ResultBase::LogicError: + throw std::logic_error( m_errorMessage ); + case ResultBase::RuntimeError: + throw std::runtime_error( m_errorMessage ); + case ResultBase::Ok: + break; + } + } + + std::string m_errorMessage; // Only populated if resultType is an error + + BasicResult( ResultBase::Type type, std::string const &message ) + : ResultValueBase(type), + m_errorMessage(message) + { + assert( m_type != ResultBase::Ok ); + } + + using ResultValueBase::ResultValueBase; + using ResultBase::m_type; + }; + + enum class ParseResultType { + Matched, NoMatch, ShortCircuitAll, ShortCircuitSame + }; + + class ParseState { + public: + + ParseState( ParseResultType type, TokenStream const &remainingTokens ) + : m_type(type), + m_remainingTokens( remainingTokens ) + {} + + auto type() const -> ParseResultType { return m_type; } + auto remainingTokens() const -> TokenStream { return m_remainingTokens; } + + private: + ParseResultType m_type; + TokenStream m_remainingTokens; + }; + + using Result = BasicResult; + using ParserResult = BasicResult; + using InternalParseResult = BasicResult; + + struct HelpColumns { + std::string left; + std::string right; + }; + + template + inline auto convertInto( std::string const &source, T& target ) -> ParserResult { + std::stringstream ss; + ss << source; + ss >> target; + if( ss.fail() ) + return ParserResult::runtimeError( "Unable to convert '" + source + "' to destination type" ); + else + return ParserResult::ok( ParseResultType::Matched ); + } + inline auto convertInto( std::string const &source, std::string& target ) -> ParserResult { + target = source; + return ParserResult::ok( ParseResultType::Matched ); + } + inline auto convertInto( std::string const &source, bool &target ) -> ParserResult { + std::string srcLC = source; + std::transform( srcLC.begin(), srcLC.end(), srcLC.begin(), []( char c ) { return static_cast( ::tolower(c) ); } ); + if (srcLC == "y" || srcLC == "1" || srcLC == "true" || srcLC == "yes" || srcLC == "on") + target = true; + else if (srcLC == "n" || srcLC == "0" || srcLC == "false" || srcLC == "no" || srcLC == "off") + target = false; + else + return ParserResult::runtimeError( "Expected a boolean value but did not recognise: '" + source + "'" ); + return ParserResult::ok( ParseResultType::Matched ); + } + + struct BoundRefBase { + BoundRefBase() = default; + BoundRefBase( BoundRefBase const & ) = delete; + BoundRefBase( BoundRefBase && ) = delete; + BoundRefBase &operator=( BoundRefBase const & ) = delete; + BoundRefBase &operator=( BoundRefBase && ) = delete; + + virtual ~BoundRefBase() = default; + + virtual auto isFlag() const -> bool = 0; + virtual auto isContainer() const -> bool { return false; } + virtual auto setValue( std::string const &arg ) -> ParserResult = 0; + virtual auto setFlag( bool flag ) -> ParserResult = 0; + }; + + struct BoundValueRefBase : BoundRefBase { + auto isFlag() const -> bool override { return false; } + + auto setFlag( bool ) -> ParserResult override { + return ParserResult::logicError( "Flags can only be set on boolean fields" ); + } + }; + + struct BoundFlagRefBase : BoundRefBase { + auto isFlag() const -> bool override { return true; } + + auto setValue( std::string const &arg ) -> ParserResult override { + bool flag; + auto result = convertInto( arg, flag ); + if( result ) + setFlag( flag ); + return result; + } + }; + + template + struct BoundRef : BoundValueRefBase { + T &m_ref; + + explicit BoundRef( T &ref ) : m_ref( ref ) {} + + auto setValue( std::string const &arg ) -> ParserResult override { + return convertInto( arg, m_ref ); + } + }; + + template + struct BoundRef> : BoundValueRefBase { + std::vector &m_ref; + + explicit BoundRef( std::vector &ref ) : m_ref( ref ) {} + + auto isContainer() const -> bool override { return true; } + + auto setValue( std::string const &arg ) -> ParserResult override { + T temp; + auto result = convertInto( arg, temp ); + if( result ) + m_ref.push_back( temp ); + return result; + } + }; + + struct BoundFlagRef : BoundFlagRefBase { + bool &m_ref; + + explicit BoundFlagRef( bool &ref ) : m_ref( ref ) {} + + auto setFlag( bool flag ) -> ParserResult override { + m_ref = flag; + return ParserResult::ok( ParseResultType::Matched ); + } + }; + + template + struct LambdaInvoker { + static_assert( std::is_same::value, "Lambda must return void or clara::ParserResult" ); + + template + static auto invoke( L const &lambda, ArgType const &arg ) -> ParserResult { + return lambda( arg ); + } + }; + + template<> + struct LambdaInvoker { + template + static auto invoke( L const &lambda, ArgType const &arg ) -> ParserResult { + lambda( arg ); + return ParserResult::ok( ParseResultType::Matched ); + } + }; + + template + inline auto invokeLambda( L const &lambda, std::string const &arg ) -> ParserResult { + ArgType temp; + auto result = convertInto( arg, temp ); + return !result + ? result + : LambdaInvoker::ReturnType>::invoke( lambda, temp ); + }; + + + template + struct BoundLambda : BoundValueRefBase { + L m_lambda; + + static_assert( UnaryLambdaTraits::isValid, "Supplied lambda must take exactly one argument" ); + explicit BoundLambda( L const &lambda ) : m_lambda( lambda ) {} + + auto setValue( std::string const &arg ) -> ParserResult override { + return invokeLambda::ArgType>( m_lambda, arg ); + } + }; + + template + struct BoundFlagLambda : BoundFlagRefBase { + L m_lambda; + + static_assert( UnaryLambdaTraits::isValid, "Supplied lambda must take exactly one argument" ); + static_assert( std::is_same::ArgType, bool>::value, "flags must be boolean" ); + + explicit BoundFlagLambda( L const &lambda ) : m_lambda( lambda ) {} + + auto setFlag( bool flag ) -> ParserResult override { + return LambdaInvoker::ReturnType>::invoke( m_lambda, flag ); + } + }; + + enum class Optionality { Optional, Required }; + + struct Parser; + + class ParserBase { + public: + virtual ~ParserBase() = default; + virtual auto validate() const -> Result { return Result::ok(); } + virtual auto parse( std::string const& exeName, TokenStream const &tokens) const -> InternalParseResult = 0; + virtual auto cardinality() const -> size_t { return 1; } + + auto parse( Args const &args ) const -> InternalParseResult { + return parse( args.exeName(), TokenStream( args ) ); + } + }; + + template + class ComposableParserImpl : public ParserBase { + public: + template + auto operator|( T const &other ) const -> Parser; + }; + + // Common code and state for Args and Opts + template + class ParserRefImpl : public ComposableParserImpl { + protected: + Optionality m_optionality = Optionality::Optional; + std::shared_ptr m_ref; + std::string m_hint; + std::string m_description; + + explicit ParserRefImpl( std::shared_ptr const &ref ) : m_ref( ref ) {} + + public: + template + ParserRefImpl( T &ref, std::string const &hint ) + : m_ref( std::make_shared>( ref ) ), + m_hint( hint ) + {} + + template + ParserRefImpl( LambdaT const &ref, std::string const &hint ) + : m_ref( std::make_shared>( ref ) ), + m_hint(hint) + {} + + auto operator()( std::string const &description ) -> DerivedT & { + m_description = description; + return static_cast( *this ); + } + + auto optional() -> DerivedT & { + m_optionality = Optionality::Optional; + return static_cast( *this ); + }; + + auto required() -> DerivedT & { + m_optionality = Optionality::Required; + return static_cast( *this ); + }; + + auto isOptional() const -> bool { + return m_optionality == Optionality::Optional; + } + + auto cardinality() const -> size_t override { + if( m_ref->isContainer() ) + return 0; + else + return 1; + } + + auto hint() const -> std::string { return m_hint; } + }; + + class ExeName : public ComposableParserImpl { + std::shared_ptr m_name; + std::shared_ptr m_ref; + + template + static auto makeRef(LambdaT const &lambda) -> std::shared_ptr { + return std::make_shared>( lambda) ; + } + + public: + ExeName() : m_name( std::make_shared( "" ) ) {} + + explicit ExeName( std::string &ref ) : ExeName() { + m_ref = std::make_shared>( ref ); + } + + template + explicit ExeName( LambdaT const& lambda ) : ExeName() { + m_ref = std::make_shared>( lambda ); + } + + // The exe name is not parsed out of the normal tokens, but is handled specially + auto parse( std::string const&, TokenStream const &tokens ) const -> InternalParseResult override { + return InternalParseResult::ok( ParseState( ParseResultType::NoMatch, tokens ) ); + } + + auto name() const -> std::string { return *m_name; } + auto set( std::string const& newName ) -> ParserResult { + + auto lastSlash = newName.find_last_of( "\\/" ); + auto filename = ( lastSlash == std::string::npos ) + ? newName + : newName.substr( lastSlash+1 ); + + *m_name = filename; + if( m_ref ) + return m_ref->setValue( filename ); + else + return ParserResult::ok( ParseResultType::Matched ); + } + }; + + class Arg : public ParserRefImpl { + public: + using ParserRefImpl::ParserRefImpl; + + auto parse( std::string const &, TokenStream const &tokens ) const -> InternalParseResult override { + auto validationResult = validate(); + if( !validationResult ) + return InternalParseResult( validationResult ); + + auto remainingTokens = tokens; + auto const &token = *remainingTokens; + if( token.type != TokenType::Argument ) + return InternalParseResult::ok( ParseState( ParseResultType::NoMatch, remainingTokens ) ); + + auto result = m_ref->setValue( remainingTokens->token ); + if( !result ) + return InternalParseResult( result ); + else + return InternalParseResult::ok( ParseState( ParseResultType::Matched, ++remainingTokens ) ); + } + }; + + inline auto normaliseOpt( std::string const &optName ) -> std::string { +#ifdef CATCH_PLATFORM_WINDOWS + if( optName[0] == '/' ) + return "-" + optName.substr( 1 ); + else +#endif + return optName; + } + + class Opt : public ParserRefImpl { + protected: + std::vector m_optNames; + + public: + template + explicit Opt( LambdaT const &ref ) : ParserRefImpl( std::make_shared>( ref ) ) {} + + explicit Opt( bool &ref ) : ParserRefImpl( std::make_shared( ref ) ) {} + + template + Opt( LambdaT const &ref, std::string const &hint ) : ParserRefImpl( ref, hint ) {} + + template + Opt( T &ref, std::string const &hint ) : ParserRefImpl( ref, hint ) {} + + auto operator[]( std::string const &optName ) -> Opt & { + m_optNames.push_back( optName ); + return *this; + } + + auto getHelpColumns() const -> std::vector { + std::ostringstream oss; + bool first = true; + for( auto const &opt : m_optNames ) { + if (first) + first = false; + else + oss << ", "; + oss << opt; + } + if( !m_hint.empty() ) + oss << " <" << m_hint << ">"; + return { { oss.str(), m_description } }; + } + + auto isMatch( std::string const &optToken ) const -> bool { + auto normalisedToken = normaliseOpt( optToken ); + for( auto const &name : m_optNames ) { + if( normaliseOpt( name ) == normalisedToken ) + return true; + } + return false; + } + + using ParserBase::parse; + + auto parse( std::string const&, TokenStream const &tokens ) const -> InternalParseResult override { + auto validationResult = validate(); + if( !validationResult ) + return InternalParseResult( validationResult ); + + auto remainingTokens = tokens; + if( remainingTokens && remainingTokens->type == TokenType::Option ) { + auto const &token = *remainingTokens; + if( isMatch(token.token ) ) { + if( m_ref->isFlag() ) { + auto result = m_ref->setFlag( true ); + if( !result ) + return InternalParseResult( result ); + if( result.value() == ParseResultType::ShortCircuitAll ) + return InternalParseResult::ok( ParseState( result.value(), remainingTokens ) ); + } else { + ++remainingTokens; + if( !remainingTokens ) + return InternalParseResult::runtimeError( "Expected argument following " + token.token ); + auto const &argToken = *remainingTokens; + if( argToken.type != TokenType::Argument ) + return InternalParseResult::runtimeError( "Expected argument following " + token.token ); + auto result = m_ref->setValue( argToken.token ); + if( !result ) + return InternalParseResult( result ); + if( result.value() == ParseResultType::ShortCircuitAll ) + return InternalParseResult::ok( ParseState( result.value(), remainingTokens ) ); + } + return InternalParseResult::ok( ParseState( ParseResultType::Matched, ++remainingTokens ) ); + } + } + return InternalParseResult::ok( ParseState( ParseResultType::NoMatch, remainingTokens ) ); + } + + auto validate() const -> Result override { + if( m_optNames.empty() ) + return Result::logicError( "No options supplied to Opt" ); + for( auto const &name : m_optNames ) { + if( name.empty() ) + return Result::logicError( "Option name cannot be empty" ); +#ifdef CATCH_PLATFORM_WINDOWS + if( name[0] != '-' && name[0] != '/' ) + return Result::logicError( "Option name must begin with '-' or '/'" ); +#else + if( name[0] != '-' ) + return Result::logicError( "Option name must begin with '-'" ); +#endif + } + return ParserRefImpl::validate(); + } + }; + + struct Help : Opt { + Help( bool &showHelpFlag ) + : Opt([&]( bool flag ) { + showHelpFlag = flag; + return ParserResult::ok( ParseResultType::ShortCircuitAll ); + }) + { + static_cast( *this ) + ("display usage information") + ["-?"]["-h"]["--help"] + .optional(); + } + }; + + + struct Parser : ParserBase { + + mutable ExeName m_exeName; + std::vector m_options; + std::vector m_args; + + auto operator|=( ExeName const &exeName ) -> Parser & { + m_exeName = exeName; + return *this; + } + + auto operator|=( Arg const &arg ) -> Parser & { + m_args.push_back(arg); + return *this; + } + + auto operator|=( Opt const &opt ) -> Parser & { + m_options.push_back(opt); + return *this; + } + + auto operator|=( Parser const &other ) -> Parser & { + m_options.insert(m_options.end(), other.m_options.begin(), other.m_options.end()); + m_args.insert(m_args.end(), other.m_args.begin(), other.m_args.end()); + return *this; + } + + template + auto operator|( T const &other ) const -> Parser { + return Parser( *this ) |= other; + } + + auto getHelpColumns() const -> std::vector { + std::vector cols; + for (auto const &o : m_options) { + auto childCols = o.getHelpColumns(); + cols.insert( cols.end(), childCols.begin(), childCols.end() ); + } + return cols; + } + + void writeToStream( std::ostream &os ) const { + if (!m_exeName.name().empty()) { + os << "usage:\n" << " " << m_exeName.name() << " "; + bool required = true, first = true; + for( auto const &arg : m_args ) { + if (first) + first = false; + else + os << " "; + if( arg.isOptional() && required ) { + os << "["; + required = false; + } + os << "<" << arg.hint() << ">"; + if( arg.cardinality() == 0 ) + os << " ... "; + } + if( !required ) + os << "]"; + if( !m_options.empty() ) + os << " options"; + os << "\n\nwhere options are:" << std::endl; + } + + auto rows = getHelpColumns(); + size_t consoleWidth = CATCH_CLARA_CONFIG_CONSOLE_WIDTH; + size_t optWidth = 0; + for( auto const &cols : rows ) + optWidth = (std::max)(optWidth, cols.left.size() + 2); + + for( auto const &cols : rows ) { + auto row = + TextFlow::Column( cols.left ).width( optWidth ).indent( 2 ) + + TextFlow::Spacer(4) + + TextFlow::Column( cols.right ).width( consoleWidth - 7 - optWidth ); + os << row << std::endl; + } + } + + friend auto operator<<( std::ostream &os, Parser const &parser ) -> std::ostream& { + parser.writeToStream( os ); + return os; + } + + auto validate() const -> Result override { + for( auto const &opt : m_options ) { + auto result = opt.validate(); + if( !result ) + return result; + } + for( auto const &arg : m_args ) { + auto result = arg.validate(); + if( !result ) + return result; + } + return Result::ok(); + } + + using ParserBase::parse; + + auto parse( std::string const& exeName, TokenStream const &tokens ) const -> InternalParseResult override { + + struct ParserInfo { + ParserBase const* parser = nullptr; + size_t count = 0; + }; + const size_t totalParsers = m_options.size() + m_args.size(); + assert( totalParsers < 512 ); + // ParserInfo parseInfos[totalParsers]; // <-- this is what we really want to do + ParserInfo parseInfos[512]; + + { + size_t i = 0; + for (auto const &opt : m_options) parseInfos[i++].parser = &opt; + for (auto const &arg : m_args) parseInfos[i++].parser = &arg; + } + + m_exeName.set( exeName ); + + auto result = InternalParseResult::ok( ParseState( ParseResultType::NoMatch, tokens ) ); + while( result.value().remainingTokens() ) { + bool tokenParsed = false; + + for( size_t i = 0; i < totalParsers; ++i ) { + auto& parseInfo = parseInfos[i]; + if( parseInfo.parser->cardinality() == 0 || parseInfo.count < parseInfo.parser->cardinality() ) { + result = parseInfo.parser->parse(exeName, result.value().remainingTokens()); + if (!result) + return result; + if (result.value().type() != ParseResultType::NoMatch) { + tokenParsed = true; + ++parseInfo.count; + break; + } + } + } + + if( result.value().type() == ParseResultType::ShortCircuitAll ) + return result; + if( !tokenParsed ) + return InternalParseResult::runtimeError( "Unrecognised token: " + result.value().remainingTokens()->token ); + } + // !TBD Check missing required options + return result; + } + }; + + template + template + auto ComposableParserImpl::operator|( T const &other ) const -> Parser { + return Parser() | static_cast( *this ) | other; + } +} // namespace detail + + +// A Combined parser +using detail::Parser; + +// A parser for options +using detail::Opt; + +// A parser for arguments +using detail::Arg; + +// Wrapper for argc, argv from main() +using detail::Args; + +// Specifies the name of the executable +using detail::ExeName; + +// Convenience wrapper for option parser that specifies the help option +using detail::Help; + +// enum of result types from a parse +using detail::ParseResultType; + +// Result type for parser operation +using detail::ParserResult; + + +}} // namespace Catch::clara + + +#endif // CATCH_CLARA_HPP_INCLUDED diff --git a/include/external/tbc_text_format.h b/include/external/tbc_text_format.h deleted file mode 100644 index 5209d804..00000000 --- a/include/external/tbc_text_format.h +++ /dev/null @@ -1,168 +0,0 @@ -/* - * Created by Phil on 18/4/2013. - * Copyright 2013 Two Blue Cubes Ltd. All rights reserved. - * - * Distributed under the Boost Software License, Version 1.0. (See accompanying - * file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) - */ -// Only use header guard if we are not using an outer namespace -#ifndef CLICHE_TBC_TEXT_FORMAT_OUTER_NAMESPACE -# ifdef TWOBLUECUBES_TEXT_FORMAT_H_INCLUDED -# ifndef TWOBLUECUBES_TEXT_FORMAT_H_ALREADY_INCLUDED -# define TWOBLUECUBES_TEXT_FORMAT_H_ALREADY_INCLUDED -# endif -# else -# define TWOBLUECUBES_TEXT_FORMAT_H_INCLUDED -# endif -#endif -#ifndef TWOBLUECUBES_TEXT_FORMAT_H_ALREADY_INCLUDED -#include -#include -#include - -// Use optional outer namespace -#ifdef CLICHE_TBC_TEXT_FORMAT_OUTER_NAMESPACE -namespace CLICHE_TBC_TEXT_FORMAT_OUTER_NAMESPACE { -#endif - -namespace Tbc { - -#ifdef TBC_TEXT_FORMAT_CONSOLE_WIDTH - const unsigned int consoleWidth = TBC_TEXT_FORMAT_CONSOLE_WIDTH; -#else - const unsigned int consoleWidth = 80; -#endif - - struct TextAttributes { - TextAttributes() - : initialIndent( std::string::npos ), - indent( 0 ), - width( consoleWidth-1 ) - {} - - TextAttributes& setInitialIndent( std::size_t _value ) { initialIndent = _value; return *this; } - TextAttributes& setIndent( std::size_t _value ) { indent = _value; return *this; } - TextAttributes& setWidth( std::size_t _value ) { width = _value; return *this; } - - std::size_t initialIndent; // indent of first line, or npos - std::size_t indent; // indent of subsequent lines, or all if initialIndent is npos - std::size_t width; // maximum width of text, including indent. Longer text will wrap - }; - - class Text { - public: - Text( std::string const& _str, TextAttributes const& _attr = TextAttributes() ) - : attr( _attr ) - { - const std::string wrappableBeforeChars = "[({<\t"; - const std::string wrappableAfterChars = "])}>-,./|\\"; - const std::string wrappableInsteadOfChars = " \n\r"; - std::string indent = _attr.initialIndent != std::string::npos - ? std::string( _attr.initialIndent, ' ' ) - : std::string( _attr.indent, ' ' ); - - typedef std::string::const_iterator iterator; - iterator it = _str.begin(); - const iterator strEnd = _str.end(); - - while( it != strEnd ) { - - if( lines.size() >= 1000 ) { - lines.push_back( "... message truncated due to excessive size" ); - return; - } - - - std::string suffix; - std::size_t width = (std::min)( static_cast( strEnd-it ), _attr.width-static_cast( indent.size() ) ); - iterator itEnd = it+width; - iterator itNext = _str.end(); - - iterator itNewLine = std::find( it, itEnd, '\n' ); - if( itNewLine != itEnd ) - itEnd = itNewLine; - - if( itEnd != strEnd ) { - bool foundWrapPoint = false; - iterator findIt = itEnd; - do { - if( wrappableAfterChars.find( *findIt ) != std::string::npos && findIt != itEnd ) { - itEnd = findIt+1; - itNext = findIt+1; - foundWrapPoint = true; - } - else if( findIt > it && wrappableBeforeChars.find( *findIt ) != std::string::npos ) { - itEnd = findIt; - itNext = findIt; - foundWrapPoint = true; - } - else if( wrappableInsteadOfChars.find( *findIt ) != std::string::npos ) { - itNext = findIt+1; - itEnd = findIt; - foundWrapPoint = true; - } - if( findIt == it ) - break; - else - --findIt; - } - while( !foundWrapPoint ); - - if( !foundWrapPoint ) { - // No good wrap char, so we'll break mid word and add a hyphen - --itEnd; - itNext = itEnd; - suffix = "-"; - } - else { - while( itEnd > it && wrappableInsteadOfChars.find( *(itEnd-1) ) != std::string::npos ) - --itEnd; - } - } - lines.push_back( indent + std::string( it, itEnd ) + suffix ); - - if( indent.size() != _attr.indent ) - indent = std::string( _attr.indent, ' ' ); - it = itNext; - } - } - - - - typedef std::vector::const_iterator const_iterator; - - const_iterator begin() const { return lines.begin(); } - const_iterator end() const { return lines.end(); } - std::string const& last() const { return lines.back(); } - std::size_t size() const { return lines.size(); } - std::string const& operator[]( std::size_t _index ) const { return lines[_index]; } - std::string toString() const { - std::ostringstream oss; - oss << *this; - return oss.str(); - } - - inline friend std::ostream& operator << ( std::ostream& _stream, Text const& _text ) { - for( Text::const_iterator it = _text.begin(), itEnd = _text.end(); - it != itEnd; ++it ) { - if( it != _text.begin() ) - _stream << "\n"; - _stream << *it; - } - return _stream; - } - - - private: - std::string str; - TextAttributes attr; - std::vector lines; - }; - -} // end namespace Tbc - -#ifdef CLICHE_TBC_TEXT_FORMAT_OUTER_NAMESPACE -} // end outer namespace -#endif - -#endif // TWOBLUECUBES_TEXT_FORMAT_H_ALREADY_INCLUDED diff --git a/include/internal/catch_approx.cpp b/include/internal/catch_approx.cpp new file mode 100644 index 00000000..82c6ff12 --- /dev/null +++ b/include/internal/catch_approx.cpp @@ -0,0 +1,56 @@ +/* + * Created by Martin on 19/07/2017. + * Copyright 2017 Two Blue Cubes Ltd. All rights reserved. + * + * Distributed under the Boost Software License, Version 1.0. (See accompanying + * file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) + */ + +#include "catch_approx.h" + +#include +#include + +namespace { + +// Performs equivalent check of std::fabs(lhs - rhs) <= margin +// But without the subtraction to allow for INFINITY in comparison +bool marginComparison(double lhs, double rhs, double margin) { + return (lhs + margin >= rhs) && (rhs + margin >= lhs); +} + +} + +namespace Catch { +namespace Detail { + + Approx::Approx ( double value ) + : m_epsilon( std::numeric_limits::epsilon()*100 ), + m_margin( 0.0 ), + m_scale( 0.0 ), + m_value( value ) + {} + + Approx Approx::custom() { + return Approx( 0 ); + } + + std::string Approx::toString() const { + std::ostringstream oss; + oss << "Approx( " << ::Catch::Detail::stringify( m_value ) << " )"; + return oss.str(); + } + + bool Approx::equalityComparisonImpl(const double other) const { + // First try with fixed margin, then compute margin based on epsilon, scale and Approx's value + // Thanks to Richard Harris for his help refining the scaled margin value + return marginComparison(m_value, other, m_margin) || marginComparison(m_value, other, m_epsilon * (m_scale + std::fabs(m_value))); + } + +} // end namespace Detail + +std::string StringMaker::convert(Catch::Detail::Approx const& value) { + return value.toString(); +} + +} // end namespace Catch diff --git a/include/internal/catch_approx.h b/include/internal/catch_approx.h new file mode 100644 index 00000000..64f92db7 --- /dev/null +++ b/include/internal/catch_approx.h @@ -0,0 +1,126 @@ +/* + * Created by Phil on 28/04/2011. + * Copyright 2010 Two Blue Cubes Ltd. All rights reserved. + * + * Distributed under the Boost Software License, Version 1.0. (See accompanying + * file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) + */ +#ifndef TWOBLUECUBES_CATCH_APPROX_HPP_INCLUDED +#define TWOBLUECUBES_CATCH_APPROX_HPP_INCLUDED + +#include "catch_enforce.h" +#include "catch_tostring.h" + +#include + +namespace Catch { +namespace Detail { + + class Approx { + private: + bool equalityComparisonImpl(double other) const; + + public: + explicit Approx ( double value ); + + static Approx custom(); + + template ::value>::type> + Approx operator()( T const& value ) { + Approx approx( static_cast(value) ); + approx.epsilon( m_epsilon ); + approx.margin( m_margin ); + approx.scale( m_scale ); + return approx; + } + + template ::value>::type> + explicit Approx( T const& value ): Approx(static_cast(value)) + {} + + + template ::value>::type> + friend bool operator == ( const T& lhs, Approx const& rhs ) { + auto lhs_v = static_cast(lhs); + return rhs.equalityComparisonImpl(lhs_v); + } + + template ::value>::type> + friend bool operator == ( Approx const& lhs, const T& rhs ) { + return operator==( rhs, lhs ); + } + + template ::value>::type> + friend bool operator != ( T const& lhs, Approx const& rhs ) { + return !operator==( lhs, rhs ); + } + + template ::value>::type> + friend bool operator != ( Approx const& lhs, T const& rhs ) { + return !operator==( rhs, lhs ); + } + + template ::value>::type> + friend bool operator <= ( T const& lhs, Approx const& rhs ) { + return static_cast(lhs) < rhs.m_value || lhs == rhs; + } + + template ::value>::type> + friend bool operator <= ( Approx const& lhs, T const& rhs ) { + return lhs.m_value < static_cast(rhs) || lhs == rhs; + } + + template ::value>::type> + friend bool operator >= ( T const& lhs, Approx const& rhs ) { + return static_cast(lhs) > rhs.m_value || lhs == rhs; + } + + template ::value>::type> + friend bool operator >= ( Approx const& lhs, T const& rhs ) { + return lhs.m_value > static_cast(rhs) || lhs == rhs; + } + + template ::value>::type> + Approx& epsilon( T const& newEpsilon ) { + double epsilonAsDouble = static_cast(newEpsilon); + CATCH_ENFORCE(epsilonAsDouble >= 0 && epsilonAsDouble <= 1.0, + "Invalid Approx::epsilon: " << epsilonAsDouble + << ", Approx::epsilon has to be between 0 and 1"); + m_epsilon = epsilonAsDouble; + return *this; + } + + template ::value>::type> + Approx& margin( T const& newMargin ) { + double marginAsDouble = static_cast(newMargin); + CATCH_ENFORCE(marginAsDouble >= 0, + "Invalid Approx::margin: " << marginAsDouble + << ", Approx::Margin has to be non-negative."); + m_margin = marginAsDouble; + return *this; + } + + template ::value>::type> + Approx& scale( T const& newScale ) { + m_scale = static_cast(newScale); + return *this; + } + + std::string toString() const; + + private: + double m_epsilon; + double m_margin; + double m_scale; + double m_value; + }; +} + +template<> +struct StringMaker { + static std::string convert(Catch::Detail::Approx const& value); +}; + +} // end namespace Catch + +#endif // TWOBLUECUBES_CATCH_APPROX_HPP_INCLUDED diff --git a/include/internal/catch_assertionhandler.cpp b/include/internal/catch_assertionhandler.cpp new file mode 100644 index 00000000..0eed7cd9 --- /dev/null +++ b/include/internal/catch_assertionhandler.cpp @@ -0,0 +1,147 @@ +/* + * Created by Phil on 8/8/2017. + * Copyright 2017 Two Blue Cubes Ltd. All rights reserved. + * + * Distributed under the Boost Software License, Version 1.0. (See accompanying + * file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) + */ + +#include "catch_assertionhandler.h" +#include "catch_assertionresult.h" +#include "catch_interfaces_capture.h" +#include "catch_interfaces_runner.h" +#include "catch_interfaces_config.h" +#include "catch_context.h" +#include "catch_debugger.h" +#include "catch_interfaces_registry_hub.h" +#include "catch_capture_matchers.h" + +#include + +namespace Catch { + + auto operator <<( std::ostream& os, ITransientExpression const& expr ) -> std::ostream& { + expr.streamReconstructedExpression( os ); + return os; + } + + LazyExpression::LazyExpression( bool isNegated ) + : m_isNegated( isNegated ) + {} + + LazyExpression::LazyExpression( LazyExpression const& other ) : m_isNegated( other.m_isNegated ) {} + + LazyExpression::operator bool() const { + return m_transientExpression != nullptr; + } + + auto operator << ( std::ostream& os, LazyExpression const& lazyExpr ) -> std::ostream& { + if( lazyExpr.m_isNegated ) + os << "!"; + + if( lazyExpr ) { + if( lazyExpr.m_isNegated && lazyExpr.m_transientExpression->isBinaryExpression() ) + os << "(" << *lazyExpr.m_transientExpression << ")"; + else + os << *lazyExpr.m_transientExpression; + } + else { + os << "{** error - unchecked empty expression requested **}"; + } + return os; + } + + AssertionHandler::AssertionHandler + ( StringRef macroName, + SourceLineInfo const& lineInfo, + StringRef capturedExpression, + ResultDisposition::Flags resultDisposition ) + : m_assertionInfo{ macroName, lineInfo, capturedExpression, resultDisposition } + { + getCurrentContext().getResultCapture()->assertionStarting( m_assertionInfo ); + } + AssertionHandler::~AssertionHandler() { + if ( m_inExceptionGuard ) { + handle( ResultWas::ThrewException, "Exception translation was disabled by CATCH_CONFIG_FAST_COMPILE" ); + getCurrentContext().getResultCapture()->exceptionEarlyReported(); + } + } + + void AssertionHandler::handle( ITransientExpression const& expr ) { + + bool negated = isFalseTest( m_assertionInfo.resultDisposition ); + bool result = expr.getResult() != negated; + + handle( result ? ResultWas::Ok : ResultWas::ExpressionFailed, &expr, negated ); + } + void AssertionHandler::handle( ResultWas::OfType resultType ) { + handle( resultType, nullptr, false ); + } + void AssertionHandler::handle( ResultWas::OfType resultType, StringRef const& message ) { + AssertionResultData data( resultType, LazyExpression( false ) ); + data.message = message; + handle( data, nullptr ); + } + void AssertionHandler::handle( ResultWas::OfType resultType, ITransientExpression const* expr, bool negated ) { + AssertionResultData data( resultType, LazyExpression( negated ) ); + handle( data, expr ); + } + void AssertionHandler::handle( AssertionResultData const& resultData, ITransientExpression const* expr ) { + + getResultCapture().assertionRun(); + + AssertionResult assertionResult{ m_assertionInfo, resultData }; + assertionResult.m_resultData.lazyExpression.m_transientExpression = expr; + + getResultCapture().assertionEnded( assertionResult ); + + if( !assertionResult.isOk() ) { + m_shouldDebugBreak = getCurrentContext().getConfig()->shouldDebugBreak(); + m_shouldThrow = + getCurrentContext().getRunner()->aborting() || + (m_assertionInfo.resultDisposition & ResultDisposition::Normal); + } + } + + auto AssertionHandler::allowThrows() const -> bool { + 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 + /////////////////////////////////////////////////////////////////// + CATCH_BREAK_INTO_DEBUGGER(); + } + reactWithoutDebugBreak(); + } + void AssertionHandler::reactWithoutDebugBreak() const { + if( m_shouldThrow ) + throw Catch::TestFailureException(); + } + + void AssertionHandler::useActiveException() { + handle( ResultWas::ThrewException, Catch::translateActiveException() ); + } + + void AssertionHandler::setExceptionGuard() { + assert( m_inExceptionGuard == false ); + m_inExceptionGuard = true; + } + void AssertionHandler::unsetExceptionGuard() { + assert( m_inExceptionGuard == true ); + m_inExceptionGuard = false; + } + + // This is the overload that takes a string and infers the Equals matcher from it + // The more general overload, that takes any string matcher, is in catch_capture_matchers.cpp + void handleExceptionMatchExpr( AssertionHandler& handler, std::string const& str, StringRef matcherString ) { + handleExceptionMatchExpr( handler, Matchers::Equals( str ), matcherString ); + } + +} // namespace Catch diff --git a/include/internal/catch_assertionhandler.h b/include/internal/catch_assertionhandler.h new file mode 100644 index 00000000..9fdbee4f --- /dev/null +++ b/include/internal/catch_assertionhandler.h @@ -0,0 +1,73 @@ +/* + * Created by Phil on 8/8/2017. + * Copyright 2017 Two Blue Cubes Ltd. All rights reserved. + * + * Distributed under the Boost Software License, Version 1.0. (See accompanying + * file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) + */ +#ifndef TWOBLUECUBES_CATCH_ASSERTIONHANDLER_H_INCLUDED +#define TWOBLUECUBES_CATCH_ASSERTIONHANDLER_H_INCLUDED + +#include "catch_decomposer.h" +#include "catch_assertioninfo.h" + +namespace Catch { + + struct TestFailureException{}; + struct AssertionResultData; + + class LazyExpression { + friend class AssertionHandler; + friend struct AssertionStats; + + ITransientExpression const* m_transientExpression = nullptr; + bool m_isNegated; + public: + LazyExpression( bool isNegated ); + LazyExpression( LazyExpression const& other ); + LazyExpression& operator = ( LazyExpression const& ) = delete; + + explicit operator bool() const; + + friend auto operator << ( std::ostream& os, LazyExpression const& lazyExpr ) -> std::ostream&; + }; + + class AssertionHandler { + AssertionInfo m_assertionInfo; + bool m_shouldDebugBreak = false; + bool m_shouldThrow = false; + bool m_inExceptionGuard = false; + + public: + AssertionHandler + ( StringRef macroName, + SourceLineInfo const& lineInfo, + StringRef capturedExpression, + ResultDisposition::Flags resultDisposition ); + ~AssertionHandler(); + + void handle( ITransientExpression const& expr ); + + template + void handle( ExprLhs const& expr ) { + handle( 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 ); + + auto shouldDebugBreak() const -> bool; + auto allowThrows() const -> bool; + void reactWithDebugBreak() const; + void reactWithoutDebugBreak() const; + void useActiveException(); + void setExceptionGuard(); + void unsetExceptionGuard(); + }; + + void handleExceptionMatchExpr( AssertionHandler& handler, std::string const& str, StringRef matcherString ); + +} // namespace Catch + +#endif // TWOBLUECUBES_CATCH_ASSERTIONHANDLER_H_INCLUDED diff --git a/include/internal/catch_assertioninfo.h b/include/internal/catch_assertioninfo.h new file mode 100644 index 00000000..5f136bf7 --- /dev/null +++ b/include/internal/catch_assertioninfo.h @@ -0,0 +1,31 @@ +/* + * Created by Phil on 8/8/2017. + * Copyright 2017 Two Blue Cubes Ltd. All rights reserved. + * + * Distributed under the Boost Software License, Version 1.0. (See accompanying + * file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) + */ +#ifndef TWOBLUECUBES_CATCH_ASSERTIONINFO_H_INCLUDED +#define TWOBLUECUBES_CATCH_ASSERTIONINFO_H_INCLUDED + +#include "catch_result_type.h" +#include "catch_common.h" +#include "catch_stringref.h" + +namespace Catch { + + struct AssertionInfo + { + StringRef macroName; + SourceLineInfo lineInfo; + StringRef capturedExpression; + ResultDisposition::Flags resultDisposition; + + // We want to delete this constructor but a compiler bug in 4.8 means + // the struct is then treated as non-aggregate + //AssertionInfo() = delete; + }; + +} // end namespace Catch + +#endif // TWOBLUECUBES_CATCH_ASSERTIONINFO_H_INCLUDED diff --git a/include/internal/catch_assertionresult.cpp b/include/internal/catch_assertionresult.cpp new file mode 100644 index 00000000..3af40dfc --- /dev/null +++ b/include/internal/catch_assertionresult.cpp @@ -0,0 +1,99 @@ +/* + * Created by Phil on 8/8/12 + * Copyright 2012 Two Blue Cubes Ltd. All rights reserved. + * + * Distributed under the Boost Software License, Version 1.0. (See accompanying + * file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) + */ + +#include "catch_assertionresult.h" + +namespace Catch { + AssertionResultData::AssertionResultData(ResultWas::OfType _resultType, LazyExpression const & _lazyExpression): + lazyExpression(_lazyExpression), + resultType(_resultType) {} + + std::string AssertionResultData::reconstructExpression() const { + + if( reconstructedExpression.empty() ) { + if( lazyExpression ) { + // !TBD Use stringstream for now, but rework above to pass stream in + std::ostringstream oss; + oss << lazyExpression; + reconstructedExpression = oss.str(); + } + } + return reconstructedExpression; + } + + AssertionResult::AssertionResult( AssertionInfo const& info, AssertionResultData const& data ) + : m_info( info ), + m_resultData( data ) + {} + + // Result was a success + bool AssertionResult::succeeded() const { + return Catch::isOk( m_resultData.resultType ); + } + + // Result was a success, or failure is suppressed + bool AssertionResult::isOk() const { + return Catch::isOk( m_resultData.resultType ) || shouldSuppressFailure( m_info.resultDisposition ); + } + + ResultWas::OfType AssertionResult::getResultType() const { + return m_resultData.resultType; + } + + bool AssertionResult::hasExpression() const { + return m_info.capturedExpression[0] != 0; + } + + bool AssertionResult::hasMessage() const { + return !m_resultData.message.empty(); + } + + std::string AssertionResult::getExpression() const { + if( isFalseTest( m_info.resultDisposition ) ) + return "!(" + std::string(m_info.capturedExpression) + ")"; + else + return m_info.capturedExpression; + } + + std::string AssertionResult::getExpressionInMacro() const { + std::string expr; + if( m_info.macroName[0] == 0 ) + expr = m_info.capturedExpression; + else { + expr.reserve( m_info.macroName.size() + m_info.capturedExpression.size() + 4 ); + expr += m_info.macroName; + expr += "( "; + expr += m_info.capturedExpression; + expr += " )"; + } + return expr; + } + + bool AssertionResult::hasExpandedExpression() const { + return hasExpression() && getExpandedExpression() != getExpression(); + } + + std::string AssertionResult::getExpandedExpression() const { + std::string expr = m_resultData.reconstructExpression(); + return expr.empty() + ? getExpression() + : expr; + } + + std::string AssertionResult::getMessage() const { + return m_resultData.message; + } + SourceLineInfo AssertionResult::getSourceInfo() const { + return m_info.lineInfo; + } + + std::string AssertionResult::getTestMacroName() const { + return m_info.macroName; + } + +} // end namespace Catch diff --git a/include/internal/catch_assertionresult.h b/include/internal/catch_assertionresult.h index 39958498..fa8e09f2 100644 --- a/include/internal/catch_assertionresult.h +++ b/include/internal/catch_assertionresult.h @@ -9,100 +9,32 @@ #define TWOBLUECUBES_CATCH_ASSERTIONRESULT_H_INCLUDED #include +#include "catch_assertioninfo.h" #include "catch_result_type.h" +#include "catch_common.h" +#include "catch_stringref.h" +#include "catch_assertionhandler.h" namespace Catch { - struct STATIC_ASSERT_Expression_Too_Complex_Please_Rewrite_As_Binary_Comparison; - - struct DecomposedExpression - { - virtual ~DecomposedExpression() {} - virtual bool isBinaryExpression() const { - return false; - } - virtual void reconstructExpression( std::string& dest ) const = 0; - - // Only simple binary comparisons can be decomposed. - // If more complex check is required then wrap sub-expressions in parentheses. - template STATIC_ASSERT_Expression_Too_Complex_Please_Rewrite_As_Binary_Comparison& operator + ( T const& ); - template STATIC_ASSERT_Expression_Too_Complex_Please_Rewrite_As_Binary_Comparison& operator - ( T const& ); - template STATIC_ASSERT_Expression_Too_Complex_Please_Rewrite_As_Binary_Comparison& operator * ( T const& ); - template STATIC_ASSERT_Expression_Too_Complex_Please_Rewrite_As_Binary_Comparison& operator / ( T const& ); - template STATIC_ASSERT_Expression_Too_Complex_Please_Rewrite_As_Binary_Comparison& operator % ( T const& ); - template STATIC_ASSERT_Expression_Too_Complex_Please_Rewrite_As_Binary_Comparison& operator && ( T const& ); - template STATIC_ASSERT_Expression_Too_Complex_Please_Rewrite_As_Binary_Comparison& operator || ( T const& ); - - private: - DecomposedExpression& operator = (DecomposedExpression const&); - }; - - struct AssertionInfo - { - AssertionInfo(); - AssertionInfo( char const * _macroName, - SourceLineInfo const& _lineInfo, - char const * _capturedExpression, - ResultDisposition::Flags _resultDisposition, - char const * _secondArg = ""); - - char const * macroName; - SourceLineInfo lineInfo; - char const * capturedExpression; - ResultDisposition::Flags resultDisposition; - char const * secondArg; - }; - struct AssertionResultData { - AssertionResultData() : decomposedExpression( CATCH_NULL ) - , resultType( ResultWas::Unknown ) - , negated( false ) - , parenthesized( false ) {} + AssertionResultData() = delete; - void negate( bool parenthesize ) { - negated = !negated; - parenthesized = parenthesize; - if( resultType == ResultWas::Ok ) - resultType = ResultWas::ExpressionFailed; - else if( resultType == ResultWas::ExpressionFailed ) - resultType = ResultWas::Ok; - } + AssertionResultData( ResultWas::OfType _resultType, LazyExpression const& _lazyExpression ); - std::string const& reconstructExpression() const { - if( decomposedExpression != CATCH_NULL ) { - decomposedExpression->reconstructExpression( reconstructedExpression ); - if( parenthesized ) { - reconstructedExpression.insert( 0, 1, '(' ); - reconstructedExpression.append( 1, ')' ); - } - if( negated ) { - reconstructedExpression.insert( 0, 1, '!' ); - } - decomposedExpression = CATCH_NULL; - } - return reconstructedExpression; - } - - mutable DecomposedExpression const* decomposedExpression; - mutable std::string reconstructedExpression; std::string message; + mutable std::string reconstructedExpression; + LazyExpression lazyExpression; ResultWas::OfType resultType; - bool negated; - bool parenthesized; + + std::string reconstructExpression() const; }; class AssertionResult { public: - AssertionResult(); + AssertionResult() = delete; AssertionResult( AssertionInfo const& info, AssertionResultData const& data ); - ~AssertionResult(); -# ifdef CATCH_CONFIG_CPP11_GENERATED_METHODS - AssertionResult( AssertionResult const& ) = default; - AssertionResult( AssertionResult && ) = default; - AssertionResult& operator = ( AssertionResult const& ) = default; - AssertionResult& operator = ( AssertionResult && ) = default; -# endif bool isOk() const; bool succeeded() const; @@ -116,10 +48,8 @@ namespace Catch { std::string getMessage() const; SourceLineInfo getSourceInfo() const; std::string getTestMacroName() const; - void discardDecomposedExpression() const; - void expandDecomposedExpression() const; - protected: + //protected: AssertionInfo m_info; AssertionResultData m_resultData; }; diff --git a/include/internal/catch_benchmark.cpp b/include/internal/catch_benchmark.cpp new file mode 100644 index 00000000..742418f7 --- /dev/null +++ b/include/internal/catch_benchmark.cpp @@ -0,0 +1,36 @@ +/* + * Created by Phil on 04/07/2017. + * Copyright 2017 Two Blue Cubes Ltd. All rights reserved. + * + * Distributed under the Boost Software License, Version 1.0. (See accompanying + * file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) + */ + +#include "catch_benchmark.h" +#include "catch_capture.hpp" +#include "catch_interfaces_reporter.h" +#include "catch_context.h" + +namespace Catch { + + auto BenchmarkLooper::getResolution() -> uint64_t { + return getEstimatedClockResolution() * getCurrentContext().getConfig()->benchmarkResolutionMultiple(); + } + + void BenchmarkLooper::reportStart() { + getResultCapture().benchmarkStarting( { m_name } ); + } + auto BenchmarkLooper::needsMoreIterations() -> bool { + auto elapsed = m_timer.getElapsedNanoseconds(); + + // Exponentially increasing iterations until we're confident in our timer resolution + if( elapsed < m_resolution ) { + m_iterationsToRun *= 10; + return true; + } + + getResultCapture().benchmarkEnded( { { m_name }, m_count, elapsed } ); + return false; + } + +} // end namespace Catch diff --git a/include/internal/catch_benchmark.h b/include/internal/catch_benchmark.h new file mode 100644 index 00000000..e546713c --- /dev/null +++ b/include/internal/catch_benchmark.h @@ -0,0 +1,57 @@ +/* + * Created by Phil on 04/07/2017. + * Copyright 2017 Two Blue Cubes Ltd. All rights reserved. + * + * Distributed under the Boost Software License, Version 1.0. (See accompanying + * file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) + */ +#ifndef TWOBLUECUBES_CATCH_BENCHMARK_H_INCLUDED +#define TWOBLUECUBES_CATCH_BENCHMARK_H_INCLUDED + +#include "catch_stringref.h" +#include "catch_timer.h" + +#include +#include + +namespace Catch { + + class BenchmarkLooper { + + std::string m_name; + std::size_t m_count = 0; + std::size_t m_iterationsToRun = 1; + uint64_t m_resolution; + Timer m_timer; + + static auto getResolution() -> uint64_t; + public: + // Keep most of this inline as it's on the code path that is being timed + BenchmarkLooper( StringRef name ) + : m_name( name ), + m_resolution( getResolution() ) + { + reportStart(); + m_timer.start(); + } + + explicit operator bool() { + if( m_count < m_iterationsToRun ) + return true; + return needsMoreIterations(); + } + + void increment() { + ++m_count; + } + + void reportStart(); + auto needsMoreIterations() -> bool; + }; + +} // end namespace Catch + +#define BENCHMARK( name ) \ + for( Catch::BenchmarkLooper looper( name ); looper; looper.increment() ) + +#endif // TWOBLUECUBES_CATCH_BENCHMARK_H_INCLUDED diff --git a/include/internal/catch_capture.hpp b/include/internal/catch_capture.hpp index 92229df7..9f205465 100644 --- a/include/internal/catch_capture.hpp +++ b/include/internal/catch_capture.hpp @@ -8,20 +8,17 @@ #ifndef TWOBLUECUBES_CATCH_CAPTURE_HPP_INCLUDED #define TWOBLUECUBES_CATCH_CAPTURE_HPP_INCLUDED -#include "catch_result_builder.h" +#include "catch_assertionhandler.h" #include "catch_message.h" #include "catch_interfaces_capture.h" #include "catch_debugger.h" -#include "catch_common.h" -#include "catch_tostring.h" -#include "catch_interfaces_runner.h" -#include "catch_compiler_capabilities.h" +#if !defined(CATCH_CONFIG_DISABLE) #if !defined(CATCH_CONFIG_DISABLE_STRINGIFICATION) -# define CATCH_INTERNAL_STRINGIFY(expr) #expr + #define CATCH_INTERNAL_STRINGIFY(...) #__VA_ARGS__ #else -# define CATCH_INTERNAL_STRINGIFY(expr) "Disabled by CATCH_CONFIG_DISABLE_STRINGIFICATION" + #define CATCH_INTERNAL_STRINGIFY(...) "Disabled by CATCH_CONFIG_DISABLE_STRINGIFICATION" #endif #if defined(CATCH_CONFIG_FAST_COMPILE) @@ -29,158 +26,138 @@ // 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( resultBuilder ) \ - resultBuilder.react(); +#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_TEST_NO_TRY( macroName, resultDisposition, expr ) \ - do { \ - Catch::ResultBuilder __catchResult( macroName, CATCH_INTERNAL_LINEINFO, CATCH_INTERNAL_STRINGIFY(expr), resultDisposition ); \ - __catchResult.setExceptionGuard(); \ - CATCH_INTERNAL_SUPPRESS_PARENTHESES_WARNINGS \ - ( __catchResult <= expr ).endExpression(); \ - CATCH_INTERNAL_UNSUPPRESS_PARENTHESES_WARNINGS \ - __catchResult.unsetExceptionGuard(); \ - INTERNAL_CATCH_REACT( __catchResult ) \ - } while( Catch::isTrue( false && static_cast( !!(expr) ) ) ) // expr 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 &&. +#define INTERNAL_CATCH_TRY( capturer ) capturer.setExceptionGuard(); +#define INTERNAL_CATCH_CATCH( capturer ) capturer.unsetExceptionGuard(); -#define INTERNAL_CHECK_THAT_NO_TRY( macroName, matcher, resultDisposition, arg ) \ - do { \ - Catch::ResultBuilder __catchResult( macroName, CATCH_INTERNAL_LINEINFO, CATCH_INTERNAL_STRINGIFY(arg) ", " CATCH_INTERNAL_STRINGIFY(matcher), resultDisposition ); \ - __catchResult.setExceptionGuard(); \ - __catchResult.captureMatch( arg, matcher, CATCH_INTERNAL_STRINGIFY(matcher) ); \ - __catchResult.unsetExceptionGuard(); \ - INTERNAL_CATCH_REACT( __catchResult ) \ - } while( Catch::alwaysFalse() ) +#else // CATCH_CONFIG_FAST_COMPILE -#else /////////////////////////////////////////////////////////////////////////////// // 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( resultBuilder ) \ - if( resultBuilder.shouldDebugBreak() ) CATCH_BREAK_INTO_DEBUGGER(); \ - resultBuilder.react(); +#define INTERNAL_CATCH_REACT( handler ) \ + if( handler.shouldDebugBreak() ) CATCH_BREAK_INTO_DEBUGGER(); \ + handler.reactWithoutDebugBreak(); + +#define INTERNAL_CATCH_TRY( capturer ) try +#define INTERNAL_CATCH_CATCH( capturer ) catch(...) { capturer.useActiveException(); } + #endif - /////////////////////////////////////////////////////////////////////////////// -#define INTERNAL_CATCH_TEST( macroName, resultDisposition, expr ) \ +#define INTERNAL_CATCH_TEST( macroName, resultDisposition, ... ) \ do { \ - Catch::ResultBuilder __catchResult( macroName, CATCH_INTERNAL_LINEINFO, CATCH_INTERNAL_STRINGIFY(expr), resultDisposition ); \ - try { \ + Catch::AssertionHandler catchAssertionHandler( macroName, CATCH_INTERNAL_LINEINFO, CATCH_INTERNAL_STRINGIFY(__VA_ARGS__), resultDisposition ); \ + INTERNAL_CATCH_TRY( catchAssertionHandler ) { \ CATCH_INTERNAL_SUPPRESS_PARENTHESES_WARNINGS \ - ( __catchResult <= expr ).endExpression(); \ + catchAssertionHandler.handle( Catch::Decomposer() <= __VA_ARGS__ ); \ CATCH_INTERNAL_UNSUPPRESS_PARENTHESES_WARNINGS \ - } \ - catch( ... ) { \ - __catchResult.useActiveException( resultDisposition ); \ - } \ - INTERNAL_CATCH_REACT( __catchResult ) \ - } while( Catch::isTrue( false && static_cast( !!(expr) ) ) ) // expr here is never evaluated at runtime but it forces the compiler to give it a look + } 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 // The double negation silences MSVC's C4800 warning, the static_cast forces short-circuit evaluation if the type has overloaded &&. /////////////////////////////////////////////////////////////////////////////// -#define INTERNAL_CATCH_IF( macroName, resultDisposition, expr ) \ - INTERNAL_CATCH_TEST( macroName, resultDisposition, expr ); \ +#define INTERNAL_CATCH_IF( macroName, resultDisposition, ... ) \ + INTERNAL_CATCH_TEST( macroName, resultDisposition, __VA_ARGS__ ); \ if( Catch::getResultCapture().lastAssertionPassed() ) /////////////////////////////////////////////////////////////////////////////// -#define INTERNAL_CATCH_ELSE( macroName, resultDisposition, expr ) \ - INTERNAL_CATCH_TEST( macroName, resultDisposition, expr ); \ +#define INTERNAL_CATCH_ELSE( macroName, resultDisposition, ... ) \ + INTERNAL_CATCH_TEST( macroName, resultDisposition, __VA_ARGS__ ); \ if( !Catch::getResultCapture().lastAssertionPassed() ) /////////////////////////////////////////////////////////////////////////////// -#define INTERNAL_CATCH_NO_THROW( macroName, resultDisposition, expr ) \ +#define INTERNAL_CATCH_NO_THROW( macroName, resultDisposition, ... ) \ do { \ - Catch::ResultBuilder __catchResult( macroName, CATCH_INTERNAL_LINEINFO, CATCH_INTERNAL_STRINGIFY(expr), resultDisposition ); \ + Catch::AssertionHandler catchAssertionHandler( macroName, CATCH_INTERNAL_LINEINFO, CATCH_INTERNAL_STRINGIFY(__VA_ARGS__), resultDisposition ); \ try { \ - static_cast(expr); \ - __catchResult.captureResult( Catch::ResultWas::Ok ); \ + static_cast(__VA_ARGS__); \ + catchAssertionHandler.handle( Catch::ResultWas::Ok ); \ } \ catch( ... ) { \ - __catchResult.useActiveException( resultDisposition ); \ + catchAssertionHandler.useActiveException(); \ } \ - INTERNAL_CATCH_REACT( __catchResult ) \ + INTERNAL_CATCH_REACT( catchAssertionHandler ) \ } while( Catch::alwaysFalse() ) /////////////////////////////////////////////////////////////////////////////// -#define INTERNAL_CATCH_THROWS( macroName, resultDisposition, matcher, expr ) \ +#define INTERNAL_CATCH_THROWS( macroName, resultDisposition, ... ) \ do { \ - Catch::ResultBuilder __catchResult( macroName, CATCH_INTERNAL_LINEINFO, CATCH_INTERNAL_STRINGIFY(expr), resultDisposition, CATCH_INTERNAL_STRINGIFY(matcher) ); \ - if( __catchResult.allowThrows() ) \ + Catch::AssertionHandler catchAssertionHandler( macroName, CATCH_INTERNAL_LINEINFO, CATCH_INTERNAL_STRINGIFY(__VA_ARGS__), resultDisposition); \ + if( catchAssertionHandler.allowThrows() ) \ try { \ - static_cast(expr); \ - __catchResult.captureResult( Catch::ResultWas::DidntThrowException ); \ + static_cast(__VA_ARGS__); \ + catchAssertionHandler.handle( Catch::ResultWas::DidntThrowException ); \ } \ catch( ... ) { \ - __catchResult.captureExpectedException( matcher ); \ + catchAssertionHandler.handle( Catch::ResultWas::Ok ); \ } \ else \ - __catchResult.captureResult( Catch::ResultWas::Ok ); \ - INTERNAL_CATCH_REACT( __catchResult ) \ + catchAssertionHandler.handle( Catch::ResultWas::Ok ); \ + INTERNAL_CATCH_REACT( catchAssertionHandler ) \ } while( Catch::alwaysFalse() ) /////////////////////////////////////////////////////////////////////////////// #define INTERNAL_CATCH_THROWS_AS( macroName, exceptionType, resultDisposition, expr ) \ do { \ - Catch::ResultBuilder __catchResult( macroName, CATCH_INTERNAL_LINEINFO, CATCH_INTERNAL_STRINGIFY(expr) ", " CATCH_INTERNAL_STRINGIFY(exceptionType), resultDisposition ); \ - if( __catchResult.allowThrows() ) \ + Catch::AssertionHandler catchAssertionHandler( macroName, CATCH_INTERNAL_LINEINFO, CATCH_INTERNAL_STRINGIFY(expr) ", " CATCH_INTERNAL_STRINGIFY(exceptionType), resultDisposition ); \ + if( catchAssertionHandler.allowThrows() ) \ try { \ static_cast(expr); \ - __catchResult.captureResult( Catch::ResultWas::DidntThrowException ); \ + catchAssertionHandler.handle( Catch::ResultWas::DidntThrowException ); \ } \ - catch( exceptionType ) { \ - __catchResult.captureResult( Catch::ResultWas::Ok ); \ + catch( exceptionType const& ) { \ + catchAssertionHandler.handle( Catch::ResultWas::Ok ); \ } \ catch( ... ) { \ - __catchResult.useActiveException( resultDisposition ); \ + catchAssertionHandler.useActiveException(); \ } \ else \ - __catchResult.captureResult( Catch::ResultWas::Ok ); \ - INTERNAL_CATCH_REACT( __catchResult ) \ + catchAssertionHandler.handle( Catch::ResultWas::Ok ); \ + INTERNAL_CATCH_REACT( catchAssertionHandler ) \ } while( Catch::alwaysFalse() ) /////////////////////////////////////////////////////////////////////////////// -#ifdef CATCH_CONFIG_VARIADIC_MACROS - #define INTERNAL_CATCH_MSG( macroName, messageType, resultDisposition, ... ) \ - do { \ - Catch::ResultBuilder __catchResult( macroName, CATCH_INTERNAL_LINEINFO, "", resultDisposition ); \ - __catchResult << __VA_ARGS__ + ::Catch::StreamEndStop(); \ - __catchResult.captureResult( messageType ); \ - INTERNAL_CATCH_REACT( __catchResult ) \ - } while( Catch::alwaysFalse() ) -#else - #define INTERNAL_CATCH_MSG( macroName, messageType, resultDisposition, log ) \ - do { \ - Catch::ResultBuilder __catchResult( macroName, CATCH_INTERNAL_LINEINFO, "", resultDisposition ); \ - __catchResult << log + ::Catch::StreamEndStop(); \ - __catchResult.captureResult( messageType ); \ - INTERNAL_CATCH_REACT( __catchResult ) \ - } while( Catch::alwaysFalse() ) -#endif +#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() ); \ + INTERNAL_CATCH_REACT( catchAssertionHandler ) \ + } while( Catch::alwaysFalse() ) /////////////////////////////////////////////////////////////////////////////// #define INTERNAL_CATCH_INFO( macroName, log ) \ Catch::ScopedMessage INTERNAL_CATCH_UNIQUE_NAME( scopedMessage ) = Catch::MessageBuilder( macroName, CATCH_INTERNAL_LINEINFO, Catch::ResultWas::Info ) << log; /////////////////////////////////////////////////////////////////////////////// -#define INTERNAL_CHECK_THAT( macroName, matcher, resultDisposition, arg ) \ +// Although this is matcher-based, it can be used with just a string +#define INTERNAL_CATCH_THROWS_STR_MATCHES( macroName, resultDisposition, matcher, ... ) \ do { \ - Catch::ResultBuilder __catchResult( macroName, CATCH_INTERNAL_LINEINFO, CATCH_INTERNAL_STRINGIFY(arg) ", " CATCH_INTERNAL_STRINGIFY(matcher), resultDisposition ); \ - try { \ - __catchResult.captureMatch( arg, matcher, CATCH_INTERNAL_STRINGIFY(matcher) ); \ - } catch( ... ) { \ - __catchResult.useActiveException( resultDisposition | Catch::ResultDisposition::ContinueOnFailure ); \ - } \ - INTERNAL_CATCH_REACT( __catchResult ) \ + Catch::AssertionHandler catchAssertionHandler( macroName, CATCH_INTERNAL_LINEINFO, CATCH_INTERNAL_STRINGIFY(__VA_ARGS__) ", " CATCH_INTERNAL_STRINGIFY(matcher), resultDisposition ); \ + if( catchAssertionHandler.allowThrows() ) \ + try { \ + static_cast(__VA_ARGS__); \ + catchAssertionHandler.handle( Catch::ResultWas::DidntThrowException ); \ + } \ + catch( ... ) { \ + handleExceptionMatchExpr( catchAssertionHandler, matcher, #matcher ); \ + } \ + else \ + catchAssertionHandler.handle( Catch::ResultWas::Ok ); \ + INTERNAL_CATCH_REACT( catchAssertionHandler ) \ } while( Catch::alwaysFalse() ) +#endif // CATCH_CONFIG_DISABLE + #endif // TWOBLUECUBES_CATCH_CAPTURE_HPP_INCLUDED diff --git a/include/internal/catch_capture_matchers.cpp b/include/internal/catch_capture_matchers.cpp new file mode 100644 index 00000000..758177e9 --- /dev/null +++ b/include/internal/catch_capture_matchers.cpp @@ -0,0 +1,24 @@ +/* + * Created by Phil on 9/8/2017. + * Copyright 2017 Two Blue Cubes Ltd. All rights reserved. + * + * Distributed under the Boost Software License, Version 1.0. (See accompanying + * file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) + */ +#include "catch_capture_matchers.h" +#include "catch_interfaces_registry_hub.h" + +namespace Catch { + + using StringMatcher = Matchers::Impl::MatcherBase; + + // This is the general overload that takes a any string matcher + // There is another overload, in catch_assertinhandler.h/.cpp, that only takes a string and infers + // the Equals matcher (so the header does not mention matchers) + void handleExceptionMatchExpr( AssertionHandler& handler, StringMatcher const& matcher, StringRef matcherString ) { + std::string exceptionMessage = Catch::translateActiveException(); + MatchExpr expr( exceptionMessage, matcher, matcherString ); + handler.handle( expr ); + } + +} // namespace Catch diff --git a/include/internal/catch_capture_matchers.h b/include/internal/catch_capture_matchers.h new file mode 100644 index 00000000..701ba1f6 --- /dev/null +++ b/include/internal/catch_capture_matchers.h @@ -0,0 +1,88 @@ +/* + * Created by Phil on 9/8/2017 + * Copyright 2017 Two Blue Cubes Ltd. All rights reserved. + * + * Distributed under the Boost Software License, Version 1.0. (See accompanying + * file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) + */ +#ifndef TWOBLUECUBES_CATCH_CAPTURE_MATCHERS_HPP_INCLUDED +#define TWOBLUECUBES_CATCH_CAPTURE_MATCHERS_HPP_INCLUDED + +#include "catch_capture.hpp" +#include "catch_matchers.h" +#include "catch_matchers_string.h" +#include "catch_matchers_vector.h" + +namespace Catch { + + template + class MatchExpr : public ITransientExpression { + 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 ), + m_matcher( matcher ), + m_matcherString( matcherString ), + m_result( matcher.match( arg ) ) + {} + + auto isBinaryExpression() const -> bool override { return true; } + auto getResult() const -> bool override { return m_result; } + + void streamReconstructedExpression( std::ostream &os ) const override { + auto matcherAsString = m_matcher.toString(); + os << Catch::Detail::stringify( m_arg ) << ' '; + if( matcherAsString == Detail::unprintableString ) + os << m_matcherString; + else + os << matcherAsString; + } + }; + + using StringMatcher = Matchers::Impl::MatcherBase; + + void handleExceptionMatchExpr( AssertionHandler& handler, StringMatcher const& matcher, StringRef matcherString ); + + template + auto makeMatchExpr( ArgT const& arg, MatcherT const& matcher, StringRef matcherString ) -> MatchExpr { + return MatchExpr( arg, matcher, matcherString ); + } + +} // 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_CATCH( catchAssertionHandler ) \ + INTERNAL_CATCH_REACT( catchAssertionHandler ) \ + } while( Catch::alwaysFalse() ) + + +/////////////////////////////////////////////////////////////////////////////// +#define INTERNAL_CATCH_THROWS_MATCHES( macroName, exceptionType, resultDisposition, matcher, ... ) \ + do { \ + Catch::AssertionHandler catchAssertionHandler( macroName, CATCH_INTERNAL_LINEINFO, CATCH_INTERNAL_STRINGIFY(__VA_ARGS__) ", " CATCH_INTERNAL_STRINGIFY(exceptionType) ", " CATCH_INTERNAL_STRINGIFY(matcher), resultDisposition ); \ + if( catchAssertionHandler.allowThrows() ) \ + try { \ + static_cast(__VA_ARGS__ ); \ + catchAssertionHandler.handle( Catch::ResultWas::DidntThrowException ); \ + } \ + catch( exceptionType const& ex ) { \ + catchAssertionHandler.handle( Catch::makeMatchExpr( ex, matcher, #matcher ) ); \ + } \ + catch( ... ) { \ + catchAssertionHandler.useActiveException(); \ + } \ + else \ + catchAssertionHandler.handle( Catch::ResultWas::Ok ); \ + INTERNAL_CATCH_REACT( catchAssertionHandler ) \ + } while( Catch::alwaysFalse() ) + +#endif // TWOBLUECUBES_CATCH_CAPTURE_MATCHERS_HPP_INCLUDED diff --git a/include/internal/catch_clara.h b/include/internal/catch_clara.h index bfe2f4b7..bdf70250 100644 --- a/include/internal/catch_clara.h +++ b/include/internal/catch_clara.h @@ -11,21 +11,27 @@ // Use Catch's value for console width (store Clara's off to the side, if present) #ifdef CLARA_CONFIG_CONSOLE_WIDTH -#define CATCH_TEMP_CLARA_CONFIG_CONSOLE_WIDTH CLARA_CONFIG_CONSOLE_WIDTH -#undef CLARA_CONFIG_CONSOLE_WIDTH +#define CATCH_TEMP_CLARA_CONFIG_CONSOLE_WIDTH CATCH_CLARA_TEXTFLOW_CONFIG_CONSOLE_WIDTH +#undef CATCH_CLARA_TEXTFLOW_CONFIG_CONSOLE_WIDTH #endif -#define CLARA_CONFIG_CONSOLE_WIDTH CATCH_CONFIG_CONSOLE_WIDTH +#define CATCH_CLARA_TEXTFLOW_CONFIG_CONSOLE_WIDTH CATCH_CONFIG_CONSOLE_WIDTH-1 +#ifdef __clang__ +#pragma clang diagnostic push +#pragma clang diagnostic ignored "-Wweak-vtables" +#pragma clang diagnostic ignored "-Wexit-time-destructors" +#pragma clang diagnostic ignored "-Wshadow" +#endif -// Declare Clara inside the Catch namespace -#define STITCH_CLARA_OPEN_NAMESPACE namespace Catch { -#include "../external/clara.h" -#undef STITCH_CLARA_OPEN_NAMESPACE +#include "../external/clara.hpp" +#ifdef __clang__ +#pragma clang diagnostic pop +#endif // Restore Clara's value for console width, if present #ifdef CATCH_TEMP_CLARA_CONFIG_CONSOLE_WIDTH -#define CLARA_CONFIG_CONSOLE_WIDTH CATCH_TEMP_CLARA_CONFIG_CONSOLE_WIDTH +#define CATCH_CLARA_TEXTFLOW_CONFIG_CONSOLE_WIDTH CATCH_TEMP_CLARA_CONFIG_CONSOLE_WIDTH #undef CATCH_TEMP_CLARA_CONFIG_CONSOLE_WIDTH #endif diff --git a/include/internal/catch_commandline.cpp b/include/internal/catch_commandline.cpp new file mode 100644 index 00000000..c0231d1a --- /dev/null +++ b/include/internal/catch_commandline.cpp @@ -0,0 +1,184 @@ +/* + * Created by Phil on 02/11/2010. + * Copyright 2010 Two Blue Cubes Ltd. All rights reserved. + * + * Distributed under the Boost Software License, Version 1.0. (See accompanying + * file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) + */ + +#include "catch_commandline.h" + +#include "catch_string_manip.h" + +#include +#include + +namespace Catch { + + clara::Parser makeCommandLineParser( ConfigData& config ) { + + using namespace clara; + + auto const setWarning = [&]( std::string const& warning ) { + if( warning != "NoAssertions" ) + return ParserResult::runtimeError( "Unrecognised warning: '" + warning + "'" ); + config.warnings = static_cast( config.warnings | WarnAbout::NoAssertions ); + return ParserResult::ok( ParseResultType::Matched ); + }; + auto const loadTestNamesFromFile = [&]( std::string const& filename ) { + std::ifstream f( filename.c_str() ); + if( !f.is_open() ) + return ParserResult::runtimeError( "Unable to load input file: '" + filename + "'" ); + + std::string line; + while( std::getline( f, line ) ) { + line = trim(line); + if( !line.empty() && !startsWith( line, '#' ) ) { + if( !startsWith( line, '"' ) ) + line = '"' + line + '"'; + config.testsOrTags.push_back( line + ',' ); + } + } + return ParserResult::ok( ParseResultType::Matched ); + }; + auto const setTestOrder = [&]( std::string const& order ) { + if( startsWith( "declared", order ) ) + config.runOrder = RunTests::InDeclarationOrder; + else if( startsWith( "lexical", order ) ) + config.runOrder = RunTests::InLexicographicalOrder; + else if( startsWith( "random", order ) ) + config.runOrder = RunTests::InRandomOrder; + else + return clara::ParserResult::runtimeError( "Unrecognised ordering: '" + order + "'" ); + return ParserResult::ok( ParseResultType::Matched ); + }; + auto const setRngSeed = [&]( std::string const& seed ) { + if( seed != "time" ) + return clara::detail::convertInto( seed, config.rngSeed ); + config.rngSeed = static_cast( std::time(nullptr) ); + return ParserResult::ok( ParseResultType::Matched ); + }; + auto const setColourUsage = [&]( std::string const& useColour ) { + auto mode = toLower( useColour ); + + if( mode == "yes" ) + config.useColour = UseColour::Yes; + else if( mode == "no" ) + config.useColour = UseColour::No; + else if( mode == "auto" ) + config.useColour = UseColour::Auto; + else + return ParserResult::runtimeError( "colour mode must be one of: auto, yes or no. '" + useColour + "' not recognised" ); + return ParserResult::ok( ParseResultType::Matched ); + }; + auto const setWaitForKeypress = [&]( std::string const& keypress ) { + auto keypressLc = toLower( keypress ); + if( keypressLc == "start" ) + config.waitForKeypress = WaitForKeypress::BeforeStart; + else if( keypressLc == "exit" ) + config.waitForKeypress = WaitForKeypress::BeforeExit; + else if( keypressLc == "both" ) + config.waitForKeypress = WaitForKeypress::BeforeStartAndExit; + else + return ParserResult::runtimeError( "keypress argument must be one of: start, exit or both. '" + keypress + "' not recognised" ); + return ParserResult::ok( ParseResultType::Matched ); + }; + auto const setVerbosity = [&]( std::string const& verbosity ) { + auto lcVerbosity = toLower( verbosity ); + if( lcVerbosity == "quiet" ) + config.verbosity = Verbosity::Quiet; + else if( lcVerbosity == "normal" ) + config.verbosity = Verbosity::Normal; + else if( lcVerbosity == "high" ) + config.verbosity = Verbosity::High; + else + return ParserResult::runtimeError( "Unrecognised verbosity, '" + verbosity + "'" ); + return ParserResult::ok( ParseResultType::Matched ); + }; + + auto cli + = ExeName( config.processName ) + | Help( config.showHelp ) + | Opt( config.listTests ) + ["-l"]["--list-tests"] + ( "list all/matching test cases" ) + | Opt( config.listTags ) + ["-t"]["--list-tags"] + ( "list all/matching tags" ) + | Opt( config.showSuccessfulTests ) + ["-s"]["--success"] + ( "include successful tests in output" ) + | Opt( config.shouldDebugBreak ) + ["-b"]["--break"] + ( "break into debugger on failure" ) + | Opt( config.noThrow ) + ["-e"]["--nothrow"] + ( "skip exception tests" ) + | Opt( config.showInvisibles ) + ["-i"]["--invisibles"] + ( "show invisibles (tabs, newlines)" ) + | Opt( config.outputFilename, "filename" ) + ["-o"]["--out"] + ( "output filename" ) + | Opt( config.reporterNames, "name" ) + ["-r"]["--reporter"] + ( "reporter to use (defaults to console)" ) + | Opt( config.name, "name" ) + ["-n"]["--name"] + ( "suite name" ) + | Opt( [&]( bool ){ config.abortAfter = 1; } ) + ["-a"]["--abort"] + ( "abort at first failure" ) + | Opt( [&]( int x ){ config.abortAfter = x; }, "no. failures" ) + ["-x"]["--abortx"] + ( "abort after x failures" ) + | Opt( setWarning, "warning name" ) + ["-w"]["--warn"] + ( "enable warnings" ) + | Opt( [&]( bool flag ) { config.showDurations = flag ? ShowDurations::Always : ShowDurations::Never; }, "yes|no" ) + ["-d"]["--durations"] + ( "show test durations" ) + | Opt( loadTestNamesFromFile, "filename" ) + ["-f"]["--input-file"] + ( "load test names to run from a file" ) + | Opt( config.filenamesAsTags ) + ["-#"]["--filenames-as-tags"] + ( "adds a tag for the filename" ) + | Opt( config.sectionsToRun, "section name" ) + ["-c"]["--section"] + ( "specify section to run" ) + | Opt( setVerbosity, "quiet|normal|high" ) + ["-v"]["--verbosity"] + ( "set output verbosity" ) + | Opt( config.listTestNamesOnly ) + ["--list-test-names-only"] + ( "list all/matching test cases names only" ) + | Opt( config.listReporters ) + ["--list-reporters"] + ( "list all reporters" ) + | Opt( setTestOrder, "decl|lex|rand" ) + ["--order"] + ( "test case order (defaults to decl)" ) + | Opt( setRngSeed, "'time'|number" ) + ["--rng-seed"] + ( "set a specific seed for random numbers" ) + | Opt( setColourUsage, "yes|no" ) + ["--use-colour"] + ( "should output be colourised" ) + | Opt( config.libIdentify ) + ["--libidentify"] + ( "report name and version according to libidentify standard" ) + | Opt( setWaitForKeypress, "start|exit|both" ) + ["--wait-for-keypress"] + ( "waits for a keypress before exiting" ) + | Opt( config.benchmarkResolutionMultiple, "multiplier" ) + ["--benchmark-resolution-multiple"] + ( "multiple of clock resolution to run benchmarks" ) + + | Arg( config.testsOrTags, "test name|pattern|tags" ) + ( "which test or tests to use" ); + + return cli; + } + +} // end namespace Catch diff --git a/include/internal/catch_commandline.h b/include/internal/catch_commandline.h new file mode 100644 index 00000000..b73cfa2d --- /dev/null +++ b/include/internal/catch_commandline.h @@ -0,0 +1,20 @@ +/* + * Created by Phil on 02/11/2010. + * Copyright 2010 Two Blue Cubes Ltd. All rights reserved. + * + * Distributed under the Boost Software License, Version 1.0. (See accompanying + * file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) + */ +#ifndef TWOBLUECUBES_CATCH_COMMANDLINE_HPP_INCLUDED +#define TWOBLUECUBES_CATCH_COMMANDLINE_HPP_INCLUDED + +#include "catch_config.hpp" +#include "catch_clara.h" + +namespace Catch { + + clara::Parser makeCommandLineParser( ConfigData& config ); + +} // end namespace Catch + +#endif // TWOBLUECUBES_CATCH_COMMANDLINE_HPP_INCLUDED diff --git a/include/internal/catch_common.cpp b/include/internal/catch_common.cpp new file mode 100644 index 00000000..0f2f6eb4 --- /dev/null +++ b/include/internal/catch_common.cpp @@ -0,0 +1,52 @@ +/* + * Created by Phil on 27/11/2013. + * Copyright 2013 Two Blue Cubes Ltd. All rights reserved. + * + * Distributed under the Boost Software License, Version 1.0. (See accompanying + * file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) + */ + +#include "catch_common.h" +#include "catch_context.h" +#include "catch_interfaces_config.h" + +#include +#include + +namespace Catch { + + SourceLineInfo::SourceLineInfo( char const* _file, std::size_t _line ) noexcept + : file( _file ), + line( _line ) + {} + bool SourceLineInfo::empty() const noexcept { + return file[0] == '\0'; + } + bool SourceLineInfo::operator == ( SourceLineInfo const& other ) const noexcept { + return line == other.line && (file == other.file || std::strcmp(file, other.file) == 0); + } + bool SourceLineInfo::operator < ( SourceLineInfo const& other ) const noexcept { + return line < other.line || ( line == other.line && (std::strcmp(file, other.file) < 0)); + } + + std::ostream& operator << ( std::ostream& os, SourceLineInfo const& info ) { +#ifndef __GNUG__ + os << info.file << '(' << info.line << ')'; +#else + os << info.file << ':' << info.line; +#endif + return os; + } + + bool isTrue( bool value ){ return value; } + bool alwaysTrue() { return true; } + bool alwaysFalse() { return false; } + + std::string StreamEndStop::operator+() const { + return std::string(); + } + + NonCopyable::NonCopyable() = default; + NonCopyable::~NonCopyable() = default; + +} diff --git a/include/internal/catch_common.h b/include/internal/catch_common.h index f1ce01ea..617a9343 100644 --- a/include/internal/catch_common.h +++ b/include/internal/catch_common.h @@ -18,95 +18,41 @@ # define INTERNAL_CATCH_UNIQUE_NAME( name ) INTERNAL_CATCH_UNIQUE_NAME_LINE( name, __LINE__ ) #endif -#define INTERNAL_CATCH_STRINGIFY2( expr ) #expr -#define INTERNAL_CATCH_STRINGIFY( expr ) INTERNAL_CATCH_STRINGIFY2( expr ) - -#include -#include +#include +#include +#include namespace Catch { - struct IConfig; - struct CaseSensitive { enum Choice { Yes, No }; }; class NonCopyable { -#ifdef CATCH_CONFIG_CPP11_GENERATED_METHODS NonCopyable( NonCopyable const& ) = delete; NonCopyable( NonCopyable && ) = delete; NonCopyable& operator = ( NonCopyable const& ) = delete; NonCopyable& operator = ( NonCopyable && ) = delete; -#else - NonCopyable( NonCopyable const& info ); - NonCopyable& operator = ( NonCopyable const& ); -#endif protected: - NonCopyable() {} + NonCopyable(); virtual ~NonCopyable(); }; - class SafeBool { - public: - typedef void (SafeBool::*type)() const; - - static type makeSafe( bool value ) { - return value ? &SafeBool::trueValue : 0; - } - private: - void trueValue() const {} - }; - - template - void deleteAll( ContainerT& container ) { - typename ContainerT::const_iterator it = container.begin(); - typename ContainerT::const_iterator itEnd = container.end(); - for(; it != itEnd; ++it ) - delete *it; - } - template - void deleteAllValues( AssociativeContainerT& container ) { - typename AssociativeContainerT::const_iterator it = container.begin(); - typename AssociativeContainerT::const_iterator itEnd = container.end(); - for(; it != itEnd; ++it ) - delete it->second; - } - - bool startsWith( std::string const& s, std::string const& prefix ); - bool startsWith( std::string const& s, char prefix ); - bool endsWith( std::string const& s, std::string const& suffix ); - bool endsWith( std::string const& s, char suffix ); - bool contains( std::string const& s, std::string const& infix ); - void toLowerInPlace( std::string& s ); - std::string toLower( std::string const& s ); - std::string trim( std::string const& str ); - bool replaceInPlace( std::string& str, std::string const& replaceThis, std::string const& withThis ); - - struct pluralise { - pluralise( std::size_t count, std::string const& label ); - - friend std::ostream& operator << ( std::ostream& os, pluralise const& pluraliser ); - - std::size_t m_count; - std::string m_label; - }; - struct SourceLineInfo { - SourceLineInfo(); - SourceLineInfo( char const* _file, std::size_t _line ); -# ifdef CATCH_CONFIG_CPP11_GENERATED_METHODS - SourceLineInfo(SourceLineInfo const& other) = default; + SourceLineInfo() = delete; + SourceLineInfo( char const* _file, std::size_t _line ) noexcept; + + SourceLineInfo( SourceLineInfo const& other ) = default; SourceLineInfo( SourceLineInfo && ) = default; SourceLineInfo& operator = ( SourceLineInfo const& ) = default; SourceLineInfo& operator = ( SourceLineInfo && ) = default; -# endif - bool empty() const; - bool operator == ( SourceLineInfo const& other ) const; - bool operator < ( SourceLineInfo const& other ) const; + + bool empty() const noexcept; + bool operator == ( SourceLineInfo const& other ) const noexcept; + bool operator < ( SourceLineInfo const& other ) const noexcept; char const* file; std::size_t line; @@ -115,23 +61,16 @@ 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; } - inline bool alwaysTrue() { return true; } - inline bool alwaysFalse() { return false; } - - void throwLogicError( std::string const& message, SourceLineInfo const& locationInfo ); - - void seedRng( IConfig const& config ); - unsigned int rngSeed(); + bool isTrue( bool value ); + bool alwaysTrue(); + bool alwaysFalse(); // Use this in variadic streaming macros to allow // >> +StreamEndStop // as well as // >> stuff +StreamEndStop struct StreamEndStop { - std::string operator+() { - return std::string(); - } + std::string operator+() const; }; template T const& operator + ( T const& value, StreamEndStop ) { @@ -139,8 +78,8 @@ namespace Catch { } } -#define CATCH_INTERNAL_LINEINFO ::Catch::SourceLineInfo( __FILE__, static_cast( __LINE__ ) ) -#define CATCH_INTERNAL_ERROR( msg ) ::Catch::throwLogicError( msg, CATCH_INTERNAL_LINEINFO ); +#define CATCH_INTERNAL_LINEINFO \ + ::Catch::SourceLineInfo( __FILE__, static_cast( __LINE__ ) ) #endif // TWOBLUECUBES_CATCH_COMMON_H_INCLUDED diff --git a/include/internal/catch_compiler_capabilities.h b/include/internal/catch_compiler_capabilities.h index 862fe065..88180af2 100644 --- a/include/internal/catch_compiler_capabilities.h +++ b/include/internal/catch_compiler_capabilities.h @@ -8,23 +8,9 @@ #ifndef TWOBLUECUBES_CATCH_COMPILER_CAPABILITIES_HPP_INCLUDED #define TWOBLUECUBES_CATCH_COMPILER_CAPABILITIES_HPP_INCLUDED -// Detect a number of compiler features - mostly C++11/14 conformance - by compiler +// Detect a number of compiler features - by compiler // The following features are defined: // -// CATCH_CONFIG_CPP11_NULLPTR : is nullptr supported? -// CATCH_CONFIG_CPP11_NOEXCEPT : is noexcept supported? -// CATCH_CONFIG_CPP11_GENERATED_METHODS : The delete and default keywords for compiler generated methods -// CATCH_CONFIG_CPP11_IS_ENUM : std::is_enum is supported? -// CATCH_CONFIG_CPP11_TUPLE : std::tuple is supported -// CATCH_CONFIG_CPP11_LONG_LONG : is long long supported? -// CATCH_CONFIG_CPP11_OVERRIDE : is override supported? -// CATCH_CONFIG_CPP11_UNIQUE_PTR : is unique_ptr supported (otherwise use auto_ptr) -// CATCH_CONFIG_CPP11_SHUFFLE : is std::shuffle supported? -// CATCH_CONFIG_CPP11_TYPE_TRAITS : are type_traits and enable_if supported? - -// CATCH_CONFIG_CPP11_OR_GREATER : Is C++11 supported? - -// CATCH_CONFIG_VARIADIC_MACROS : are variadic macros supported? // CATCH_CONFIG_COUNTER : is the __COUNTER__ macro supported? // CATCH_CONFIG_WINDOWS_SEH : is Windows SEH supported? // CATCH_CONFIG_POSIX_SIGNALS : are POSIX signals supported? @@ -34,18 +20,13 @@ // **************** // In general each macro has a _NO_ form -// (e.g. CATCH_CONFIG_CPP11_NO_NULLPTR) which disables the feature. +// (e.g. CATCH_CONFIG_NO_POSIX_SIGNALS) which disables the feature. // Many features, at point of detection, define an _INTERNAL_ macro, so they // can be combined, en-mass, with the _NO_ forms later. -// All the C++11 features can be disabled with CATCH_CONFIG_NO_CPP11 #ifdef __cplusplus -# if __cplusplus >= 201103L -# define CATCH_CPP11_OR_GREATER -# endif - # if __cplusplus >= 201402L # define CATCH_CPP14_OR_GREATER # endif @@ -54,19 +35,11 @@ #ifdef __clang__ -# if __has_feature(cxx_nullptr) -# define CATCH_INTERNAL_CONFIG_CPP11_NULLPTR -# endif - -# if __has_feature(cxx_noexcept) -# define CATCH_INTERNAL_CONFIG_CPP11_NOEXCEPT -# endif - -# if defined(CATCH_CPP11_OR_GREATER) -# define CATCH_INTERNAL_SUPPRESS_ETD_WARNINGS \ +# define CATCH_INTERNAL_SUPPRESS_GLOBALS_WARNINGS \ _Pragma( "clang diagnostic push" ) \ - _Pragma( "clang diagnostic ignored \"-Wexit-time-destructors\"" ) -# define CATCH_INTERNAL_UNSUPPRESS_ETD_WARNINGS \ + _Pragma( "clang diagnostic ignored \"-Wexit-time-destructors\"" ) \ + _Pragma( "clang diagnostic ignored \"-Wglobal-constructors\"") +# define CATCH_INTERNAL_UNSUPPRESS_GLOBALS_WARNINGS \ _Pragma( "clang diagnostic pop" ) # define CATCH_INTERNAL_SUPPRESS_PARENTHESES_WARNINGS \ @@ -74,7 +47,6 @@ _Pragma( "clang diagnostic ignored \"-Wparentheses\"" ) # define CATCH_INTERNAL_UNSUPPRESS_PARENTHESES_WARNINGS \ _Pragma( "clang diagnostic pop" ) -# endif #endif // __clang__ @@ -104,174 +76,34 @@ #endif // __CYGWIN__ -//////////////////////////////////////////////////////////////////////////////// -// Borland -#ifdef __BORLANDC__ - - -#endif // __BORLANDC__ - -//////////////////////////////////////////////////////////////////////////////// -// EDG -#ifdef __EDG_VERSION__ - - -#endif // __EDG_VERSION__ - -//////////////////////////////////////////////////////////////////////////////// -// Digital Mars -#ifdef __DMC__ - - -#endif // __DMC__ - -//////////////////////////////////////////////////////////////////////////////// -// GCC -#ifdef __GNUC__ - -# if __GNUC__ == 4 && __GNUC_MINOR__ >= 6 && defined(__GXX_EXPERIMENTAL_CXX0X__) -# define CATCH_INTERNAL_CONFIG_CPP11_NULLPTR -# endif - - -// - otherwise more recent versions define __cplusplus >= 201103L -// and will get picked up below - - -#endif // __GNUC__ - //////////////////////////////////////////////////////////////////////////////// // Visual C++ #ifdef _MSC_VER -#define CATCH_INTERNAL_CONFIG_WINDOWS_SEH - -#if (_MSC_VER >= 1600) -# define CATCH_INTERNAL_CONFIG_CPP11_NULLPTR -# define CATCH_INTERNAL_CONFIG_CPP11_UNIQUE_PTR -#endif - -#if (_MSC_VER >= 1900 ) // (VC++ 13 (VS2015)) -#define CATCH_INTERNAL_CONFIG_CPP11_NOEXCEPT -#define CATCH_INTERNAL_CONFIG_CPP11_GENERATED_METHODS -#define CATCH_INTERNAL_CONFIG_CPP11_SHUFFLE -#define CATCH_INTERNAL_CONFIG_CPP11_TYPE_TRAITS -#endif +// Universal Windows platform does not support SEH +// Or console colours (or console at all...) +# if defined(WINAPI_FAMILY) && (WINAPI_FAMILY == WINAPI_FAMILY_APP) +# define CATCH_CONFIG_COLOUR_NONE +# else +# define CATCH_INTERNAL_CONFIG_WINDOWS_SEH +# endif #endif // _MSC_VER //////////////////////////////////////////////////////////////////////////////// -// Use variadic macros if the compiler supports them -#if ( defined _MSC_VER && _MSC_VER > 1400 && !defined __EDGE__) || \ - ( defined __WAVE__ && __WAVE_HAS_VARIADICS ) || \ - ( defined __GNUC__ && __GNUC__ >= 3 ) || \ - ( !defined __cplusplus && __STDC_VERSION__ >= 199901L || __cplusplus >= 201103L ) - -#define CATCH_INTERNAL_CONFIG_VARIADIC_MACROS - -#endif - -// Use __COUNTER__ if the compiler supports it -#if ( defined _MSC_VER && _MSC_VER >= 1300 ) || \ - ( defined __GNUC__ && ( __GNUC__ > 4 || (__GNUC__ == 4 && __GNUC_MINOR__ >= 3 )) ) || \ - ( defined __clang__ && __clang_major__ >= 3 ) - -// Use of __COUNTER__ is suppressed during code analysis in CLion/AppCode 2017.2.x and former, -// because __COUNTER__ is not properly handled by it. -// This does not affect compilation -#if ( !defined __JETBRAINS_IDE__ || __JETBRAINS_IDE__ >= 20170300L ) +// Use of __COUNTER__ is suppressed during code analysis in +// CLion/AppCode 2017.2.x and former, because __COUNTER__ is not properly +// handled by it. +// Otherwise all supported compilers support COUNTER macro, +// but user still might want to turn it off +#if ( !defined(__JETBRAINS_IDE__) || __JETBRAINS_IDE__ >= 20170300L ) #define CATCH_INTERNAL_CONFIG_COUNTER #endif -#endif - -//////////////////////////////////////////////////////////////////////////////// -// C++ language feature support - -// catch all support for C++11 -#if defined(CATCH_CPP11_OR_GREATER) - -# if !defined(CATCH_INTERNAL_CONFIG_CPP11_NULLPTR) -# define CATCH_INTERNAL_CONFIG_CPP11_NULLPTR -# endif - -# ifndef CATCH_INTERNAL_CONFIG_CPP11_NOEXCEPT -# define CATCH_INTERNAL_CONFIG_CPP11_NOEXCEPT -# endif - -# ifndef CATCH_INTERNAL_CONFIG_CPP11_GENERATED_METHODS -# define CATCH_INTERNAL_CONFIG_CPP11_GENERATED_METHODS -# endif - -# ifndef CATCH_INTERNAL_CONFIG_CPP11_IS_ENUM -# define CATCH_INTERNAL_CONFIG_CPP11_IS_ENUM -# endif - -# ifndef CATCH_INTERNAL_CONFIG_CPP11_TUPLE -# define CATCH_INTERNAL_CONFIG_CPP11_TUPLE -# endif - -# ifndef CATCH_INTERNAL_CONFIG_VARIADIC_MACROS -# define CATCH_INTERNAL_CONFIG_VARIADIC_MACROS -# endif - -# if !defined(CATCH_INTERNAL_CONFIG_CPP11_LONG_LONG) -# define CATCH_INTERNAL_CONFIG_CPP11_LONG_LONG -# endif - -# if !defined(CATCH_INTERNAL_CONFIG_CPP11_OVERRIDE) -# define CATCH_INTERNAL_CONFIG_CPP11_OVERRIDE -# endif -# if !defined(CATCH_INTERNAL_CONFIG_CPP11_UNIQUE_PTR) -# define CATCH_INTERNAL_CONFIG_CPP11_UNIQUE_PTR -# endif -# if !defined(CATCH_INTERNAL_CONFIG_CPP11_SHUFFLE) -# define CATCH_INTERNAL_CONFIG_CPP11_SHUFFLE -# endif -# if !defined(CATCH_INTERNAL_CONFIG_CPP11_TYPE_TRAITS) -# define CATCH_INTERNAL_CONFIG_CPP11_TYPE_TRAITS -# endif - -#endif // __cplusplus >= 201103L - -// Now set the actual defines based on the above + anything the user has configured -#if defined(CATCH_INTERNAL_CONFIG_CPP11_NULLPTR) && !defined(CATCH_CONFIG_CPP11_NO_NULLPTR) && !defined(CATCH_CONFIG_CPP11_NULLPTR) && !defined(CATCH_CONFIG_NO_CPP11) -# define CATCH_CONFIG_CPP11_NULLPTR -#endif -#if defined(CATCH_INTERNAL_CONFIG_CPP11_NOEXCEPT) && !defined(CATCH_CONFIG_CPP11_NO_NOEXCEPT) && !defined(CATCH_CONFIG_CPP11_NOEXCEPT) && !defined(CATCH_CONFIG_NO_CPP11) -# define CATCH_CONFIG_CPP11_NOEXCEPT -#endif -#if defined(CATCH_INTERNAL_CONFIG_CPP11_GENERATED_METHODS) && !defined(CATCH_CONFIG_CPP11_NO_GENERATED_METHODS) && !defined(CATCH_CONFIG_CPP11_GENERATED_METHODS) && !defined(CATCH_CONFIG_NO_CPP11) -# define CATCH_CONFIG_CPP11_GENERATED_METHODS -#endif -#if defined(CATCH_INTERNAL_CONFIG_CPP11_IS_ENUM) && !defined(CATCH_CONFIG_CPP11_NO_IS_ENUM) && !defined(CATCH_CONFIG_CPP11_IS_ENUM) && !defined(CATCH_CONFIG_NO_CPP11) -# define CATCH_CONFIG_CPP11_IS_ENUM -#endif -#if defined(CATCH_INTERNAL_CONFIG_CPP11_TUPLE) && !defined(CATCH_CONFIG_CPP11_NO_TUPLE) && !defined(CATCH_CONFIG_CPP11_TUPLE) && !defined(CATCH_CONFIG_NO_CPP11) -# define CATCH_CONFIG_CPP11_TUPLE -#endif -#if defined(CATCH_INTERNAL_CONFIG_VARIADIC_MACROS) && !defined(CATCH_CONFIG_NO_VARIADIC_MACROS) && !defined(CATCH_CONFIG_VARIADIC_MACROS) -# define CATCH_CONFIG_VARIADIC_MACROS -#endif -#if defined(CATCH_INTERNAL_CONFIG_CPP11_LONG_LONG) && !defined(CATCH_CONFIG_CPP11_NO_LONG_LONG) && !defined(CATCH_CONFIG_CPP11_LONG_LONG) && !defined(CATCH_CONFIG_NO_CPP11) -# define CATCH_CONFIG_CPP11_LONG_LONG -#endif -#if defined(CATCH_INTERNAL_CONFIG_CPP11_OVERRIDE) && !defined(CATCH_CONFIG_CPP11_NO_OVERRIDE) && !defined(CATCH_CONFIG_CPP11_OVERRIDE) && !defined(CATCH_CONFIG_NO_CPP11) -# define CATCH_CONFIG_CPP11_OVERRIDE -#endif -#if defined(CATCH_INTERNAL_CONFIG_CPP11_UNIQUE_PTR) && !defined(CATCH_CONFIG_CPP11_NO_UNIQUE_PTR) && !defined(CATCH_CONFIG_CPP11_UNIQUE_PTR) && !defined(CATCH_CONFIG_NO_CPP11) -# define CATCH_CONFIG_CPP11_UNIQUE_PTR -#endif #if defined(CATCH_INTERNAL_CONFIG_COUNTER) && !defined(CATCH_CONFIG_NO_COUNTER) && !defined(CATCH_CONFIG_COUNTER) # define CATCH_CONFIG_COUNTER #endif -#if defined(CATCH_INTERNAL_CONFIG_CPP11_SHUFFLE) && !defined(CATCH_CONFIG_CPP11_NO_SHUFFLE) && !defined(CATCH_CONFIG_CPP11_SHUFFLE) && !defined(CATCH_CONFIG_NO_CPP11) -# define CATCH_CONFIG_CPP11_SHUFFLE -#endif -# if defined(CATCH_INTERNAL_CONFIG_CPP11_TYPE_TRAITS) && !defined(CATCH_CONFIG_CPP11_NO_TYPE_TRAITS) && !defined(CATCH_CONFIG_CPP11_TYPE_TRAITS) && !defined(CATCH_CONFIG_NO_CPP11) -# define CATCH_CONFIG_CPP11_TYPE_TRAITS -# endif #if defined(CATCH_INTERNAL_CONFIG_WINDOWS_SEH) && !defined(CATCH_CONFIG_NO_WINDOWS_SEH) && !defined(CATCH_CONFIG_WINDOWS_SEH) # define CATCH_CONFIG_WINDOWS_SEH #endif @@ -284,40 +116,11 @@ # define CATCH_INTERNAL_SUPPRESS_PARENTHESES_WARNINGS # define CATCH_INTERNAL_UNSUPPRESS_PARENTHESES_WARNINGS #endif -#if !defined(CATCH_INTERNAL_SUPPRESS_ETD_WARNINGS) -# define CATCH_INTERNAL_SUPPRESS_ETD_WARNINGS -# define CATCH_INTERNAL_UNSUPPRESS_ETD_WARNINGS +#if !defined(CATCH_INTERNAL_SUPPRESS_GLOBALS_WARNINGS) +# define CATCH_INTERNAL_SUPPRESS_GLOBALS_WARNINGS +# define CATCH_INTERNAL_UNSUPPRESS_GLOBALS_WARNINGS #endif -// noexcept support: -#if defined(CATCH_CONFIG_CPP11_NOEXCEPT) && !defined(CATCH_NOEXCEPT) -# define CATCH_NOEXCEPT noexcept -# define CATCH_NOEXCEPT_IS(x) noexcept(x) -#else -# define CATCH_NOEXCEPT throw() -# define CATCH_NOEXCEPT_IS(x) -#endif - -// nullptr support -#ifdef CATCH_CONFIG_CPP11_NULLPTR -# define CATCH_NULL nullptr -#else -# define CATCH_NULL NULL -#endif - -// override support -#ifdef CATCH_CONFIG_CPP11_OVERRIDE -# define CATCH_OVERRIDE override -#else -# define CATCH_OVERRIDE -#endif - -// unique_ptr support -#ifdef CATCH_CONFIG_CPP11_UNIQUE_PTR -# define CATCH_AUTO_PTR( T ) std::unique_ptr -#else -# define CATCH_AUTO_PTR( T ) std::auto_ptr -#endif #endif // TWOBLUECUBES_CATCH_COMPILER_CAPABILITIES_HPP_INCLUDED diff --git a/include/internal/catch_config.cpp b/include/internal/catch_config.cpp new file mode 100644 index 00000000..0f79a9ad --- /dev/null +++ b/include/internal/catch_config.cpp @@ -0,0 +1,73 @@ +/* + * Created by Martin on 19/07/2017. + * + * Distributed under the Boost Software License, Version 1.0. (See accompanying + * file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) + */ + +#include "catch_config.hpp" +#include "catch_enforce.h" +#include "catch_stream.h" + +namespace Catch { + + Config::Config( ConfigData const& data ) + : m_data( data ), + m_stream( openStream() ) + { + if( !data.testsOrTags.empty() ) { + TestSpecParser parser( ITagAliasRegistry::get() ); + for( auto const& testOrTags : data.testsOrTags ) + parser.parse( testOrTags ); + m_testSpec = parser.testSpec(); + } + } + + std::string const& Config::getFilename() const { + return m_data.outputFilename ; + } + + bool Config::listTests() const { return m_data.listTests; } + bool Config::listTestNamesOnly() const { return m_data.listTestNamesOnly; } + bool Config::listTags() const { return m_data.listTags; } + bool Config::listReporters() const { return m_data.listReporters; } + + std::string Config::getProcessName() const { return m_data.processName; } + + std::vector const& Config::getReporterNames() const { return m_data.reporterNames; } + std::vector const& Config::getSectionsToRun() const { return m_data.sectionsToRun; } + + TestSpec const& Config::testSpec() const { return m_testSpec; } + + bool Config::showHelp() const { return m_data.showHelp; } + + // IConfig interface + bool Config::allowThrows() const { return !m_data.noThrow; } + std::ostream& Config::stream() const { return m_stream->stream(); } + std::string Config::name() const { return m_data.name.empty() ? m_data.processName : m_data.name; } + bool Config::includeSuccessfulResults() const { return m_data.showSuccessfulTests; } + bool Config::warnAboutMissingAssertions() const { return m_data.warnings & WarnAbout::NoAssertions; } + ShowDurations::OrNot Config::showDurations() const { return m_data.showDurations; } + RunTests::InWhatOrder Config::runOrder() const { return m_data.runOrder; } + unsigned int Config::rngSeed() const { return m_data.rngSeed; } + int Config::benchmarkResolutionMultiple() const { return m_data.benchmarkResolutionMultiple; } + UseColour::YesOrNo Config::useColour() const { return m_data.useColour; } + bool Config::shouldDebugBreak() const { return m_data.shouldDebugBreak; } + int Config::abortAfter() const { return m_data.abortAfter; } + bool Config::showInvisibles() const { return m_data.showInvisibles; } + Verbosity Config::verbosity() const { return m_data.verbosity; } + + IStream const* Config::openStream() { + if( m_data.outputFilename.empty() ) + return new CoutStream(); + else if( m_data.outputFilename[0] == '%' ) { + if( m_data.outputFilename == "%debug" ) + return new DebugOutStream(); + else + CATCH_ERROR( "Unrecognised stream: '" << m_data.outputFilename << "'" ); + } + else + return new FileStream( m_data.outputFilename ); + } + +} // end namespace Catch diff --git a/include/internal/catch_config.hpp b/include/internal/catch_config.hpp index b7f64bf1..fcaa0500 100644 --- a/include/internal/catch_config.hpp +++ b/include/internal/catch_config.hpp @@ -8,15 +8,15 @@ #ifndef TWOBLUECUBES_CATCH_CONFIG_HPP_INCLUDED #define TWOBLUECUBES_CATCH_CONFIG_HPP_INCLUDED -#include "catch_test_spec_parser.hpp" -#include "catch_context.h" +#include "catch_test_spec_parser.h" #include "catch_interfaces_config.h" + +// Libstdc++ doesn't like incomplete classes for unique_ptr #include "catch_stream.h" #include #include #include -#include #ifndef CATCH_CONFIG_CONSOLE_WIDTH #define CATCH_CONFIG_CONSOLE_WIDTH 80 @@ -24,54 +24,32 @@ namespace Catch { + struct IStream; + struct ConfigData { + bool listTests = false; + bool listTags = false; + bool listReporters = false; + bool listTestNamesOnly = false; - ConfigData() - : listTests( false ), - listTags( false ), - listReporters( false ), - listTestNamesOnly( false ), - listExtraInfo( false ), - showSuccessfulTests( false ), - shouldDebugBreak( false ), - noThrow( false ), - showHelp( false ), - showInvisibles( false ), - filenamesAsTags( false ), - libIdentify( false ), - abortAfter( -1 ), - rngSeed( 0 ), - verbosity( Verbosity::Normal ), - warnings( WarnAbout::Nothing ), - showDurations( ShowDurations::DefaultForReporter ), - runOrder( RunTests::InDeclarationOrder ), - useColour( UseColour::Auto ), - waitForKeypress( WaitForKeypress::Never ) - {} + bool showSuccessfulTests = false; + bool shouldDebugBreak = false; + bool noThrow = false; + bool showHelp = false; + bool showInvisibles = false; + bool filenamesAsTags = false; + bool libIdentify = false; - bool listTests; - bool listTags; - bool listReporters; - bool listTestNamesOnly; - bool listExtraInfo; + int abortAfter = -1; + unsigned int rngSeed = 0; + int benchmarkResolutionMultiple = 100; - bool showSuccessfulTests; - bool shouldDebugBreak; - bool noThrow; - bool showHelp; - bool showInvisibles; - bool filenamesAsTags; - bool libIdentify; - - int abortAfter; - unsigned int rngSeed; - - Verbosity::Level verbosity; - WarnAbout::What warnings; - ShowDurations::OrNot showDurations; - RunTests::InWhatOrder runOrder; - UseColour::YesOrNo useColour; - WaitForKeypress::When waitForKeypress; + Verbosity verbosity = Verbosity::Normal; + WarnAbout::What warnings = WarnAbout::Nothing; + ShowDurations::OrNot showDurations = ShowDurations::DefaultForReporter; + RunTests::InWhatOrder runOrder = RunTests::InDeclarationOrder; + UseColour::YesOrNo useColour = UseColour::Auto; + WaitForKeypress::When waitForKeypress = WaitForKeypress::Never; std::string outputFilename; std::string name; @@ -83,80 +61,51 @@ namespace Catch { }; - class Config : public SharedImpl { - private: - Config( Config const& other ); - Config& operator = ( Config const& other ); - virtual void dummy(); + class Config : public IConfig { public: - Config() - {} + Config() = default; + Config( ConfigData const& data ); + virtual ~Config() = default; - Config( ConfigData const& data ) - : m_data( data ), - m_stream( openStream() ) - { - if( !data.testsOrTags.empty() ) { - TestSpecParser parser( ITagAliasRegistry::get() ); - for( std::size_t i = 0; i < data.testsOrTags.size(); ++i ) - parser.parse( data.testsOrTags[i] ); - m_testSpec = parser.testSpec(); - } - } + std::string const& getFilename() const; - virtual ~Config() {} + bool listTests() const; + bool listTestNamesOnly() const; + bool listTags() const; + bool listReporters() const; - std::string const& getFilename() const { - return m_data.outputFilename ; - } + std::string getProcessName() const; - bool listTests() const { return m_data.listTests; } - bool listTestNamesOnly() const { return m_data.listTestNamesOnly; } - bool listTags() const { return m_data.listTags; } - bool listReporters() const { return m_data.listReporters; } - bool listExtraInfo() const { return m_data.listExtraInfo; } + std::vector const& getReporterNames() const; + std::vector const& getSectionsToRun() const override; - std::string getProcessName() const { return m_data.processName; } + virtual TestSpec const& testSpec() const override; - std::vector const& getReporterNames() const { return m_data.reporterNames; } - std::vector const& getSectionsToRun() const CATCH_OVERRIDE { return m_data.sectionsToRun; } - - virtual TestSpec const& testSpec() const CATCH_OVERRIDE { return m_testSpec; } - - bool showHelp() const { return m_data.showHelp; } + bool showHelp() const; // IConfig interface - virtual bool allowThrows() const CATCH_OVERRIDE { return !m_data.noThrow; } - virtual std::ostream& stream() const CATCH_OVERRIDE { return m_stream->stream(); } - virtual std::string name() const CATCH_OVERRIDE { return m_data.name.empty() ? m_data.processName : m_data.name; } - virtual bool includeSuccessfulResults() const CATCH_OVERRIDE { return m_data.showSuccessfulTests; } - virtual bool warnAboutMissingAssertions() const CATCH_OVERRIDE { return m_data.warnings & WarnAbout::NoAssertions; } - virtual ShowDurations::OrNot showDurations() const CATCH_OVERRIDE { return m_data.showDurations; } - virtual RunTests::InWhatOrder runOrder() const CATCH_OVERRIDE { return m_data.runOrder; } - virtual unsigned int rngSeed() const CATCH_OVERRIDE { return m_data.rngSeed; } - virtual UseColour::YesOrNo useColour() const CATCH_OVERRIDE { return m_data.useColour; } - virtual bool shouldDebugBreak() const CATCH_OVERRIDE { return m_data.shouldDebugBreak; } - virtual int abortAfter() const CATCH_OVERRIDE { return m_data.abortAfter; } - virtual bool showInvisibles() const CATCH_OVERRIDE { return m_data.showInvisibles; } + bool allowThrows() const override; + std::ostream& stream() const override; + std::string name() const override; + bool includeSuccessfulResults() const override; + bool warnAboutMissingAssertions() const override; + ShowDurations::OrNot showDurations() const override; + RunTests::InWhatOrder runOrder() const override; + unsigned int rngSeed() const override; + int benchmarkResolutionMultiple() const override; + UseColour::YesOrNo useColour() const override; + bool shouldDebugBreak() const override; + int abortAfter() const override; + bool showInvisibles() const override; + Verbosity verbosity() const override; private: - IStream const* openStream() { - if( m_data.outputFilename.empty() ) - return new CoutStream(); - else if( m_data.outputFilename[0] == '%' ) { - if( m_data.outputFilename == "%debug" ) - return new DebugOutStream(); - else - throw std::domain_error( "Unrecognised stream: " + m_data.outputFilename ); - } - else - return new FileStream( m_data.outputFilename ); - } + IStream const* openStream(); ConfigData m_data; - CATCH_AUTO_PTR( IStream const ) m_stream; + std::unique_ptr m_stream; TestSpec m_testSpec; }; diff --git a/include/internal/catch_console_colour_impl.hpp b/include/internal/catch_console_colour.cpp similarity index 79% rename from include/internal/catch_console_colour_impl.hpp rename to include/internal/catch_console_colour.cpp index 426bba29..8b055b33 100644 --- a/include/internal/catch_console_colour_impl.hpp +++ b/include/internal/catch_console_colour.cpp @@ -5,17 +5,29 @@ * Distributed under the Boost Software License, Version 1.0. (See accompanying * file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) */ -#ifndef TWOBLUECUBES_CATCH_CONSOLE_COLOUR_IMPL_HPP_INCLUDED -#define TWOBLUECUBES_CATCH_CONSOLE_COLOUR_IMPL_HPP_INCLUDED -#include "catch_console_colour.hpp" -#include "catch_errno_guard.hpp" + +#if defined(__clang__) +# pragma clang diagnostic push +# pragma clang diagnostic ignored "-Wexit-time-destructors" +#endif + + +#include "catch_console_colour.h" +#include "catch_enforce.h" +#include "catch_errno_guard.h" +#include "catch_interfaces_config.h" +#include "catch_stream.h" +#include "catch_context.h" +#include "catch_platform.h" +#include "catch_debugger.h" +#include "catch_windows_h_proxy.h" namespace Catch { namespace { struct IColourImpl { - virtual ~IColourImpl() {} + virtual ~IColourImpl() = default; virtual void use( Colour::Code _colourCode ) = 0; }; @@ -42,8 +54,6 @@ namespace Catch { #if defined ( CATCH_CONFIG_COLOUR_WINDOWS ) ///////////////////////////////////////// -#include "catch_windows_h_proxy.h" - namespace Catch { namespace { @@ -57,7 +67,7 @@ namespace { originalBackgroundAttributes = csbiInfo.wAttributes & ~( FOREGROUND_GREEN | FOREGROUND_RED | FOREGROUND_BLUE | FOREGROUND_INTENSITY ); } - virtual void use( Colour::Code _colourCode ) { + virtual void use( Colour::Code _colourCode ) override { switch( _colourCode ) { case Colour::None: return setTextAttribute( originalForegroundAttributes ); case Colour::White: return setTextAttribute( FOREGROUND_GREEN | FOREGROUND_RED | FOREGROUND_BLUE ); @@ -73,7 +83,7 @@ namespace { case Colour::BrightGreen: return setTextAttribute( FOREGROUND_INTENSITY | FOREGROUND_GREEN ); case Colour::BrightWhite: return setTextAttribute( FOREGROUND_INTENSITY | FOREGROUND_GREEN | FOREGROUND_RED | FOREGROUND_BLUE ); - case Colour::Bright: throw std::logic_error( "not a colour" ); + case Colour::Bright: CATCH_INTERNAL_ERROR( "not a colour" ); } } @@ -89,14 +99,12 @@ namespace { IColourImpl* platformColourInstance() { static Win32ColourImpl s_instance; - Ptr config = getCurrentContext().getConfig(); + IConfigPtr config = getCurrentContext().getConfig(); UseColour::YesOrNo colourMode = config ? config->useColour() : UseColour::Auto; if( colourMode == UseColour::Auto ) - colourMode = !isDebuggerActive() - ? UseColour::Yes - : UseColour::No; + colourMode = UseColour::Yes; return colourMode == UseColour::Yes ? &s_instance : NoColourImpl::instance(); @@ -118,7 +126,7 @@ namespace { // https://github.com/philsquared/Catch/pull/131 class PosixColourImpl : public IColourImpl { public: - virtual void use( Colour::Code _colourCode ) { + virtual void use( Colour::Code _colourCode ) override { switch( _colourCode ) { case Colour::None: case Colour::White: return setColour( "[0m" ); @@ -134,7 +142,7 @@ namespace { case Colour::BrightGreen: return setColour( "[1;32m" ); case Colour::BrightWhite: return setColour( "[1;37m" ); - case Colour::Bright: throw std::logic_error( "not a colour" ); + case Colour::Bright: CATCH_INTERNAL_ERROR( "not a colour" ); } } static IColourImpl* instance() { @@ -148,14 +156,21 @@ namespace { } }; + bool useColourOnPlatform() { + return +#ifdef CATCH_PLATFORM_MAC + !isDebuggerActive() && +#endif + isatty(STDOUT_FILENO); + } IColourImpl* platformColourInstance() { ErrnoGuard guard; - Ptr config = getCurrentContext().getConfig(); + IConfigPtr config = getCurrentContext().getConfig(); UseColour::YesOrNo colourMode = config ? config->useColour() : UseColour::Auto; if( colourMode == UseColour::Auto ) - colourMode = (!isDebuggerActive() && isatty(STDOUT_FILENO) ) + colourMode = useColourOnPlatform() ? UseColour::Yes : UseColour::No; return colourMode == UseColour::Yes @@ -178,8 +193,17 @@ namespace Catch { namespace Catch { - Colour::Colour( Code _colourCode ) : m_moved( false ) { use( _colourCode ); } - Colour::Colour( Colour const& _other ) : m_moved( false ) { const_cast( _other ).m_moved = true; } + Colour::Colour( Code _colourCode ) { use( _colourCode ); } + Colour::Colour( Colour&& rhs ) noexcept { + m_moved = rhs.m_moved; + rhs.m_moved = true; + } + Colour& Colour::operator=( Colour&& rhs ) noexcept { + m_moved = rhs.m_moved; + rhs.m_moved = true; + return *this; + } + Colour::~Colour(){ if( !m_moved ) use( None ); } void Colour::use( Code _colourCode ) { @@ -187,6 +211,13 @@ namespace Catch { impl->use( _colourCode ); } + std::ostream& operator << ( std::ostream& os, Colour const& ) { + return os; + } + } // end namespace Catch -#endif // TWOBLUECUBES_CATCH_CONSOLE_COLOUR_IMPL_HPP_INCLUDED +#if defined(__clang__) +# pragma clang diagnostic pop +#endif + diff --git a/include/internal/catch_console_colour.hpp b/include/internal/catch_console_colour.h similarity index 88% rename from include/internal/catch_console_colour.hpp rename to include/internal/catch_console_colour.h index f0a8a697..d1e68f87 100644 --- a/include/internal/catch_console_colour.hpp +++ b/include/internal/catch_console_colour.h @@ -50,17 +50,18 @@ namespace Catch { // Use constructed object for RAII guard Colour( Code _colourCode ); - Colour( Colour const& other ); + Colour( Colour&& other ) noexcept; + Colour& operator=( Colour&& other ) noexcept; ~Colour(); // Use static method for one-shot changes static void use( Code _colourCode ); private: - bool m_moved; + bool m_moved = false; }; - inline std::ostream& operator << ( std::ostream& os, Colour const& ) { return os; } + std::ostream& operator << ( std::ostream& os, Colour const& ); } // end namespace Catch diff --git a/include/internal/catch_context.cpp b/include/internal/catch_context.cpp new file mode 100644 index 00000000..c917a1d4 --- /dev/null +++ b/include/internal/catch_context.cpp @@ -0,0 +1,67 @@ +/* + * Created by Phil on 31/12/2010. + * Copyright 2010 Two Blue Cubes Ltd. All rights reserved. + * + * Distributed under the Boost Software License, Version 1.0. (See accompanying + * file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) + */ +#include "catch_context.h" +#include "catch_common.h" + +namespace Catch { + + class Context : public IMutableContext, NonCopyable { + + public: // IContext + virtual IResultCapture* getResultCapture() override { + return m_resultCapture; + } + virtual IRunner* getRunner() override { + return m_runner; + } + + virtual IConfigPtr getConfig() const override { + return m_config; + } + + virtual ~Context() override; + + public: // IMutableContext + virtual void setResultCapture( IResultCapture* resultCapture ) override { + m_resultCapture = resultCapture; + } + virtual void setRunner( IRunner* runner ) override { + m_runner = runner; + } + virtual void setConfig( IConfigPtr const& config ) override { + m_config = config; + } + + friend IMutableContext& getCurrentMutableContext(); + + private: + IConfigPtr m_config; + IRunner* m_runner = nullptr; + IResultCapture* m_resultCapture = nullptr; + }; + + namespace { + Context* currentContext = nullptr; + } + IMutableContext& getCurrentMutableContext() { + if( !currentContext ) + currentContext = new Context(); + return *currentContext; + } + IContext& getCurrentContext() { + return getCurrentMutableContext(); + } + + void cleanUpContext() { + delete currentContext; + currentContext = nullptr; + } + IContext::~IContext() = default; + IMutableContext::~IMutableContext() = default; + Context::~Context() = default; +} diff --git a/include/internal/catch_context.h b/include/internal/catch_context.h index 8b683864..c88236a9 100644 --- a/include/internal/catch_context.h +++ b/include/internal/catch_context.h @@ -8,28 +8,23 @@ #ifndef TWOBLUECUBES_CATCH_CONTEXT_H_INCLUDED #define TWOBLUECUBES_CATCH_CONTEXT_H_INCLUDED -#include "catch_interfaces_generators.h" -#include "catch_ptr.hpp" - +#include namespace Catch { - class TestCase; - class Stream; struct IResultCapture; struct IRunner; - struct IGeneratorsForTest; struct IConfig; + using IConfigPtr = std::shared_ptr; + struct IContext { virtual ~IContext(); virtual IResultCapture* getResultCapture() = 0; virtual IRunner* getRunner() = 0; - virtual size_t getGeneratorIndex( std::string const& fileInfo, size_t totalSize ) = 0; - virtual bool advanceGeneratorsForCurrentTest() = 0; - virtual Ptr getConfig() const = 0; + virtual IConfigPtr getConfig() const = 0; }; struct IMutableContext : IContext @@ -37,14 +32,12 @@ namespace Catch { virtual ~IMutableContext(); virtual void setResultCapture( IResultCapture* resultCapture ) = 0; virtual void setRunner( IRunner* runner ) = 0; - virtual void setConfig( Ptr const& config ) = 0; + virtual void setConfig( IConfigPtr const& config ) = 0; }; IContext& getCurrentContext(); IMutableContext& getCurrentMutableContext(); void cleanUpContext(); - Stream createStream( std::string const& streamName ); - } #endif // TWOBLUECUBES_CATCH_CONTEXT_H_INCLUDED diff --git a/include/internal/catch_context_impl.hpp b/include/internal/catch_context_impl.hpp deleted file mode 100644 index 8516ad93..00000000 --- a/include/internal/catch_context_impl.hpp +++ /dev/null @@ -1,110 +0,0 @@ -/* - * Created by Phil on 31/12/2010. - * Copyright 2010 Two Blue Cubes Ltd. All rights reserved. - * - * Distributed under the Boost Software License, Version 1.0. (See accompanying - * file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) - */ -#ifndef TWOBLUECUBES_CATCH_CONTEXT_IMPL_HPP_INCLUDED -#define TWOBLUECUBES_CATCH_CONTEXT_IMPL_HPP_INCLUDED - -#include "catch_run_context.hpp" - -#include "catch_context.h" -#include "catch_stream.hpp" -#include "catch_common.h" - -namespace Catch { - - class Context : public IMutableContext { - - Context() : m_config( CATCH_NULL ), m_runner( CATCH_NULL ), m_resultCapture( CATCH_NULL ) {} - Context( Context const& ); - void operator=( Context const& ); - - public: - virtual ~Context() { - deleteAllValues( m_generatorsByTestName ); - } - - public: // IContext - virtual IResultCapture* getResultCapture() { - return m_resultCapture; - } - virtual IRunner* getRunner() { - return m_runner; - } - virtual size_t getGeneratorIndex( std::string const& fileInfo, size_t totalSize ) { - return getGeneratorsForCurrentTest() - .getGeneratorInfo( fileInfo, totalSize ) - .getCurrentIndex(); - } - virtual bool advanceGeneratorsForCurrentTest() { - IGeneratorsForTest* generators = findGeneratorsForCurrentTest(); - return generators && generators->moveNext(); - } - - virtual Ptr getConfig() const { - return m_config; - } - - public: // IMutableContext - virtual void setResultCapture( IResultCapture* resultCapture ) { - m_resultCapture = resultCapture; - } - virtual void setRunner( IRunner* runner ) { - m_runner = runner; - } - virtual void setConfig( Ptr const& config ) { - m_config = config; - } - - friend IMutableContext& getCurrentMutableContext(); - - private: - IGeneratorsForTest* findGeneratorsForCurrentTest() { - std::string testName = getResultCapture()->getCurrentTestName(); - - std::map::const_iterator it = - m_generatorsByTestName.find( testName ); - return it != m_generatorsByTestName.end() - ? it->second - : CATCH_NULL; - } - - IGeneratorsForTest& getGeneratorsForCurrentTest() { - IGeneratorsForTest* generators = findGeneratorsForCurrentTest(); - if( !generators ) { - std::string testName = getResultCapture()->getCurrentTestName(); - generators = createGeneratorsForTest(); - m_generatorsByTestName.insert( std::make_pair( testName, generators ) ); - } - return *generators; - } - - private: - Ptr m_config; - IRunner* m_runner; - IResultCapture* m_resultCapture; - std::map m_generatorsByTestName; - }; - - namespace { - Context* currentContext = CATCH_NULL; - } - IMutableContext& getCurrentMutableContext() { - if( !currentContext ) - currentContext = new Context(); - return *currentContext; - } - IContext& getCurrentContext() { - return getCurrentMutableContext(); - } - - void cleanUpContext() { - delete currentContext; - currentContext = CATCH_NULL; - } -} - -#endif // TWOBLUECUBES_CATCH_CONTEXT_IMPL_HPP_INCLUDED diff --git a/include/internal/catch_debug_console.cpp b/include/internal/catch_debug_console.cpp new file mode 100644 index 00000000..d817c561 --- /dev/null +++ b/include/internal/catch_debug_console.cpp @@ -0,0 +1,29 @@ +/* + * Created by Martin on 29/08/2017. + * + * Distributed under the Boost Software License, Version 1.0. (See accompanying + * file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) + * + */ + +#include "catch_debug_console.h" +#include "catch_stream.h" +#include "catch_platform.h" + +#ifdef CATCH_PLATFORM_WINDOWS + +#include "catch_windows_h_proxy.h" + + namespace Catch { + void writeToDebugConsole( std::string const& text ) { + ::OutputDebugStringA( text.c_str() ); + } + } +#else + namespace Catch { + void writeToDebugConsole( std::string const& text ) { + // !TBD: Need a version for Mac/ XCode and other IDEs + Catch::cout() << text; + } + } +#endif // Platform diff --git a/include/internal/catch_debug_console.h b/include/internal/catch_debug_console.h new file mode 100644 index 00000000..2c229a83 --- /dev/null +++ b/include/internal/catch_debug_console.h @@ -0,0 +1,17 @@ +/* + * Created by Martin on 29/08/2017. + * + * Distributed under the Boost Software License, Version 1.0. (See accompanying + * file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) + * + */ +#ifndef TWOBLUECUBES_CATCH_DEBUG_CONSOLE_H_INCLUDED +#define TWOBLUECUBES_CATCH_DEBUG_CONSOLE_H_INCLUDED + +#include + +namespace Catch { + void writeToDebugConsole( std::string const& text ); +} + +#endif // TWOBLUECUBES_CATCH_DEBUG_CONSOLE_H_INCLUDED diff --git a/include/internal/catch_debugger.hpp b/include/internal/catch_debugger.cpp similarity index 83% rename from include/internal/catch_debugger.hpp rename to include/internal/catch_debugger.cpp index 00017d16..c1ef340c 100644 --- a/include/internal/catch_debugger.hpp +++ b/include/internal/catch_debugger.cpp @@ -6,11 +6,11 @@ * file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) * */ -#ifndef TWOBLUECUBES_CATCH_DEBUGGER_HPP_INCLUDED -#define TWOBLUECUBES_CATCH_DEBUGGER_HPP_INCLUDED #include "catch_debugger.h" -#include "catch_errno_guard.hpp" +#include "catch_errno_guard.h" +#include "catch_stream.h" +#include "catch_platform.h" #ifdef CATCH_PLATFORM_MAC @@ -20,7 +20,7 @@ #include #include - namespace Catch{ + namespace Catch { // The following function is taken directly from the following technical note: // http://developer.apple.com/library/mac/#qa/qa2004/qa1361.html @@ -31,7 +31,7 @@ int mib[4]; struct kinfo_proc info; - size_t size; + std::size_t size; // Initialize the flags so that, if sysctl fails for some bizarre // reason, we get a predictable result. @@ -49,7 +49,7 @@ // Call sysctl. size = sizeof(info); - if( sysctl(mib, sizeof(mib) / sizeof(*mib), &info, &size, CATCH_NULL, 0) != 0 ) { + if( sysctl(mib, sizeof(mib) / sizeof(*mib), &info, &size, nullptr, 0) != 0 ) { Catch::cerr() << "\n** Call to sysctl failed - unable to determine if debugger is active **\n" << std::endl; return false; } @@ -106,26 +106,6 @@ } #else namespace Catch { - inline bool isDebuggerActive() { return false; } + bool isDebuggerActive() { return false; } } #endif // Platform - -#ifdef CATCH_PLATFORM_WINDOWS - -#include "catch_windows_h_proxy.h" - - namespace Catch { - void writeToDebugConsole( std::string const& text ) { - ::OutputDebugStringA( text.c_str() ); - } - } -#else - namespace Catch { - void writeToDebugConsole( std::string const& text ) { - // !TBD: Need a version for Mac/ XCode and other IDEs - Catch::cout() << text; - } - } -#endif // Platform - -#endif // TWOBLUECUBES_CATCH_DEBUGGER_HPP_INCLUDED diff --git a/include/internal/catch_debugger.h b/include/internal/catch_debugger.h index 65361c24..f842ff5b 100644 --- a/include/internal/catch_debugger.h +++ b/include/internal/catch_debugger.h @@ -11,25 +11,13 @@ #include "catch_platform.h" -#include - -namespace Catch{ - +namespace Catch { bool isDebuggerActive(); - void writeToDebugConsole( std::string const& text ); } #ifdef CATCH_PLATFORM_MAC - // The following code snippet based on: - // http://cocoawithlove.com/2008/03/break-into-debugger.html - #if defined(__ppc64__) || defined(__ppc__) - #define CATCH_TRAP() \ - __asm__("li r0, 20\nsc\nnop\nli r0, 37\nli r4, 2\nsc\nnop\n" \ - : : : "memory","r0","r3","r4" ) /* NOLINT */ - #else - #define CATCH_TRAP() __asm__("int $3\n" : : /* NOLINT */ ) - #endif + #define CATCH_TRAP() __asm__("int $3\n" : : ) /* NOLINT */ #elif defined(CATCH_PLATFORM_LINUX) // If we can use inline assembler, do it because this allows us to break diff --git a/include/internal/catch_decomposer.cpp b/include/internal/catch_decomposer.cpp new file mode 100644 index 00000000..8c19629b --- /dev/null +++ b/include/internal/catch_decomposer.cpp @@ -0,0 +1,24 @@ +/* + * Created by Phil Nash on 8/8/2017. + * Copyright 2017 Two Blue Cubes Ltd. All rights reserved. + * + * Distributed under the Boost Software License, Version 1.0. (See accompanying + * file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) + */ + +#include "catch_decomposer.h" +#include "catch_config.hpp" + +namespace Catch { + + ITransientExpression::~ITransientExpression() = default; + + void formatReconstructedExpression( std::ostream &os, std::string const& lhs, StringRef op, std::string const& rhs ) { + if( lhs.size() + rhs.size() < 40 && + lhs.find('\n') == std::string::npos && + rhs.find('\n') == std::string::npos ) + os << lhs << " " << op << " " << rhs; + else + os << lhs << "\n" << op << "\n" << rhs; + } +} diff --git a/include/internal/catch_decomposer.h b/include/internal/catch_decomposer.h new file mode 100644 index 00000000..38b62f45 --- /dev/null +++ b/include/internal/catch_decomposer.h @@ -0,0 +1,169 @@ +/* + * Created by Phil Nash on 8/8/2017. + * Copyright 2017 Two Blue Cubes Ltd. All rights reserved. + * + * Distributed under the Boost Software License, Version 1.0. (See accompanying + * file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) + */ +#ifndef TWOBLUECUBES_CATCH_DECOMPOSER_H_INCLUDED +#define TWOBLUECUBES_CATCH_DECOMPOSER_H_INCLUDED + +#include "catch_tostring.h" +#include "catch_stringref.h" + +#include + +#ifdef _MSC_VER +#pragma warning(push) +#pragma warning(disable:4389) // '==' : signed/unsigned mismatch +#pragma warning(disable:4018) // more "signed/unsigned mismatch" +#pragma warning(disable:4312) // Converting int to T* using reinterpret_cast (issue on x64 platform) +#pragma warning(disable:4180) // qualifier applied to function type has no meaning +#endif + +namespace Catch { + + struct ITransientExpression { + virtual auto isBinaryExpression() const -> bool = 0; + virtual auto getResult() const -> bool = 0; + virtual void streamReconstructedExpression( std::ostream &os ) const = 0; + + // We don't actually need a virtual destructore, but many static analysers + // complain if it's not here :-( + virtual ~ITransientExpression(); + }; + + 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 ) ); + } + + public: + BinaryExpr( bool comparisonResult, LhsT lhs, StringRef op, RhsT rhs ) + : m_result( comparisonResult ), + m_lhs( lhs ), + m_op( op ), + m_rhs( rhs ) + {} + }; + + template + 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 ) {} + }; + + + // Specialised comparison functions to handle equality comparisons between ints and pointers (NULL deduces as an int) + template + auto compareEqual( LhsT const& lhs, RhsT const& rhs ) -> bool { return lhs == rhs; }; + template + auto compareEqual( T* const& lhs, int rhs ) -> bool { return lhs == reinterpret_cast( rhs ); } + template + auto compareEqual( T* const& lhs, long rhs ) -> bool { return lhs == reinterpret_cast( rhs ); } + template + auto compareEqual( int lhs, T* const& rhs ) -> bool { return reinterpret_cast( lhs ) == rhs; } + template + auto compareEqual( long lhs, T* const& rhs ) -> bool { return reinterpret_cast( lhs ) == rhs; } + + template + auto compareNotEqual( LhsT const& lhs, RhsT&& rhs ) -> bool { return lhs != rhs; }; + template + auto compareNotEqual( T* const& lhs, int rhs ) -> bool { return lhs != reinterpret_cast( rhs ); } + template + auto compareNotEqual( T* const& lhs, long rhs ) -> bool { return lhs != reinterpret_cast( rhs ); } + template + auto compareNotEqual( int lhs, T* const& rhs ) -> bool { return reinterpret_cast( lhs ) != rhs; } + template + auto compareNotEqual( long lhs, T* const& rhs ) -> bool { return reinterpret_cast( lhs ) != rhs; } + + + template + class ExprLhs { + LhsT m_lhs; + public: + ExprLhs( LhsT lhs ) : m_lhs( lhs ) {} + + template + auto operator == ( RhsT const& rhs ) -> BinaryExpr const { + return BinaryExpr( compareEqual( m_lhs, rhs ), m_lhs, "==", rhs ); + } + auto operator == ( bool rhs ) -> BinaryExpr const { + return BinaryExpr( m_lhs == rhs, m_lhs, "==", rhs ); + } + + template + auto operator != ( RhsT const& rhs ) -> BinaryExpr const { + return BinaryExpr( compareNotEqual( m_lhs, rhs ), m_lhs, "!=", rhs ); + } + auto operator != ( bool rhs ) -> BinaryExpr const { + return BinaryExpr( m_lhs != rhs, m_lhs, "!=", rhs ); + } + + template + auto operator > ( RhsT const& rhs ) -> BinaryExpr const { + return BinaryExpr( m_lhs > rhs, m_lhs, ">", rhs ); + } + template + auto operator < ( RhsT const& rhs ) -> BinaryExpr const { + return BinaryExpr( m_lhs < rhs, m_lhs, "<", rhs ); + } + template + auto operator >= ( RhsT const& rhs ) -> BinaryExpr const { + return BinaryExpr( m_lhs >= rhs, m_lhs, ">=", rhs ); + } + template + auto operator <= ( RhsT const& rhs ) -> BinaryExpr const { + return BinaryExpr( m_lhs <= rhs, m_lhs, "<=", rhs ); + } + + auto makeUnaryExpr() const -> UnaryExpr { + return UnaryExpr( m_lhs ); + } + }; + + void handleExpression( ITransientExpression const& expr ); + + template + void handleExpression( ExprLhs const& expr ) { + handleExpression( expr.makeUnaryExpr() ); + } + + struct Decomposer { + template + auto operator <= ( T const& lhs ) -> ExprLhs { + return ExprLhs( lhs ); + } + auto operator <=( bool value ) -> ExprLhs { + return ExprLhs( value ); + } + }; + +} // end namespace Catch + +#ifdef _MSC_VER +#pragma warning(pop) +#endif + +#endif // TWOBLUECUBES_CATCH_DECOMPOSER_H_INCLUDED diff --git a/include/internal/catch_default_main.hpp b/include/internal/catch_default_main.hpp index e9f466c0..4372e9cc 100644 --- a/include/internal/catch_default_main.hpp +++ b/include/internal/catch_default_main.hpp @@ -8,6 +8,8 @@ #ifndef TWOBLUECUBES_CATCH_DEFAULT_MAIN_HPP_INCLUDED #define TWOBLUECUBES_CATCH_DEFAULT_MAIN_HPP_INCLUDED +#include "catch_session.h" + #ifndef __OBJC__ #if defined(WIN32) && defined(_UNICODE) && !defined(DO_NOT_USE_WMAIN) @@ -18,8 +20,7 @@ extern "C" int wmain (int argc, wchar_t * argv[], wchar_t * []) { int main (int argc, char * argv[]) { #endif - int result = Catch::Session().run( argc, argv ); - return ( result < 0xff ? result : 0xff ); + return Catch::Session().run( argc, argv ); } #else // __OBJC__ @@ -31,13 +32,13 @@ int main (int argc, char * const argv[]) { #endif Catch::registerTestMethods(); - int result = Catch::Session().run( argc, (char* const*)argv ); + int result = Catch::Session().run( argc, (char**)argv ); #if !CATCH_ARC_ENABLED [pool drain]; #endif - return ( result < 0xff ? result : 0xff ); + return result; } #endif // __OBJC__ diff --git a/include/internal/catch_enforce.h b/include/internal/catch_enforce.h new file mode 100644 index 00000000..e489cf7e --- /dev/null +++ b/include/internal/catch_enforce.h @@ -0,0 +1,24 @@ +/* + * Created by Martin on 01/08/2017. + * + * Distributed under the Boost Software License, Version 1.0. (See accompanying + * file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) + */ +#ifndef TWOBLUECUBES_CATCH_ENFORCE_H_INCLUDED +#define TWOBLUECUBES_CATCH_ENFORCE_H_INCLUDED + +#include "catch_common.h" + +#include +#include + +#define CATCH_PREPARE_EXCEPTION( type, msg ) \ + type( static_cast( std::ostringstream() << msg ).str() ) +#define CATCH_INTERNAL_ERROR( msg ) \ + throw CATCH_PREPARE_EXCEPTION( std::logic_error, CATCH_INTERNAL_LINEINFO << ": Internal Catch error: " << msg); +#define CATCH_ERROR( msg ) \ + throw CATCH_PREPARE_EXCEPTION( std::domain_error, msg ) +#define CATCH_ENFORCE( condition, msg ) \ + do{ if( !(condition) ) CATCH_ERROR( msg ); } while(false) + +#endif // TWOBLUECUBES_CATCH_ENFORCE_H_INCLUDED diff --git a/include/internal/catch_errno_guard.cpp b/include/internal/catch_errno_guard.cpp new file mode 100644 index 00000000..070583bc --- /dev/null +++ b/include/internal/catch_errno_guard.cpp @@ -0,0 +1,15 @@ +/* + * Created by Martin on 06/03/2017. + * + * Distributed under the Boost Software License, Version 1.0. (See accompanying + * file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) + */ + +#include "catch_errno_guard.h" + +#include + +namespace Catch { + ErrnoGuard::ErrnoGuard():m_oldErrno(errno){} + ErrnoGuard::~ErrnoGuard() { errno = m_oldErrno; } +} diff --git a/include/internal/catch_errno_guard.hpp b/include/internal/catch_errno_guard.h similarity index 53% rename from include/internal/catch_errno_guard.hpp rename to include/internal/catch_errno_guard.h index 48794b08..b1d1fc1c 100644 --- a/include/internal/catch_errno_guard.hpp +++ b/include/internal/catch_errno_guard.h @@ -4,22 +4,19 @@ * Distributed under the Boost Software License, Version 1.0. (See accompanying * file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) */ -#ifndef TWOBLUECUBES_CATCH_ERRNO_GUARD_HPP_INCLUDED -#define TWOBLUECUBES_CATCH_ERRNO_GUARD_HPP_INCLUDED - -#include - +#ifndef TWOBLUECUBES_CATCH_ERRNO_GUARD_H_INCLUDED +#define TWOBLUECUBES_CATCH_ERRNO_GUARD_H_INCLUDED namespace Catch { class ErrnoGuard { public: - ErrnoGuard():m_oldErrno(errno){} - ~ErrnoGuard() { errno = m_oldErrno; } + ErrnoGuard(); + ~ErrnoGuard(); private: int m_oldErrno; }; } -#endif // TWOBLUECUBES_CATCH_ERRNO_GUARD_HPP_INCLUDED +#endif // TWOBLUECUBES_CATCH_ERRNO_GUARD_H_INCLUDED diff --git a/include/internal/catch_exception_translator_registry.cpp b/include/internal/catch_exception_translator_registry.cpp new file mode 100644 index 00000000..77f74a88 --- /dev/null +++ b/include/internal/catch_exception_translator_registry.cpp @@ -0,0 +1,62 @@ +/* + * Created by Phil on 20/04/2011. + * Copyright 2011 Two Blue Cubes Ltd. All rights reserved. + * + * Distributed under the Boost Software License, Version 1.0. (See accompanying + * file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) + */ + +#include "catch_assertionhandler.h" +#include "catch_exception_translator_registry.h" + +#ifdef __OBJC__ +#import "Foundation/Foundation.h" +#endif + +namespace Catch { + + ExceptionTranslatorRegistry::~ExceptionTranslatorRegistry() { + } + + void ExceptionTranslatorRegistry::registerTranslator( const IExceptionTranslator* translator ) { + m_translators.push_back( std::unique_ptr( translator ) ); + } + + std::string ExceptionTranslatorRegistry::translateActiveException() const { + try { +#ifdef __OBJC__ + // In Objective-C try objective-c exceptions first + @try { + return tryTranslators(); + } + @catch (NSException *exception) { + return Catch::Detail::stringify( [exception description] ); + } +#else + return tryTranslators(); +#endif + } + catch( TestFailureException& ) { + std::rethrow_exception(std::current_exception()); + } + catch( std::exception& ex ) { + return ex.what(); + } + catch( std::string& msg ) { + return msg; + } + catch( const char* msg ) { + return msg; + } + catch(...) { + return "Unknown exception"; + } + } + + std::string ExceptionTranslatorRegistry::tryTranslators() const { + if( m_translators.empty() ) + std::rethrow_exception(std::current_exception()); + else + return m_translators[0]->translate( m_translators.begin()+1, m_translators.end() ); + } +} diff --git a/include/internal/catch_exception_translator_registry.h b/include/internal/catch_exception_translator_registry.h new file mode 100644 index 00000000..da2f4f12 --- /dev/null +++ b/include/internal/catch_exception_translator_registry.h @@ -0,0 +1,30 @@ +/* + * Created by Phil on 20/04/2011. + * Copyright 2011 Two Blue Cubes Ltd. All rights reserved. + * + * Distributed under the Boost Software License, Version 1.0. (See accompanying + * file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) + */ +#ifndef TWOBLUECUBES_CATCH_EXCEPTION_TRANSLATOR_REGISTRY_HPP_INCLUDED +#define TWOBLUECUBES_CATCH_EXCEPTION_TRANSLATOR_REGISTRY_HPP_INCLUDED + +#include "catch_interfaces_exception.h" +#include +#include +#include + +namespace Catch { + + class ExceptionTranslatorRegistry : public IExceptionTranslatorRegistry { + public: + ~ExceptionTranslatorRegistry(); + virtual void registerTranslator( const IExceptionTranslator* translator ); + virtual std::string translateActiveException() const override; + std::string tryTranslators() const; + + private: + std::vector> m_translators; + }; +} + +#endif // TWOBLUECUBES_CATCH_EXCEPTION_TRANSLATOR_REGISTRY_HPP_INCLUDED diff --git a/include/internal/catch_exception_translator_registry.hpp b/include/internal/catch_exception_translator_registry.hpp deleted file mode 100644 index c4bdb400..00000000 --- a/include/internal/catch_exception_translator_registry.hpp +++ /dev/null @@ -1,73 +0,0 @@ -/* - * Created by Phil on 20/04/2011. - * Copyright 2011 Two Blue Cubes Ltd. All rights reserved. - * - * Distributed under the Boost Software License, Version 1.0. (See accompanying - * file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) - */ -#ifndef TWOBLUECUBES_CATCH_EXCEPTION_TRANSLATOR_REGISTRY_HPP_INCLUDED -#define TWOBLUECUBES_CATCH_EXCEPTION_TRANSLATOR_REGISTRY_HPP_INCLUDED - -#include "catch_interfaces_exception.h" -#include "catch_tostring.h" - -#ifdef __OBJC__ -#import "Foundation/Foundation.h" -#endif - -namespace Catch { - - class ExceptionTranslatorRegistry : public IExceptionTranslatorRegistry { - public: - ~ExceptionTranslatorRegistry() { - deleteAll( m_translators ); - } - - virtual void registerTranslator( const IExceptionTranslator* translator ) { - m_translators.push_back( translator ); - } - - virtual std::string translateActiveException() const { - try { -#ifdef __OBJC__ - // In Objective-C try objective-c exceptions first - @try { - return tryTranslators(); - } - @catch (NSException *exception) { - return Catch::toString( [exception description] ); - } -#else - return tryTranslators(); -#endif - } - catch( TestFailureException& ) { - throw; - } - catch( std::exception& ex ) { - return ex.what(); - } - catch( std::string& msg ) { - return msg; - } - catch( const char* msg ) { - return msg; - } - catch(...) { - return "Unknown exception"; - } - } - - std::string tryTranslators() const { - if( m_translators.empty() ) - throw; - else - return m_translators[0]->translate( m_translators.begin()+1, m_translators.end() ); - } - - private: - std::vector m_translators; - }; -} - -#endif // TWOBLUECUBES_CATCH_EXCEPTION_TRANSLATOR_REGISTRY_HPP_INCLUDED diff --git a/include/internal/catch_external_interfaces.h b/include/internal/catch_external_interfaces.h new file mode 100644 index 00000000..7abc69fc --- /dev/null +++ b/include/internal/catch_external_interfaces.h @@ -0,0 +1,14 @@ +/* + * Created by Martin on 17/08/2017. + * + * Distributed under the Boost Software License, Version 1.0. (See accompanying + * file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) + */ +#ifndef TWOBLUECUBES_CATCH_EXTERNAL_INTERFACES_H_INCLUDED +#define TWOBLUECUBES_CATCH_EXTERNAL_INTERFACES_H_INCLUDED + +#include "../reporters/catch_reporter_bases.hpp" +#include "catch_console_colour.h" +#include "catch_reporter_registrars.hpp" + +#endif // TWOBLUECUBES_CATCH_EXTERNAL_INTERFACES_H_INCLUDED diff --git a/include/internal/catch_fatal_condition.cpp b/include/internal/catch_fatal_condition.cpp new file mode 100644 index 00000000..39b63455 --- /dev/null +++ b/include/internal/catch_fatal_condition.cpp @@ -0,0 +1,176 @@ +/* + * Created by Phil on 21/08/2014 + * Copyright 2014 Two Blue Cubes Ltd. All rights reserved. + * + * Distributed under the Boost Software License, Version 1.0. (See accompanying + * file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) + * + */ + +#include "catch_fatal_condition.h" + +#include "catch_context.h" +#include "catch_interfaces_capture.h" + +namespace { + // Report the error condition + void reportFatal( char const * const message ) { + Catch::getCurrentContext().getResultCapture()->handleFatalErrorCondition( message ); + } +} + +#if defined ( CATCH_PLATFORM_WINDOWS ) ///////////////////////////////////////// + +# if !defined ( CATCH_CONFIG_WINDOWS_SEH ) + +namespace Catch { + void FatalConditionHandler::reset() {} +} + +# else // CATCH_CONFIG_WINDOWS_SEH is defined + +namespace Catch { + struct SignalDefs { DWORD id; const char* name; }; + + // There is no 1-1 mapping between signals and windows exceptions. + // Windows can easily distinguish between SO and SigSegV, + // but SigInt, SigTerm, etc are handled differently. + static SignalDefs signalDefs[] = { + { EXCEPTION_ILLEGAL_INSTRUCTION, "SIGILL - Illegal instruction signal" }, + { EXCEPTION_STACK_OVERFLOW, "SIGSEGV - Stack overflow" }, + { EXCEPTION_ACCESS_VIOLATION, "SIGSEGV - Segmentation violation signal" }, + { EXCEPTION_INT_DIVIDE_BY_ZERO, "Divide by zero error" }, + }; + + LONG CALLBACK FatalConditionHandler::handleVectoredException(PEXCEPTION_POINTERS ExceptionInfo) { + for (auto const& def : signalDefs) { + if (ExceptionInfo->ExceptionRecord->ExceptionCode == def.id) { + reportFatal(def.name); + } + } + // If its not an exception we care about, pass it along. + // This stops us from eating debugger breaks etc. + return EXCEPTION_CONTINUE_SEARCH; + } + + FatalConditionHandler::FatalConditionHandler() { + isSet = true; + // 32k seems enough for Catch to handle stack overflow, + // but the value was found experimentally, so there is no strong guarantee + guaranteeSize = 32 * 1024; + exceptionHandlerHandle = nullptr; + // Register as first handler in current chain + exceptionHandlerHandle = AddVectoredExceptionHandler(1, handleVectoredException); + // Pass in guarantee size to be filled + SetThreadStackGuarantee(&guaranteeSize); + } + + void FatalConditionHandler::reset() { + if (isSet) { + // Unregister handler and restore the old guarantee + RemoveVectoredExceptionHandler(exceptionHandlerHandle); + SetThreadStackGuarantee(&guaranteeSize); + exceptionHandlerHandle = nullptr; + isSet = false; + } + } + + FatalConditionHandler::~FatalConditionHandler() { + reset(); + } + +bool FatalConditionHandler::isSet = false; +ULONG FatalConditionHandler::guaranteeSize = 0; +PVOID FatalConditionHandler::exceptionHandlerHandle = nullptr; + + +} // namespace Catch + +# endif // CATCH_CONFIG_WINDOWS_SEH + +#else // Not Windows - assumed to be POSIX compatible ////////////////////////// + +# if !defined(CATCH_CONFIG_POSIX_SIGNALS) + +namespace Catch { + void FatalConditionHandler::reset() {} +} + + +# else // CATCH_CONFIG_POSIX_SIGNALS is defined + +#include + +namespace Catch { + + struct SignalDefs { + int id; + const char* name; + }; + static SignalDefs signalDefs[] = { + { SIGINT, "SIGINT - Terminal interrupt signal" }, + { SIGILL, "SIGILL - Illegal instruction signal" }, + { SIGFPE, "SIGFPE - Floating point error signal" }, + { SIGSEGV, "SIGSEGV - Segmentation violation signal" }, + { SIGTERM, "SIGTERM - Termination request signal" }, + { SIGABRT, "SIGABRT - Abort (abnormal termination) signal" } + }; + + + void FatalConditionHandler::handleSignal( int sig ) { + char const * name = ""; + for (auto const& def : signalDefs) { + if (sig == def.id) { + name = def.name; + break; + } + } + reset(); + reportFatal(name); + raise( sig ); + } + + FatalConditionHandler::FatalConditionHandler() { + isSet = true; + stack_t sigStack; + sigStack.ss_sp = altStackMem; + sigStack.ss_size = SIGSTKSZ; + sigStack.ss_flags = 0; + sigaltstack(&sigStack, &oldSigStack); + struct sigaction sa = { }; + + sa.sa_handler = handleSignal; + sa.sa_flags = SA_ONSTACK; + for (std::size_t i = 0; i < sizeof(signalDefs)/sizeof(SignalDefs); ++i) { + sigaction(signalDefs[i].id, &sa, &oldSigActions[i]); + } + } + + + FatalConditionHandler::~FatalConditionHandler() { + reset(); + } + + void FatalConditionHandler::reset() { + if( isSet ) { + // Set signals back to previous values -- hopefully nobody overwrote them in the meantime + for( std::size_t i = 0; i < sizeof(signalDefs)/sizeof(SignalDefs); ++i ) { + sigaction(signalDefs[i].id, &oldSigActions[i], nullptr); + } + // Return the old stack + sigaltstack(&oldSigStack, nullptr); + isSet = false; + } + } + + bool FatalConditionHandler::isSet = false; + struct sigaction FatalConditionHandler::oldSigActions[sizeof(signalDefs)/sizeof(SignalDefs)] = {}; + stack_t FatalConditionHandler::oldSigStack = {}; + char FatalConditionHandler::altStackMem[SIGSTKSZ] = {}; + + +} // namespace Catch + +# endif // CATCH_CONFIG_POSIX_SIGNALS + +#endif // not Windows diff --git a/include/internal/catch_fatal_condition.h b/include/internal/catch_fatal_condition.h new file mode 100644 index 00000000..9977702a --- /dev/null +++ b/include/internal/catch_fatal_condition.h @@ -0,0 +1,86 @@ +/* + * Created by Phil on 21/08/2014 + * Copyright 2014 Two Blue Cubes Ltd. All rights reserved. + * + * Distributed under the Boost Software License, Version 1.0. (See accompanying + * file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) + * + */ +#ifndef TWOBLUECUBES_CATCH_FATAL_CONDITION_H_INCLUDED +#define TWOBLUECUBES_CATCH_FATAL_CONDITION_H_INCLUDED + +#include +#include "catch_platform.h" +#include "catch_compiler_capabilities.h" + + +#if defined ( CATCH_PLATFORM_WINDOWS ) ///////////////////////////////////////// +#include "catch_windows_h_proxy.h" + +# if !defined ( CATCH_CONFIG_WINDOWS_SEH ) + +namespace Catch { + struct FatalConditionHandler { + void reset(); + }; +} + +# else // CATCH_CONFIG_WINDOWS_SEH is defined + +namespace Catch { + + struct FatalConditionHandler { + + static LONG CALLBACK handleVectoredException(PEXCEPTION_POINTERS ExceptionInfo); + FatalConditionHandler(); + static void reset(); + ~FatalConditionHandler(); + + private: + static bool isSet; + static ULONG guaranteeSize; + static PVOID exceptionHandlerHandle; + }; + +} // namespace Catch + +# endif // CATCH_CONFIG_WINDOWS_SEH + +#else // Not Windows - assumed to be POSIX compatible ////////////////////////// + +# if !defined(CATCH_CONFIG_POSIX_SIGNALS) + +namespace Catch { + struct FatalConditionHandler { + void reset(); + }; +} + + +# else // CATCH_CONFIG_POSIX_SIGNALS is defined + +#include + +namespace Catch { + + struct FatalConditionHandler { + + static bool isSet; + static struct sigaction oldSigActions[];// [sizeof(signalDefs) / sizeof(SignalDefs)]; + static stack_t oldSigStack; + static char altStackMem[]; + + static void handleSignal( int sig ); + + FatalConditionHandler(); + ~FatalConditionHandler(); + static void reset(); + }; + +} // namespace Catch + +# endif // CATCH_CONFIG_POSIX_SIGNALS + +#endif // not Windows + +#endif // TWOBLUECUBES_CATCH_FATAL_CONDITION_H_INCLUDED diff --git a/include/internal/catch_fatal_condition.hpp b/include/internal/catch_fatal_condition.hpp deleted file mode 100644 index 1dcd545d..00000000 --- a/include/internal/catch_fatal_condition.hpp +++ /dev/null @@ -1,200 +0,0 @@ -/* - * Created by Phil on 21/08/2014 - * Copyright 2014 Two Blue Cubes Ltd. All rights reserved. - * - * Distributed under the Boost Software License, Version 1.0. (See accompanying - * file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) - * - */ -#ifndef TWOBLUECUBES_CATCH_FATAL_CONDITION_H_INCLUDED -#define TWOBLUECUBES_CATCH_FATAL_CONDITION_H_INCLUDED - - -namespace Catch { - - // Report the error condition - inline void reportFatal( std::string const& message ) { - IContext& context = Catch::getCurrentContext(); - IResultCapture* resultCapture = context.getResultCapture(); - resultCapture->handleFatalErrorCondition( message ); - } - -} // namespace Catch - -#if defined ( CATCH_PLATFORM_WINDOWS ) ///////////////////////////////////////// -#include "catch_windows_h_proxy.h" - -# if !defined ( CATCH_CONFIG_WINDOWS_SEH ) - -namespace Catch { - struct FatalConditionHandler { - void reset() {} - }; -} - -# else // CATCH_CONFIG_WINDOWS_SEH is defined - -namespace Catch { - - struct SignalDefs { DWORD id; const char* name; }; - extern SignalDefs signalDefs[]; - // There is no 1-1 mapping between signals and windows exceptions. - // Windows can easily distinguish between SO and SigSegV, - // but SigInt, SigTerm, etc are handled differently. - SignalDefs signalDefs[] = { - { EXCEPTION_ILLEGAL_INSTRUCTION, "SIGILL - Illegal instruction signal" }, - { EXCEPTION_STACK_OVERFLOW, "SIGSEGV - Stack overflow" }, - { EXCEPTION_ACCESS_VIOLATION, "SIGSEGV - Segmentation violation signal" }, - { EXCEPTION_INT_DIVIDE_BY_ZERO, "Divide by zero error" }, - }; - - struct FatalConditionHandler { - - static LONG CALLBACK handleVectoredException(PEXCEPTION_POINTERS ExceptionInfo) { - for (int i = 0; i < sizeof(signalDefs) / sizeof(SignalDefs); ++i) { - if (ExceptionInfo->ExceptionRecord->ExceptionCode == signalDefs[i].id) { - reportFatal(signalDefs[i].name); - } - } - // If its not an exception we care about, pass it along. - // This stops us from eating debugger breaks etc. - return EXCEPTION_CONTINUE_SEARCH; - } - - FatalConditionHandler() { - isSet = true; - // 32k seems enough for Catch to handle stack overflow, - // but the value was found experimentally, so there is no strong guarantee - guaranteeSize = 32 * 1024; - exceptionHandlerHandle = CATCH_NULL; - // Register as first handler in current chain - exceptionHandlerHandle = AddVectoredExceptionHandler(1, handleVectoredException); - // Pass in guarantee size to be filled - SetThreadStackGuarantee(&guaranteeSize); - } - - static void reset() { - if (isSet) { - // Unregister handler and restore the old guarantee - RemoveVectoredExceptionHandler(exceptionHandlerHandle); - SetThreadStackGuarantee(&guaranteeSize); - exceptionHandlerHandle = CATCH_NULL; - isSet = false; - } - } - - ~FatalConditionHandler() { - reset(); - } - private: - static bool isSet; - static ULONG guaranteeSize; - static PVOID exceptionHandlerHandle; - }; - - bool FatalConditionHandler::isSet = false; - ULONG FatalConditionHandler::guaranteeSize = 0; - PVOID FatalConditionHandler::exceptionHandlerHandle = CATCH_NULL; - -} // namespace Catch - -# endif // CATCH_CONFIG_WINDOWS_SEH - -#else // Not Windows - assumed to be POSIX compatible ////////////////////////// - -# if !defined(CATCH_CONFIG_POSIX_SIGNALS) - -namespace Catch { - struct FatalConditionHandler { - void reset() {} - }; -} - - -# else // CATCH_CONFIG_POSIX_SIGNALS is defined - -#include - -namespace Catch { - - struct SignalDefs { - int id; - const char* name; - }; - extern SignalDefs signalDefs[]; - SignalDefs signalDefs[] = { - { SIGINT, "SIGINT - Terminal interrupt signal" }, - { SIGILL, "SIGILL - Illegal instruction signal" }, - { SIGFPE, "SIGFPE - Floating point error signal" }, - { SIGSEGV, "SIGSEGV - Segmentation violation signal" }, - { SIGTERM, "SIGTERM - Termination request signal" }, - { SIGABRT, "SIGABRT - Abort (abnormal termination) signal" } - }; - - struct FatalConditionHandler { - - static bool isSet; - static struct sigaction oldSigActions [sizeof(signalDefs)/sizeof(SignalDefs)]; - static stack_t oldSigStack; - static char altStackMem[SIGSTKSZ]; - - static void handleSignal( int sig ) { - std::string name = ""; - for (std::size_t i = 0; i < sizeof(signalDefs) / sizeof(SignalDefs); ++i) { - SignalDefs &def = signalDefs[i]; - if (sig == def.id) { - name = def.name; - break; - } - } - reset(); - reportFatal(name); - raise( sig ); - } - - FatalConditionHandler() { - isSet = true; - stack_t sigStack; - sigStack.ss_sp = altStackMem; - sigStack.ss_size = SIGSTKSZ; - sigStack.ss_flags = 0; - sigaltstack(&sigStack, &oldSigStack); - struct sigaction sa = { 0 }; - - sa.sa_handler = handleSignal; - sa.sa_flags = SA_ONSTACK; - for (std::size_t i = 0; i < sizeof(signalDefs)/sizeof(SignalDefs); ++i) { - sigaction(signalDefs[i].id, &sa, &oldSigActions[i]); - } - } - - - ~FatalConditionHandler() { - reset(); - } - static void reset() { - if( isSet ) { - // Set signals back to previous values -- hopefully nobody overwrote them in the meantime - for( std::size_t i = 0; i < sizeof(signalDefs)/sizeof(SignalDefs); ++i ) { - sigaction(signalDefs[i].id, &oldSigActions[i], CATCH_NULL); - } - // Return the old stack - sigaltstack(&oldSigStack, CATCH_NULL); - isSet = false; - } - } - }; - - bool FatalConditionHandler::isSet = false; - struct sigaction FatalConditionHandler::oldSigActions[sizeof(signalDefs)/sizeof(SignalDefs)] = {}; - stack_t FatalConditionHandler::oldSigStack = {}; - char FatalConditionHandler::altStackMem[SIGSTKSZ] = {}; - - -} // namespace Catch - -# endif // CATCH_CONFIG_POSIX_SIGNALS - -#endif // not Windows - -#endif // TWOBLUECUBES_CATCH_FATAL_CONDITION_H_INCLUDED diff --git a/include/internal/catch_generators.hpp b/include/internal/catch_generators.hpp deleted file mode 100644 index a9ba7eb8..00000000 --- a/include/internal/catch_generators.hpp +++ /dev/null @@ -1,189 +0,0 @@ -/* - * Created by Phil on 27/01/2011. - * Copyright 2011 Two Blue Cubes Ltd. All rights reserved. - * - * Distributed under the Boost Software License, Version 1.0. (See accompanying - * file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) - */ -#ifndef TWOBLUECUBES_CATCH_GENERATORS_HPP_INCLUDED -#define TWOBLUECUBES_CATCH_GENERATORS_HPP_INCLUDED - -#include "catch_context.h" - -#include -#include -#include - -namespace Catch { - -template -struct IGenerator { - virtual ~IGenerator() {} - virtual T getValue( std::size_t index ) const = 0; - virtual std::size_t size () const = 0; -}; - -template -class BetweenGenerator : public IGenerator { -public: - BetweenGenerator( T from, T to ) : m_from( from ), m_to( to ){} - - virtual T getValue( std::size_t index ) const { - return m_from+static_cast( index ); - } - - virtual std::size_t size() const { - return static_cast( 1+m_to-m_from ); - } - -private: - - T m_from; - T m_to; -}; - -template -class ValuesGenerator : public IGenerator { -public: - ValuesGenerator(){} - - void add( T value ) { - m_values.push_back( value ); - } - - virtual T getValue( std::size_t index ) const { - return m_values[index]; - } - - virtual std::size_t size() const { - return m_values.size(); - } - -private: - std::vector m_values; -}; - -template -class CompositeGenerator { -public: - CompositeGenerator() : m_totalSize( 0 ) {} - - // *** Move semantics, similar to auto_ptr *** - CompositeGenerator( CompositeGenerator& other ) - : m_fileInfo( other.m_fileInfo ), - m_totalSize( 0 ) - { - move( other ); - } - - CompositeGenerator& setFileInfo( const char* fileInfo ) { - m_fileInfo = fileInfo; - return *this; - } - - ~CompositeGenerator() { - deleteAll( m_composed ); - } - - operator T () const { - size_t overallIndex = getCurrentContext().getGeneratorIndex( m_fileInfo, m_totalSize ); - - typename std::vector*>::const_iterator it = m_composed.begin(); - typename std::vector*>::const_iterator itEnd = m_composed.end(); - for( size_t index = 0; it != itEnd; ++it ) - { - const IGenerator* generator = *it; - if( overallIndex >= index && overallIndex < index + generator->size() ) - { - return generator->getValue( overallIndex-index ); - } - index += generator->size(); - } - CATCH_INTERNAL_ERROR( "Indexed past end of generated range" ); - return T(); // Suppress spurious "not all control paths return a value" warning in Visual Studio - if you know how to fix this please do so - } - - void add( const IGenerator* generator ) { - m_totalSize += generator->size(); - m_composed.push_back( generator ); - } - - CompositeGenerator& then( CompositeGenerator& other ) { - move( other ); - return *this; - } - - CompositeGenerator& then( T value ) { - ValuesGenerator* valuesGen = new ValuesGenerator(); - valuesGen->add( value ); - add( valuesGen ); - return *this; - } - -private: - - void move( CompositeGenerator& other ) { - m_composed.insert( m_composed.end(), other.m_composed.begin(), other.m_composed.end() ); - m_totalSize += other.m_totalSize; - other.m_composed.clear(); - } - - std::vector*> m_composed; - std::string m_fileInfo; - size_t m_totalSize; -}; - -namespace Generators -{ - template - CompositeGenerator between( T from, T to ) { - CompositeGenerator generators; - generators.add( new BetweenGenerator( from, to ) ); - return generators; - } - - template - CompositeGenerator values( T val1, T val2 ) { - CompositeGenerator generators; - ValuesGenerator* valuesGen = new ValuesGenerator(); - valuesGen->add( val1 ); - valuesGen->add( val2 ); - generators.add( valuesGen ); - return generators; - } - - template - CompositeGenerator values( T val1, T val2, T val3 ){ - CompositeGenerator generators; - ValuesGenerator* valuesGen = new ValuesGenerator(); - valuesGen->add( val1 ); - valuesGen->add( val2 ); - valuesGen->add( val3 ); - generators.add( valuesGen ); - return generators; - } - - template - CompositeGenerator values( T val1, T val2, T val3, T val4 ) { - CompositeGenerator generators; - ValuesGenerator* valuesGen = new ValuesGenerator(); - valuesGen->add( val1 ); - valuesGen->add( val2 ); - valuesGen->add( val3 ); - valuesGen->add( val4 ); - generators.add( valuesGen ); - return generators; - } - -} // end namespace Generators - -using namespace Generators; - -} // end namespace Catch - -#define INTERNAL_CATCH_LINESTR2( line ) #line -#define INTERNAL_CATCH_LINESTR( line ) INTERNAL_CATCH_LINESTR2( line ) - -#define INTERNAL_CATCH_GENERATE( expr ) expr.setFileInfo( __FILE__ "(" INTERNAL_CATCH_LINESTR( __LINE__ ) ")" ) - -#endif // TWOBLUECUBES_CATCH_GENERATORS_HPP_INCLUDED diff --git a/include/internal/catch_generators_impl.hpp b/include/internal/catch_generators_impl.hpp deleted file mode 100644 index fea699ae..00000000 --- a/include/internal/catch_generators_impl.hpp +++ /dev/null @@ -1,86 +0,0 @@ -/* - * Created by Phil on 28/01/2011. - * Copyright 2011 Two Blue Cubes Ltd. All rights reserved. - * - * Distributed under the Boost Software License, Version 1.0. (See accompanying - * file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) - */ -#ifndef TWOBLUECUBES_CATCH_GENERATORS_IMPL_HPP_INCLUDED -#define TWOBLUECUBES_CATCH_GENERATORS_IMPL_HPP_INCLUDED - -#include "catch_interfaces_generators.h" - -#include "catch_common.h" - -#include -#include -#include - -namespace Catch { - - struct GeneratorInfo : IGeneratorInfo { - - GeneratorInfo( std::size_t size ) - : m_size( size ), - m_currentIndex( 0 ) - {} - - bool moveNext() { - if( ++m_currentIndex == m_size ) { - m_currentIndex = 0; - return false; - } - return true; - } - - std::size_t getCurrentIndex() const { - return m_currentIndex; - } - - std::size_t m_size; - std::size_t m_currentIndex; - }; - - /////////////////////////////////////////////////////////////////////////// - - class GeneratorsForTest : public IGeneratorsForTest { - - public: - ~GeneratorsForTest() { - deleteAll( m_generatorsInOrder ); - } - - IGeneratorInfo& getGeneratorInfo( std::string const& fileInfo, std::size_t size ) { - std::map::const_iterator it = m_generatorsByName.find( fileInfo ); - if( it == m_generatorsByName.end() ) { - IGeneratorInfo* info = new GeneratorInfo( size ); - m_generatorsByName.insert( std::make_pair( fileInfo, info ) ); - m_generatorsInOrder.push_back( info ); - return *info; - } - return *it->second; - } - - bool moveNext() { - std::vector::const_iterator it = m_generatorsInOrder.begin(); - std::vector::const_iterator itEnd = m_generatorsInOrder.end(); - for(; it != itEnd; ++it ) { - if( (*it)->moveNext() ) - return true; - } - return false; - } - - private: - std::map m_generatorsByName; - std::vector m_generatorsInOrder; - }; - - IGeneratorsForTest* createGeneratorsForTest() - { - return new GeneratorsForTest(); - } - -} // end namespace Catch - -#endif // TWOBLUECUBES_CATCH_GENERATORS_IMPL_HPP_INCLUDED diff --git a/include/internal/catch_impl.hpp b/include/internal/catch_impl.hpp index 85cf266f..0b29a39f 100644 --- a/include/internal/catch_impl.hpp +++ b/include/internal/catch_impl.hpp @@ -8,99 +8,22 @@ #ifndef TWOBLUECUBES_CATCH_IMPL_HPP_INCLUDED #define TWOBLUECUBES_CATCH_IMPL_HPP_INCLUDED -// Collect all the implementation files together here -// These are the equivalent of what would usually be cpp files - #ifdef __clang__ #pragma clang diagnostic push #pragma clang diagnostic ignored "-Wweak-vtables" #endif -#include "../catch_session.hpp" -#include "catch_registry_hub.hpp" -#include "catch_notimplemented_exception.hpp" -#include "catch_context_impl.hpp" -#include "catch_console_colour_impl.hpp" -#include "catch_generators_impl.hpp" -#include "catch_assertionresult.hpp" -#include "catch_test_case_info.hpp" -#include "catch_test_spec.hpp" -#include "catch_version.hpp" -#include "catch_message.hpp" -#include "catch_legacy_reporter_adapter.hpp" -#include "catch_timer.hpp" -#include "catch_common.hpp" -#include "catch_section.hpp" -#include "catch_debugger.hpp" -#include "catch_tostring.hpp" -#include "catch_result_builder.hpp" -#include "catch_tag_alias_registry.hpp" -#include "catch_test_case_tracker.hpp" -#include "catch_matchers_string.hpp" +// Keep these here for external reporters +#include "catch_test_spec.h" +#include "catch_test_case_tracker.h" -#include "../reporters/catch_reporter_multi.hpp" -#include "../reporters/catch_reporter_xml.hpp" -#include "../reporters/catch_reporter_junit.hpp" -#include "../reporters/catch_reporter_console.hpp" -#include "../reporters/catch_reporter_compact.hpp" +#include "catch_leak_detector.h" + +// Cpp files will be included in the single-header file here +// ~*~* CATCH_CPP_STITCH_PLACE *~*~ namespace Catch { - // These are all here to avoid warnings about not having any out of line - // virtual methods - NonCopyable::~NonCopyable() {} - IShared::~IShared() {} - IStream::~IStream() CATCH_NOEXCEPT {} - FileStream::~FileStream() CATCH_NOEXCEPT {} - CoutStream::~CoutStream() CATCH_NOEXCEPT {} - DebugOutStream::~DebugOutStream() CATCH_NOEXCEPT {} - StreamBufBase::~StreamBufBase() CATCH_NOEXCEPT {} - IContext::~IContext() {} - IResultCapture::~IResultCapture() {} - ITestCase::~ITestCase() {} - ITestCaseRegistry::~ITestCaseRegistry() {} - IRegistryHub::~IRegistryHub() {} - IMutableRegistryHub::~IMutableRegistryHub() {} - IExceptionTranslator::~IExceptionTranslator() {} - IExceptionTranslatorRegistry::~IExceptionTranslatorRegistry() {} - IReporter::~IReporter() {} - IReporterFactory::~IReporterFactory() {} - IReporterRegistry::~IReporterRegistry() {} - IStreamingReporter::~IStreamingReporter() {} - AssertionStats::~AssertionStats() {} - SectionStats::~SectionStats() {} - TestCaseStats::~TestCaseStats() {} - TestGroupStats::~TestGroupStats() {} - TestRunStats::~TestRunStats() {} - CumulativeReporterBase::SectionNode::~SectionNode() {} - CumulativeReporterBase::~CumulativeReporterBase() {} - - StreamingReporterBase::~StreamingReporterBase() {} - ConsoleReporter::~ConsoleReporter() {} - CompactReporter::~CompactReporter() {} - IRunner::~IRunner() {} - IMutableContext::~IMutableContext() {} - IConfig::~IConfig() {} - XmlReporter::~XmlReporter() {} - JunitReporter::~JunitReporter() {} - TestRegistry::~TestRegistry() {} - FreeFunctionTestCase::~FreeFunctionTestCase() {} - IGeneratorInfo::~IGeneratorInfo() {} - IGeneratorsForTest::~IGeneratorsForTest() {} - WildcardPattern::~WildcardPattern() {} - TestSpec::Pattern::~Pattern() {} - TestSpec::NamePattern::~NamePattern() {} - TestSpec::TagPattern::~TagPattern() {} - TestSpec::ExcludedPattern::~ExcludedPattern() {} - Matchers::Impl::MatcherUntypedBase::~MatcherUntypedBase() {} - - void Config::dummy() {} - - namespace TestCaseTracking { - ITracker::~ITracker() {} - TrackerBase::~TrackerBase() {} - SectionTracker::~SectionTracker() {} - IndexTracker::~IndexTracker() {} - } + LeakDetector leakDetector; } #ifdef __clang__ diff --git a/include/internal/catch_interfaces_capture.cpp b/include/internal/catch_interfaces_capture.cpp new file mode 100644 index 00000000..3c090bfe --- /dev/null +++ b/include/internal/catch_interfaces_capture.cpp @@ -0,0 +1,5 @@ +#include "catch_interfaces_capture.h" + +namespace Catch { + IResultCapture::~IResultCapture() = default; +} diff --git a/include/internal/catch_interfaces_capture.h b/include/internal/catch_interfaces_capture.h index 54cf0e4d..5b370114 100644 --- a/include/internal/catch_interfaces_capture.h +++ b/include/internal/catch_interfaces_capture.h @@ -9,29 +9,34 @@ #define TWOBLUECUBES_CATCH_INTERFACES_CAPTURE_H_INCLUDED #include -#include "catch_result_type.h" -#include "catch_common.h" + +#include "catch_stringref.h" namespace Catch { - class TestCase; class AssertionResult; struct AssertionInfo; struct SectionInfo; struct SectionEndInfo; struct MessageInfo; - class ScopedMessageBuilder; struct Counts; + struct BenchmarkInfo; + struct BenchmarkStats; 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; virtual void sectionEndedEarly( SectionEndInfo const& endInfo ) = 0; + + virtual void benchmarkStarting( BenchmarkInfo const& info ) = 0; + virtual void benchmarkEnded( BenchmarkStats const& stats ) = 0; + virtual void pushScopedMessage( MessageInfo const& message ) = 0; virtual void popScopedMessage( MessageInfo const& message ) = 0; @@ -40,7 +45,7 @@ namespace Catch { virtual void exceptionEarlyReported() = 0; - virtual void handleFatalErrorCondition( std::string const& message ) = 0; + virtual void handleFatalErrorCondition( StringRef message ) = 0; virtual bool lastAssertionPassed() = 0; virtual void assertionPassed() = 0; diff --git a/include/internal/catch_interfaces_config.cpp b/include/internal/catch_interfaces_config.cpp new file mode 100644 index 00000000..b6f5daa2 --- /dev/null +++ b/include/internal/catch_interfaces_config.cpp @@ -0,0 +1,5 @@ +#include "internal/catch_interfaces_config.h" + +namespace Catch { + IConfig::~IConfig() = default; +} diff --git a/include/internal/catch_interfaces_config.h b/include/internal/catch_interfaces_config.h index f65515d1..2584ccc0 100644 --- a/include/internal/catch_interfaces_config.h +++ b/include/internal/catch_interfaces_config.h @@ -8,19 +8,20 @@ #ifndef TWOBLUECUBES_CATCH_INTERFACES_CONFIG_H_INCLUDED #define TWOBLUECUBES_CATCH_INTERFACES_CONFIG_H_INCLUDED +#include "catch_common.h" + #include #include #include - -#include "catch_ptr.hpp" +#include namespace Catch { - struct Verbosity { enum Level { - NoOutput = 0, - Quiet, - Normal - }; }; + enum class Verbosity { + Quiet = 0, + Normal, + High + }; struct WarnAbout { enum What { Nothing = 0x00, @@ -51,7 +52,7 @@ namespace Catch { class TestSpec; - struct IConfig : IShared { + struct IConfig : NonCopyable { virtual ~IConfig(); @@ -67,10 +68,13 @@ namespace Catch { virtual TestSpec const& testSpec() const = 0; virtual RunTests::InWhatOrder runOrder() const = 0; virtual unsigned int rngSeed() const = 0; + virtual int benchmarkResolutionMultiple() const = 0; virtual UseColour::YesOrNo useColour() const = 0; virtual std::vector const& getSectionsToRun() const = 0; - + virtual Verbosity verbosity() const = 0; }; + + using IConfigPtr = std::shared_ptr; } #endif // TWOBLUECUBES_CATCH_INTERFACES_CONFIG_H_INCLUDED diff --git a/include/internal/catch_interfaces_exception.cpp b/include/internal/catch_interfaces_exception.cpp new file mode 100644 index 00000000..8494a2cc --- /dev/null +++ b/include/internal/catch_interfaces_exception.cpp @@ -0,0 +1,6 @@ +#include "internal/catch_interfaces_exception.h" + +namespace Catch { + IExceptionTranslator::~IExceptionTranslator() = default; + IExceptionTranslatorRegistry::~IExceptionTranslatorRegistry() = default; +} diff --git a/include/internal/catch_interfaces_exception.h b/include/internal/catch_interfaces_exception.h index bf29b715..3473ff52 100644 --- a/include/internal/catch_interfaces_exception.h +++ b/include/internal/catch_interfaces_exception.h @@ -8,17 +8,22 @@ #ifndef TWOBLUECUBES_CATCH_INTERFACES_EXCEPTION_H_INCLUDED #define TWOBLUECUBES_CATCH_INTERFACES_EXCEPTION_H_INCLUDED +#include "catch_interfaces_registry_hub.h" + +#if defined(CATCH_CONFIG_DISABLE) + #define INTERNAL_CATCH_TRANSLATE_EXCEPTION_NO_REG( translatorName, signature) \ + static std::string translatorName( signature ) +#endif + +#include #include #include -#include "catch_interfaces_registry_hub.h" - namespace Catch { - - typedef std::string(*exceptionTranslateFunction)(); + using exceptionTranslateFunction = std::string(*)(); struct IExceptionTranslator; - typedef std::vector ExceptionTranslators; + using ExceptionTranslators = std::vector>; struct IExceptionTranslator { virtual ~IExceptionTranslator(); @@ -40,10 +45,10 @@ namespace Catch { : m_translateFunction( translateFunction ) {} - virtual std::string translate( ExceptionTranslators::const_iterator it, ExceptionTranslators::const_iterator itEnd ) const CATCH_OVERRIDE { + std::string translate( ExceptionTranslators::const_iterator it, ExceptionTranslators::const_iterator itEnd ) const override { try { if( it == itEnd ) - throw; + std::rethrow_exception(std::current_exception()); else return (*it)->translate( it+1, itEnd ); } diff --git a/include/internal/catch_interfaces_generators.h b/include/internal/catch_interfaces_generators.h deleted file mode 100644 index d163d5a1..00000000 --- a/include/internal/catch_interfaces_generators.h +++ /dev/null @@ -1,32 +0,0 @@ -/* - * Created by Phil on 7/8/2012. - * Copyright 2011 Two Blue Cubes Ltd. All rights reserved. - * - * Distributed under the Boost Software License, Version 1.0. (See accompanying - * file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) - */ -#ifndef TWOBLUECUBES_CATCH_INTERFACES_GENERATORS_H_INCLUDED -#define TWOBLUECUBES_CATCH_INTERFACES_GENERATORS_H_INCLUDED - -#include - -namespace Catch { - - struct IGeneratorInfo { - virtual ~IGeneratorInfo(); - virtual bool moveNext() = 0; - virtual std::size_t getCurrentIndex() const = 0; - }; - - struct IGeneratorsForTest { - virtual ~IGeneratorsForTest(); - - virtual IGeneratorInfo& getGeneratorInfo( std::string const& fileInfo, std::size_t size ) = 0; - virtual bool moveNext() = 0; - }; - - IGeneratorsForTest* createGeneratorsForTest(); - -} // end namespace Catch - -#endif // TWOBLUECUBES_CATCH_INTERFACES_GENERATORS_H_INCLUDED diff --git a/include/internal/catch_interfaces_registry_hub.cpp b/include/internal/catch_interfaces_registry_hub.cpp new file mode 100644 index 00000000..bd5b8082 --- /dev/null +++ b/include/internal/catch_interfaces_registry_hub.cpp @@ -0,0 +1,6 @@ +#include "internal/catch_interfaces_registry_hub.h" + +namespace Catch { + IRegistryHub::~IRegistryHub() = default; + IMutableRegistryHub::~IMutableRegistryHub() = default; +} diff --git a/include/internal/catch_interfaces_registry_hub.h b/include/internal/catch_interfaces_registry_hub.h index 5e0a7f01..1afbbec7 100644 --- a/include/internal/catch_interfaces_registry_hub.h +++ b/include/internal/catch_interfaces_registry_hub.h @@ -8,9 +8,10 @@ #ifndef TWOBLUECUBES_CATCH_INTERFACES_REGISTRY_HUB_H_INCLUDED #define TWOBLUECUBES_CATCH_INTERFACES_REGISTRY_HUB_H_INCLUDED -#include "catch_ptr.hpp" +#include "catch_common.h" #include +#include namespace Catch { @@ -21,6 +22,9 @@ namespace Catch { struct IReporterRegistry; struct IReporterFactory; struct ITagAliasRegistry; + class StartupExceptionRegistry; + + using IReporterFactoryPtr = std::shared_ptr; struct IRegistryHub { virtual ~IRegistryHub(); @@ -30,15 +34,19 @@ namespace Catch { virtual ITagAliasRegistry const& getTagAliasRegistry() const = 0; virtual IExceptionTranslatorRegistry& getExceptionTranslatorRegistry() = 0; + + + virtual StartupExceptionRegistry const& getStartupExceptionRegistry() const = 0; }; struct IMutableRegistryHub { virtual ~IMutableRegistryHub(); - virtual void registerReporter( std::string const& name, Ptr const& factory ) = 0; - virtual void registerListener( Ptr const& factory ) = 0; + virtual void registerReporter( std::string const& name, IReporterFactoryPtr const& factory ) = 0; + virtual void registerListener( IReporterFactoryPtr const& factory ) = 0; virtual void registerTest( TestCase const& testInfo ) = 0; virtual void registerTranslator( const IExceptionTranslator* translator ) = 0; virtual void registerTagAlias( std::string const& alias, std::string const& tag, SourceLineInfo const& lineInfo ) = 0; + virtual void registerStartupException() noexcept = 0; }; IRegistryHub& getRegistryHub(); diff --git a/include/internal/catch_interfaces_reporter.cpp b/include/internal/catch_interfaces_reporter.cpp new file mode 100644 index 00000000..129a5f4d --- /dev/null +++ b/include/internal/catch_interfaces_reporter.cpp @@ -0,0 +1,135 @@ +/* + * Created by Martin on 19/07/2017. + * + * Distributed under the Boost Software License, Version 1.0. (See accompanying + * file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) + */ + +#include "catch_interfaces_reporter.h" +#include "../reporters/catch_reporter_multi.h" + +namespace Catch { + + ReporterConfig::ReporterConfig( IConfigPtr const& _fullConfig ) + : m_stream( &_fullConfig->stream() ), m_fullConfig( _fullConfig ) {} + + ReporterConfig::ReporterConfig( IConfigPtr const& _fullConfig, std::ostream& _stream ) + : m_stream( &_stream ), m_fullConfig( _fullConfig ) {} + + std::ostream& ReporterConfig::stream() const { return *m_stream; } + IConfigPtr ReporterConfig::fullConfig() const { return m_fullConfig; } + + + TestRunInfo::TestRunInfo( std::string const& _name ) : name( _name ) {} + + GroupInfo::GroupInfo( std::string const& _name, + std::size_t _groupIndex, + std::size_t _groupsCount ) + : name( _name ), + groupIndex( _groupIndex ), + groupsCounts( _groupsCount ) + {} + + AssertionStats::AssertionStats( AssertionResult const& _assertionResult, + std::vector const& _infoMessages, + Totals const& _totals ) + : assertionResult( _assertionResult ), + infoMessages( _infoMessages ), + totals( _totals ) + { + assertionResult.m_resultData.lazyExpression.m_transientExpression = _assertionResult.m_resultData.lazyExpression.m_transientExpression; + + if( assertionResult.hasMessage() ) { + // Copy message into messages list. + // !TBD This should have been done earlier, somewhere + MessageBuilder builder( assertionResult.getTestMacroName(), assertionResult.getSourceInfo(), assertionResult.getResultType() ); + builder << assertionResult.getMessage(); + builder.m_info.message = builder.m_stream.str(); + + infoMessages.push_back( builder.m_info ); + } + } + + AssertionStats::~AssertionStats() = default; + + SectionStats::SectionStats( SectionInfo const& _sectionInfo, + Counts const& _assertions, + double _durationInSeconds, + bool _missingAssertions ) + : sectionInfo( _sectionInfo ), + assertions( _assertions ), + durationInSeconds( _durationInSeconds ), + missingAssertions( _missingAssertions ) + {} + + SectionStats::~SectionStats() = default; + + + TestCaseStats::TestCaseStats( TestCaseInfo const& _testInfo, + Totals const& _totals, + std::string const& _stdOut, + std::string const& _stdErr, + bool _aborting ) + : testInfo( _testInfo ), + totals( _totals ), + stdOut( _stdOut ), + stdErr( _stdErr ), + aborting( _aborting ) + {} + + TestCaseStats::~TestCaseStats() = default; + + + TestGroupStats::TestGroupStats( GroupInfo const& _groupInfo, + Totals const& _totals, + bool _aborting ) + : groupInfo( _groupInfo ), + totals( _totals ), + aborting( _aborting ) + {} + + TestGroupStats::TestGroupStats( GroupInfo const& _groupInfo ) + : groupInfo( _groupInfo ), + aborting( false ) + {} + + TestGroupStats::~TestGroupStats() = default; + + TestRunStats::TestRunStats( TestRunInfo const& _runInfo, + Totals const& _totals, + bool _aborting ) + : runInfo( _runInfo ), + totals( _totals ), + aborting( _aborting ) + {} + + TestRunStats::~TestRunStats() = default; + + void IStreamingReporter::fatalErrorEncountered( StringRef ) {} + bool IStreamingReporter::isMulti() const { return false; } + + IReporterFactory::~IReporterFactory() = default; + IReporterRegistry::~IReporterRegistry() = default; + + void addReporter( IStreamingReporterPtr& existingReporter, IStreamingReporterPtr&& additionalReporter ) { + + if( !existingReporter ) { + existingReporter = std::move( additionalReporter ); + return; + } + + MultipleReporters* multi = nullptr; + + if( existingReporter->isMulti() ) { + multi = static_cast( existingReporter.get() ); + } + else { + auto newMulti = std::unique_ptr( new MultipleReporters ); + newMulti->add( std::move( existingReporter ) ); + multi = newMulti.get(); + existingReporter = std::move( newMulti ); + } + multi->add( std::move( additionalReporter ) ); + } + +} // end namespace Catch diff --git a/include/internal/catch_interfaces_reporter.h b/include/internal/catch_interfaces_reporter.h index 6c47d8e4..b620abb6 100644 --- a/include/internal/catch_interfaces_reporter.h +++ b/include/internal/catch_interfaces_reporter.h @@ -10,46 +10,42 @@ #include "catch_section_info.h" #include "catch_common.h" -#include "catch_totals.hpp" -#include "catch_ptr.hpp" #include "catch_config.hpp" +#include "catch_totals.h" #include "catch_test_case_info.h" #include "catch_assertionresult.h" #include "catch_message.h" #include "catch_option.hpp" +#include "catch_stringref.h" + #include -#include +#include #include +#include +#include + +namespace Catch { -namespace Catch -{ struct ReporterConfig { - explicit ReporterConfig( Ptr const& _fullConfig ) - : m_stream( &_fullConfig->stream() ), m_fullConfig( _fullConfig ) {} + explicit ReporterConfig( IConfigPtr const& _fullConfig ); - ReporterConfig( Ptr const& _fullConfig, std::ostream& _stream ) - : m_stream( &_stream ), m_fullConfig( _fullConfig ) {} + ReporterConfig( IConfigPtr const& _fullConfig, std::ostream& _stream ); - std::ostream& stream() const { return *m_stream; } - Ptr fullConfig() const { return m_fullConfig; } + std::ostream& stream() const; + IConfigPtr fullConfig() const; private: std::ostream* m_stream; - Ptr m_fullConfig; + IConfigPtr m_fullConfig; }; struct ReporterPreferences { - ReporterPreferences() - : shouldRedirectStdOut( false ) - {} - - bool shouldRedirectStdOut; + bool shouldRedirectStdOut = false; }; template struct LazyStat : Option { - LazyStat() : used( false ) {} LazyStat& operator=( T const& _value ) { Option::operator=( _value ); used = false; @@ -59,21 +55,17 @@ namespace Catch Option::reset(); used = false; } - bool used; + bool used = false; }; struct TestRunInfo { - TestRunInfo( std::string const& _name ) : name( _name ) {} + TestRunInfo( std::string const& _name ); std::string name; }; struct GroupInfo { GroupInfo( std::string const& _name, std::size_t _groupIndex, - std::size_t _groupsCount ) - : name( _name ), - groupIndex( _groupIndex ), - groupsCounts( _groupsCount ) - {} + std::size_t _groupsCount ); std::string name; std::size_t groupIndex; @@ -83,29 +75,13 @@ namespace Catch struct AssertionStats { AssertionStats( AssertionResult const& _assertionResult, std::vector const& _infoMessages, - Totals const& _totals ) - : assertionResult( _assertionResult ), - infoMessages( _infoMessages ), - totals( _totals ) - { - if( assertionResult.hasMessage() ) { - // Copy message into messages list. - // !TBD This should have been done earlier, somewhere - MessageBuilder builder( assertionResult.getTestMacroName(), assertionResult.getSourceInfo(), assertionResult.getResultType() ); - builder << assertionResult.getMessage(); - builder.m_info.message = builder.m_stream.str(); + Totals const& _totals ); - infoMessages.push_back( builder.m_info ); - } - } - virtual ~AssertionStats(); - -# ifdef CATCH_CONFIG_CPP11_GENERATED_METHODS AssertionStats( AssertionStats const& ) = default; AssertionStats( AssertionStats && ) = default; AssertionStats& operator = ( AssertionStats const& ) = default; AssertionStats& operator = ( AssertionStats && ) = default; -# endif + virtual ~AssertionStats(); AssertionResult assertionResult; std::vector infoMessages; @@ -116,19 +92,12 @@ namespace Catch SectionStats( SectionInfo const& _sectionInfo, Counts const& _assertions, double _durationInSeconds, - bool _missingAssertions ) - : sectionInfo( _sectionInfo ), - assertions( _assertions ), - durationInSeconds( _durationInSeconds ), - missingAssertions( _missingAssertions ) - {} - virtual ~SectionStats(); -# ifdef CATCH_CONFIG_CPP11_GENERATED_METHODS + bool _missingAssertions ); SectionStats( SectionStats const& ) = default; SectionStats( SectionStats && ) = default; SectionStats& operator = ( SectionStats const& ) = default; SectionStats& operator = ( SectionStats && ) = default; -# endif + virtual ~SectionStats(); SectionInfo sectionInfo; Counts assertions; @@ -141,21 +110,13 @@ namespace Catch Totals const& _totals, std::string const& _stdOut, std::string const& _stdErr, - bool _aborting ) - : testInfo( _testInfo ), - totals( _totals ), - stdOut( _stdOut ), - stdErr( _stdErr ), - aborting( _aborting ) - {} - virtual ~TestCaseStats(); + bool _aborting ); -# ifdef CATCH_CONFIG_CPP11_GENERATED_METHODS TestCaseStats( TestCaseStats const& ) = default; TestCaseStats( TestCaseStats && ) = default; TestCaseStats& operator = ( TestCaseStats const& ) = default; TestCaseStats& operator = ( TestCaseStats && ) = default; -# endif + virtual ~TestCaseStats(); TestCaseInfo testInfo; Totals totals; @@ -167,23 +128,14 @@ namespace Catch struct TestGroupStats { TestGroupStats( GroupInfo const& _groupInfo, Totals const& _totals, - bool _aborting ) - : groupInfo( _groupInfo ), - totals( _totals ), - aborting( _aborting ) - {} - TestGroupStats( GroupInfo const& _groupInfo ) - : groupInfo( _groupInfo ), - aborting( false ) - {} - virtual ~TestGroupStats(); + bool _aborting ); + TestGroupStats( GroupInfo const& _groupInfo ); -# ifdef CATCH_CONFIG_CPP11_GENERATED_METHODS TestGroupStats( TestGroupStats const& ) = default; TestGroupStats( TestGroupStats && ) = default; TestGroupStats& operator = ( TestGroupStats const& ) = default; TestGroupStats& operator = ( TestGroupStats && ) = default; -# endif + virtual ~TestGroupStats(); GroupInfo groupInfo; Totals totals; @@ -193,38 +145,34 @@ namespace Catch struct TestRunStats { TestRunStats( TestRunInfo const& _runInfo, Totals const& _totals, - bool _aborting ) - : runInfo( _runInfo ), - totals( _totals ), - aborting( _aborting ) - {} - virtual ~TestRunStats(); + bool _aborting ); -# ifndef CATCH_CONFIG_CPP11_GENERATED_METHODS - TestRunStats( TestRunStats const& _other ) - : runInfo( _other.runInfo ), - totals( _other.totals ), - aborting( _other.aborting ) - {} -# else TestRunStats( TestRunStats const& ) = default; TestRunStats( TestRunStats && ) = default; TestRunStats& operator = ( TestRunStats const& ) = default; TestRunStats& operator = ( TestRunStats && ) = default; -# endif + virtual ~TestRunStats(); TestRunInfo runInfo; Totals totals; bool aborting; }; - class MultipleReporters; + struct BenchmarkInfo { + std::string name; + }; + struct BenchmarkStats { + BenchmarkInfo info; + std::size_t iterations; + uint64_t elapsedTimeInNanoseconds; + }; - struct IStreamingReporter : IShared { - virtual ~IStreamingReporter(); + struct IStreamingReporter { + virtual ~IStreamingReporter() = default; - // Implementing class must also provide the following static method: + // Implementing class must also provide the following static methods: // static std::string getDescription(); + // static std::set getSupportedVerbosities() virtual ReporterPreferences getPreferences() const = 0; @@ -236,11 +184,17 @@ namespace Catch virtual void testCaseStarting( TestCaseInfo const& testInfo ) = 0; virtual void sectionStarting( SectionInfo const& sectionInfo ) = 0; + // *** experimental *** + virtual void benchmarkStarting( BenchmarkInfo const& ) {} + virtual void assertionStarting( AssertionInfo const& assertionInfo ) = 0; // The return value indicates if the messages buffer should be cleared: virtual bool assertionEnded( AssertionStats const& assertionStats ) = 0; + // *** experimental *** + virtual void benchmarkEnded( BenchmarkStats const& ) {} + virtual void sectionEnded( SectionStats const& sectionStats ) = 0; virtual void testCaseEnded( TestCaseStats const& testCaseStats ) = 0; virtual void testGroupEnded( TestGroupStats const& testGroupStats ) = 0; @@ -248,28 +202,32 @@ namespace Catch virtual void skipTest( TestCaseInfo const& testInfo ) = 0; - virtual MultipleReporters* tryAsMulti() { return CATCH_NULL; } + // Default empty implementation provided + virtual void fatalErrorEncountered( StringRef name ); + + virtual bool isMulti() const; }; + using IStreamingReporterPtr = std::unique_ptr; - - struct IReporterFactory : IShared { + struct IReporterFactory { virtual ~IReporterFactory(); - virtual IStreamingReporter* create( ReporterConfig const& config ) const = 0; + virtual IStreamingReporterPtr create( ReporterConfig const& config ) const = 0; virtual std::string getDescription() const = 0; }; + using IReporterFactoryPtr = std::shared_ptr; struct IReporterRegistry { - typedef std::map > FactoryMap; - typedef std::vector > Listeners; + using FactoryMap = std::map; + using Listeners = std::vector; virtual ~IReporterRegistry(); - virtual IStreamingReporter* create( std::string const& name, Ptr const& config ) const = 0; + virtual IStreamingReporterPtr create( std::string const& name, IConfigPtr const& config ) const = 0; virtual FactoryMap const& getFactories() const = 0; virtual Listeners const& getListeners() const = 0; }; - Ptr addReporter( Ptr const& existingReporter, Ptr const& additionalReporter ); + void addReporter( IStreamingReporterPtr& existingReporter, IStreamingReporterPtr&& additionalReporter ); -} +} // end namespace Catch #endif // TWOBLUECUBES_CATCH_INTERFACES_REPORTER_H_INCLUDED diff --git a/include/internal/catch_interfaces_runner.cpp b/include/internal/catch_interfaces_runner.cpp new file mode 100644 index 00000000..2b052eb8 --- /dev/null +++ b/include/internal/catch_interfaces_runner.cpp @@ -0,0 +1,5 @@ +#include "internal/catch_interfaces_runner.h" + +namespace Catch { + IRunner::~IRunner() = default; +} diff --git a/include/internal/catch_interfaces_runner.h b/include/internal/catch_interfaces_runner.h index 25decfb0..a0deaf7e 100644 --- a/include/internal/catch_interfaces_runner.h +++ b/include/internal/catch_interfaces_runner.h @@ -9,7 +9,6 @@ #define TWOBLUECUBES_CATCH_INTERFACES_RUNNER_H_INCLUDED namespace Catch { - class TestCase; struct IRunner { virtual ~IRunner(); diff --git a/include/internal/catch_interfaces_tag_alias_registry.h b/include/internal/catch_interfaces_tag_alias_registry.h index cd6ac51d..24bc535c 100644 --- a/include/internal/catch_interfaces_tag_alias_registry.h +++ b/include/internal/catch_interfaces_tag_alias_registry.h @@ -8,14 +8,16 @@ #ifndef TWOBLUECUBES_CATCH_INTERFACES_TAG_ALIAS_REGISTRY_H_INCLUDED #define TWOBLUECUBES_CATCH_INTERFACES_TAG_ALIAS_REGISTRY_H_INCLUDED -#include "catch_tag_alias.h" -#include "catch_option.hpp" +#include namespace Catch { + struct TagAlias; + struct ITagAliasRegistry { virtual ~ITagAliasRegistry(); - virtual Option find( std::string const& alias ) const = 0; + // Nullptr if not present + virtual TagAlias const* find( std::string const& alias ) const = 0; virtual std::string expandAliases( std::string const& unexpandedTestSpec ) const = 0; static ITagAliasRegistry const& get(); diff --git a/include/internal/catch_interfaces_testcase.cpp b/include/internal/catch_interfaces_testcase.cpp new file mode 100644 index 00000000..35c3db08 --- /dev/null +++ b/include/internal/catch_interfaces_testcase.cpp @@ -0,0 +1,6 @@ +#include "internal/catch_interfaces_testcase.h" + +namespace Catch { + ITestInvoker::~ITestInvoker() = default; + ITestCaseRegistry::~ITestCaseRegistry() = default; +} diff --git a/include/internal/catch_interfaces_testcase.h b/include/internal/catch_interfaces_testcase.h index a1052b71..9e02b14f 100644 --- a/include/internal/catch_interfaces_testcase.h +++ b/include/internal/catch_interfaces_testcase.h @@ -8,20 +8,20 @@ #ifndef TWOBLUECUBES_CATCH_INTERFACES_TESTCASE_H_INCLUDED #define TWOBLUECUBES_CATCH_INTERFACES_TESTCASE_H_INCLUDED -#include "catch_ptr.hpp" - #include +#include namespace Catch { class TestSpec; - struct ITestCase : IShared { + struct ITestInvoker { virtual void invoke () const = 0; - protected: - virtual ~ITestCase(); + virtual ~ITestInvoker(); }; + using ITestCasePtr = std::shared_ptr; + class TestCase; struct IConfig; diff --git a/include/internal/catch_leak_detector.cpp b/include/internal/catch_leak_detector.cpp new file mode 100644 index 00000000..32109310 --- /dev/null +++ b/include/internal/catch_leak_detector.cpp @@ -0,0 +1,33 @@ +/* + * Created by Martin on 12/07/2017. + * + * Distributed under the Boost Software License, Version 1.0. (See accompanying + * file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) + */ + + #include "catch_leak_detector.h" + + +namespace Catch { + +#ifdef CATCH_CONFIG_WINDOWS_CRTDBG +#include + + LeakDetector::LeakDetector() { + int flag = _CrtSetDbgFlag(_CRTDBG_REPORT_FLAG); + flag |= _CRTDBG_LEAK_CHECK_DF; + flag |= _CRTDBG_ALLOC_MEM_DF; + _CrtSetDbgFlag(flag); + _CrtSetReportMode(_CRT_WARN, _CRTDBG_MODE_FILE | _CRTDBG_MODE_DEBUG); + _CrtSetReportFile(_CRT_WARN, _CRTDBG_FILE_STDERR); + // Change this to leaking allocation's number to break there + _CrtSetBreakAlloc(-1); + } + +#else + + LeakDetector::LeakDetector(){} + +#endif + +} diff --git a/include/internal/catch_leak_detector.h b/include/internal/catch_leak_detector.h new file mode 100644 index 00000000..bfb0b429 --- /dev/null +++ b/include/internal/catch_leak_detector.h @@ -0,0 +1,17 @@ +/* + * Created by Martin on 12/07/2017. + * + * Distributed under the Boost Software License, Version 1.0. (See accompanying + * file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) + */ +#ifndef TWOBLUECUBES_CATCH_LEAK_DETECTOR_H_INCLUDED +#define TWOBLUECUBES_CATCH_LEAK_DETECTOR_H_INCLUDED + +namespace Catch { + + struct LeakDetector { + LeakDetector(); + }; + +} +#endif // TWOBLUECUBES_CATCH_LEAK_DETECTOR_H_INCLUDED diff --git a/include/internal/catch_legacy_reporter_adapter.h b/include/internal/catch_legacy_reporter_adapter.h deleted file mode 100644 index 72c43d7d..00000000 --- a/include/internal/catch_legacy_reporter_adapter.h +++ /dev/null @@ -1,60 +0,0 @@ -/* - * Created by Phil on 6th April 2013. - * Copyright 2013 Two Blue Cubes Ltd. All rights reserved. - * - * Distributed under the Boost Software License, Version 1.0. (See accompanying - * file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) - */ -#ifndef TWOBLUECUBES_CATCH_LEGACY_REPORTER_ADAPTER_H_INCLUDED -#define TWOBLUECUBES_CATCH_LEGACY_REPORTER_ADAPTER_H_INCLUDED - -#include "catch_interfaces_reporter.h" - -namespace Catch -{ - // Deprecated - struct IReporter : IShared { - virtual ~IReporter(); - - virtual bool shouldRedirectStdout() const = 0; - - virtual void StartTesting() = 0; - virtual void EndTesting( Totals const& totals ) = 0; - virtual void StartGroup( std::string const& groupName ) = 0; - virtual void EndGroup( std::string const& groupName, Totals const& totals ) = 0; - virtual void StartTestCase( TestCaseInfo const& testInfo ) = 0; - virtual void EndTestCase( TestCaseInfo const& testInfo, Totals const& totals, std::string const& stdOut, std::string const& stdErr ) = 0; - virtual void StartSection( std::string const& sectionName, std::string const& description ) = 0; - virtual void EndSection( std::string const& sectionName, Counts const& assertions ) = 0; - virtual void NoAssertionsInSection( std::string const& sectionName ) = 0; - virtual void NoAssertionsInTestCase( std::string const& testName ) = 0; - virtual void Aborted() = 0; - virtual void Result( AssertionResult const& result ) = 0; - }; - - class LegacyReporterAdapter : public SharedImpl - { - public: - LegacyReporterAdapter( Ptr const& legacyReporter ); - virtual ~LegacyReporterAdapter(); - - virtual ReporterPreferences getPreferences() const; - virtual void noMatchingTestCases( std::string const& ); - virtual void testRunStarting( TestRunInfo const& ); - virtual void testGroupStarting( GroupInfo const& groupInfo ); - virtual void testCaseStarting( TestCaseInfo const& testInfo ); - virtual void sectionStarting( SectionInfo const& sectionInfo ); - virtual void assertionStarting( AssertionInfo const& ); - virtual bool assertionEnded( AssertionStats const& assertionStats ); - virtual void sectionEnded( SectionStats const& sectionStats ); - virtual void testCaseEnded( TestCaseStats const& testCaseStats ); - virtual void testGroupEnded( TestGroupStats const& testGroupStats ); - virtual void testRunEnded( TestRunStats const& testRunStats ); - virtual void skipTest( TestCaseInfo const& ); - - private: - Ptr m_legacyReporter; - }; -} - -#endif // TWOBLUECUBES_CATCH_LEGACY_REPORTER_ADAPTER_H_INCLUDED diff --git a/include/internal/catch_legacy_reporter_adapter.hpp b/include/internal/catch_legacy_reporter_adapter.hpp deleted file mode 100644 index 6034581e..00000000 --- a/include/internal/catch_legacy_reporter_adapter.hpp +++ /dev/null @@ -1,84 +0,0 @@ -/* - * Created by Phil on 6th April 2013. - * Copyright 2013 Two Blue Cubes Ltd. All rights reserved. - * - * Distributed under the Boost Software License, Version 1.0. (See accompanying - * file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) - */ -#ifndef TWOBLUECUBES_CATCH_LEGACY_REPORTER_ADAPTER_HPP_INCLUDED -#define TWOBLUECUBES_CATCH_LEGACY_REPORTER_ADAPTER_HPP_INCLUDED - -#include "catch_legacy_reporter_adapter.h" - -namespace Catch -{ - LegacyReporterAdapter::LegacyReporterAdapter( Ptr const& legacyReporter ) - : m_legacyReporter( legacyReporter ) - {} - LegacyReporterAdapter::~LegacyReporterAdapter() {} - - ReporterPreferences LegacyReporterAdapter::getPreferences() const { - ReporterPreferences prefs; - prefs.shouldRedirectStdOut = m_legacyReporter->shouldRedirectStdout(); - return prefs; - } - - void LegacyReporterAdapter::noMatchingTestCases( std::string const& ) {} - void LegacyReporterAdapter::testRunStarting( TestRunInfo const& ) { - m_legacyReporter->StartTesting(); - } - void LegacyReporterAdapter::testGroupStarting( GroupInfo const& groupInfo ) { - m_legacyReporter->StartGroup( groupInfo.name ); - } - void LegacyReporterAdapter::testCaseStarting( TestCaseInfo const& testInfo ) { - m_legacyReporter->StartTestCase( testInfo ); - } - void LegacyReporterAdapter::sectionStarting( SectionInfo const& sectionInfo ) { - m_legacyReporter->StartSection( sectionInfo.name, sectionInfo.description ); - } - void LegacyReporterAdapter::assertionStarting( AssertionInfo const& ) { - // Not on legacy interface - } - - bool LegacyReporterAdapter::assertionEnded( AssertionStats const& assertionStats ) { - if( assertionStats.assertionResult.getResultType() != ResultWas::Ok ) { - for( std::vector::const_iterator it = assertionStats.infoMessages.begin(), itEnd = assertionStats.infoMessages.end(); - it != itEnd; - ++it ) { - if( it->type == ResultWas::Info ) { - ResultBuilder rb( it->macroName.c_str(), it->lineInfo, "", ResultDisposition::Normal ); - rb << it->message; - rb.setResultType( ResultWas::Info ); - AssertionResult result = rb.build(); - m_legacyReporter->Result( result ); - } - } - } - m_legacyReporter->Result( assertionStats.assertionResult ); - return true; - } - void LegacyReporterAdapter::sectionEnded( SectionStats const& sectionStats ) { - if( sectionStats.missingAssertions ) - m_legacyReporter->NoAssertionsInSection( sectionStats.sectionInfo.name ); - m_legacyReporter->EndSection( sectionStats.sectionInfo.name, sectionStats.assertions ); - } - void LegacyReporterAdapter::testCaseEnded( TestCaseStats const& testCaseStats ) { - m_legacyReporter->EndTestCase - ( testCaseStats.testInfo, - testCaseStats.totals, - testCaseStats.stdOut, - testCaseStats.stdErr ); - } - void LegacyReporterAdapter::testGroupEnded( TestGroupStats const& testGroupStats ) { - if( testGroupStats.aborting ) - m_legacyReporter->Aborted(); - m_legacyReporter->EndGroup( testGroupStats.groupInfo.name, testGroupStats.totals ); - } - void LegacyReporterAdapter::testRunEnded( TestRunStats const& testRunStats ) { - m_legacyReporter->EndTesting( testRunStats.totals ); - } - void LegacyReporterAdapter::skipTest( TestCaseInfo const& ) { - } -} - -#endif // TWOBLUECUBES_CATCH_LEGACY_REPORTER_ADAPTER_H_INCLUDED diff --git a/include/internal/catch_list.cpp b/include/internal/catch_list.cpp new file mode 100644 index 00000000..4391c441 --- /dev/null +++ b/include/internal/catch_list.cpp @@ -0,0 +1,165 @@ +/* + * Created by Phil on 5/11/2010. + * Copyright 2010 Two Blue Cubes Ltd. All rights reserved. + * + * Distributed under the Boost Software License, Version 1.0. (See accompanying + * file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) + */ + +#include "catch_list.h" + +#include "catch_interfaces_registry_hub.h" +#include "catch_interfaces_reporter.h" +#include "catch_interfaces_testcase.h" + +#include "catch_stream.h" +#include "catch_text.h" + +#include "catch_console_colour.h" +#include "catch_test_spec_parser.h" +#include "catch_tostring.h" +#include "catch_string_manip.h" + +#include +#include +#include + +namespace Catch { + + std::size_t listTests( Config const& config ) { + TestSpec testSpec = config.testSpec(); + if( config.testSpec().hasFilters() ) + Catch::cout() << "Matching test cases:\n"; + else { + Catch::cout() << "All available test cases:\n"; + testSpec = TestSpecParser( ITagAliasRegistry::get() ).parse( "*" ).testSpec(); + } + + auto matchedTestCases = filterTests( getAllTestCasesSorted( config ), testSpec, config ); + for( auto const& testCaseInfo : matchedTestCases ) { + Colour::Code colour = testCaseInfo.isHidden() + ? Colour::SecondaryText + : Colour::None; + Colour colourGuard( colour ); + + Catch::cout() << Column( testCaseInfo.name ).initialIndent( 2 ).indent( 4 ) << "\n"; + if( config.verbosity() >= Verbosity::High ) { + Catch::cout() << Column( Catch::Detail::stringify( testCaseInfo.lineInfo ) ).indent(4) << std::endl; + std::string description = testCaseInfo.description; + if( description.empty() ) + description = "(NO DESCRIPTION)"; + Catch::cout() << Column( description ).indent(4) << std::endl; + } + if( !testCaseInfo.tags.empty() ) + Catch::cout() << Column( testCaseInfo.tagsAsString() ).indent( 6 ) << "\n"; + } + + if( !config.testSpec().hasFilters() ) + Catch::cout() << pluralise( matchedTestCases.size(), "test case" ) << '\n' << std::endl; + else + Catch::cout() << pluralise( matchedTestCases.size(), "matching test case" ) << '\n' << std::endl; + return matchedTestCases.size(); + } + + std::size_t listTestsNamesOnly( Config const& config ) { + TestSpec testSpec = config.testSpec(); + if( !config.testSpec().hasFilters() ) + testSpec = TestSpecParser( ITagAliasRegistry::get() ).parse( "*" ).testSpec(); + std::size_t matchedTests = 0; + std::vector matchedTestCases = filterTests( getAllTestCasesSorted( config ), testSpec, config ); + for( auto const& testCaseInfo : matchedTestCases ) { + matchedTests++; + if( startsWith( testCaseInfo.name, '#' ) ) + Catch::cout() << '"' << testCaseInfo.name << '"'; + else + Catch::cout() << testCaseInfo.name; + if ( config.verbosity() >= Verbosity::High ) + Catch::cout() << "\t@" << testCaseInfo.lineInfo; + Catch::cout() << std::endl; + } + return matchedTests; + } + + void TagInfo::add( std::string const& spelling ) { + ++count; + spellings.insert( spelling ); + } + + std::string TagInfo::all() const { + std::string out; + for( auto const& spelling : spellings ) + out += "[" + spelling + "]"; + return out; + } + + std::size_t listTags( Config const& config ) { + TestSpec testSpec = config.testSpec(); + if( config.testSpec().hasFilters() ) + Catch::cout() << "Tags for matching test cases:\n"; + else { + Catch::cout() << "All available tags:\n"; + testSpec = TestSpecParser( ITagAliasRegistry::get() ).parse( "*" ).testSpec(); + } + + std::map tagCounts; + + std::vector matchedTestCases = filterTests( getAllTestCasesSorted( config ), testSpec, config ); + for( auto const& testCase : matchedTestCases ) { + for( auto const& tagName : testCase.getTestCaseInfo().tags ) { + std::string lcaseTagName = toLower( tagName ); + auto countIt = tagCounts.find( lcaseTagName ); + if( countIt == tagCounts.end() ) + countIt = tagCounts.insert( std::make_pair( lcaseTagName, TagInfo() ) ).first; + countIt->second.add( tagName ); + } + } + + for( auto const& tagCount : tagCounts ) { + std::ostringstream oss; + oss << " " << std::setw(2) << tagCount.second.count << " "; + auto wrapper = Column( tagCount.second.all() ) + .initialIndent( 0 ) + .indent( oss.str().size() ) + .width( CATCH_CONFIG_CONSOLE_WIDTH-10 ); + Catch::cout() << oss.str() << wrapper << '\n'; + } + Catch::cout() << pluralise( tagCounts.size(), "tag" ) << '\n' << std::endl; + return tagCounts.size(); + } + + std::size_t listReporters( Config const& /*config*/ ) { + Catch::cout() << "Available reporters:\n"; + IReporterRegistry::FactoryMap const& factories = getRegistryHub().getReporterRegistry().getFactories(); + std::size_t maxNameLen = 0; + for( auto const& factoryKvp : factories ) + maxNameLen = (std::max)( maxNameLen, factoryKvp.first.size() ); + + for( auto const& factoryKvp : factories ) { + Catch::cout() + << Column( factoryKvp.first + ":" ) + .indent(2) + .width( 5+maxNameLen ) + + Column( factoryKvp.second->getDescription() ) + .initialIndent(0) + .indent(2) + .width( CATCH_CONFIG_CONSOLE_WIDTH - maxNameLen-8 ) + << "\n"; + } + Catch::cout() << std::endl; + return factories.size(); + } + + Option list( Config const& config ) { + Option listedCount; + if( config.listTests() ) + listedCount = listedCount.valueOr(0) + listTests( config ); + if( config.listTestNamesOnly() ) + listedCount = listedCount.valueOr(0) + listTestsNamesOnly( config ); + if( config.listTags() ) + listedCount = listedCount.valueOr(0) + listTags( config ); + if( config.listReporters() ) + listedCount = listedCount.valueOr(0) + listReporters( config ); + return listedCount; + } + +} // end namespace Catch diff --git a/include/internal/catch_list.h b/include/internal/catch_list.h new file mode 100644 index 00000000..4bc96ec5 --- /dev/null +++ b/include/internal/catch_list.h @@ -0,0 +1,38 @@ +/* + * Created by Phil on 5/11/2010. + * Copyright 2010 Two Blue Cubes Ltd. All rights reserved. + * + * Distributed under the Boost Software License, Version 1.0. (See accompanying + * file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) + */ +#ifndef TWOBLUECUBES_CATCH_LIST_H_INCLUDED +#define TWOBLUECUBES_CATCH_LIST_H_INCLUDED + +#include "catch_option.hpp" +#include "catch_config.hpp" + +#include + +namespace Catch { + + std::size_t listTests( Config const& config ); + + std::size_t listTestsNamesOnly( Config const& config ); + + struct TagInfo { + void add( std::string const& spelling ); + std::string all() const; + + std::set spellings; + std::size_t count = 0; + }; + + std::size_t listTags( Config const& config ); + + std::size_t listReporters( Config const& /*config*/ ); + + Option list( Config const& config ); + +} // end namespace Catch + +#endif // TWOBLUECUBES_CATCH_LIST_H_INCLUDED diff --git a/include/internal/catch_list.hpp b/include/internal/catch_list.hpp deleted file mode 100644 index 8a02611a..00000000 --- a/include/internal/catch_list.hpp +++ /dev/null @@ -1,190 +0,0 @@ -/* - * Created by Phil on 5/11/2010. - * Copyright 2010 Two Blue Cubes Ltd. All rights reserved. - * - * Distributed under the Boost Software License, Version 1.0. (See accompanying - * file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) - */ -#ifndef TWOBLUECUBES_CATCH_LIST_HPP_INCLUDED -#define TWOBLUECUBES_CATCH_LIST_HPP_INCLUDED - -#include "catch_commandline.hpp" -#include "catch_text.h" -#include "catch_console_colour.hpp" -#include "catch_interfaces_reporter.h" -#include "catch_test_spec_parser.hpp" - -#include -#include - -namespace Catch { - - inline std::size_t listTests( Config const& config ) { - - TestSpec testSpec = config.testSpec(); - if( config.testSpec().hasFilters() ) - Catch::cout() << "Matching test cases:\n"; - else { - Catch::cout() << "All available test cases:\n"; - testSpec = TestSpecParser( ITagAliasRegistry::get() ).parse( "*" ).testSpec(); - } - - std::size_t matchedTests = 0; - TextAttributes nameAttr, descAttr, tagsAttr; - nameAttr.setInitialIndent( 2 ).setIndent( 4 ); - descAttr.setIndent( 4 ); - tagsAttr.setIndent( 6 ); - - std::vector matchedTestCases = filterTests( getAllTestCasesSorted( config ), testSpec, config ); - for( std::vector::const_iterator it = matchedTestCases.begin(), itEnd = matchedTestCases.end(); - it != itEnd; - ++it ) { - matchedTests++; - TestCaseInfo const& testCaseInfo = it->getTestCaseInfo(); - Colour::Code colour = testCaseInfo.isHidden() - ? Colour::SecondaryText - : Colour::None; - Colour colourGuard( colour ); - - Catch::cout() << Text( testCaseInfo.name, nameAttr ) << std::endl; - if( config.listExtraInfo() ) { - Catch::cout() << " " << testCaseInfo.lineInfo << std::endl; - std::string description = testCaseInfo.description; - if( description.empty() ) - description = "(NO DESCRIPTION)"; - Catch::cout() << Text( description, descAttr ) << std::endl; - } - if( !testCaseInfo.tags.empty() ) - Catch::cout() << Text( testCaseInfo.tagsAsString, tagsAttr ) << std::endl; - } - - if( !config.testSpec().hasFilters() ) - Catch::cout() << pluralise( matchedTests, "test case" ) << '\n' << std::endl; - else - Catch::cout() << pluralise( matchedTests, "matching test case" ) << '\n' << std::endl; - return matchedTests; - } - - inline std::size_t listTestsNamesOnly( Config const& config ) { - TestSpec testSpec = config.testSpec(); - if( !config.testSpec().hasFilters() ) - testSpec = TestSpecParser( ITagAliasRegistry::get() ).parse( "*" ).testSpec(); - std::size_t matchedTests = 0; - std::vector matchedTestCases = filterTests( getAllTestCasesSorted( config ), testSpec, config ); - for( std::vector::const_iterator it = matchedTestCases.begin(), itEnd = matchedTestCases.end(); - it != itEnd; - ++it ) { - matchedTests++; - TestCaseInfo const& testCaseInfo = it->getTestCaseInfo(); - if( startsWith( testCaseInfo.name, '#' ) ) - Catch::cout() << '"' << testCaseInfo.name << '"'; - else - Catch::cout() << testCaseInfo.name; - if ( config.listExtraInfo() ) - Catch::cout() << "\t@" << testCaseInfo.lineInfo; - Catch::cout() << std::endl; - } - return matchedTests; - } - - struct TagInfo { - TagInfo() : count ( 0 ) {} - void add( std::string const& spelling ) { - ++count; - spellings.insert( spelling ); - } - std::string all() const { - std::string out; - for( std::set::const_iterator it = spellings.begin(), itEnd = spellings.end(); - it != itEnd; - ++it ) - out += "[" + *it + "]"; - return out; - } - std::set spellings; - std::size_t count; - }; - - inline std::size_t listTags( Config const& config ) { - TestSpec testSpec = config.testSpec(); - if( config.testSpec().hasFilters() ) - Catch::cout() << "Tags for matching test cases:\n"; - else { - Catch::cout() << "All available tags:\n"; - testSpec = TestSpecParser( ITagAliasRegistry::get() ).parse( "*" ).testSpec(); - } - - std::map tagCounts; - - std::vector matchedTestCases = filterTests( getAllTestCasesSorted( config ), testSpec, config ); - for( std::vector::const_iterator it = matchedTestCases.begin(), itEnd = matchedTestCases.end(); - it != itEnd; - ++it ) { - for( std::set::const_iterator tagIt = it->getTestCaseInfo().tags.begin(), - tagItEnd = it->getTestCaseInfo().tags.end(); - tagIt != tagItEnd; - ++tagIt ) { - std::string tagName = *tagIt; - std::string lcaseTagName = toLower( tagName ); - std::map::iterator countIt = tagCounts.find( lcaseTagName ); - if( countIt == tagCounts.end() ) - countIt = tagCounts.insert( std::make_pair( lcaseTagName, TagInfo() ) ).first; - countIt->second.add( tagName ); - } - } - - for( std::map::const_iterator countIt = tagCounts.begin(), - countItEnd = tagCounts.end(); - countIt != countItEnd; - ++countIt ) { - std::ostringstream oss; - oss << " " << std::setw(2) << countIt->second.count << " "; - Text wrapper( countIt->second.all(), TextAttributes() - .setInitialIndent( 0 ) - .setIndent( oss.str().size() ) - .setWidth( CATCH_CONFIG_CONSOLE_WIDTH-10 ) ); - Catch::cout() << oss.str() << wrapper << '\n'; - } - Catch::cout() << pluralise( tagCounts.size(), "tag" ) << '\n' << std::endl; - return tagCounts.size(); - } - - inline std::size_t listReporters( Config const& /*config*/ ) { - Catch::cout() << "Available reporters:\n"; - IReporterRegistry::FactoryMap const& factories = getRegistryHub().getReporterRegistry().getFactories(); - IReporterRegistry::FactoryMap::const_iterator itBegin = factories.begin(), itEnd = factories.end(), it; - std::size_t maxNameLen = 0; - for(it = itBegin; it != itEnd; ++it ) - maxNameLen = (std::max)( maxNameLen, it->first.size() ); - - for(it = itBegin; it != itEnd; ++it ) { - Text wrapper( it->second->getDescription(), TextAttributes() - .setInitialIndent( 0 ) - .setIndent( 7+maxNameLen ) - .setWidth( CATCH_CONFIG_CONSOLE_WIDTH - maxNameLen-8 ) ); - Catch::cout() << " " - << it->first - << ':' - << std::string( maxNameLen - it->first.size() + 2, ' ' ) - << wrapper << '\n'; - } - Catch::cout() << std::endl; - return factories.size(); - } - - inline Option list( Config const& config ) { - Option listedCount; - if( config.listTests() || ( config.listExtraInfo() && !config.listTestNamesOnly() ) ) - listedCount = listedCount.valueOr(0) + listTests( config ); - if( config.listTestNamesOnly() ) - listedCount = listedCount.valueOr(0) + listTestsNamesOnly( config ); - if( config.listTags() ) - listedCount = listedCount.valueOr(0) + listTags( config ); - if( config.listReporters() ) - listedCount = listedCount.valueOr(0) + listReporters( config ); - return listedCount; - } - -} // end namespace Catch - -#endif // TWOBLUECUBES_CATCH_LIST_HPP_INCLUDED diff --git a/include/internal/catch_matchers.cpp b/include/internal/catch_matchers.cpp new file mode 100644 index 00000000..32104a11 --- /dev/null +++ b/include/internal/catch_matchers.cpp @@ -0,0 +1,28 @@ +/* + * Created by Phil Nash on 19/07/2017. + * + * Distributed under the Boost Software License, Version 1.0. (See accompanying + * file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) + */ + +#include "catch_matchers.h" + +namespace Catch { +namespace Matchers { + namespace Impl { + + std::string MatcherUntypedBase::toString() const { + if( m_cachedToString.empty() ) + m_cachedToString = describe(); + return m_cachedToString; + } + + MatcherUntypedBase::~MatcherUntypedBase() = default; + + } // namespace Impl +} // namespace Matchers + +using namespace Matchers; +using Matchers::Impl::MatcherBase; + +} // namespace Catch diff --git a/include/internal/catch_matchers.hpp b/include/internal/catch_matchers.h similarity index 64% rename from include/internal/catch_matchers.hpp rename to include/internal/catch_matchers.h index 7c00e9c2..8c15d425 100644 --- a/include/internal/catch_matchers.hpp +++ b/include/internal/catch_matchers.h @@ -10,6 +10,9 @@ #include "catch_common.h" +#include +#include + namespace Catch { namespace Matchers { namespace Impl { @@ -20,18 +23,15 @@ namespace Matchers { class MatcherUntypedBase { public: - std::string toString() const { - if( m_cachedToString.empty() ) - m_cachedToString = describe(); - return m_cachedToString; - } + MatcherUntypedBase() = default; + MatcherUntypedBase ( MatcherUntypedBase const& ) = default; + MatcherUntypedBase& operator = ( MatcherUntypedBase const& ) = delete; + std::string toString() const; protected: virtual ~MatcherUntypedBase(); virtual std::string describe() const = 0; mutable std::string m_cachedToString; - private: - MatcherUntypedBase& operator = ( MatcherUntypedBase const& ); }; template @@ -54,21 +54,24 @@ namespace Matchers { template struct MatchAllOf : MatcherBase { - virtual bool match( ArgT const& arg ) const CATCH_OVERRIDE { - for( std::size_t i = 0; i < m_matchers.size(); ++i ) { - if (!m_matchers[i]->match(arg)) + bool match( ArgT const& arg ) const override { + for( auto matcher : m_matchers ) { + if (!matcher->match(arg)) return false; } return true; } - virtual std::string describe() const CATCH_OVERRIDE { + std::string describe() const override { std::string description; description.reserve( 4 + m_matchers.size()*32 ); description += "( "; - for( std::size_t i = 0; i < m_matchers.size(); ++i ) { - if( i != 0 ) + bool first = true; + for( auto matcher : m_matchers ) { + if( first ) + first = false; + else description += " and "; - description += m_matchers[i]->toString(); + description += matcher->toString(); } description += " )"; return description; @@ -84,21 +87,24 @@ namespace Matchers { template struct MatchAnyOf : MatcherBase { - virtual bool match( ArgT const& arg ) const CATCH_OVERRIDE { - for( std::size_t i = 0; i < m_matchers.size(); ++i ) { - if (m_matchers[i]->match(arg)) + bool match( ArgT const& arg ) const override { + for( auto matcher : m_matchers ) { + if (matcher->match(arg)) return true; } return false; } - virtual std::string describe() const CATCH_OVERRIDE { + std::string describe() const override { std::string description; description.reserve( 4 + m_matchers.size()*32 ); description += "( "; - for( std::size_t i = 0; i < m_matchers.size(); ++i ) { - if( i != 0 ) + bool first = true; + for( auto matcher : m_matchers ) { + if( first ) + first = false; + else description += " or "; - description += m_matchers[i]->toString(); + description += matcher->toString(); } description += " )"; return description; @@ -117,11 +123,11 @@ namespace Matchers { MatchNotOf( MatcherBase const& underlyingMatcher ) : m_underlyingMatcher( underlyingMatcher ) {} - virtual bool match( ArgT const& arg ) const CATCH_OVERRIDE { + bool match( ArgT const& arg ) const override { return !m_underlyingMatcher.match( arg ); } - virtual std::string describe() const CATCH_OVERRIDE { + std::string describe() const override { return "not " + m_underlyingMatcher.toString(); } MatcherBase const& m_underlyingMatcher; @@ -142,31 +148,6 @@ namespace Matchers { } // namespace Impl - - // The following functions create the actual matcher objects. - // This allows the types to be inferred - // - deprecated: prefer ||, && and ! - template - Impl::MatchNotOf Not( Impl::MatcherBase const& underlyingMatcher ) { - return Impl::MatchNotOf( underlyingMatcher ); - } - template - Impl::MatchAllOf AllOf( Impl::MatcherBase const& m1, Impl::MatcherBase const& m2 ) { - return Impl::MatchAllOf() && m1 && m2; - } - template - Impl::MatchAllOf AllOf( Impl::MatcherBase const& m1, Impl::MatcherBase const& m2, Impl::MatcherBase const& m3 ) { - return Impl::MatchAllOf() && m1 && m2 && m3; - } - template - Impl::MatchAnyOf AnyOf( Impl::MatcherBase const& m1, Impl::MatcherBase const& m2 ) { - return Impl::MatchAnyOf() || m1 || m2; - } - template - Impl::MatchAnyOf AnyOf( Impl::MatcherBase const& m1, Impl::MatcherBase const& m2, Impl::MatcherBase const& m3 ) { - return Impl::MatchAnyOf() || m1 || m2 || m3; - } - } // namespace Matchers using namespace Matchers; diff --git a/include/internal/catch_matchers_string.hpp b/include/internal/catch_matchers_string.cpp similarity index 98% rename from include/internal/catch_matchers_string.hpp rename to include/internal/catch_matchers_string.cpp index ca117eda..2f8ffdb8 100644 --- a/include/internal/catch_matchers_string.hpp +++ b/include/internal/catch_matchers_string.cpp @@ -6,7 +6,8 @@ * file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) */ -#include "catch_matchers.hpp" +#include "catch_matchers_string.h" +#include "catch_string_manip.h" namespace Catch { namespace Matchers { diff --git a/include/internal/catch_matchers_string.h b/include/internal/catch_matchers_string.h index 69dabcb5..3306951b 100644 --- a/include/internal/catch_matchers_string.h +++ b/include/internal/catch_matchers_string.h @@ -8,7 +8,9 @@ #ifndef TWOBLUECUBES_CATCH_MATCHERS_STRING_H_INCLUDED #define TWOBLUECUBES_CATCH_MATCHERS_STRING_H_INCLUDED -#include "catch_matchers.hpp" +#include "catch_matchers.h" + +#include namespace Catch { namespace Matchers { @@ -27,7 +29,7 @@ namespace Matchers { struct StringMatcherBase : MatcherBase { StringMatcherBase( std::string const& operation, CasedString const& comparator ); - virtual std::string describe() const CATCH_OVERRIDE; + std::string describe() const override; CasedString m_comparator; std::string m_operation; @@ -35,19 +37,19 @@ namespace Matchers { struct EqualsMatcher : StringMatcherBase { EqualsMatcher( CasedString const& comparator ); - virtual bool match( std::string const& source ) const CATCH_OVERRIDE; + bool match( std::string const& source ) const override; }; struct ContainsMatcher : StringMatcherBase { ContainsMatcher( CasedString const& comparator ); - virtual bool match( std::string const& source ) const CATCH_OVERRIDE; + bool match( std::string const& source ) const override; }; struct StartsWithMatcher : StringMatcherBase { StartsWithMatcher( CasedString const& comparator ); - virtual bool match( std::string const& source ) const CATCH_OVERRIDE; + bool match( std::string const& source ) const override; }; struct EndsWithMatcher : StringMatcherBase { EndsWithMatcher( CasedString const& comparator ); - virtual bool match( std::string const& source ) const CATCH_OVERRIDE; + bool match( std::string const& source ) const override; }; } // namespace StdString diff --git a/include/internal/catch_matchers_vector.h b/include/internal/catch_matchers_vector.h index 6a48a4e0..ae2741b1 100644 --- a/include/internal/catch_matchers_vector.h +++ b/include/internal/catch_matchers_vector.h @@ -8,7 +8,7 @@ #ifndef TWOBLUECUBES_CATCH_MATCHERS_VECTOR_H_INCLUDED #define TWOBLUECUBES_CATCH_MATCHERS_VECTOR_H_INCLUDED -#include "catch_matchers.hpp" +#include "catch_matchers.h" namespace Catch { namespace Matchers { @@ -20,12 +20,17 @@ namespace Matchers { ContainsElementMatcher(T const &comparator) : m_comparator( comparator) {} - bool match(std::vector const &v) const CATCH_OVERRIDE { - return std::find(v.begin(), v.end(), m_comparator) != v.end(); + bool match(std::vector const &v) const override { + for (auto const& el : v) { + if (el == m_comparator) { + return true; + } + } + return false; } - virtual std::string describe() const CATCH_OVERRIDE { - return "Contains: " + Catch::toString( m_comparator ); + std::string describe() const override { + return "Contains: " + ::Catch::Detail::stringify( m_comparator ); } T const& m_comparator; @@ -36,17 +41,26 @@ namespace Matchers { ContainsMatcher(std::vector const &comparator) : m_comparator( comparator ) {} - bool match(std::vector const &v) const CATCH_OVERRIDE { + bool match(std::vector const &v) const override { // !TBD: see note in EqualsMatcher if (m_comparator.size() > v.size()) return false; - for (size_t i = 0; i < m_comparator.size(); ++i) - if (std::find(v.begin(), v.end(), m_comparator[i]) == v.end()) + for (auto const& comparator : m_comparator) { + auto present = false; + for (const auto& el : v) { + if (el == comparator) { + present = true; + break; + } + } + if (!present) { return false; + } + } return true; } - virtual std::string describe() const CATCH_OVERRIDE { - return "Contains: " + Catch::toString( m_comparator ); + std::string describe() const override { + return "Contains: " + ::Catch::Detail::stringify( m_comparator ); } std::vector const& m_comparator; @@ -57,20 +71,20 @@ namespace Matchers { EqualsMatcher(std::vector const &comparator) : m_comparator( comparator ) {} - bool match(std::vector const &v) const CATCH_OVERRIDE { + bool match(std::vector const &v) const override { // !TBD: This currently works if all elements can be compared using != // - a more general approach would be via a compare template that defaults // to using !=. but could be specialised for, e.g. std::vector etc // - then just call that directly if (m_comparator.size() != v.size()) return false; - for (size_t i = 0; i < v.size(); ++i) + for (std::size_t i = 0; i < v.size(); ++i) if (m_comparator[i] != v[i]) return false; return true; } - virtual std::string describe() const CATCH_OVERRIDE { - return "Equals: " + Catch::toString( m_comparator ); + std::string describe() const override { + return "Equals: " + ::Catch::Detail::stringify( m_comparator ); } std::vector const& m_comparator; }; diff --git a/include/internal/catch_message.hpp b/include/internal/catch_message.cpp similarity index 66% rename from include/internal/catch_message.hpp rename to include/internal/catch_message.cpp index 11d07466..a66c5271 100644 --- a/include/internal/catch_message.hpp +++ b/include/internal/catch_message.cpp @@ -5,10 +5,9 @@ * Distributed under the Boost Software License, Version 1.0. (See accompanying * file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) */ -#ifndef TWOBLUECUBES_CATCH_MESSAGE_HPP_INCLUDED -#define TWOBLUECUBES_CATCH_MESSAGE_HPP_INCLUDED #include "catch_message.h" +#include "catch_interfaces_capture.h" namespace Catch { @@ -21,21 +20,34 @@ namespace Catch { sequence( ++globalCount ) {} + bool MessageInfo::operator==( MessageInfo const& other ) const { + return sequence == other.sequence; + } + + bool MessageInfo::operator<( MessageInfo const& other ) const { + return sequence < other.sequence; + } + // This may need protecting if threading support is added unsigned int MessageInfo::globalCount = 0; //////////////////////////////////////////////////////////////////////////// + Catch::MessageBuilder::MessageBuilder( std::string const& macroName, + SourceLineInfo const& lineInfo, + ResultWas::OfType type ) + :m_info(macroName, lineInfo, type) {} + + //////////////////////////////////////////////////////////////////////////// + + ScopedMessage::ScopedMessage( MessageBuilder const& builder ) : m_info( builder.m_info ) { m_info.message = builder.m_stream.str(); getResultCapture().pushScopedMessage( m_info ); } - ScopedMessage::ScopedMessage( ScopedMessage const& other ) - : m_info( other.m_info ) - {} ScopedMessage::~ScopedMessage() { if ( !std::uncaught_exception() ){ @@ -45,5 +57,3 @@ namespace Catch { } // end namespace Catch - -#endif // TWOBLUECUBES_CATCH_MESSAGE_HPP_INCLUDED diff --git a/include/internal/catch_message.h b/include/internal/catch_message.h index 84ff95ea..c90d30d3 100644 --- a/include/internal/catch_message.h +++ b/include/internal/catch_message.h @@ -9,6 +9,7 @@ #define TWOBLUECUBES_CATCH_MESSAGE_H_INCLUDED #include +#include #include "catch_result_type.h" #include "catch_common.h" @@ -20,27 +21,33 @@ namespace Catch { ResultWas::OfType _type ); std::string macroName; + std::string message; SourceLineInfo lineInfo; ResultWas::OfType type; - std::string message; unsigned int sequence; - bool operator == ( MessageInfo const& other ) const { - return sequence == other.sequence; - } - bool operator < ( MessageInfo const& other ) const { - return sequence < other.sequence; - } + bool operator == ( MessageInfo const& other ) const; + bool operator < ( MessageInfo const& other ) const; private: static unsigned int globalCount; }; - struct MessageBuilder { + struct MessageStream { + + template + MessageStream& operator << ( T const& value ) { + m_stream << value; + return *this; + } + + // !TBD reuse a global/ thread-local stream + std::ostringstream m_stream; + }; + + struct MessageBuilder : MessageStream { MessageBuilder( std::string const& macroName, SourceLineInfo const& lineInfo, - ResultWas::OfType type ) - : m_info( macroName, lineInfo, type ) - {} + ResultWas::OfType type ); template MessageBuilder& operator << ( T const& value ) { @@ -49,13 +56,11 @@ namespace Catch { } MessageInfo m_info; - std::ostringstream m_stream; }; class ScopedMessage { public: ScopedMessage( MessageBuilder const& builder ); - ScopedMessage( ScopedMessage const& other ); ~ScopedMessage(); MessageInfo m_info; diff --git a/include/internal/catch_notimplemented_exception.h b/include/internal/catch_notimplemented_exception.h deleted file mode 100644 index a448268d..00000000 --- a/include/internal/catch_notimplemented_exception.h +++ /dev/null @@ -1,34 +0,0 @@ -/* - * Created by Phil on 5/8/2012. - * Copyright 2012 Two Blue Cubes Ltd. All rights reserved. - * - * Distributed under the Boost Software License, Version 1.0. (See accompanying - * file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) - */ -#ifndef TWOBLUECUBES_CATCH_NOTIMPLEMENTED_EXCEPTION_H_INCLUDED -#define TWOBLUECUBES_CATCH_NOTIMPLEMENTED_EXCEPTION_H_INCLUDED - -#include "catch_common.h" - -namespace Catch { - - class NotImplementedException : public std::exception - { - public: - NotImplementedException( SourceLineInfo const& lineInfo ); - - virtual ~NotImplementedException() CATCH_NOEXCEPT {} - - virtual const char* what() const CATCH_NOEXCEPT; - - private: - std::string m_what; - SourceLineInfo m_lineInfo; - }; - -} // end namespace Catch - -/////////////////////////////////////////////////////////////////////////////// -#define CATCH_NOT_IMPLEMENTED throw Catch::NotImplementedException( CATCH_INTERNAL_LINEINFO ) - -#endif // TWOBLUECUBES_CATCH_NOTIMPLEMENTED_EXCEPTION_H_INCLUDED diff --git a/include/internal/catch_notimplemented_exception.hpp b/include/internal/catch_notimplemented_exception.hpp deleted file mode 100644 index e4afdc67..00000000 --- a/include/internal/catch_notimplemented_exception.hpp +++ /dev/null @@ -1,30 +0,0 @@ -/* - * Created by Phil on 5/8/2012. - * Copyright 2012 Two Blue Cubes Ltd. All rights reserved. - * - * Distributed under the Boost Software License, Version 1.0. (See accompanying - * file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) - */ -#ifndef TWOBLUECUBES_CATCH_NOTIMPLEMENTED_EXCEPTION_HPP_INCLUDED -#define TWOBLUECUBES_CATCH_NOTIMPLEMENTED_EXCEPTION_HPP_INCLUDED - -#include "catch_notimplemented_exception.h" -#include - -namespace Catch { - - NotImplementedException::NotImplementedException( SourceLineInfo const& lineInfo ) - : m_lineInfo( lineInfo ) { - std::ostringstream oss; - oss << lineInfo << ": function "; - oss << "not implemented"; - m_what = oss.str(); - } - - const char* NotImplementedException::what() const CATCH_NOEXCEPT { - return m_what.c_str(); - } - -} // end namespace Catch - -#endif // TWOBLUECUBES_CATCH_NOTIMPLEMENTED_EXCEPTION_HPP_INCLUDED diff --git a/include/internal/catch_objc.hpp b/include/internal/catch_objc.hpp index 2e349315..b43f3352 100644 --- a/include/internal/catch_objc.hpp +++ b/include/internal/catch_objc.hpp @@ -18,6 +18,8 @@ // in catch.hpp first to make sure they are included by the single // header for non obj-usage #include "catch_test_case_info.h" +#include "catch_string_manip.h" +#include "catch_tostring.h" /////////////////////////////////////////////////////////////////////////////// // This protocol is really only here for (self) documenting purposes, since @@ -33,7 +35,7 @@ namespace Catch { - class OcMethod : public SharedImpl { + class OcMethod : public ITestInvoker { public: OcMethod( Class cls, SEL sel ) : m_cls( cls ), m_sel( sel ) {} @@ -70,9 +72,9 @@ namespace Catch { } } - inline size_t registerTestMethods() { - size_t noTestMethods = 0; - int noClasses = objc_getClassList( CATCH_NULL, 0 ); + inline std::size_t registerTestMethods() { + std::size_t noTestMethods = 0; + int noClasses = objc_getClassList( nullptr, 0 ); Class* classes = (CATCH_UNSAFE_UNRETAINED Class *)malloc( sizeof(Class) * noClasses); objc_getClassList( classes, noClasses ); @@ -91,7 +93,7 @@ namespace Catch { std::string desc = Detail::getAnnotation( cls, "Description", testCaseName ); const char* className = class_getName( cls ); - getMutableRegistryHub().registerTest( makeTestCase( new OcMethod( cls, selector ), className, name.c_str(), desc.c_str(), SourceLineInfo() ) ); + getMutableRegistryHub().registerTest( makeTestCase( new OcMethod( cls, selector ), className, name.c_str(), desc.c_str(), SourceLineInfo("",0) ) ); noTestMethods++; } } @@ -101,6 +103,8 @@ namespace Catch { return noTestMethods; } +#if !defined(CATCH_CONFIG_DISABLE_MATCHERS) + namespace Matchers { namespace Impl { namespace NSStringMatchers { @@ -112,61 +116,61 @@ namespace Catch { arcSafeRelease( m_substr ); } - virtual bool match( NSString* arg ) const CATCH_OVERRIDE { + bool match( NSString* arg ) const override { return false; } - NSString* m_substr; + NSString* CATCH_ARC_STRONG m_substr; }; struct Equals : StringHolder { Equals( NSString* substr ) : StringHolder( substr ){} - virtual bool match( NSString* str ) const CATCH_OVERRIDE { + bool match( NSString* str ) const override { return (str != nil || m_substr == nil ) && [str isEqualToString:m_substr]; } - virtual std::string describe() const CATCH_OVERRIDE { - return "equals string: " + Catch::toString( m_substr ); + std::string describe() const override { + return "equals string: " + Catch::Detail::stringify( m_substr ); } }; struct Contains : StringHolder { Contains( NSString* substr ) : StringHolder( substr ){} - virtual bool match( NSString* str ) const { + bool match( NSString* str ) const { return (str != nil || m_substr == nil ) && [str rangeOfString:m_substr].location != NSNotFound; } - virtual std::string describe() const CATCH_OVERRIDE { - return "contains string: " + Catch::toString( m_substr ); + std::string describe() const override { + return "contains string: " + Catch::Detail::stringify( m_substr ); } }; struct StartsWith : StringHolder { StartsWith( NSString* substr ) : StringHolder( substr ){} - virtual bool match( NSString* str ) const { + bool match( NSString* str ) const override { return (str != nil || m_substr == nil ) && [str rangeOfString:m_substr].location == 0; } - virtual std::string describe() const CATCH_OVERRIDE { - return "starts with: " + Catch::toString( m_substr ); + std::string describe() const override { + return "starts with: " + Catch::Detail::stringify( m_substr ); } }; struct EndsWith : StringHolder { EndsWith( NSString* substr ) : StringHolder( substr ){} - virtual bool match( NSString* str ) const { + bool match( NSString* str ) const override { return (str != nil || m_substr == nil ) && [str rangeOfString:m_substr].location == [str length] - [m_substr length]; } - virtual std::string describe() const CATCH_OVERRIDE { - return "ends with: " + Catch::toString( m_substr ); + std::string describe() const override { + return "ends with: " + Catch::Detail::stringify( m_substr ); } }; @@ -189,18 +193,23 @@ namespace Catch { using namespace Matchers; +#endif // CATCH_CONFIG_DISABLE_MATCHERS + } // namespace Catch /////////////////////////////////////////////////////////////////////////////// -#define OC_TEST_CASE( name, desc )\ -+(NSString*) INTERNAL_CATCH_UNIQUE_NAME( Catch_Name_test ) \ -{\ +#define OC_MAKE_UNIQUE_NAME( root, uniqueSuffix ) root##uniqueSuffix +#define OC_TEST_CASE2( name, desc, uniqueSuffix ) \ ++(NSString*) OC_MAKE_UNIQUE_NAME( Catch_Name_test_, uniqueSuffix ) \ +{ \ return @ name; \ -}\ -+(NSString*) INTERNAL_CATCH_UNIQUE_NAME( Catch_Description_test ) \ +} \ ++(NSString*) OC_MAKE_UNIQUE_NAME( Catch_Description_test_, uniqueSuffix ) \ { \ return @ desc; \ } \ --(void) INTERNAL_CATCH_UNIQUE_NAME( Catch_TestCase_test ) +-(void) OC_MAKE_UNIQUE_NAME( Catch_TestCase_test_, uniqueSuffix ) + +#define OC_TEST_CASE( name, desc ) OC_TEST_CASE2( name, desc, __LINE__ ) #endif // TWOBLUECUBES_CATCH_OBJC_HPP_INCLUDED diff --git a/include/internal/catch_option.hpp b/include/internal/catch_option.hpp index 02f849dd..d790b245 100644 --- a/include/internal/catch_option.hpp +++ b/include/internal/catch_option.hpp @@ -8,20 +8,18 @@ #ifndef TWOBLUECUBES_CATCH_OPTION_HPP_INCLUDED #define TWOBLUECUBES_CATCH_OPTION_HPP_INCLUDED -#include "catch_common.h" - namespace Catch { // An optional type template class Option { public: - Option() : nullableValue( CATCH_NULL ) {} + Option() : nullableValue( nullptr ) {} Option( T const& _value ) : nullableValue( new( storage ) T( _value ) ) {} Option( Option const& _other ) - : nullableValue( _other ? new( storage ) T( *_other ) : CATCH_NULL ) + : nullableValue( _other ? new( storage ) T( *_other ) : nullptr ) {} ~Option() { @@ -45,7 +43,7 @@ namespace Catch { void reset() { if( nullableValue ) nullableValue->~T(); - nullableValue = CATCH_NULL; + nullableValue = nullptr; } T& operator*() { return *nullableValue; } @@ -57,27 +55,17 @@ namespace Catch { return nullableValue ? *nullableValue : defaultValue; } - bool some() const { return nullableValue != CATCH_NULL; } - bool none() const { return nullableValue == CATCH_NULL; } + bool some() const { return nullableValue != nullptr; } + bool none() const { return nullableValue == nullptr; } - bool operator !() const { return nullableValue == CATCH_NULL; } - operator SafeBool::type() const { - return SafeBool::makeSafe( some() ); + bool operator !() const { return nullableValue == nullptr; } + explicit operator bool() const { + return some(); } private: T *nullableValue; - union { - char storage[sizeof(T)]; - - // These are here to force alignment for the storage - long double dummy1; - void (*dummy2)(); - long double dummy3; -#ifdef CATCH_CONFIG_CPP11_LONG_LONG - long long dummy4; -#endif - }; + alignas(alignof(T)) char storage[sizeof(T)]; }; } // end namespace Catch diff --git a/include/internal/catch_platform.h b/include/internal/catch_platform.h index 09b91bfb..90cf0eb7 100644 --- a/include/internal/catch_platform.h +++ b/include/internal/catch_platform.h @@ -9,20 +9,19 @@ #ifndef TWOBLUECUBES_CATCH_PLATFORM_H_INCLUDED #define TWOBLUECUBES_CATCH_PLATFORM_H_INCLUDED -#if defined(__MAC_OS_X_VERSION_MIN_REQUIRED) +#ifdef __APPLE__ +# include +# if TARGET_OS_MAC == 1 # define CATCH_PLATFORM_MAC -#elif defined(__IPHONE_OS_VERSION_MIN_REQUIRED) +# elif TARGET_OS_IPHONE == 1 # define CATCH_PLATFORM_IPHONE +# endif + #elif defined(linux) || defined(__linux) || defined(__linux__) # define CATCH_PLATFORM_LINUX + #elif defined(WIN32) || defined(__WIN32__) || defined(_WIN32) || defined(_MSC_VER) # define CATCH_PLATFORM_WINDOWS -# if !defined(NOMINMAX) && !defined(CATCH_CONFIG_NO_NOMINMAX) -# define CATCH_DEFINES_NOMINMAX -# endif -# if !defined(WIN32_LEAN_AND_MEAN) && !defined(CATCH_CONFIG_NO_WIN32_LEAN_AND_MEAN) -# define CATCH_DEFINES_WIN32_LEAN_AND_MEAN -# endif #endif #endif // TWOBLUECUBES_CATCH_PLATFORM_H_INCLUDED diff --git a/include/internal/catch_ptr.hpp b/include/internal/catch_ptr.hpp deleted file mode 100644 index 940e5d19..00000000 --- a/include/internal/catch_ptr.hpp +++ /dev/null @@ -1,93 +0,0 @@ -/* - * Created by Phil on 02/05/2012. - * Copyright 2012 Two Blue Cubes Ltd. All rights reserved. - * - * Distributed under the Boost Software License, Version 1.0. (See accompanying - * file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) - */ -#ifndef TWOBLUECUBES_CATCH_PTR_HPP_INCLUDED -#define TWOBLUECUBES_CATCH_PTR_HPP_INCLUDED - -#include "catch_common.h" - -#ifdef __clang__ -#pragma clang diagnostic push -#pragma clang diagnostic ignored "-Wpadded" -#endif - -namespace Catch { - - // An intrusive reference counting smart pointer. - // T must implement addRef() and release() methods - // typically implementing the IShared interface - template - class Ptr { - public: - Ptr() : m_p( CATCH_NULL ){} - Ptr( T* p ) : m_p( p ){ - if( m_p ) - m_p->addRef(); - } - Ptr( Ptr const& other ) : m_p( other.m_p ){ - if( m_p ) - m_p->addRef(); - } - ~Ptr(){ - if( m_p ) - m_p->release(); - } - void reset() { - if( m_p ) - m_p->release(); - m_p = CATCH_NULL; - } - Ptr& operator = ( T* p ){ - Ptr temp( p ); - swap( temp ); - return *this; - } - Ptr& operator = ( Ptr const& other ){ - Ptr temp( other ); - swap( temp ); - return *this; - } - void swap( Ptr& other ) { std::swap( m_p, other.m_p ); } - T* get() const{ return m_p; } - T& operator*() const { return *m_p; } - T* operator->() const { return m_p; } - bool operator !() const { return m_p == CATCH_NULL; } - operator SafeBool::type() const { return SafeBool::makeSafe( m_p != CATCH_NULL ); } - - private: - T* m_p; - }; - - struct IShared : NonCopyable { - virtual ~IShared(); - virtual void addRef() const = 0; - virtual void release() const = 0; - }; - - template - struct SharedImpl : T { - - SharedImpl() : m_rc( 0 ){} - - virtual void addRef() const { - ++m_rc; - } - virtual void release() const { - if( --m_rc == 0 ) - delete this; - } - - mutable unsigned int m_rc; - }; - -} // end namespace Catch - -#ifdef __clang__ -#pragma clang diagnostic pop -#endif - -#endif // TWOBLUECUBES_CATCH_PTR_HPP_INCLUDED diff --git a/include/internal/catch_random_number_generator.cpp b/include/internal/catch_random_number_generator.cpp new file mode 100644 index 00000000..412faeba --- /dev/null +++ b/include/internal/catch_random_number_generator.cpp @@ -0,0 +1,31 @@ +/* + * Created by Martin on 30/08/2017. + * + * Distributed under the Boost Software License, Version 1.0. (See accompanying + * file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) + */ + +#include "catch_random_number_generator.h" +#include "catch_context.h" +#include "catch_interfaces_config.h" + +#include + +namespace Catch { + + void seedRng( IConfig const& config ) { + if( config.rngSeed() != 0 ) + std::srand( config.rngSeed() ); + } + unsigned int rngSeed() { + return getCurrentContext().getConfig()->rngSeed(); + } + + RandomNumberGenerator::result_type RandomNumberGenerator::operator()( result_type n ) const { + return std::rand() % n; + } + RandomNumberGenerator::result_type RandomNumberGenerator::operator()() const { + return std::rand() % (max)(); + } + +} diff --git a/include/internal/catch_random_number_generator.h b/include/internal/catch_random_number_generator.h new file mode 100644 index 00000000..2395bd39 --- /dev/null +++ b/include/internal/catch_random_number_generator.h @@ -0,0 +1,40 @@ +/* + * Created by Martin on 30/08/2017. + * + * Distributed under the Boost Software License, Version 1.0. (See accompanying + * file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) + */ +#ifndef TWOBLUECUBES_CATCH_RANDOM_NUMBER_GENERATOR_H_INCLUDED +#define TWOBLUECUBES_CATCH_RANDOM_NUMBER_GENERATOR_H_INCLUDED + +#include "catch_random_number_generator.h" + +#include + +namespace Catch { + + struct IConfig; + + void seedRng( IConfig const& config ); + + unsigned int rngSeed(); + + struct RandomNumberGenerator { + using result_type = unsigned int; + + static constexpr result_type (min)() { return 0; } + static constexpr result_type (max)() { return 1000000; } + + result_type operator()( result_type n ) const; + result_type operator()() const; + + template + static void shuffle( V& vector ) { + RandomNumberGenerator rng; + std::shuffle( vector.begin(), vector.end(), rng ); + } + }; + +} + +#endif // TWOBLUECUBES_CATCH_RANDOM_NUMBER_GENERATOR_H_INCLUDED diff --git a/include/internal/catch_registry_hub.hpp b/include/internal/catch_registry_hub.cpp similarity index 56% rename from include/internal/catch_registry_hub.hpp rename to include/internal/catch_registry_hub.cpp index 2523cd9d..ab5ad361 100644 --- a/include/internal/catch_registry_hub.hpp +++ b/include/internal/catch_registry_hub.cpp @@ -5,69 +5,72 @@ * Distributed under the Boost Software License, Version 1.0. (See accompanying * file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) */ -#ifndef TWOBLUECUBES_CATCH_REGISTRY_HUB_HPP_INCLUDED -#define TWOBLUECUBES_CATCH_REGISTRY_HUB_HPP_INCLUDED #include "catch_interfaces_registry_hub.h" -#include "catch_test_case_registry_impl.hpp" -#include "catch_reporter_registry.hpp" -#include "catch_exception_translator_registry.hpp" +#include "catch_context.h" +#include "catch_test_case_registry_impl.h" +#include "catch_reporter_registry.h" +#include "catch_exception_translator_registry.h" #include "catch_tag_alias_registry.h" +#include "catch_startup_exception_registry.h" namespace Catch { namespace { - class RegistryHub : public IRegistryHub, public IMutableRegistryHub { - - RegistryHub( RegistryHub const& ); - void operator=( RegistryHub const& ); + class RegistryHub : public IRegistryHub, public IMutableRegistryHub, + private NonCopyable { public: // IRegistryHub - RegistryHub() { - } - virtual IReporterRegistry const& getReporterRegistry() const CATCH_OVERRIDE { + RegistryHub() = default; + IReporterRegistry const& getReporterRegistry() const override { return m_reporterRegistry; } - virtual ITestCaseRegistry const& getTestCaseRegistry() const CATCH_OVERRIDE { + ITestCaseRegistry const& getTestCaseRegistry() const override { return m_testCaseRegistry; } - virtual IExceptionTranslatorRegistry& getExceptionTranslatorRegistry() CATCH_OVERRIDE { + IExceptionTranslatorRegistry& getExceptionTranslatorRegistry() override { return m_exceptionTranslatorRegistry; } - virtual ITagAliasRegistry const& getTagAliasRegistry() const CATCH_OVERRIDE { + ITagAliasRegistry const& getTagAliasRegistry() const override { return m_tagAliasRegistry; } - + StartupExceptionRegistry const& getStartupExceptionRegistry() const override { + return m_exceptionRegistry; + } public: // IMutableRegistryHub - virtual void registerReporter( std::string const& name, Ptr const& factory ) CATCH_OVERRIDE { + void registerReporter( std::string const& name, IReporterFactoryPtr const& factory ) override { m_reporterRegistry.registerReporter( name, factory ); } - virtual void registerListener( Ptr const& factory ) CATCH_OVERRIDE { + void registerListener( IReporterFactoryPtr const& factory ) override { m_reporterRegistry.registerListener( factory ); } - virtual void registerTest( TestCase const& testInfo ) CATCH_OVERRIDE { + void registerTest( TestCase const& testInfo ) override { m_testCaseRegistry.registerTest( testInfo ); } - virtual void registerTranslator( const IExceptionTranslator* translator ) CATCH_OVERRIDE { + void registerTranslator( const IExceptionTranslator* translator ) override { m_exceptionTranslatorRegistry.registerTranslator( translator ); } - virtual void registerTagAlias( std::string const& alias, std::string const& tag, SourceLineInfo const& lineInfo ) CATCH_OVERRIDE { + void registerTagAlias( std::string const& alias, std::string const& tag, SourceLineInfo const& lineInfo ) override { m_tagAliasRegistry.add( alias, tag, lineInfo ); } + void registerStartupException() noexcept override { + m_exceptionRegistry.add(std::current_exception()); + } private: TestRegistry m_testCaseRegistry; ReporterRegistry m_reporterRegistry; ExceptionTranslatorRegistry m_exceptionTranslatorRegistry; TagAliasRegistry m_tagAliasRegistry; + StartupExceptionRegistry m_exceptionRegistry; }; // Single, global, instance - inline RegistryHub*& getTheRegistryHub() { - static RegistryHub* theRegistryHub = CATCH_NULL; + RegistryHub*& getTheRegistryHub() { + static RegistryHub* theRegistryHub = nullptr; if( !theRegistryHub ) theRegistryHub = new RegistryHub(); return theRegistryHub; @@ -82,7 +85,7 @@ namespace Catch { } void cleanUp() { delete getTheRegistryHub(); - getTheRegistryHub() = CATCH_NULL; + getTheRegistryHub() = nullptr; cleanUpContext(); } std::string translateActiveException() { @@ -91,5 +94,3 @@ namespace Catch { } // end namespace Catch - -#endif // TWOBLUECUBES_CATCH_REGISTRY_HUB_HPP_INCLUDED diff --git a/include/internal/catch_reporter_registrars.hpp b/include/internal/catch_reporter_registrars.hpp index 1a47d192..ce88fa34 100644 --- a/include/internal/catch_reporter_registrars.hpp +++ b/include/internal/catch_reporter_registrars.hpp @@ -1,3 +1,4 @@ + /* * Created by Phil on 31/12/2010. * Copyright 2010 Two Blue Cubes Ltd. All rights reserved. @@ -9,51 +10,19 @@ #define TWOBLUECUBES_CATCH_REPORTER_REGISTRARS_HPP_INCLUDED #include "catch_interfaces_registry_hub.h" -#include "catch_legacy_reporter_adapter.h" namespace Catch { - template - class LegacyReporterRegistrar { - - class ReporterFactory : public IReporterFactory { - virtual IStreamingReporter* create( ReporterConfig const& config ) const { - return new LegacyReporterAdapter( new T( config ) ); - } - - virtual std::string getDescription() const { - return T::getDescription(); - } - }; - - public: - - LegacyReporterRegistrar( std::string const& name ) { - getMutableRegistryHub().registerReporter( name, new ReporterFactory() ); - } - }; - template class ReporterRegistrar { - class ReporterFactory : public SharedImpl { + class ReporterFactory : public IReporterFactory { - // *** Please Note ***: - // - If you end up here looking at a compiler error because it's trying to register - // your custom reporter class be aware that the native reporter interface has changed - // to IStreamingReporter. The "legacy" interface, IReporter, is still supported via - // an adapter. Just use REGISTER_LEGACY_REPORTER to take advantage of the adapter. - // However please consider updating to the new interface as the old one is now - // deprecated and will probably be removed quite soon! - // Please contact me via github if you have any questions at all about this. - // In fact, ideally, please contact me anyway to let me know you've hit this - as I have - // no idea who is actually using custom reporters at all (possibly no-one!). - // The new interface is designed to minimise exposure to interface changes in the future. - virtual IStreamingReporter* create( ReporterConfig const& config ) const { - return new T( config ); + virtual IStreamingReporterPtr create( ReporterConfig const& config ) const override { + return std::unique_ptr( new T( config ) ); } - virtual std::string getDescription() const { + virtual std::string getDescription() const override { return T::getDescription(); } }; @@ -61,19 +30,19 @@ namespace Catch { public: ReporterRegistrar( std::string const& name ) { - getMutableRegistryHub().registerReporter( name, new ReporterFactory() ); + getMutableRegistryHub().registerReporter( name, std::make_shared() ); } }; template class ListenerRegistrar { - class ListenerFactory : public SharedImpl { + class ListenerFactory : public IReporterFactory { - virtual IStreamingReporter* create( ReporterConfig const& config ) const { - return new T( config ); + virtual IStreamingReporterPtr create( ReporterConfig const& config ) const override { + return std::unique_ptr( new T( config ) ); } - virtual std::string getDescription() const { + virtual std::string getDescription() const override { return std::string(); } }; @@ -81,22 +50,27 @@ namespace Catch { public: ListenerRegistrar() { - getMutableRegistryHub().registerListener( new ListenerFactory() ); + getMutableRegistryHub().registerListener( std::make_shared() ); } }; } -#define INTERNAL_CATCH_REGISTER_LEGACY_REPORTER( name, reporterType ) \ - namespace{ Catch::LegacyReporterRegistrar catch_internal_RegistrarFor##reporterType( name ); } +#if !defined(CATCH_CONFIG_DISABLE) -#define INTERNAL_CATCH_REGISTER_REPORTER( name, reporterType ) \ - namespace{ Catch::ReporterRegistrar catch_internal_RegistrarFor##reporterType( name ); } - -// Deprecated - use the form without INTERNAL_ -#define INTERNAL_CATCH_REGISTER_LISTENER( listenerType ) \ - namespace{ Catch::ListenerRegistrar catch_internal_RegistrarFor##listenerType; } +#define CATCH_REGISTER_REPORTER( name, reporterType ) \ + CATCH_INTERNAL_SUPPRESS_GLOBALS_WARNINGS \ + namespace{ Catch::ReporterRegistrar catch_internal_RegistrarFor##reporterType( name ); } \ + CATCH_INTERNAL_UNSUPPRESS_GLOBALS_WARNINGS #define CATCH_REGISTER_LISTENER( listenerType ) \ - namespace{ Catch::ListenerRegistrar catch_internal_RegistrarFor##listenerType; } + CATCH_INTERNAL_SUPPRESS_GLOBALS_WARNINGS \ + namespace{ Catch::ListenerRegistrar catch_internal_RegistrarFor##listenerType; } \ + CATCH_INTERNAL_SUPPRESS_GLOBALS_WARNINGS +#else // CATCH_CONFIG_DISABLE + +#define CATCH_REGISTER_REPORTER(name, reporterType) +#define CATCH_REGISTER_LISTENER(listenerType) + +#endif // CATCH_CONFIG_DISABLE #endif // TWOBLUECUBES_CATCH_REPORTER_REGISTRARS_HPP_INCLUDED diff --git a/include/internal/catch_reporter_registry.cpp b/include/internal/catch_reporter_registry.cpp new file mode 100644 index 00000000..f017e059 --- /dev/null +++ b/include/internal/catch_reporter_registry.cpp @@ -0,0 +1,34 @@ +/* + * Created by Martin on 31/08/2017. + * + * Distributed under the Boost Software License, Version 1.0. (See accompanying + * file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) + */ +#include "catch_reporter_registry.h" + +namespace Catch { + + ReporterRegistry::~ReporterRegistry() = default; + + IStreamingReporterPtr ReporterRegistry::create( std::string const& name, IConfigPtr const& config ) const { + auto it = m_factories.find( name ); + if( it == m_factories.end() ) + return nullptr; + return it->second->create( ReporterConfig( config ) ); + } + + void ReporterRegistry::registerReporter( std::string const& name, IReporterFactoryPtr const& factory ) { + m_factories.emplace(name, factory); + } + void ReporterRegistry::registerListener( IReporterFactoryPtr const& factory ) { + m_listeners.push_back( factory ); + } + + IReporterRegistry::FactoryMap const& ReporterRegistry::getFactories() const { + return m_factories; + } + IReporterRegistry::Listeners const& ReporterRegistry::getListeners() const { + return m_listeners; + } + +} diff --git a/include/internal/catch_reporter_registry.h b/include/internal/catch_reporter_registry.h new file mode 100644 index 00000000..916e9247 --- /dev/null +++ b/include/internal/catch_reporter_registry.h @@ -0,0 +1,37 @@ +/* + * Created by Phil on 29/10/2010. + * Copyright 2010 Two Blue Cubes Ltd. All rights reserved. + * + * Distributed under the Boost Software License, Version 1.0. (See accompanying + * file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) + */ +#ifndef TWOBLUECUBES_CATCH_REPORTER_REGISTRY_H_INCLUDED +#define TWOBLUECUBES_CATCH_REPORTER_REGISTRY_H_INCLUDED + +#include "catch_interfaces_reporter.h" + +#include + +namespace Catch { + + class ReporterRegistry : public IReporterRegistry { + + public: + + ~ReporterRegistry() override; + + IStreamingReporterPtr create( std::string const& name, IConfigPtr const& config ) const override; + + void registerReporter( std::string const& name, IReporterFactoryPtr const& factory ); + void registerListener( IReporterFactoryPtr const& factory ); + + FactoryMap const& getFactories() const override; + Listeners const& getListeners() const override; + + private: + FactoryMap m_factories; + Listeners m_listeners; + }; +} + +#endif // TWOBLUECUBES_CATCH_REPORTER_REGISTRY_H_INCLUDED diff --git a/include/internal/catch_reporter_registry.hpp b/include/internal/catch_reporter_registry.hpp deleted file mode 100644 index 71f23ff7..00000000 --- a/include/internal/catch_reporter_registry.hpp +++ /dev/null @@ -1,50 +0,0 @@ -/* - * Created by Phil on 29/10/2010. - * Copyright 2010 Two Blue Cubes Ltd. All rights reserved. - * - * Distributed under the Boost Software License, Version 1.0. (See accompanying - * file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) - */ -#ifndef TWOBLUECUBES_CATCH_REPORTER_REGISTRY_HPP_INCLUDED -#define TWOBLUECUBES_CATCH_REPORTER_REGISTRY_HPP_INCLUDED - -#include "catch_interfaces_reporter.h" - -#include - -namespace Catch { - - class ReporterRegistry : public IReporterRegistry { - - public: - - virtual ~ReporterRegistry() CATCH_OVERRIDE {} - - virtual IStreamingReporter* create( std::string const& name, Ptr const& config ) const CATCH_OVERRIDE { - FactoryMap::const_iterator it = m_factories.find( name ); - if( it == m_factories.end() ) - return CATCH_NULL; - return it->second->create( ReporterConfig( config ) ); - } - - void registerReporter( std::string const& name, Ptr const& factory ) { - m_factories.insert( std::make_pair( name, factory ) ); - } - void registerListener( Ptr const& factory ) { - m_listeners.push_back( factory ); - } - - virtual FactoryMap const& getFactories() const CATCH_OVERRIDE { - return m_factories; - } - virtual Listeners const& getListeners() const CATCH_OVERRIDE { - return m_listeners; - } - - private: - FactoryMap m_factories; - Listeners m_listeners; - }; -} - -#endif // TWOBLUECUBES_CATCH_REPORTER_REGISTRY_HPP_INCLUDED diff --git a/include/internal/catch_result_builder.h b/include/internal/catch_result_builder.h deleted file mode 100644 index 3ee1fca6..00000000 --- a/include/internal/catch_result_builder.h +++ /dev/null @@ -1,133 +0,0 @@ -/* - * Created by Phil on 28/5/2014. - * Copyright 2014 Two Blue Cubes Ltd. All rights reserved. - * - * Distributed under the Boost Software License, Version 1.0. (See accompanying - * file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) - */ -#ifndef TWOBLUECUBES_CATCH_RESULT_BUILDER_H_INCLUDED -#define TWOBLUECUBES_CATCH_RESULT_BUILDER_H_INCLUDED - -#include "catch_result_type.h" -#include "catch_assertionresult.h" -#include "catch_common.h" -#include "catch_matchers.hpp" - -namespace Catch { - - struct TestFailureException{}; - - template class ExpressionLhs; - - struct CopyableStream { - CopyableStream() {} - CopyableStream( CopyableStream const& other ) { - oss << other.oss.str(); - } - CopyableStream& operator=( CopyableStream const& other ) { - oss.str(std::string()); - oss << other.oss.str(); - return *this; - } - std::ostringstream oss; - }; - - class ResultBuilder : public DecomposedExpression { - public: - ResultBuilder( char const* macroName, - SourceLineInfo const& lineInfo, - char const* capturedExpression, - ResultDisposition::Flags resultDisposition, - char const* secondArg = "" ); - ~ResultBuilder(); - - template - ExpressionLhs operator <= ( T const& operand ); - ExpressionLhs operator <= ( bool value ); - - template - ResultBuilder& operator << ( T const& value ) { - stream().oss << value; - return *this; - } - - ResultBuilder& setResultType( ResultWas::OfType result ); - ResultBuilder& setResultType( bool result ); - - void endExpression( DecomposedExpression const& expr ); - - virtual void reconstructExpression( std::string& dest ) const CATCH_OVERRIDE; - - AssertionResult build() const; - AssertionResult build( DecomposedExpression const& expr ) const; - - void useActiveException( ResultDisposition::Flags resultDisposition = ResultDisposition::Normal ); - void captureResult( ResultWas::OfType resultType ); - void captureExpression(); - void captureExpectedException( std::string const& expectedMessage ); - void captureExpectedException( Matchers::Impl::MatcherBase const& matcher ); - void handleResult( AssertionResult const& result ); - void react(); - bool shouldDebugBreak() const; - bool allowThrows() const; - - template - void captureMatch( ArgT const& arg, MatcherT const& matcher, char const* matcherString ); - - void setExceptionGuard(); - void unsetExceptionGuard(); - - private: - AssertionInfo m_assertionInfo; - AssertionResultData m_data; - - CopyableStream &stream() - { - if(!m_usedStream) - { - m_usedStream = true; - m_stream().oss.str(""); - } - return m_stream(); - } - - static CopyableStream &m_stream() - { - static CopyableStream s; - return s; - } - - bool m_shouldDebugBreak; - bool m_shouldThrow; - bool m_guardException; - bool m_usedStream; - }; - -} // namespace Catch - -// Include after due to circular dependency: -#include "catch_expression_lhs.hpp" - -namespace Catch { - - template - ExpressionLhs ResultBuilder::operator <= ( T const& operand ) { - return ExpressionLhs( *this, operand ); - } - - inline ExpressionLhs ResultBuilder::operator <= ( bool value ) { - return ExpressionLhs( *this, value ); - } - - template - void ResultBuilder::captureMatch( ArgT const& arg, MatcherT const& matcher, - char const* matcherString ) { - MatchExpression expr( arg, matcher, matcherString ); - setResultType( matcher.match( arg ) ); - endExpression( expr ); - } - - -} // namespace Catch - -#endif // TWOBLUECUBES_CATCH_RESULT_BUILDER_H_INCLUDED diff --git a/include/internal/catch_result_builder.hpp b/include/internal/catch_result_builder.hpp deleted file mode 100644 index e7a5bdd2..00000000 --- a/include/internal/catch_result_builder.hpp +++ /dev/null @@ -1,173 +0,0 @@ -/* - * Created by Phil on 28/5/2014. - * Copyright 2014 Two Blue Cubes Ltd. All rights reserved. - * - * Distributed under the Boost Software License, Version 1.0. (See accompanying - * file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) - */ -#ifndef TWOBLUECUBES_CATCH_RESULT_BUILDER_HPP_INCLUDED -#define TWOBLUECUBES_CATCH_RESULT_BUILDER_HPP_INCLUDED - -#include "catch_result_builder.h" -#include "catch_context.h" -#include "catch_interfaces_config.h" -#include "catch_interfaces_runner.h" -#include "catch_interfaces_capture.h" -#include "catch_interfaces_registry_hub.h" -#include "catch_wildcard_pattern.hpp" - -namespace Catch { - - ResultBuilder::ResultBuilder( char const* macroName, - SourceLineInfo const& lineInfo, - char const* capturedExpression, - ResultDisposition::Flags resultDisposition, - char const* secondArg ) - : m_assertionInfo( macroName, lineInfo, capturedExpression, resultDisposition, secondArg ), - m_shouldDebugBreak( false ), - m_shouldThrow( false ), - m_guardException( false ), - m_usedStream( false ) - {} - - ResultBuilder::~ResultBuilder() { -#if defined(CATCH_CONFIG_FAST_COMPILE) - if ( m_guardException ) { - stream().oss << "Exception translation was disabled by CATCH_CONFIG_FAST_COMPILE"; - captureResult( ResultWas::ThrewException ); - getCurrentContext().getResultCapture()->exceptionEarlyReported(); - } -#endif - } - - ResultBuilder& ResultBuilder::setResultType( ResultWas::OfType result ) { - m_data.resultType = result; - return *this; - } - ResultBuilder& ResultBuilder::setResultType( bool result ) { - m_data.resultType = result ? ResultWas::Ok : ResultWas::ExpressionFailed; - return *this; - } - - void ResultBuilder::endExpression( DecomposedExpression const& expr ) { - // Flip bool results if FalseTest flag is set - if( isFalseTest( m_assertionInfo.resultDisposition ) ) { - m_data.negate( expr.isBinaryExpression() ); - } - - getResultCapture().assertionRun(); - - if(getCurrentContext().getConfig()->includeSuccessfulResults() || m_data.resultType != ResultWas::Ok) - { - AssertionResult result = build( expr ); - handleResult( result ); - } - else - getResultCapture().assertionPassed(); - } - - void ResultBuilder::useActiveException( ResultDisposition::Flags resultDisposition ) { - m_assertionInfo.resultDisposition = resultDisposition; - stream().oss << Catch::translateActiveException(); - captureResult( ResultWas::ThrewException ); - } - - void ResultBuilder::captureResult( ResultWas::OfType resultType ) { - setResultType( resultType ); - captureExpression(); - } - - void ResultBuilder::captureExpectedException( std::string const& expectedMessage ) { - if( expectedMessage.empty() ) - captureExpectedException( Matchers::Impl::MatchAllOf() ); - else - captureExpectedException( Matchers::Equals( expectedMessage ) ); - } - - - void ResultBuilder::captureExpectedException( Matchers::Impl::MatcherBase const& matcher ) { - - assert( !isFalseTest( m_assertionInfo.resultDisposition ) ); - AssertionResultData data = m_data; - data.resultType = ResultWas::Ok; - data.reconstructedExpression = capturedExpressionWithSecondArgument(m_assertionInfo.capturedExpression, m_assertionInfo.secondArg); - - std::string actualMessage = Catch::translateActiveException(); - if( !matcher.match( actualMessage ) ) { - data.resultType = ResultWas::ExpressionFailed; - data.reconstructedExpression = actualMessage; - } - AssertionResult result( m_assertionInfo, data ); - handleResult( result ); - } - - void ResultBuilder::captureExpression() { - AssertionResult result = build(); - handleResult( result ); - } - - void ResultBuilder::handleResult( AssertionResult const& result ) - { - getResultCapture().assertionEnded( result ); - - if( !result.isOk() ) { - if( getCurrentContext().getConfig()->shouldDebugBreak() ) - m_shouldDebugBreak = true; - if( getCurrentContext().getRunner()->aborting() || (m_assertionInfo.resultDisposition & ResultDisposition::Normal) ) - m_shouldThrow = true; - } - } - - void ResultBuilder::react() { -#if defined(CATCH_CONFIG_FAST_COMPILE) - 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 throw statement - /////////////////////////////////////////////////////////////////// - CATCH_BREAK_INTO_DEBUGGER(); - } -#endif - if( m_shouldThrow ) - throw Catch::TestFailureException(); - } - - bool ResultBuilder::shouldDebugBreak() const { return m_shouldDebugBreak; } - bool ResultBuilder::allowThrows() const { return getCurrentContext().getConfig()->allowThrows(); } - - AssertionResult ResultBuilder::build() const - { - return build( *this ); - } - - // CAVEAT: The returned AssertionResult stores a pointer to the argument expr, - // a temporary DecomposedExpression, which in turn holds references to - // operands, possibly temporary as well. - // It should immediately be passed to handleResult; if the expression - // needs to be reported, its string expansion must be composed before - // the temporaries are destroyed. - AssertionResult ResultBuilder::build( DecomposedExpression const& expr ) const - { - assert( m_data.resultType != ResultWas::Unknown ); - AssertionResultData data = m_data; - - if(m_usedStream) - data.message = m_stream().oss.str(); - data.decomposedExpression = &expr; // for lazy reconstruction - return AssertionResult( m_assertionInfo, data ); - } - - void ResultBuilder::reconstructExpression( std::string& dest ) const { - dest = capturedExpressionWithSecondArgument(m_assertionInfo.capturedExpression, m_assertionInfo.secondArg); - } - - void ResultBuilder::setExceptionGuard() { - m_guardException = true; - } - void ResultBuilder::unsetExceptionGuard() { - m_guardException = false; - } - -} // end namespace Catch - -#endif // TWOBLUECUBES_CATCH_RESULT_BUILDER_HPP_INCLUDED diff --git a/include/internal/catch_result_type.cpp b/include/internal/catch_result_type.cpp new file mode 100644 index 00000000..6e048865 --- /dev/null +++ b/include/internal/catch_result_type.cpp @@ -0,0 +1,28 @@ +/* + * Created by Phil on 07/01/2011. + * Copyright 2011 Two Blue Cubes Ltd. All rights reserved. + * + * Distributed under the Boost Software License, Version 1.0. (See accompanying + * file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) + */ + +#include "catch_result_type.h" + +namespace Catch { + + bool isOk( ResultWas::OfType resultType ) { + return ( resultType & ResultWas::FailureBit ) == 0; + } + bool isJustInfo( int flags ) { + return flags == ResultWas::Info; + } + + ResultDisposition::Flags operator | ( ResultDisposition::Flags lhs, ResultDisposition::Flags rhs ) { + return static_cast( static_cast( lhs ) | static_cast( rhs ) ); + } + + bool shouldContinueOnFailure( int flags ) { return ( flags & ResultDisposition::ContinueOnFailure ) != 0; } + bool isFalseTest( int flags ) { return ( flags & ResultDisposition::FalseTest ) != 0; } + bool shouldSuppressFailure( int flags ) { return ( flags & ResultDisposition::SuppressFail ) != 0; } + +} // end namespace Catch diff --git a/include/internal/catch_result_type.h b/include/internal/catch_result_type.h index 4c3d77dc..6934fc11 100644 --- a/include/internal/catch_result_type.h +++ b/include/internal/catch_result_type.h @@ -31,12 +31,8 @@ namespace Catch { }; }; - inline bool isOk( ResultWas::OfType resultType ) { - return ( resultType & ResultWas::FailureBit ) == 0; - } - inline bool isJustInfo( int flags ) { - return flags == ResultWas::Info; - } + bool isOk( ResultWas::OfType resultType ); + bool isJustInfo( int flags ); // ResultDisposition::Flags enum @@ -48,13 +44,11 @@ namespace Catch { SuppressFail = 0x08 // Failures are reported but do not fail the test }; }; - inline ResultDisposition::Flags operator | ( ResultDisposition::Flags lhs, ResultDisposition::Flags rhs ) { - return static_cast( static_cast( lhs ) | static_cast( rhs ) ); - } + ResultDisposition::Flags operator | ( ResultDisposition::Flags lhs, ResultDisposition::Flags rhs ); - inline bool shouldContinueOnFailure( int flags ) { return ( flags & ResultDisposition::ContinueOnFailure ) != 0; } - inline bool isFalseTest( int flags ) { return ( flags & ResultDisposition::FalseTest ) != 0; } - inline bool shouldSuppressFailure( int flags ) { return ( flags & ResultDisposition::SuppressFail ) != 0; } + bool shouldContinueOnFailure( int flags ); + bool isFalseTest( int flags ); + bool shouldSuppressFailure( int flags ); } // end namespace Catch diff --git a/include/internal/catch_run_context.cpp b/include/internal/catch_run_context.cpp new file mode 100644 index 00000000..11b85b24 --- /dev/null +++ b/include/internal/catch_run_context.cpp @@ -0,0 +1,335 @@ +#include "catch_run_context.h" +#include "catch_context.h" +#include "catch_enforce.h" +#include "catch_random_number_generator.h" +#include "catch_stream.h" + +#include +#include + +namespace Catch { + + StreamRedirect::StreamRedirect(std::ostream& stream, std::string& targetString) + : m_stream(stream), + m_prevBuf(stream.rdbuf()), + m_targetString(targetString) { + stream.rdbuf(m_oss.rdbuf()); + } + + StreamRedirect::~StreamRedirect() { + m_targetString += m_oss.str(); + m_stream.rdbuf(m_prevBuf); + } + + StdErrRedirect::StdErrRedirect(std::string & targetString) + :m_cerrBuf(cerr().rdbuf()), m_clogBuf(clog().rdbuf()), + m_targetString(targetString) { + cerr().rdbuf(m_oss.rdbuf()); + clog().rdbuf(m_oss.rdbuf()); + } + + 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_context.setRunner(this); + m_context.setConfig(m_config); + m_context.setResultCapture(this); + m_reporter->testRunStarting(m_runInfo); + } + + RunContext::~RunContext() { + m_reporter->testRunEnded(TestRunStats(m_runInfo, m_totals, aborting())); + } + + void RunContext::testGroupStarting(std::string const& testSpec, std::size_t groupIndex, std::size_t groupsCount) { + m_reporter->testGroupStarting(GroupInfo(testSpec, groupIndex, groupsCount)); + } + + void RunContext::testGroupEnded(std::string const& testSpec, Totals const& totals, std::size_t groupIndex, std::size_t groupsCount) { + m_reporter->testGroupEnded(TestGroupStats(GroupInfo(testSpec, groupIndex, groupsCount), totals, aborting())); + } + + Totals RunContext::runTest(TestCase const& testCase) { + Totals prevTotals = m_totals; + + std::string redirectedCout; + std::string redirectedCerr; + + TestCaseInfo testInfo = testCase.getTestCaseInfo(); + + m_reporter->testCaseStarting(testInfo); + + m_activeTestCase = &testCase; + + + ITracker& rootTracker = m_trackerContext.startRun(); + assert(rootTracker.isSectionTracker()); + static_cast(rootTracker).addInitialFilters(m_config->getSectionsToRun()); + do { + m_trackerContext.startCycle(); + m_testCaseTracker = &SectionTracker::acquire(m_trackerContext, TestCaseTracking::NameAndLocation(testInfo.name, testInfo.lineInfo)); + runCurrentTest(redirectedCout, redirectedCerr); + } while (!m_testCaseTracker->isSuccessfullyCompleted() && !aborting()); + + Totals deltaTotals = m_totals.delta(prevTotals); + if (testInfo.expectedToFail() && deltaTotals.testCases.passed > 0) { + deltaTotals.assertions.failed++; + deltaTotals.testCases.passed--; + deltaTotals.testCases.failed++; + } + m_totals.testCases += deltaTotals.testCases; + m_reporter->testCaseEnded(TestCaseStats(testInfo, + deltaTotals, + redirectedCout, + redirectedCerr, + aborting())); + + m_activeTestCase = nullptr; + m_testCaseTracker = nullptr; + + return deltaTotals; + } + + IConfigPtr RunContext::config() const { + return m_config; + } + + IStreamingReporter& RunContext::reporter() const { + 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++; + } else if (!result.isOk()) { + if( m_activeTestCase->getTestCaseInfo().okToFail() ) + m_totals.assertions.failedButOk++; + else + m_totals.assertions.failed++; + } + + // 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. + static_cast(m_reporter->assertionEnded(AssertionStats(result, m_messages, m_totals))); + + // Reset working state + m_lastAssertionInfo = { "", m_lastAssertionInfo.lineInfo, "{Unknown expression after the reported line}", m_lastAssertionInfo.resultDisposition }; + m_lastResult = result; + } + + bool RunContext::sectionStarted(SectionInfo const & sectionInfo, Counts & assertions) { + ITracker& sectionTracker = SectionTracker::acquire(m_trackerContext, TestCaseTracking::NameAndLocation(sectionInfo.name, sectionInfo.lineInfo)); + if (!sectionTracker.isOpen()) + return false; + m_activeSections.push_back(§ionTracker); + + m_lastAssertionInfo.lineInfo = sectionInfo.lineInfo; + + m_reporter->sectionStarting(sectionInfo); + + assertions = m_totals.assertions; + + return true; + } + + bool RunContext::testForMissingAssertions(Counts& assertions) { + if (assertions.total() != 0) + return false; + if (!m_config->warnAboutMissingAssertions()) + return false; + if (m_trackerContext.currentTracker().hasChildren()) + return false; + m_totals.assertions.failed++; + assertions.failed++; + return true; + } + + void RunContext::sectionEnded(SectionEndInfo const & endInfo) { + Counts assertions = m_totals.assertions - endInfo.prevAssertions; + bool missingAssertions = testForMissingAssertions(assertions); + + if (!m_activeSections.empty()) { + m_activeSections.back()->close(); + m_activeSections.pop_back(); + } + + m_reporter->sectionEnded(SectionStats(endInfo.sectionInfo, assertions, endInfo.durationInSeconds, missingAssertions)); + m_messages.clear(); + } + + void RunContext::sectionEndedEarly(SectionEndInfo const & endInfo) { + if (m_unfinishedSections.empty()) + m_activeSections.back()->fail(); + else + m_activeSections.back()->close(); + m_activeSections.pop_back(); + + m_unfinishedSections.push_back(endInfo); + } + void RunContext::benchmarkStarting( BenchmarkInfo const& info ) { + m_reporter->benchmarkStarting( info ); + } + void RunContext::benchmarkEnded( BenchmarkStats const& stats ) { + m_reporter->benchmarkEnded( stats ); + } + + void RunContext::pushScopedMessage(MessageInfo const & message) { + m_messages.push_back(message); + } + + void RunContext::popScopedMessage(MessageInfo const & message) { + m_messages.erase(std::remove(m_messages.begin(), m_messages.end(), message), m_messages.end()); + } + + std::string RunContext::getCurrentTestName() const { + return m_activeTestCase + ? m_activeTestCase->getTestCaseInfo().name + : std::string(); + } + + const AssertionResult * RunContext::getLastResult() const { + return &(*m_lastResult); + } + + void RunContext::exceptionEarlyReported() { + m_shouldReportUnexpected = false; + } + + void RunContext::handleFatalErrorCondition( StringRef message ) { + // First notify reporter that bad things happened + m_reporter->fatalErrorEncountered(message); + + // Don't rebuild the result -- the stringification itself can cause more fatal errors + // Instead, fake a result data. + AssertionResultData tempResult( ResultWas::FatalErrorCondition, { false } ); + tempResult.message = message; + AssertionResult result(m_lastAssertionInfo, tempResult); + + getResultCapture().assertionEnded(result); + + handleUnfinishedSections(); + + // Recreate section for test case (as we will lose the one that was in scope) + auto const& testCaseInfo = m_activeTestCase->getTestCaseInfo(); + SectionInfo testCaseSection(testCaseInfo.lineInfo, testCaseInfo.name, testCaseInfo.description); + + Counts assertions; + assertions.failed = 1; + SectionStats testCaseSectionStats(testCaseSection, assertions, 0, false); + m_reporter->sectionEnded(testCaseSectionStats); + + auto const& testInfo = m_activeTestCase->getTestCaseInfo(); + + Totals deltaTotals; + deltaTotals.testCases.failed = 1; + deltaTotals.assertions.failed = 1; + m_reporter->testCaseEnded(TestCaseStats(testInfo, + deltaTotals, + std::string(), + std::string(), + false)); + m_totals.testCases.failed++; + testGroupEnded(std::string(), m_totals, 1, 1); + m_reporter->testRunEnded(TestRunStats(m_runInfo, m_totals, false)); + } + + bool RunContext::lastAssertionPassed() { + return m_totals.assertions.passed == (m_prevPassed + 1); + } + + void RunContext::assertionPassed() { + ++m_totals.assertions.passed; + m_lastAssertionInfo.capturedExpression = "{Unknown expression after the reported line}"; + m_lastAssertionInfo.macroName = ""; + } + + void RunContext::assertionRun() { + m_prevPassed = m_totals.assertions.passed; + } + + bool RunContext::aborting() const { + return m_totals.assertions.failed == static_cast(m_config->abortAfter()); + } + + void RunContext::runCurrentTest(std::string & redirectedCout, std::string & redirectedCerr) { + auto const& testCaseInfo = m_activeTestCase->getTestCaseInfo(); + SectionInfo testCaseSection(testCaseInfo.lineInfo, testCaseInfo.name, testCaseInfo.description); + m_reporter->sectionStarting(testCaseSection); + Counts prevAssertions = m_totals.assertions; + double duration = 0; + m_shouldReportUnexpected = true; + try { + m_lastAssertionInfo = { "TEST_CASE", testCaseInfo.lineInfo, "", ResultDisposition::Normal }; + + seedRng(*m_config); + + Timer timer; + timer.start(); + if (m_reporter->getPreferences().shouldRedirectStdOut) { + StreamRedirect coutRedir(cout(), redirectedCout); + StdErrRedirect errRedir(redirectedCerr); + invokeActiveTestCase(); + } else { + invokeActiveTestCase(); + } + duration = timer.getElapsedSeconds(); + } catch (TestFailureException&) { + // This just means the test was aborted due to failure + } 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(); + } + } + m_testCaseTracker->close(); + handleUnfinishedSections(); + m_messages.clear(); + + Counts assertions = m_totals.assertions - prevAssertions; + bool missingAssertions = testForMissingAssertions(assertions); + SectionStats testCaseSectionStats(testCaseSection, assertions, duration, missingAssertions); + m_reporter->sectionEnded(testCaseSectionStats); + } + + void RunContext::invokeActiveTestCase() { + FatalConditionHandler fatalConditionHandler; // Handle signals + m_activeTestCase->invoke(); + fatalConditionHandler.reset(); + } + + void RunContext::handleUnfinishedSections() { + // If sections ended prematurely due to an exception we stored their + // infos here so we can tear them down outside the unwind process. + for (auto it = m_unfinishedSections.rbegin(), + itEnd = m_unfinishedSections.rend(); + it != itEnd; + ++it) + sectionEnded(*it); + m_unfinishedSections.clear(); + } + + IResultCapture& getResultCapture() { + if (auto* capture = getCurrentContext().getResultCapture()) + return *capture; + else + CATCH_INTERNAL_ERROR("No result capture instance"); + } +} diff --git a/include/internal/catch_run_context.h b/include/internal/catch_run_context.h new file mode 100644 index 00000000..56e03a7a --- /dev/null +++ b/include/internal/catch_run_context.h @@ -0,0 +1,146 @@ + /* + * Created by Phil on 22/10/2010. + * Copyright 2010 Two Blue Cubes Ltd. All rights reserved. + * + * Distributed under the Boost Software License, Version 1.0. (See accompanying + * file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) + */ +#ifndef TWOBLUECUBES_CATCH_RUNNER_IMPL_HPP_INCLUDED +#define TWOBLUECUBES_CATCH_RUNNER_IMPL_HPP_INCLUDED + +#include "catch_interfaces_runner.h" +#include "catch_interfaces_reporter.h" +#include "catch_interfaces_exception.h" +#include "catch_config.hpp" +#include "catch_test_registry.h" +#include "catch_test_case_info.h" +#include "catch_capture.hpp" +#include "catch_totals.h" +#include "catch_test_spec.h" +#include "catch_test_case_tracker.h" +#include "catch_timer.h" +#include "catch_assertionhandler.h" +#include "catch_fatal_condition.h" + +#include + +namespace Catch { + + struct IMutableContext; + + class StreamRedirect { + + public: + StreamRedirect(std::ostream& stream, std::string& targetString); + + ~StreamRedirect(); + + private: + std::ostream& m_stream; + std::streambuf* m_prevBuf; + std::ostringstream 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; + std::ostringstream m_oss; + std::string& m_targetString; + }; + + /////////////////////////////////////////////////////////////////////////// + + class RunContext : public IResultCapture, public IRunner { + + public: + RunContext( RunContext const& ) = delete; + RunContext& operator =( RunContext const& ) = delete; + + explicit RunContext(IConfigPtr const& _config, IStreamingReporterPtr&& reporter); + + virtual ~RunContext(); + + 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 + + + void assertionStarting(AssertionInfo const& info) override; + void assertionEnded(AssertionResult const& result) 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 benchmarkStarting( BenchmarkInfo const& info ) override; + void benchmarkEnded( BenchmarkStats const& stats ) override; + + void pushScopedMessage(MessageInfo const& message) override; + void popScopedMessage(MessageInfo const& message) override; + + std::string getCurrentTestName() const override; + + const AssertionResult* getLastResult() const override; + + void exceptionEarlyReported() override; + + void handleFatalErrorCondition( StringRef message ) override; + + bool lastAssertionPassed() override; + + 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 invokeActiveTestCase(); + + private: + + void handleUnfinishedSections(); + + TestRunInfo m_runInfo; + IMutableContext& m_context; + TestCase const* m_activeTestCase = nullptr; + ITracker* m_testCaseTracker; + Option m_lastResult; + + IConfigPtr m_config; + Totals m_totals; + IStreamingReporterPtr m_reporter; + std::vector m_messages; + AssertionInfo m_lastAssertionInfo; + std::vector m_unfinishedSections; + std::vector m_activeSections; + TrackerContext m_trackerContext; + std::size_t m_prevPassed = 0; + bool m_shouldReportUnexpected = true; + }; + + IResultCapture& getResultCapture(); + +} // end namespace Catch + +#endif // TWOBLUECUBES_CATCH_RUNNER_IMPL_HPP_INCLUDED diff --git a/include/internal/catch_section.hpp b/include/internal/catch_section.cpp similarity index 73% rename from include/internal/catch_section.hpp rename to include/internal/catch_section.cpp index aab721f6..c2572d5e 100644 --- a/include/internal/catch_section.hpp +++ b/include/internal/catch_section.cpp @@ -5,24 +5,12 @@ * Distributed under the Boost Software License, Version 1.0. (See accompanying * file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) */ -#ifndef TWOBLUECUBES_CATCH_SECTION_HPP_INCLUDED -#define TWOBLUECUBES_CATCH_SECTION_HPP_INCLUDED #include "catch_section.h" #include "catch_capture.hpp" -#include "catch_compiler_capabilities.h" namespace Catch { - SectionInfo::SectionInfo - ( SourceLineInfo const& _lineInfo, - std::string const& _name, - std::string const& _description ) - : name( _name ), - description( _description ), - lineInfo( _lineInfo ) - {} - Section::Section( SectionInfo const& info ) : m_info( info ), m_sectionIncluded( getResultCapture().sectionStarted( m_info, m_assertions ) ) @@ -54,5 +42,3 @@ namespace Catch { } // end namespace Catch - -#endif // TWOBLUECUBES_CATCH_SECTION_HPP_INCLUDED diff --git a/include/internal/catch_section.h b/include/internal/catch_section.h index d8b3ae42..1e5b1c3c 100644 --- a/include/internal/catch_section.h +++ b/include/internal/catch_section.h @@ -9,7 +9,7 @@ #define TWOBLUECUBES_CATCH_SECTION_H_INCLUDED #include "catch_section_info.h" -#include "catch_totals.hpp" +#include "catch_totals.h" #include "catch_timer.h" #include @@ -22,7 +22,7 @@ namespace Catch { ~Section(); // This indicates whether the section should be executed or not - operator bool() const; + explicit operator bool() const; private: SectionInfo m_info; @@ -35,12 +35,7 @@ namespace Catch { } // end namespace Catch -#ifdef CATCH_CONFIG_VARIADIC_MACROS #define INTERNAL_CATCH_SECTION( ... ) \ if( Catch::Section const& INTERNAL_CATCH_UNIQUE_NAME( catch_internal_Section ) = Catch::SectionInfo( CATCH_INTERNAL_LINEINFO, __VA_ARGS__ ) ) -#else - #define INTERNAL_CATCH_SECTION( name, desc ) \ - if( Catch::Section const& INTERNAL_CATCH_UNIQUE_NAME( catch_internal_Section ) = Catch::SectionInfo( CATCH_INTERNAL_LINEINFO, name, desc ) ) -#endif #endif // TWOBLUECUBES_CATCH_SECTION_H_INCLUDED diff --git a/include/internal/catch_section_info.cpp b/include/internal/catch_section_info.cpp new file mode 100644 index 00000000..e2846b8a --- /dev/null +++ b/include/internal/catch_section_info.cpp @@ -0,0 +1,25 @@ +/* + * Created by Martin on 01/08/2017. + * + * Distributed under the Boost Software License, Version 1.0. (See accompanying + * file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) + */ + +#include "catch_section_info.h" + +namespace Catch { + + SectionInfo::SectionInfo + ( SourceLineInfo const& _lineInfo, + std::string const& _name, + std::string const& _description ) + : name( _name ), + description( _description ), + lineInfo( _lineInfo ) + {} + + SectionEndInfo::SectionEndInfo( SectionInfo const& _sectionInfo, Counts const& _prevAssertions, double _durationInSeconds ) + : sectionInfo( _sectionInfo ), prevAssertions( _prevAssertions ), durationInSeconds( _durationInSeconds ) + {} + +} // end namespace Catch diff --git a/include/internal/catch_section_info.h b/include/internal/catch_section_info.h index c9f1c7f1..86681ba7 100644 --- a/include/internal/catch_section_info.h +++ b/include/internal/catch_section_info.h @@ -9,7 +9,7 @@ #define TWOBLUECUBES_CATCH_SECTION_INFO_H_INCLUDED #include "catch_common.h" -#include "catch_totals.hpp" +#include "catch_totals.h" #include @@ -27,9 +27,7 @@ namespace Catch { }; struct SectionEndInfo { - SectionEndInfo( SectionInfo const& _sectionInfo, Counts const& _prevAssertions, double _durationInSeconds ) - : sectionInfo( _sectionInfo ), prevAssertions( _prevAssertions ), durationInSeconds( _durationInSeconds ) - {} + SectionEndInfo( SectionInfo const& _sectionInfo, Counts const& _prevAssertions, double _durationInSeconds ); SectionInfo sectionInfo; Counts prevAssertions; diff --git a/include/internal/catch_section_info.hpp b/include/internal/catch_section_info.hpp deleted file mode 100644 index aebbf6a5..00000000 --- a/include/internal/catch_section_info.hpp +++ /dev/null @@ -1,110 +0,0 @@ -/* - * Created by Phil Nash on 4/5/2012 - * Copyright 2012 Two Blue Cubes Ltd. All rights reserved. - * - * Distributed under the Boost Software License, Version 1.0. (See accompanying - * file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) - */ -#ifndef TWOBLUECUBES_CATCH_SECTION_INFO_HPP_INCLUDED -#define TWOBLUECUBES_CATCH_SECTION_INFO_HPP_INCLUDED - -#include "catch_section_info.h" - -namespace Catch { - - class RunningSection { - public: - - typedef std::vector SubSections; - - enum State { - Root, - Unknown, - Branch, - TestedBranch, - TestedLeaf - }; - - RunningSection( RunningSection* parent, std::string const& name ) - : m_state( Unknown ), - m_parent( parent ), - m_name( name ) - {} - - RunningSection( std::string const& name ) - : m_state( Root ), - m_parent( CATCH_NULL ), - m_name( name ) - {} - - ~RunningSection() { - deleteAll( m_subSections ); - } - - std::string getName() const { - return m_name; - } - - bool shouldRun() const { - return m_state < TestedBranch; - } - - bool isBranch() const { - return m_state == Branch; - } - - const RunningSection* getParent() const { - return m_parent; - } - - bool hasUntestedSections() const { - if( m_state == Unknown ) - return true; - for( SubSections::const_iterator it = m_subSections.begin(); - it != m_subSections.end(); - ++it) - if( (*it)->hasUntestedSections() ) - return true; - return false; - } - - // Mutable methods: - - RunningSection* getParent() { - return m_parent; - } - - RunningSection* findOrAddSubSection( std::string const& name, bool& changed ) { - for( SubSections::const_iterator it = m_subSections.begin(); - it != m_subSections.end(); - ++it) - if( (*it)->getName() == name ) - return *it; - RunningSection* subSection = new RunningSection( this, name ); - m_subSections.push_back( subSection ); - m_state = Branch; - changed = true; - return subSection; - } - - bool ran() { - if( m_state >= Branch ) - return false; - m_state = TestedLeaf; - return true; - } - - void ranToCompletion() { - if( m_state == Branch && !hasUntestedSections() ) - m_state = TestedBranch; - } - - private: - State m_state; - RunningSection* m_parent; - std::string m_name; - SubSections m_subSections; - }; -} - -#endif // TWOBLUECUBES_CATCH_SECTION_INFO_HPP_INCLUDED diff --git a/include/internal/catch_session.cpp b/include/internal/catch_session.cpp new file mode 100644 index 00000000..5a6a8434 --- /dev/null +++ b/include/internal/catch_session.cpp @@ -0,0 +1,276 @@ +/* + * Created by Martin on 31/08/2017. + * + * Distributed under the Boost Software License, Version 1.0. (See accompanying + * file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) + */ + +#include "catch_session.h" +#include "catch_commandline.h" +#include "catch_console_colour.h" +#include "catch_enforce.h" +#include "catch_list.h" +#include "catch_run_context.h" +#include "catch_stream.h" +#include "catch_test_spec.h" +#include "catch_version.h" +#include "catch_interfaces_reporter.h" +#include "catch_random_number_generator.h" +#include "catch_startup_exception_registry.h" +#include "catch_text.h" + +#include +#include + + +namespace { + const int MaxExitCode = 255; + using Catch::IStreamingReporterPtr; + using Catch::IConfigPtr; + using Catch::Config; + + 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; + } + +#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 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); + } + + 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; + + 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); + } + } + +} + +namespace Catch { + + Session::Session() { + static bool alreadyInstantiated = false; + if( alreadyInstantiated ) { + try { CATCH_INTERNAL_ERROR( "Only one instance of Catch::Session can ever be used" ); } + catch(...) { getMutableRegistryHub().registerStartupException(); } + } + + const auto& exceptions = getRegistryHub().getStartupExceptionRegistry().getExceptions(); + if ( !exceptions.empty() ) { + m_startupExceptions = true; + Colour colourGuard( Colour::Red ); + Catch::cerr() << "Errors occured during startup!" << '\n'; + // iterate over all exceptions and notify user + for ( const auto& ex_ptr : exceptions ) { + try { + std::rethrow_exception(ex_ptr); + } catch ( std::exception const& ex ) { + Catch::cerr() << Column( ex.what() ).indent(2) << '\n'; + } + } + } + + alreadyInstantiated = true; + m_cli = makeCommandLineParser( m_configData ); + } + Session::~Session() { + Catch::cleanUp(); + } + + void Session::showHelp() const { + Catch::cout() + << "\nCatch v" << libraryVersion() << "\n" + << m_cli << std::endl + << "For more detailed usage please see the project docs\n" << std::endl; + } + void Session::libIdentify() { + Catch::cout() + << std::left << std::setw(16) << "description: " << "A Catch test executable\n" + << std::left << std::setw(16) << "category: " << "testframework\n" + << std::left << std::setw(16) << "framework: " << "Catch Test\n" + << std::left << std::setw(16) << "version: " << libraryVersion() << std::endl; + } + + int Session::applyCommandLine( int argc, char* argv[] ) { + if( m_startupExceptions ) + return 1; + + auto result = m_cli.parse( clara::Args( argc, argv ) ); + if( !result ) { + Catch::cerr() + << Colour( Colour::Red ) + << "\nError(s) in input:\n" + << Column( result.errorMessage() ).indent( 2 ) + << "\n\n"; + Catch::cerr() << "Run with -? for usage\n" << std::endl; + return MaxExitCode; + } + + if( m_configData.showHelp ) + showHelp(); + if( m_configData.libIdentify ) + libIdentify(); + m_config.reset(); + return 0; + } + + void Session::useConfigData( ConfigData const& configData ) { + m_configData = configData; + m_config.reset(); + } + + int Session::run( int argc, char* argv[] ) { + if( m_startupExceptions ) + return 1; + int returnCode = applyCommandLine( argc, argv ); + if( returnCode == 0 ) + returnCode = run(); + return returnCode; + } + +#if defined(WIN32) && defined(UNICODE) + int Session::run( int argc, wchar_t* const argv[] ) { + + char **utf8Argv = new char *[ argc ]; + + for ( int i = 0; i < argc; ++i ) { + int bufSize = WideCharToMultiByte( CP_UTF8, 0, argv[i], -1, NULL, 0, NULL, NULL ); + + utf8Argv[ i ] = new char[ bufSize ]; + + WideCharToMultiByte( CP_UTF8, 0, argv[i], -1, utf8Argv[i], bufSize, NULL, NULL ); + } + + int returnCode = run( argc, utf8Argv ); + + for ( int i = 0; i < argc; ++i ) + delete [] utf8Argv[ i ]; + + delete [] utf8Argv; + + return returnCode; + } +#endif + int Session::run() { + if( ( m_configData.waitForKeypress & WaitForKeypress::BeforeStart ) != 0 ) { + Catch::cout() << "...waiting for enter/ return before starting" << std::endl; + static_cast(std::getchar()); + } + int exitCode = runInternal(); + if( ( m_configData.waitForKeypress & WaitForKeypress::BeforeExit ) != 0 ) { + Catch::cout() << "...waiting for enter/ return before exiting, with code: " << exitCode << std::endl; + static_cast(std::getchar()); + } + return exitCode; + } + + clara::Parser const& Session::cli() const { + return m_cli; + } + void Session::cli( clara::Parser const& newParser ) { + m_cli = newParser; + } + ConfigData& Session::configData() { + return m_configData; + } + Config& Session::config() { + if( !m_config ) + m_config = std::make_shared( m_configData ); + return *m_config; + } + + int Session::runInternal() { + if( m_startupExceptions ) + return 1; + + if( m_configData.showHelp || m_configData.libIdentify ) + return 0; + + try + { + config(); // Force config to be constructed + + seedRng( *m_config ); + + if( m_configData.filenamesAsTags ) + applyFilenamesAsTags( *m_config ); + + // Handle list request + if( Option listed = list( config() ) ) + return static_cast( *listed ); + + return (std::min)( MaxExitCode, static_cast( runTests( m_config ).assertions.failed ) ); + } + catch( std::exception& ex ) { + Catch::cerr() << ex.what() << std::endl; + return MaxExitCode; + } + } + +} // end namespace Catch diff --git a/include/internal/catch_session.h b/include/internal/catch_session.h new file mode 100644 index 00000000..b814aa6a --- /dev/null +++ b/include/internal/catch_session.h @@ -0,0 +1,53 @@ +/* + * Created by Phil on 31/10/2010. + * Copyright 2010 Two Blue Cubes Ltd. All rights reserved. + * + * Distributed under the Boost Software License, Version 1.0. (See accompanying + * file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) + */ +#ifndef TWOBLUECUBES_CATCH_RUNNER_HPP_INCLUDED +#define TWOBLUECUBES_CATCH_RUNNER_HPP_INCLUDED + +#include "catch_commandline.h" +#include "catch_config.hpp" +#include "catch_text.h" + +#include + +namespace Catch { + + class Session : NonCopyable { + public: + + Session(); + ~Session() override; + + void showHelp() const; + void libIdentify(); + + int applyCommandLine( int argc, char* argv[] ); + + void useConfigData( ConfigData const& configData ); + + int run( int argc, char* argv[] ); + #if defined(WIN32) && defined(UNICODE) + int run( int argc, wchar_t* const argv[] ); + #endif + int run(); + + clara::Parser const& cli() const; + void cli( clara::Parser const& newParser ); + ConfigData& configData(); + Config& config(); + private: + int runInternal(); + + clara::Parser m_cli; + ConfigData m_configData; + std::shared_ptr m_config; + bool m_startupExceptions = false; + }; + +} // end namespace Catch + +#endif // TWOBLUECUBES_CATCH_RUNNER_HPP_INCLUDED diff --git a/include/internal/catch_startup_exception_registry.cpp b/include/internal/catch_startup_exception_registry.cpp new file mode 100644 index 00000000..d58d18a5 --- /dev/null +++ b/include/internal/catch_startup_exception_registry.cpp @@ -0,0 +1,26 @@ +/* + * Created by Martin on 04/06/2017. + * Copyright 2017 Two Blue Cubes Ltd. All rights reserved. + * + * Distributed under the Boost Software License, Version 1.0. (See accompanying + * file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) + */ + +#include "catch_startup_exception_registry.h" + +namespace Catch { + void StartupExceptionRegistry::add( std::exception_ptr const& exception ) noexcept { + try { + m_exceptions.push_back(exception); + } + catch(...) { + // If we run out of memory during start-up there's really not a lot more we can do about it + std::terminate(); + } + } + + std::vector const& StartupExceptionRegistry::getExceptions() const noexcept { + return m_exceptions; + } + +} // end namespace Catch diff --git a/include/internal/catch_startup_exception_registry.h b/include/internal/catch_startup_exception_registry.h new file mode 100644 index 00000000..feb56601 --- /dev/null +++ b/include/internal/catch_startup_exception_registry.h @@ -0,0 +1,27 @@ +/* + * Created by Martin on 04/06/2017. + * Copyright 2017 Two Blue Cubes Ltd. All rights reserved. + * + * Distributed under the Boost Software License, Version 1.0. (See accompanying + * file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) + */ +#ifndef TWOBLUECUBES_CATCH_STARTUP_EXCEPTION_REGISTRY_H_INCLUDED +#define TWOBLUECUBES_CATCH_STARTUP_EXCEPTION_REGISTRY_H_INCLUDED + + +#include +#include + +namespace Catch { + + class StartupExceptionRegistry { + public: + void add(std::exception_ptr const& exception) noexcept; + std::vector const& getExceptions() const noexcept; + private: + std::vector m_exceptions; + }; + +} // end namespace Catch + +#endif // TWOBLUECUBES_CATCH_STARTUP_EXCEPTION_REGISTRY_H_INCLUDED diff --git a/include/internal/catch_stream.hpp b/include/internal/catch_stream.cpp similarity index 81% rename from include/internal/catch_stream.hpp rename to include/internal/catch_stream.cpp index b8838d34..73889ce8 100644 --- a/include/internal/catch_stream.hpp +++ b/include/internal/catch_stream.cpp @@ -6,11 +6,11 @@ * file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) * */ -#ifndef TWOBLUECUBES_CATCH_STREAM_HPP_INCLUDED -#define TWOBLUECUBES_CATCH_STREAM_HPP_INCLUDED +#include "catch_common.h" +#include "catch_enforce.h" #include "catch_stream.h" -#include "catch_debugger.h" +#include "catch_debug_console.h" #include #include @@ -18,7 +18,7 @@ namespace Catch { - template + template class StreamBufImpl : public StreamBufBase { char data[bufferSize]; WriterF m_writer; @@ -28,12 +28,12 @@ namespace Catch { setp( data, data + sizeof(data) ); } - ~StreamBufImpl() CATCH_NOEXCEPT { - sync(); + ~StreamBufImpl() noexcept { + StreamBufImpl::sync(); } private: - int overflow( int c ) { + int overflow( int c ) override { sync(); if( c != EOF ) { @@ -45,7 +45,7 @@ namespace Catch { return 0; } - int sync() { + int sync() override { if( pbase() != pptr() ) { m_writer( std::string( pbase(), static_cast( pptr() - pbase() ) ) ); setp( pbase(), epptr() ); @@ -57,13 +57,11 @@ namespace Catch { /////////////////////////////////////////////////////////////////////////// + Catch::IStream::~IStream() = default; + FileStream::FileStream( std::string const& filename ) { m_ofs.open( filename.c_str() ); - if( m_ofs.fail() ) { - std::ostringstream oss; - oss << "Unable to open file: '" << filename << '\''; - throw std::domain_error( oss.str() ); - } + CATCH_ENFORCE( !m_ofs.fail(), "Unable to open file: '" << filename << "'" ); } std::ostream& FileStream::stream() const { @@ -108,5 +106,3 @@ namespace Catch { } #endif } - -#endif // TWOBLUECUBES_CATCH_STREAM_HPP_INCLUDED diff --git a/include/internal/catch_stream.h b/include/internal/catch_stream.h index 4323c318..41780b98 100644 --- a/include/internal/catch_stream.h +++ b/include/internal/catch_stream.h @@ -9,7 +9,6 @@ #ifndef TWOBLUECUBES_CATCH_STREAM_H_INCLUDED #define TWOBLUECUBES_CATCH_STREAM_H_INCLUDED -#include "catch_compiler_capabilities.h" #include "catch_streambuf.h" #include @@ -25,7 +24,7 @@ namespace Catch { struct IStream { - virtual ~IStream() CATCH_NOEXCEPT; + virtual ~IStream(); virtual std::ostream& stream() const = 0; }; @@ -33,9 +32,9 @@ namespace Catch { mutable std::ofstream m_ofs; public: FileStream( std::string const& filename ); - virtual ~FileStream() CATCH_NOEXCEPT; + ~FileStream() override = default; public: // IStream - virtual std::ostream& stream() const CATCH_OVERRIDE; + std::ostream& stream() const override; }; @@ -43,22 +42,22 @@ namespace Catch { mutable std::ostream m_os; public: CoutStream(); - virtual ~CoutStream() CATCH_NOEXCEPT; + ~CoutStream() override = default; public: // IStream - virtual std::ostream& stream() const CATCH_OVERRIDE; + std::ostream& stream() const override; }; class DebugOutStream : public IStream { - CATCH_AUTO_PTR( StreamBufBase ) m_streamBuf; + std::unique_ptr m_streamBuf; mutable std::ostream m_os; public: DebugOutStream(); - virtual ~DebugOutStream() CATCH_NOEXCEPT; + ~DebugOutStream() override = default; public: // IStream - virtual std::ostream& stream() const CATCH_OVERRIDE; + std::ostream& stream() const override; }; } diff --git a/include/internal/catch_streambuf.cpp b/include/internal/catch_streambuf.cpp new file mode 100644 index 00000000..608917fb --- /dev/null +++ b/include/internal/catch_streambuf.cpp @@ -0,0 +1,12 @@ +/* + * Created by Martin on 31/08/2017. + * + * Distributed under the Boost Software License, Version 1.0. (See accompanying + * file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) + */ + +#include "catch_streambuf.h" + +namespace Catch { + StreamBufBase::~StreamBufBase() = default; +} diff --git a/include/internal/catch_streambuf.h b/include/internal/catch_streambuf.h index 4f5e238c..18b94ccb 100644 --- a/include/internal/catch_streambuf.h +++ b/include/internal/catch_streambuf.h @@ -8,15 +8,13 @@ #ifndef TWOBLUECUBES_CATCH_STREAMBUF_H_INCLUDED #define TWOBLUECUBES_CATCH_STREAMBUF_H_INCLUDED -#include "catch_compiler_capabilities.h" - #include namespace Catch { class StreamBufBase : public std::streambuf { public: - virtual ~StreamBufBase() CATCH_NOEXCEPT; + virtual ~StreamBufBase(); }; } diff --git a/include/internal/catch_common.hpp b/include/internal/catch_string_manip.cpp similarity index 60% rename from include/internal/catch_common.hpp rename to include/internal/catch_string_manip.cpp index 9f943777..84762044 100644 --- a/include/internal/catch_common.hpp +++ b/include/internal/catch_string_manip.cpp @@ -1,15 +1,14 @@ /* - * Created by Phil on 27/11/2013. - * Copyright 2013 Two Blue Cubes Ltd. All rights reserved. + * Created by Martin on 25/07/2017. * * Distributed under the Boost Software License, Version 1.0. (See accompanying * file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) */ -#ifndef TWOBLUECUBES_CATCH_COMMON_HPP_INCLUDED -#define TWOBLUECUBES_CATCH_COMMON_HPP_INCLUDED -#include "catch_common.h" +#include "catch_string_manip.h" +#include +#include #include #include @@ -75,45 +74,4 @@ namespace Catch { return os; } - SourceLineInfo::SourceLineInfo() : file(""), line( 0 ){} - SourceLineInfo::SourceLineInfo( char const* _file, std::size_t _line ) - : file( _file ), - line( _line ) - {} - bool SourceLineInfo::empty() const { - return file[0] == '\0'; - } - bool SourceLineInfo::operator == ( SourceLineInfo const& other ) const { - return line == other.line && (file == other.file || std::strcmp(file, other.file) == 0); - } - bool SourceLineInfo::operator < ( SourceLineInfo const& other ) const { - return line < other.line || ( line == other.line && (std::strcmp(file, other.file) < 0)); - } - - void seedRng( IConfig const& config ) { - if( config.rngSeed() != 0 ) - std::srand( config.rngSeed() ); - } - unsigned int rngSeed() { - return getCurrentContext().getConfig()->rngSeed(); - } - - std::ostream& operator << ( std::ostream& os, SourceLineInfo const& info ) { -#ifndef __GNUG__ - os << info.file << '(' << info.line << ')'; -#else - os << info.file << ':' << info.line; -#endif - return os; - } - - void throwLogicError( std::string const& message, SourceLineInfo const& locationInfo ) { - std::ostringstream oss; - oss << locationInfo << ": Internal Catch error: '" << message << '\''; - if( alwaysTrue() ) - throw std::logic_error( oss.str() ); - } } - -#endif // TWOBLUECUBES_CATCH_COMMON_HPP_INCLUDED - diff --git a/include/internal/catch_string_manip.h b/include/internal/catch_string_manip.h new file mode 100644 index 00000000..6292cd57 --- /dev/null +++ b/include/internal/catch_string_manip.h @@ -0,0 +1,36 @@ +/* + * Created by Martin on 25/07/2017. + * + * Distributed under the Boost Software License, Version 1.0. (See accompanying + * file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) + */ +#ifndef TWOBLUECUBES_CATCH_STRING_MANIP_H_INCLUDED +#define TWOBLUECUBES_CATCH_STRING_MANIP_H_INCLUDED + +#include +#include + +namespace Catch { + + bool startsWith( std::string const& s, std::string const& prefix ); + bool startsWith( std::string const& s, char prefix ); + bool endsWith( std::string const& s, std::string const& suffix ); + bool endsWith( std::string const& s, char suffix ); + bool contains( std::string const& s, std::string const& infix ); + void toLowerInPlace( std::string& s ); + std::string toLower( std::string const& s ); + std::string trim( std::string const& str ); + bool replaceInPlace( std::string& str, std::string const& replaceThis, std::string const& withThis ); + + struct pluralise { + pluralise( std::size_t count, std::string const& label ); + + friend std::ostream& operator << ( std::ostream& os, pluralise const& pluraliser ); + + std::size_t m_count; + std::string m_label; + }; +} + +#endif // TWOBLUECUBES_CATCH_STRING_MANIP_H_INCLUDED + diff --git a/include/internal/catch_stringref.cpp b/include/internal/catch_stringref.cpp new file mode 100644 index 00000000..059f1c6f --- /dev/null +++ b/include/internal/catch_stringref.cpp @@ -0,0 +1,174 @@ +/* + * Copyright 2016 Two Blue Cubes Ltd. All rights reserved. + * + * Distributed under the Boost Software License, Version 1.0. (See accompanying + * file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) + */ + + +#if defined(__clang__) +# pragma clang diagnostic push +# pragma clang diagnostic ignored "-Wexit-time-destructors" +#endif + +#include "catch_stringref.h" + +#include +#include +#include + + + +namespace Catch { + + auto getEmptyStringRef() -> StringRef { + static StringRef s_emptyStringRef(""); + return s_emptyStringRef; + } + + StringRef::StringRef() noexcept + : StringRef( getEmptyStringRef() ) + {} + + StringRef::StringRef( StringRef const& other ) noexcept + : m_start( other.m_start ), + m_size( other.m_size ) + {} + + StringRef::StringRef( StringRef&& other ) noexcept + : m_start( other.m_start ), + m_size( other.m_size ), + m_data( other.m_data ) + { + other.m_data = nullptr; + } + + StringRef::StringRef( char const* rawChars ) noexcept + : m_start( rawChars ), + m_size( static_cast( std::strlen( rawChars ) ) ) + { + assert( rawChars != nullptr ); + } + + StringRef::StringRef( char const* rawChars, size_type size ) noexcept + : m_start( rawChars ), + m_size( size ) + { + size_type rawSize = rawChars == nullptr ? 0 : static_cast( std::strlen( rawChars ) ); + if( rawSize < size ) + m_size = rawSize; + } + + StringRef::StringRef( std::string const& stdString ) noexcept + : m_start( stdString.c_str() ), + m_size( stdString.size() ) + {} + + StringRef::~StringRef() noexcept { + delete[] m_data; + } + + auto StringRef::operator = ( StringRef other ) noexcept -> StringRef& { + swap( other ); + return *this; + } + StringRef::operator std::string() const { + return std::string( m_start, m_size ); + } + + void StringRef::swap( StringRef& other ) noexcept { + std::swap( m_start, other.m_start ); + std::swap( m_size, other.m_size ); + std::swap( m_data, other.m_data ); + } + + auto StringRef::c_str() const -> char const* { + if( isSubstring() ) + const_cast( this )->takeOwnership(); + return m_start; + } + auto StringRef::data() const noexcept -> char const* { + return m_start; + } + + auto StringRef::isOwned() const noexcept -> bool { + return m_data != nullptr; + } + auto StringRef::isSubstring() const noexcept -> bool { + return m_start[m_size] != '\0'; + } + + void StringRef::takeOwnership() { + if( !isOwned() ) { + m_data = new char[m_size+1]; + memcpy( m_data, m_start, m_size ); + m_data[m_size] = '\0'; + m_start = m_data; + } + } + auto StringRef::substr( size_type start, size_type size ) const noexcept -> StringRef { + if( start < m_size ) + return StringRef( m_start+start, size ); + else + return StringRef(); + } + auto StringRef::operator == ( StringRef const& other ) const noexcept -> bool { + return + size() == other.size() && + (std::strncmp( m_start, other.m_start, size() ) == 0); + } + auto StringRef::operator != ( StringRef const& other ) const noexcept -> bool { + return !operator==( other ); + } + + auto StringRef::operator[](size_type index) const noexcept -> char { + return m_start[index]; + } + + auto StringRef::empty() const noexcept -> bool { + return m_size == 0; + } + + auto StringRef::size() const noexcept -> size_type { + return m_size; + } + auto StringRef::numberOfCharacters() const noexcept -> size_type { + size_type noChars = m_size; + // Make adjustments for uft encodings + for( size_type i=0; i < m_size; ++i ) { + char c = m_start[i]; + if( ( c & 0b11000000 ) == 0b11000000 ) { + if( ( c & 0b11100000 ) == 0b11000000 ) + noChars--; + else if( ( c & 0b11110000 ) == 0b11100000 ) + noChars-=2; + else if( ( c & 0b11111000 ) == 0b11110000 ) + noChars-=3; + } + } + return noChars; + } + + auto operator + ( StringRef const& lhs, StringRef const& rhs ) -> std::string { + std::string str; + str.reserve( lhs.size() + rhs.size() ); + str += lhs; + str += rhs; + return str; + } + auto operator + ( StringRef const& lhs, const char* rhs ) -> std::string { + return std::string( lhs ) + std::string( rhs ); + } + auto operator + ( char const* lhs, StringRef const& rhs ) -> std::string { + return std::string( lhs ) + std::string( rhs ); + } + + auto operator << ( std::ostream& os, StringRef const& str ) -> std::ostream& { + return os << str.c_str(); + } + +} // namespace Catch + +#if defined(__clang__) +# pragma clang diagnostic pop +#endif diff --git a/include/internal/catch_stringref.h b/include/internal/catch_stringref.h new file mode 100644 index 00000000..b2aad29c --- /dev/null +++ b/include/internal/catch_stringref.h @@ -0,0 +1,80 @@ +/* + * Copyright 2016 Two Blue Cubes Ltd. All rights reserved. + * + * Distributed under the Boost Software License, Version 1.0. (See accompanying + * file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) + */ +#ifndef CATCH_STRINGREF_H_INCLUDED +#define CATCH_STRINGREF_H_INCLUDED + +#include +#include +#include + +namespace Catch { + + class StringData; + + /// A non-owning string class (similar to the forthcoming std::string_view) + /// Note that, because a StringRef may be a substring of another string, + /// it may not be null terminated. c_str() must return a null terminated + /// string, however, and so the StringRef will internally take ownership + /// (taking a copy), if necessary. In theory this ownership is not externally + /// visible - but it does mean (substring) StringRefs should not be shared between + /// threads. + class StringRef { + friend struct StringRefTestAccess; + + using size_type = std::size_t; + + char const* m_start; + size_type m_size; + + char* m_data = nullptr; + + void takeOwnership(); + + public: // construction/ assignment + StringRef() noexcept; + StringRef( StringRef const& other ) noexcept; + StringRef( StringRef&& other ) noexcept; + StringRef( char const* rawChars ) noexcept; + StringRef( char const* rawChars, size_type size ) noexcept; + StringRef( std::string const& stdString ) noexcept; + ~StringRef() noexcept; + + auto operator = ( StringRef other ) noexcept -> StringRef&; + operator std::string() const; + + void swap( StringRef& other ) noexcept; + + public: // operators + auto operator == ( StringRef const& other ) const noexcept -> bool; + auto operator != ( StringRef const& other ) const noexcept -> bool; + + auto operator[] ( size_type index ) const noexcept -> char; + + public: // named queries + auto empty() const noexcept -> bool; + auto size() const noexcept -> size_type; + auto numberOfCharacters() const noexcept -> size_type; + auto c_str() const -> char const*; + + public: // substrings and searches + auto substr( size_type start, size_type size ) const noexcept -> StringRef; + + private: // ownership queries - may not be consistent between calls + auto isOwned() const noexcept -> bool; + auto isSubstring() const noexcept -> bool; + auto data() const noexcept -> char const*; + }; + + auto operator + ( StringRef const& lhs, StringRef const& rhs ) -> std::string; + auto operator + ( StringRef const& lhs, char const* rhs ) -> std::string; + auto operator + ( char const* lhs, StringRef const& rhs ) -> std::string; + + auto operator << ( std::ostream& os, StringRef const& sr ) -> std::ostream&; + +} // namespace Catch + +#endif // CATCH_STRINGREF_H_INCLUDED diff --git a/include/internal/catch_suppress_warnings.h b/include/internal/catch_suppress_warnings.h index ad405922..65ed6a62 100644 --- a/include/internal/catch_suppress_warnings.h +++ b/include/internal/catch_suppress_warnings.h @@ -16,8 +16,6 @@ # pragma clang diagnostic ignored "-Wunused-variable" # pragma clang diagnostic push # pragma clang diagnostic ignored "-Wpadded" -# pragma clang diagnostic ignored "-Wc++98-compat" -# pragma clang diagnostic ignored "-Wc++98-compat-pedantic" # pragma clang diagnostic ignored "-Wswitch-enum" # pragma clang diagnostic ignored "-Wcovered-switch-default" # endif diff --git a/include/internal/catch_tag_alias.cpp b/include/internal/catch_tag_alias.cpp new file mode 100644 index 00000000..2ea4540e --- /dev/null +++ b/include/internal/catch_tag_alias.cpp @@ -0,0 +1,5 @@ +#include "catch_tag_alias.h" + +namespace Catch { + TagAlias::TagAlias(std::string const & _tag, SourceLineInfo _lineInfo): tag(_tag), lineInfo(_lineInfo) {} +} diff --git a/include/internal/catch_tag_alias.h b/include/internal/catch_tag_alias.h index bb23bc8d..a9e6eb37 100644 --- a/include/internal/catch_tag_alias.h +++ b/include/internal/catch_tag_alias.h @@ -15,18 +15,12 @@ namespace Catch { struct TagAlias { - TagAlias( std::string const& _tag, SourceLineInfo _lineInfo ) : tag( _tag ), lineInfo( _lineInfo ) {} + TagAlias(std::string const& _tag, SourceLineInfo _lineInfo); std::string tag; SourceLineInfo lineInfo; }; - struct RegistrarForTagAliases { - RegistrarForTagAliases( char const* alias, char const* tag, SourceLineInfo const& lineInfo ); - }; - } // end namespace Catch #endif // TWOBLUECUBES_CATCH_TAG_ALIAS_H_INCLUDED - -#define CATCH_REGISTER_TAG_ALIAS( alias, spec ) namespace{ Catch::RegistrarForTagAliases INTERNAL_CATCH_UNIQUE_NAME( AutoRegisterTagAlias )( alias, spec, CATCH_INTERNAL_LINEINFO ); } diff --git a/include/internal/catch_tag_alias_autoregistrar.cpp b/include/internal/catch_tag_alias_autoregistrar.cpp new file mode 100644 index 00000000..35ed059f --- /dev/null +++ b/include/internal/catch_tag_alias_autoregistrar.cpp @@ -0,0 +1,15 @@ +#include "catch_tag_alias_autoregistrar.h" +#include "catch_interfaces_registry_hub.h" + +namespace Catch { + + RegistrarForTagAliases::RegistrarForTagAliases(char const* alias, char const* tag, SourceLineInfo const& lineInfo) { + try { + getMutableRegistryHub().registerTagAlias(alias, tag, lineInfo); + } catch (...) { + // Do not throw when constructing global objects, instead register the exception to be processed later + getMutableRegistryHub().registerStartupException(); + } + } + +} diff --git a/include/internal/catch_tag_alias_autoregistrar.h b/include/internal/catch_tag_alias_autoregistrar.h new file mode 100644 index 00000000..e9b961e4 --- /dev/null +++ b/include/internal/catch_tag_alias_autoregistrar.h @@ -0,0 +1,22 @@ +/* + * Created by Martin on 27/07/2017. + * + * Distributed under the Boost Software License, Version 1.0. (See accompanying + * file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) + */ +#ifndef TWOBLUECUBES_CATCH_TAG_ALIAS_AUTOREGISTRAR_H_INCLUDED +#define TWOBLUECUBES_CATCH_TAG_ALIAS_AUTOREGISTRAR_H_INCLUDED + +#include "catch_common.h" + +namespace Catch { + + struct RegistrarForTagAliases { + RegistrarForTagAliases( char const* alias, char const* tag, SourceLineInfo const& lineInfo ); + }; + +} // end namespace Catch + +#define CATCH_REGISTER_TAG_ALIAS( alias, spec ) namespace{ Catch::RegistrarForTagAliases INTERNAL_CATCH_UNIQUE_NAME( AutoRegisterTagAlias )( alias, spec, CATCH_INTERNAL_LINEINFO ); } + +#endif // TWOBLUECUBES_CATCH_TAG_ALIAS_AUTOREGISTRAR_H_INCLUDED diff --git a/include/internal/catch_tag_alias_registry.cpp b/include/internal/catch_tag_alias_registry.cpp new file mode 100644 index 00000000..cfc98f17 --- /dev/null +++ b/include/internal/catch_tag_alias_registry.cpp @@ -0,0 +1,57 @@ +/* + * Created by Phil on 27/6/2014. + * Copyright 2014 Two Blue Cubes Ltd. All rights reserved. + * + * Distributed under the Boost Software License, Version 1.0. (See accompanying + * file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) + */ + +#include "catch_tag_alias_registry.h" +#include "catch_console_colour.h" +#include "catch_enforce.h" +#include "catch_interfaces_registry_hub.h" +#include "catch_stream.h" +#include "catch_string_manip.h" + +namespace Catch { + + TagAliasRegistry::~TagAliasRegistry() {} + + TagAlias const* TagAliasRegistry::find( std::string const& alias ) const { + auto it = m_registry.find( alias ); + if( it != m_registry.end() ) + return &(it->second); + else + return nullptr; + } + + std::string TagAliasRegistry::expandAliases( std::string const& unexpandedTestSpec ) const { + std::string expandedTestSpec = unexpandedTestSpec; + for( auto const& registryKvp : m_registry ) { + std::size_t pos = expandedTestSpec.find( registryKvp.first ); + if( pos != std::string::npos ) { + expandedTestSpec = expandedTestSpec.substr( 0, pos ) + + registryKvp.second.tag + + expandedTestSpec.substr( pos + registryKvp.first.size() ); + } + } + return expandedTestSpec; + } + + void TagAliasRegistry::add( std::string const& alias, std::string const& tag, SourceLineInfo const& lineInfo ) { + CATCH_ENFORCE( startsWith(alias, "[@") && endsWith(alias, ']'), + "error: tag alias, '" << alias << "' is not of the form [@alias name].\n" << lineInfo ); + + CATCH_ENFORCE( m_registry.insert(std::make_pair(alias, TagAlias(tag, lineInfo))).second, + "error: tag alias, '" << alias << "' already registered.\n" + << "\tFirst seen at: " << find(alias)->lineInfo << "\n" + << "\tRedefined at: " << lineInfo ); + } + + ITagAliasRegistry::~ITagAliasRegistry() {} + + ITagAliasRegistry const& ITagAliasRegistry::get() { + return getRegistryHub().getTagAliasRegistry(); + } + +} // end namespace Catch diff --git a/include/internal/catch_tag_alias_registry.h b/include/internal/catch_tag_alias_registry.h index aea41275..d3bb8ffb 100644 --- a/include/internal/catch_tag_alias_registry.h +++ b/include/internal/catch_tag_alias_registry.h @@ -9,6 +9,7 @@ #define TWOBLUECUBES_CATCH_TAG_ALIAS_REGISTRY_H_INCLUDED #include "catch_interfaces_tag_alias_registry.h" +#include "catch_tag_alias.h" #include @@ -16,9 +17,9 @@ namespace Catch { class TagAliasRegistry : public ITagAliasRegistry { public: - virtual ~TagAliasRegistry(); - virtual Option find( std::string const& alias ) const; - virtual std::string expandAliases( std::string const& unexpandedTestSpec ) const; + ~TagAliasRegistry() override; + TagAlias const* find( std::string const& alias ) const override; + std::string expandAliases( std::string const& unexpandedTestSpec ) const override; void add( std::string const& alias, std::string const& tag, SourceLineInfo const& lineInfo ); private: diff --git a/include/internal/catch_tag_alias_registry.hpp b/include/internal/catch_tag_alias_registry.hpp deleted file mode 100644 index 11d9e65a..00000000 --- a/include/internal/catch_tag_alias_registry.hpp +++ /dev/null @@ -1,77 +0,0 @@ -/* - * Created by Phil on 27/6/2014. - * Copyright 2014 Two Blue Cubes Ltd. All rights reserved. - * - * Distributed under the Boost Software License, Version 1.0. (See accompanying - * file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) - */ -#ifndef TWOBLUECUBES_CATCH_TAG_ALIAS_REGISTRY_HPP_INCLUDED -#define TWOBLUECUBES_CATCH_TAG_ALIAS_REGISTRY_HPP_INCLUDED - -#include "catch_tag_alias_registry.h" -#include "catch_console_colour.hpp" -#include "catch_interfaces_registry_hub.h" -#include "catch_stream.h" - -namespace Catch { - - TagAliasRegistry::~TagAliasRegistry() {} - - Option TagAliasRegistry::find( std::string const& alias ) const { - std::map::const_iterator it = m_registry.find( alias ); - if( it != m_registry.end() ) - return it->second; - else - return Option(); - } - - std::string TagAliasRegistry::expandAliases( std::string const& unexpandedTestSpec ) const { - std::string expandedTestSpec = unexpandedTestSpec; - for( std::map::const_iterator it = m_registry.begin(), itEnd = m_registry.end(); - it != itEnd; - ++it ) { - std::size_t pos = expandedTestSpec.find( it->first ); - if( pos != std::string::npos ) { - expandedTestSpec = expandedTestSpec.substr( 0, pos ) + - it->second.tag + - expandedTestSpec.substr( pos + it->first.size() ); - } - } - return expandedTestSpec; - } - - void TagAliasRegistry::add( std::string const& alias, std::string const& tag, SourceLineInfo const& lineInfo ) { - - if( !startsWith( alias, "[@" ) || !endsWith( alias, ']' ) ) { - std::ostringstream oss; - oss << Colour( Colour::Red ) - << "error: tag alias, \"" << alias << "\" is not of the form [@alias name].\n" - << Colour( Colour::FileName ) - << lineInfo << '\n'; - throw std::domain_error( oss.str().c_str() ); - } - if( !m_registry.insert( std::make_pair( alias, TagAlias( tag, lineInfo ) ) ).second ) { - std::ostringstream oss; - oss << Colour( Colour::Red ) - << "error: tag alias, \"" << alias << "\" already registered.\n" - << "\tFirst seen at " - << Colour( Colour::Red ) << find(alias)->lineInfo << '\n' - << Colour( Colour::Red ) << "\tRedefined at " - << Colour( Colour::FileName) << lineInfo << '\n'; - throw std::domain_error( oss.str().c_str() ); - } - } - - ITagAliasRegistry::~ITagAliasRegistry() {} - - ITagAliasRegistry const& ITagAliasRegistry::get() { - return getRegistryHub().getTagAliasRegistry(); - } - - RegistrarForTagAliases::RegistrarForTagAliases( char const* alias, char const* tag, SourceLineInfo const& lineInfo ) { - getMutableRegistryHub().registerTagAlias( alias, tag, lineInfo ); - } - -} // end namespace Catch - -#endif // TWOBLUECUBES_CATCH_TAG_ALIAS_REGISTRY_HPP_INCLUDED diff --git a/include/internal/catch_test_case_info.hpp b/include/internal/catch_test_case_info.cpp similarity index 55% rename from include/internal/catch_test_case_info.hpp rename to include/internal/catch_test_case_info.cpp index 4aa70441..1f56dbf7 100644 --- a/include/internal/catch_test_case_info.hpp +++ b/include/internal/catch_test_case_info.cpp @@ -5,21 +5,21 @@ * Distributed under the Boost Software License, Version 1.0. (See accompanying * file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) */ -#ifndef TWOBLUECUBES_CATCH_TEST_CASE_INFO_HPP_INCLUDED -#define TWOBLUECUBES_CATCH_TEST_CASE_INFO_HPP_INCLUDED -#include "catch_test_spec.hpp" #include "catch_test_case_info.h" +#include "catch_enforce.h" +#include "catch_test_spec.h" #include "catch_interfaces_testcase.h" -#include "catch_common.h" +#include "catch_string_manip.h" #include +#include +#include namespace Catch { - inline TestCaseInfo::SpecialProperties parseSpecialTag( std::string const& tag ) { + TestCaseInfo::SpecialProperties parseSpecialTag( std::string const& tag ) { if( startsWith( tag, '.' ) || - tag == "hide" || tag == "!hide" ) return TestCaseInfo::IsHidden; else if( tag == "!throws" ) @@ -30,38 +30,34 @@ namespace Catch { return TestCaseInfo::MayFail; else if( tag == "!nonportable" ) return TestCaseInfo::NonPortable; + else if( tag == "!benchmark" ) + return static_cast( TestCaseInfo::Benchmark | TestCaseInfo::IsHidden ); else return TestCaseInfo::None; } - inline bool isReservedTag( std::string const& tag ) { + bool isReservedTag( std::string const& tag ) { return parseSpecialTag( tag ) == TestCaseInfo::None && tag.size() > 0 && !std::isalnum( tag[0] ); } - inline void enforceNotReservedTag( std::string const& tag, SourceLineInfo const& _lineInfo ) { - if( isReservedTag( tag ) ) { - std::ostringstream ss; - ss << Colour(Colour::Red) - << "Tag name [" << tag << "] not allowed.\n" - << "Tag names starting with non alpha-numeric characters are reserved\n" - << Colour(Colour::FileName) - << _lineInfo << '\n'; - throw std::runtime_error(ss.str()); - } + void enforceNotReservedTag( std::string const& tag, SourceLineInfo const& _lineInfo ) { + CATCH_ENFORCE( !isReservedTag(tag), + "Tag name: [" << tag << "] is not allowed.\n" + << "Tag names starting with non alpha-numeric characters are reserved\n" + << _lineInfo ); } - TestCase makeTestCase( ITestCase* _testCase, + TestCase makeTestCase( ITestInvoker* _testCase, std::string const& _className, std::string const& _name, std::string const& _descOrTags, SourceLineInfo const& _lineInfo ) { - bool isHidden( startsWith( _name, "./" ) ); // Legacy support + bool isHidden = false; // Parse out tags - std::set tags; + std::vector tags; std::string desc, tag; bool inTag = false; - for( std::size_t i = 0; i < _descOrTags.size(); ++i ) { - char c = _descOrTags[i]; + for (char c : _descOrTags) { if( !inTag ) { if( c == '[' ) inTag = true; @@ -71,12 +67,12 @@ namespace Catch { else { if( c == ']' ) { TestCaseInfo::SpecialProperties prop = parseSpecialTag( tag ); - if( prop == TestCaseInfo::IsHidden ) + if( ( prop & TestCaseInfo::IsHidden ) != 0 ) isHidden = true; else if( prop == TestCaseInfo::None ) enforceNotReservedTag( tag, _lineInfo ); - tags.insert( tag ); + tags.push_back( tag ); tag.clear(); inTag = false; } @@ -85,33 +81,30 @@ namespace Catch { } } if( isHidden ) { - tags.insert( "hide" ); - tags.insert( "." ); + tags.push_back( "." ); } TestCaseInfo info( _name, _className, desc, tags, _lineInfo ); return TestCase( _testCase, info ); } - void setTags( TestCaseInfo& testCaseInfo, std::set const& tags ) - { - testCaseInfo.tags = tags; + void setTags( TestCaseInfo& testCaseInfo, std::vector tags ) { + std::sort(begin(tags), end(tags)); + tags.erase(std::unique(begin(tags), end(tags)), end(tags)); testCaseInfo.lcaseTags.clear(); - std::ostringstream oss; - for( std::set::const_iterator it = tags.begin(), itEnd = tags.end(); it != itEnd; ++it ) { - oss << '[' << *it << ']'; - std::string lcaseTag = toLower( *it ); + for( auto const& tag : tags ) { + std::string lcaseTag = toLower( tag ); testCaseInfo.properties = static_cast( testCaseInfo.properties | parseSpecialTag( lcaseTag ) ); - testCaseInfo.lcaseTags.insert( lcaseTag ); + testCaseInfo.lcaseTags.push_back( lcaseTag ); } - testCaseInfo.tagsAsString = oss.str(); + testCaseInfo.tags = std::move(tags); } TestCaseInfo::TestCaseInfo( std::string const& _name, std::string const& _className, std::string const& _description, - std::set const& _tags, + std::vector const& _tags, SourceLineInfo const& _lineInfo ) : name( _name ), className( _className ), @@ -122,17 +115,6 @@ namespace Catch { setTags( *this, _tags ); } - TestCaseInfo::TestCaseInfo( TestCaseInfo const& other ) - : name( other.name ), - className( other.className ), - description( other.description ), - tags( other.tags ), - lcaseTags( other.lcaseTags ), - tagsAsString( other.tagsAsString ), - lineInfo( other.lineInfo ), - properties( other.properties ) - {} - bool TestCaseInfo::isHidden() const { return ( properties & IsHidden ) != 0; } @@ -146,13 +128,26 @@ namespace Catch { return ( properties & (ShouldFail ) ) != 0; } + std::string TestCaseInfo::tagsAsString() const { + std::string ret; + // '[' and ']' per tag + std::size_t full_size = 2 * tags.size(); + for (const auto& tag : tags) { + full_size += tag.size(); + } + ret.reserve(full_size); + for (const auto& tag : tags) { + ret.push_back('['); + ret.append(tag); + ret.push_back(']'); + } - TestCase::TestCase( ITestCase* testCase, TestCaseInfo const& info ) : TestCaseInfo( info ), test( testCase ) {} + return ret; + } + + + TestCase::TestCase( ITestInvoker* testCase, TestCaseInfo const& info ) : TestCaseInfo( info ), test( testCase ) {} - TestCase::TestCase( TestCase const& other ) - : TestCaseInfo( other ), - test( other.test ) - {} TestCase TestCase::withName( std::string const& _newName ) const { TestCase other( *this ); @@ -160,18 +155,6 @@ namespace Catch { return other; } - void TestCase::swap( TestCase& other ) { - test.swap( other.test ); - name.swap( other.name ); - className.swap( other.className ); - description.swap( other.description ); - tags.swap( other.tags ); - lcaseTags.swap( other.lcaseTags ); - tagsAsString.swap( other.tagsAsString ); - std::swap( TestCaseInfo::properties, static_cast( other ).properties ); - std::swap( lineInfo, other.lineInfo ); - } - void TestCase::invoke() const { test->invoke(); } @@ -185,11 +168,6 @@ namespace Catch { bool TestCase::operator < ( TestCase const& other ) const { return name < other.name; } - TestCase& TestCase::operator = ( TestCase const& other ) { - TestCase temp( other ); - swap( temp ); - return *this; - } TestCaseInfo const& TestCase::getTestCaseInfo() const { @@ -197,5 +175,3 @@ namespace Catch { } } // end namespace Catch - -#endif // TWOBLUECUBES_CATCH_TEST_CASE_INFO_HPP_INCLUDED diff --git a/include/internal/catch_test_case_info.h b/include/internal/catch_test_case_info.h index b821abd3..2a911b06 100644 --- a/include/internal/catch_test_case_info.h +++ b/include/internal/catch_test_case_info.h @@ -9,10 +9,10 @@ #define TWOBLUECUBES_CATCH_TEST_CASE_INFO_H_INCLUDED #include "catch_common.h" -#include "catch_ptr.hpp" #include -#include +#include +#include #ifdef __clang__ #pragma clang diagnostic push @@ -21,7 +21,7 @@ namespace Catch { - struct ITestCase; + struct ITestInvoker; struct TestCaseInfo { enum SpecialProperties{ @@ -30,30 +30,30 @@ namespace Catch { ShouldFail = 1 << 2, MayFail = 1 << 3, Throws = 1 << 4, - NonPortable = 1 << 5 + NonPortable = 1 << 5, + Benchmark = 1 << 6 }; TestCaseInfo( std::string const& _name, std::string const& _className, std::string const& _description, - std::set const& _tags, + std::vector const& _tags, SourceLineInfo const& _lineInfo ); - TestCaseInfo( TestCaseInfo const& other ); - - friend void setTags( TestCaseInfo& testCaseInfo, std::set const& tags ); + friend void setTags( TestCaseInfo& testCaseInfo, std::vector tags ); bool isHidden() const; bool throws() const; bool okToFail() const; bool expectedToFail() const; + std::string tagsAsString() const; + std::string name; std::string className; std::string description; - std::set tags; - std::set lcaseTags; - std::string tagsAsString; + std::vector tags; + std::vector lcaseTags; SourceLineInfo lineInfo; SpecialProperties properties; }; @@ -61,8 +61,7 @@ namespace Catch { class TestCase : public TestCaseInfo { public: - TestCase( ITestCase* testCase, TestCaseInfo const& info ); - TestCase( TestCase const& other ); + TestCase( ITestInvoker* testCase, TestCaseInfo const& info ); TestCase withName( std::string const& _newName ) const; @@ -70,16 +69,14 @@ namespace Catch { TestCaseInfo const& getTestCaseInfo() const; - void swap( TestCase& other ); bool operator == ( TestCase const& other ) const; bool operator < ( TestCase const& other ) const; - TestCase& operator = ( TestCase const& other ); private: - Ptr test; + std::shared_ptr test; }; - TestCase makeTestCase( ITestCase* testCase, + TestCase makeTestCase( ITestInvoker* testCase, std::string const& className, std::string const& name, std::string const& description, diff --git a/include/internal/catch_test_case_registry_impl.cpp b/include/internal/catch_test_case_registry_impl.cpp new file mode 100644 index 00000000..b9aae92c --- /dev/null +++ b/include/internal/catch_test_case_registry_impl.cpp @@ -0,0 +1,112 @@ +/* + * Created by Martin on 25/07/2017 + * + * Distributed under the Boost Software License, Version 1.0. (See accompanying + * file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) + */ + +#include "catch_test_case_registry_impl.h" + +#include "catch_context.h" +#include "catch_enforce.h" +#include "catch_interfaces_registry_hub.h" +#include "catch_random_number_generator.h" +#include "catch_string_manip.h" +#include "catch_test_case_info.h" + +#include + +namespace Catch { + + std::vector sortTests( IConfig const& config, std::vector const& unsortedTestCases ) { + + std::vector sorted = unsortedTestCases; + + switch( config.runOrder() ) { + case RunTests::InLexicographicalOrder: + std::sort( sorted.begin(), sorted.end() ); + break; + case RunTests::InRandomOrder: + seedRng( config ); + RandomNumberGenerator::shuffle( sorted ); + break; + case RunTests::InDeclarationOrder: + // already in declaration order + break; + } + return sorted; + } + bool matchTest( TestCase const& testCase, TestSpec const& testSpec, IConfig const& config ) { + return testSpec.matches( testCase ) && ( config.allowThrows() || !testCase.throws() ); + } + + void enforceNoDuplicateTestCases( std::vector const& functions ) { + std::set seenFunctions; + for( auto const& function : functions ) { + auto prev = seenFunctions.insert( function ); + CATCH_ENFORCE( prev.second, + "error: TEST_CASE( \"" << function.name << "\" ) already defined.\n" + << "\tFirst seen at " << prev.first->getTestCaseInfo().lineInfo << "\n" + << "\tRedefined at " << function.getTestCaseInfo().lineInfo ); + } + } + + std::vector filterTests( std::vector const& testCases, TestSpec const& testSpec, IConfig const& config ) { + std::vector filtered; + filtered.reserve( testCases.size() ); + for( auto const& testCase : testCases ) + if( matchTest( testCase, testSpec, config ) ) + filtered.push_back( testCase ); + return filtered; + } + std::vector const& getAllTestCasesSorted( IConfig const& config ) { + return getRegistryHub().getTestCaseRegistry().getAllTestsSorted( config ); + } + + void TestRegistry::registerTest( TestCase const& testCase ) { + std::string name = testCase.getTestCaseInfo().name; + if( name.empty() ) { + std::ostringstream oss; + oss << "Anonymous test case " << ++m_unnamedCount; + return registerTest( testCase.withName( oss.str() ) ); + } + m_functions.push_back( testCase ); + } + + std::vector const& TestRegistry::getAllTests() const { + return m_functions; + } + std::vector const& TestRegistry::getAllTestsSorted( IConfig const& config ) const { + if( m_sortedFunctions.empty() ) + enforceNoDuplicateTestCases( m_functions ); + + if( m_currentSortOrder != config.runOrder() || m_sortedFunctions.empty() ) { + m_sortedFunctions = sortTests( config, m_functions ); + m_currentSortOrder = config.runOrder(); + } + return m_sortedFunctions; + } + + + + /////////////////////////////////////////////////////////////////////////// + TestInvokerAsFunction::TestInvokerAsFunction( void(*testAsFunction)() ) noexcept : m_testAsFunction( testAsFunction ) {} + + void TestInvokerAsFunction::invoke() const { + m_testAsFunction(); + } + + std::string extractClassName( std::string const& classOrQualifiedMethodName ) { + std::string className = classOrQualifiedMethodName; + if( startsWith( className, '&' ) ) + { + std::size_t lastColons = className.rfind( "::" ); + std::size_t penultimateColons = className.rfind( "::", lastColons-1 ); + if( penultimateColons == std::string::npos ) + penultimateColons = 1; + className = className.substr( penultimateColons, lastColons-penultimateColons ); + } + return className; + } + +} // end namespace Catch diff --git a/include/internal/catch_test_case_registry_impl.h b/include/internal/catch_test_case_registry_impl.h new file mode 100644 index 00000000..ad45e5a0 --- /dev/null +++ b/include/internal/catch_test_case_registry_impl.h @@ -0,0 +1,69 @@ +/* + * Created by Phil on 7/1/2011 + * Copyright 2010 Two Blue Cubes Ltd. All rights reserved. + * + * Distributed under the Boost Software License, Version 1.0. (See accompanying + * file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) + */ +#ifndef TWOBLUECUBES_CATCH_TEST_CASE_REGISTRY_IMPL_HPP_INCLUDED +#define TWOBLUECUBES_CATCH_TEST_CASE_REGISTRY_IMPL_HPP_INCLUDED + +#include "catch_test_registry.h" +#include "catch_test_spec.h" +#include "catch_interfaces_config.h" + +#include +#include +#include +#include + +namespace Catch { + + class TestCase; + struct IConfig; + + std::vector sortTests( IConfig const& config, std::vector const& unsortedTestCases ); + bool matchTest( TestCase const& testCase, TestSpec const& testSpec, IConfig const& config ); + + void enforceNoDuplicateTestCases( std::vector const& functions ); + + std::vector filterTests( std::vector const& testCases, TestSpec const& testSpec, IConfig const& config ); + std::vector const& getAllTestCasesSorted( IConfig const& config ); + + class TestRegistry : public ITestCaseRegistry { + public: + virtual ~TestRegistry() = default; + + virtual void registerTest( TestCase const& testCase ); + + std::vector const& getAllTests() const override; + std::vector const& getAllTestsSorted( IConfig const& config ) const override; + + private: + std::vector m_functions; + mutable RunTests::InWhatOrder m_currentSortOrder = RunTests::InDeclarationOrder; + mutable std::vector m_sortedFunctions; + std::size_t m_unnamedCount = 0; + std::ios_base::Init m_ostreamInit; // Forces cout/ cerr to be initialised + }; + + /////////////////////////////////////////////////////////////////////////// + + class TestInvokerAsFunction : public ITestInvoker { + void(*m_testAsFunction)(); + public: + TestInvokerAsFunction( void(*testAsFunction)() ) noexcept; + + void invoke() const override; + }; + + + std::string extractClassName( std::string const& classOrQualifiedMethodName ); + + /////////////////////////////////////////////////////////////////////////// + + +} // end namespace Catch + + +#endif // TWOBLUECUBES_CATCH_TEST_CASE_REGISTRY_IMPL_HPP_INCLUDED diff --git a/include/internal/catch_test_case_tracker.cpp b/include/internal/catch_test_case_tracker.cpp new file mode 100644 index 00000000..bcb7793f --- /dev/null +++ b/include/internal/catch_test_case_tracker.cpp @@ -0,0 +1,286 @@ +/* + * Created by Martin on 19/07/2017 + * + * Distributed under the Boost Software License, Version 1.0. (See accompanying + * file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) + */ + +#include "catch_test_case_tracker.h" + +#include "catch_enforce.h" + +#include +#include +#include +#include + +#if defined(__clang__) +# pragma clang diagnostic push +# pragma clang diagnostic ignored "-Wexit-time-destructors" +#endif + +namespace Catch { +namespace TestCaseTracking { + + NameAndLocation::NameAndLocation( std::string const& _name, SourceLineInfo const& _location ) + : name( _name ), + location( _location ) + {} + + + ITracker::~ITracker() = default; + + + TrackerContext& TrackerContext::instance() { + static TrackerContext s_instance; + return s_instance; + } + + ITracker& TrackerContext::startRun() { + m_rootTracker = std::make_shared( NameAndLocation( "{root}", CATCH_INTERNAL_LINEINFO ), *this, nullptr ); + m_currentTracker = nullptr; + m_runState = Executing; + return *m_rootTracker; + } + + void TrackerContext::endRun() { + m_rootTracker.reset(); + m_currentTracker = nullptr; + m_runState = NotStarted; + } + + void TrackerContext::startCycle() { + m_currentTracker = m_rootTracker.get(); + m_runState = Executing; + } + void TrackerContext::completeCycle() { + m_runState = CompletedCycle; + } + + bool TrackerContext::completedCycle() const { + return m_runState == CompletedCycle; + } + ITracker& TrackerContext::currentTracker() { + return *m_currentTracker; + } + void TrackerContext::setCurrentTracker( ITracker* tracker ) { + m_currentTracker = tracker; + } + + + + TrackerBase::TrackerHasName::TrackerHasName( NameAndLocation const& nameAndLocation ) : m_nameAndLocation( nameAndLocation ) {} + bool TrackerBase::TrackerHasName::operator ()( ITrackerPtr const& tracker ) const { + return + tracker->nameAndLocation().name == m_nameAndLocation.name && + tracker->nameAndLocation().location == m_nameAndLocation.location; + } + + TrackerBase::TrackerBase( NameAndLocation const& nameAndLocation, TrackerContext& ctx, ITracker* parent ) + : m_nameAndLocation( nameAndLocation ), + m_ctx( ctx ), + m_parent( parent ) + {} + + NameAndLocation const& TrackerBase::nameAndLocation() const { + return m_nameAndLocation; + } + bool TrackerBase::isComplete() const { + return m_runState == CompletedSuccessfully || m_runState == Failed; + } + bool TrackerBase::isSuccessfullyCompleted() const { + return m_runState == CompletedSuccessfully; + } + bool TrackerBase::isOpen() const { + return m_runState != NotStarted && !isComplete(); + } + bool TrackerBase::hasChildren() const { + return !m_children.empty(); + } + + + void TrackerBase::addChild( ITrackerPtr const& child ) { + m_children.push_back( child ); + } + + ITrackerPtr TrackerBase::findChild( NameAndLocation const& nameAndLocation ) { + auto it = std::find_if( m_children.begin(), m_children.end(), TrackerHasName( nameAndLocation ) ); + return( it != m_children.end() ) + ? *it + : nullptr; + } + ITracker& TrackerBase::parent() { + assert( m_parent ); // Should always be non-null except for root + return *m_parent; + } + + void TrackerBase::openChild() { + if( m_runState != ExecutingChildren ) { + m_runState = ExecutingChildren; + if( m_parent ) + m_parent->openChild(); + } + } + + bool TrackerBase::isSectionTracker() const { return false; } + bool TrackerBase::isIndexTracker() const { return false; } + + void TrackerBase::open() { + m_runState = Executing; + moveToThis(); + if( m_parent ) + m_parent->openChild(); + } + + void TrackerBase::close() { + + // Close any still open children (e.g. generators) + while( &m_ctx.currentTracker() != this ) + m_ctx.currentTracker().close(); + + switch( m_runState ) { + case NeedsAnotherRun: + break; + + case Executing: + m_runState = CompletedSuccessfully; + break; + case ExecutingChildren: + if( m_children.empty() || m_children.back()->isComplete() ) + m_runState = CompletedSuccessfully; + break; + + case NotStarted: + case CompletedSuccessfully: + case Failed: + CATCH_INTERNAL_ERROR( "Illogical state: " << m_runState ); + + default: + CATCH_INTERNAL_ERROR( "Unknown state: " << m_runState ); + } + moveToParent(); + m_ctx.completeCycle(); + } + void TrackerBase::fail() { + m_runState = Failed; + if( m_parent ) + m_parent->markAsNeedingAnotherRun(); + moveToParent(); + m_ctx.completeCycle(); + } + void TrackerBase::markAsNeedingAnotherRun() { + m_runState = NeedsAnotherRun; + } + + void TrackerBase::moveToParent() { + assert( m_parent ); + m_ctx.setCurrentTracker( m_parent ); + } + void TrackerBase::moveToThis() { + m_ctx.setCurrentTracker( this ); + } + + SectionTracker::SectionTracker( NameAndLocation const& nameAndLocation, TrackerContext& ctx, ITracker* parent ) + : TrackerBase( nameAndLocation, ctx, parent ) + { + if( parent ) { + while( !parent->isSectionTracker() ) + parent = &parent->parent(); + + SectionTracker& parentSection = static_cast( *parent ); + addNextFilters( parentSection.m_filters ); + } + } + + bool SectionTracker::isSectionTracker() const { return true; } + + SectionTracker& SectionTracker::acquire( TrackerContext& ctx, NameAndLocation const& nameAndLocation ) { + std::shared_ptr section; + + ITracker& currentTracker = ctx.currentTracker(); + if( ITrackerPtr childTracker = currentTracker.findChild( nameAndLocation ) ) { + assert( childTracker ); + assert( childTracker->isSectionTracker() ); + section = std::static_pointer_cast( childTracker ); + } + else { + section = std::make_shared( nameAndLocation, ctx, ¤tTracker ); + currentTracker.addChild( section ); + } + if( !ctx.completedCycle() ) + section->tryOpen(); + return *section; + } + + void SectionTracker::tryOpen() { + if( !isComplete() && (m_filters.empty() || m_filters[0].empty() || m_filters[0] == m_nameAndLocation.name ) ) + open(); + } + + void SectionTracker::addInitialFilters( std::vector const& filters ) { + if( !filters.empty() ) { + m_filters.push_back(""); // Root - should never be consulted + m_filters.push_back(""); // Test Case - not a section filter + m_filters.insert( m_filters.end(), filters.begin(), filters.end() ); + } + } + void SectionTracker::addNextFilters( std::vector const& filters ) { + if( filters.size() > 1 ) + m_filters.insert( m_filters.end(), ++filters.begin(), filters.end() ); + } + + IndexTracker::IndexTracker( NameAndLocation const& nameAndLocation, TrackerContext& ctx, ITracker* parent, int size ) + : TrackerBase( nameAndLocation, ctx, parent ), + m_size( size ) + {} + + bool IndexTracker::isIndexTracker() const { return true; } + + IndexTracker& IndexTracker::acquire( TrackerContext& ctx, NameAndLocation const& nameAndLocation, int size ) { + std::shared_ptr tracker; + + ITracker& currentTracker = ctx.currentTracker(); + if( ITrackerPtr childTracker = currentTracker.findChild( nameAndLocation ) ) { + assert( childTracker ); + assert( childTracker->isIndexTracker() ); + tracker = std::static_pointer_cast( childTracker ); + } + else { + tracker = std::make_shared( nameAndLocation, ctx, ¤tTracker, size ); + currentTracker.addChild( tracker ); + } + + if( !ctx.completedCycle() && !tracker->isComplete() ) { + if( tracker->m_runState != ExecutingChildren && tracker->m_runState != NeedsAnotherRun ) + tracker->moveNext(); + tracker->open(); + } + + return *tracker; + } + + int IndexTracker::index() const { return m_index; } + + void IndexTracker::moveNext() { + m_index++; + m_children.clear(); + } + + void IndexTracker::close() { + TrackerBase::close(); + if( m_runState == CompletedSuccessfully && m_index < m_size-1 ) + m_runState = Executing; + } + +} // namespace TestCaseTracking + +using TestCaseTracking::ITracker; +using TestCaseTracking::TrackerContext; +using TestCaseTracking::SectionTracker; +using TestCaseTracking::IndexTracker; + +} // namespace Catch + +#if defined(__clang__) +# pragma clang diagnostic pop +#endif diff --git a/include/internal/catch_test_case_tracker.h b/include/internal/catch_test_case_tracker.h new file mode 100644 index 00000000..a4b0440a --- /dev/null +++ b/include/internal/catch_test_case_tracker.h @@ -0,0 +1,183 @@ +/* + * Created by Phil Nash on 23/7/2013 + * Copyright 2013 Two Blue Cubes Ltd. All rights reserved. + * + * Distributed under the Boost Software License, Version 1.0. (See accompanying + * file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) + */ +#ifndef TWOBLUECUBES_CATCH_TEST_CASE_TRACKER_HPP_INCLUDED +#define TWOBLUECUBES_CATCH_TEST_CASE_TRACKER_HPP_INCLUDED + +#include "catch_compiler_capabilities.h" +#include "catch_common.h" + +#include +#include +#include + +namespace Catch { +namespace TestCaseTracking { + + struct NameAndLocation { + std::string name; + SourceLineInfo location; + + NameAndLocation( std::string const& _name, SourceLineInfo const& _location ); + }; + + struct ITracker; + + using ITrackerPtr = std::shared_ptr; + + struct ITracker { + virtual ~ITracker(); + + // static queries + virtual NameAndLocation const& nameAndLocation() const = 0; + + // dynamic queries + virtual bool isComplete() const = 0; // Successfully completed or failed + virtual bool isSuccessfullyCompleted() const = 0; + virtual bool isOpen() const = 0; // Started but not complete + virtual bool hasChildren() const = 0; + + virtual ITracker& parent() = 0; + + // actions + virtual void close() = 0; // Successfully complete + virtual void fail() = 0; + virtual void markAsNeedingAnotherRun() = 0; + + virtual void addChild( ITrackerPtr const& child ) = 0; + virtual ITrackerPtr findChild( NameAndLocation const& nameAndLocation ) = 0; + virtual void openChild() = 0; + + // Debug/ checking + virtual bool isSectionTracker() const = 0; + virtual bool isIndexTracker() const = 0; + }; + + class TrackerContext { + + enum RunState { + NotStarted, + Executing, + CompletedCycle + }; + + ITrackerPtr m_rootTracker; + ITracker* m_currentTracker = nullptr; + RunState m_runState = NotStarted; + + public: + + static TrackerContext& instance(); + + ITracker& startRun(); + void endRun(); + + void startCycle(); + void completeCycle(); + + bool completedCycle() const; + ITracker& currentTracker(); + void setCurrentTracker( ITracker* tracker ); + }; + + class TrackerBase : public ITracker { + protected: + enum CycleState { + NotStarted, + Executing, + ExecutingChildren, + NeedsAnotherRun, + CompletedSuccessfully, + Failed + }; + + class TrackerHasName { + NameAndLocation m_nameAndLocation; + public: + TrackerHasName( NameAndLocation const& nameAndLocation ); + bool operator ()( ITrackerPtr const& tracker ) const; + }; + + using Children = std::vector; + NameAndLocation m_nameAndLocation; + TrackerContext& m_ctx; + ITracker* m_parent; + Children m_children; + CycleState m_runState = NotStarted; + + public: + TrackerBase( NameAndLocation const& nameAndLocation, TrackerContext& ctx, ITracker* parent ); + + NameAndLocation const& nameAndLocation() const override; + bool isComplete() const override; + bool isSuccessfullyCompleted() const override; + bool isOpen() const override; + bool hasChildren() const override; + + + void addChild( ITrackerPtr const& child ) override; + + ITrackerPtr findChild( NameAndLocation const& nameAndLocation ) override; + ITracker& parent() override; + + void openChild() override; + + bool isSectionTracker() const override; + bool isIndexTracker() const override; + + void open(); + + void close() override; + void fail() override; + void markAsNeedingAnotherRun() override; + + private: + void moveToParent(); + void moveToThis(); + }; + + class SectionTracker : public TrackerBase { + std::vector m_filters; + public: + SectionTracker( NameAndLocation const& nameAndLocation, TrackerContext& ctx, ITracker* parent ); + + bool isSectionTracker() const override; + + static SectionTracker& acquire( TrackerContext& ctx, NameAndLocation const& nameAndLocation ); + + void tryOpen(); + + void addInitialFilters( std::vector const& filters ); + void addNextFilters( std::vector const& filters ); + }; + + class IndexTracker : public TrackerBase { + int m_size; + int m_index = -1; + public: + IndexTracker( NameAndLocation const& nameAndLocation, TrackerContext& ctx, ITracker* parent, int size ); + + bool isIndexTracker() const override; + void close() override; + + static IndexTracker& acquire( TrackerContext& ctx, NameAndLocation const& nameAndLocation, int size ); + + int index() const; + + void moveNext(); + }; + +} // namespace TestCaseTracking + +using TestCaseTracking::ITracker; +using TestCaseTracking::TrackerContext; +using TestCaseTracking::SectionTracker; +using TestCaseTracking::IndexTracker; + +} // namespace Catch + +#endif // TWOBLUECUBES_CATCH_TEST_CASE_TRACKER_HPP_INCLUDED diff --git a/include/internal/catch_test_case_tracker.hpp b/include/internal/catch_test_case_tracker.hpp deleted file mode 100644 index 705f92ab..00000000 --- a/include/internal/catch_test_case_tracker.hpp +++ /dev/null @@ -1,371 +0,0 @@ -/* - * Created by Phil Nash on 23/7/2013 - * Copyright 2013 Two Blue Cubes Ltd. All rights reserved. - * - * Distributed under the Boost Software License, Version 1.0. (See accompanying - * file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) - */ -#ifndef TWOBLUECUBES_CATCH_TEST_CASE_TRACKER_HPP_INCLUDED -#define TWOBLUECUBES_CATCH_TEST_CASE_TRACKER_HPP_INCLUDED - -#include "catch_compiler_capabilities.h" -#include "catch_ptr.hpp" - -#include -#include -#include -#include -#include - -CATCH_INTERNAL_SUPPRESS_ETD_WARNINGS - -namespace Catch { -namespace TestCaseTracking { - - struct NameAndLocation { - std::string name; - SourceLineInfo location; - - NameAndLocation( std::string const& _name, SourceLineInfo const& _location ) - : name( _name ), - location( _location ) - {} - }; - - struct ITracker : SharedImpl<> { - virtual ~ITracker(); - - // static queries - virtual NameAndLocation const& nameAndLocation() const = 0; - - // dynamic queries - virtual bool isComplete() const = 0; // Successfully completed or failed - virtual bool isSuccessfullyCompleted() const = 0; - virtual bool isOpen() const = 0; // Started but not complete - virtual bool hasChildren() const = 0; - - virtual ITracker& parent() = 0; - - // actions - virtual void close() = 0; // Successfully complete - virtual void fail() = 0; - virtual void markAsNeedingAnotherRun() = 0; - - virtual void addChild( Ptr const& child ) = 0; - virtual ITracker* findChild( NameAndLocation const& nameAndLocation ) = 0; - virtual void openChild() = 0; - - // Debug/ checking - virtual bool isSectionTracker() const = 0; - virtual bool isIndexTracker() const = 0; - }; - - class TrackerContext { - - enum RunState { - NotStarted, - Executing, - CompletedCycle - }; - - Ptr m_rootTracker; - ITracker* m_currentTracker; - RunState m_runState; - - public: - - static TrackerContext& instance() { - static TrackerContext s_instance; - return s_instance; - } - - TrackerContext() - : m_currentTracker( CATCH_NULL ), - m_runState( NotStarted ) - {} - - - ITracker& startRun(); - - void endRun() { - m_rootTracker.reset(); - m_currentTracker = CATCH_NULL; - m_runState = NotStarted; - } - - void startCycle() { - m_currentTracker = m_rootTracker.get(); - m_runState = Executing; - } - void completeCycle() { - m_runState = CompletedCycle; - } - - bool completedCycle() const { - return m_runState == CompletedCycle; - } - ITracker& currentTracker() { - return *m_currentTracker; - } - void setCurrentTracker( ITracker* tracker ) { - m_currentTracker = tracker; - } - }; - - class TrackerBase : public ITracker { - protected: - enum CycleState { - NotStarted, - Executing, - ExecutingChildren, - NeedsAnotherRun, - CompletedSuccessfully, - Failed - }; - class TrackerHasName { - NameAndLocation m_nameAndLocation; - public: - TrackerHasName( NameAndLocation const& nameAndLocation ) : m_nameAndLocation( nameAndLocation ) {} - bool operator ()( Ptr const& tracker ) { - return - tracker->nameAndLocation().name == m_nameAndLocation.name && - tracker->nameAndLocation().location == m_nameAndLocation.location; - } - }; - typedef std::vector > Children; - NameAndLocation m_nameAndLocation; - TrackerContext& m_ctx; - ITracker* m_parent; - Children m_children; - CycleState m_runState; - public: - TrackerBase( NameAndLocation const& nameAndLocation, TrackerContext& ctx, ITracker* parent ) - : m_nameAndLocation( nameAndLocation ), - m_ctx( ctx ), - m_parent( parent ), - m_runState( NotStarted ) - {} - virtual ~TrackerBase(); - - virtual NameAndLocation const& nameAndLocation() const CATCH_OVERRIDE { - return m_nameAndLocation; - } - virtual bool isComplete() const CATCH_OVERRIDE { - return m_runState == CompletedSuccessfully || m_runState == Failed; - } - virtual bool isSuccessfullyCompleted() const CATCH_OVERRIDE { - return m_runState == CompletedSuccessfully; - } - virtual bool isOpen() const CATCH_OVERRIDE { - return m_runState != NotStarted && !isComplete(); - } - virtual bool hasChildren() const CATCH_OVERRIDE { - return !m_children.empty(); - } - - - virtual void addChild( Ptr const& child ) CATCH_OVERRIDE { - m_children.push_back( child ); - } - - virtual ITracker* findChild( NameAndLocation const& nameAndLocation ) CATCH_OVERRIDE { - Children::const_iterator it = std::find_if( m_children.begin(), m_children.end(), TrackerHasName( nameAndLocation ) ); - return( it != m_children.end() ) - ? it->get() - : CATCH_NULL; - } - virtual ITracker& parent() CATCH_OVERRIDE { - assert( m_parent ); // Should always be non-null except for root - return *m_parent; - } - - virtual void openChild() CATCH_OVERRIDE { - if( m_runState != ExecutingChildren ) { - m_runState = ExecutingChildren; - if( m_parent ) - m_parent->openChild(); - } - } - - virtual bool isSectionTracker() const CATCH_OVERRIDE { return false; } - virtual bool isIndexTracker() const CATCH_OVERRIDE { return false; } - - void open() { - m_runState = Executing; - moveToThis(); - if( m_parent ) - m_parent->openChild(); - } - - virtual void close() CATCH_OVERRIDE { - - // Close any still open children (e.g. generators) - while( &m_ctx.currentTracker() != this ) - m_ctx.currentTracker().close(); - - switch( m_runState ) { - case NotStarted: - case CompletedSuccessfully: - case Failed: - throw std::logic_error( "Illogical state" ); - - case NeedsAnotherRun: - break;; - - case Executing: - m_runState = CompletedSuccessfully; - break; - case ExecutingChildren: - if( m_children.empty() || m_children.back()->isComplete() ) - m_runState = CompletedSuccessfully; - break; - - default: - throw std::logic_error( "Unexpected state" ); - } - moveToParent(); - m_ctx.completeCycle(); - } - virtual void fail() CATCH_OVERRIDE { - m_runState = Failed; - if( m_parent ) - m_parent->markAsNeedingAnotherRun(); - moveToParent(); - m_ctx.completeCycle(); - } - virtual void markAsNeedingAnotherRun() CATCH_OVERRIDE { - m_runState = NeedsAnotherRun; - } - private: - void moveToParent() { - assert( m_parent ); - m_ctx.setCurrentTracker( m_parent ); - } - void moveToThis() { - m_ctx.setCurrentTracker( this ); - } - }; - - class SectionTracker : public TrackerBase { - std::vector m_filters; - public: - SectionTracker( NameAndLocation const& nameAndLocation, TrackerContext& ctx, ITracker* parent ) - : TrackerBase( nameAndLocation, ctx, parent ) - { - if( parent ) { - while( !parent->isSectionTracker() ) - parent = &parent->parent(); - - SectionTracker& parentSection = static_cast( *parent ); - addNextFilters( parentSection.m_filters ); - } - } - virtual ~SectionTracker(); - - virtual bool isSectionTracker() const CATCH_OVERRIDE { return true; } - - static SectionTracker& acquire( TrackerContext& ctx, NameAndLocation const& nameAndLocation ) { - SectionTracker* section = CATCH_NULL; - - ITracker& currentTracker = ctx.currentTracker(); - if( ITracker* childTracker = currentTracker.findChild( nameAndLocation ) ) { - assert( childTracker ); - assert( childTracker->isSectionTracker() ); - section = static_cast( childTracker ); - } - else { - section = new SectionTracker( nameAndLocation, ctx, ¤tTracker ); - currentTracker.addChild( section ); - } - if( !ctx.completedCycle() ) - section->tryOpen(); - return *section; - } - - void tryOpen() { - if( !isComplete() && (m_filters.empty() || m_filters[0].empty() || m_filters[0] == m_nameAndLocation.name ) ) - open(); - } - - void addInitialFilters( std::vector const& filters ) { - if( !filters.empty() ) { - m_filters.push_back(""); // Root - should never be consulted - m_filters.push_back(""); // Test Case - not a section filter - m_filters.insert( m_filters.end(), filters.begin(), filters.end() ); - } - } - void addNextFilters( std::vector const& filters ) { - if( filters.size() > 1 ) - m_filters.insert( m_filters.end(), ++filters.begin(), filters.end() ); - } - }; - - class IndexTracker : public TrackerBase { - int m_size; - int m_index; - public: - IndexTracker( NameAndLocation const& nameAndLocation, TrackerContext& ctx, ITracker* parent, int size ) - : TrackerBase( nameAndLocation, ctx, parent ), - m_size( size ), - m_index( -1 ) - {} - virtual ~IndexTracker(); - - virtual bool isIndexTracker() const CATCH_OVERRIDE { return true; } - - static IndexTracker& acquire( TrackerContext& ctx, NameAndLocation const& nameAndLocation, int size ) { - IndexTracker* tracker = CATCH_NULL; - - ITracker& currentTracker = ctx.currentTracker(); - if( ITracker* childTracker = currentTracker.findChild( nameAndLocation ) ) { - assert( childTracker ); - assert( childTracker->isIndexTracker() ); - tracker = static_cast( childTracker ); - } - else { - tracker = new IndexTracker( nameAndLocation, ctx, ¤tTracker, size ); - currentTracker.addChild( tracker ); - } - - if( !ctx.completedCycle() && !tracker->isComplete() ) { - if( tracker->m_runState != ExecutingChildren && tracker->m_runState != NeedsAnotherRun ) - tracker->moveNext(); - tracker->open(); - } - - return *tracker; - } - - int index() const { return m_index; } - - void moveNext() { - m_index++; - m_children.clear(); - } - - virtual void close() CATCH_OVERRIDE { - TrackerBase::close(); - if( m_runState == CompletedSuccessfully && m_index < m_size-1 ) - m_runState = Executing; - } - }; - - inline ITracker& TrackerContext::startRun() { - m_rootTracker = new SectionTracker( NameAndLocation( "{root}", CATCH_INTERNAL_LINEINFO ), *this, CATCH_NULL ); - m_currentTracker = CATCH_NULL; - m_runState = Executing; - return *m_rootTracker; - } - -} // namespace TestCaseTracking - -using TestCaseTracking::ITracker; -using TestCaseTracking::TrackerContext; -using TestCaseTracking::SectionTracker; -using TestCaseTracking::IndexTracker; - -} // namespace Catch - -CATCH_INTERNAL_UNSUPPRESS_ETD_WARNINGS - -#endif // TWOBLUECUBES_CATCH_TEST_CASE_TRACKER_HPP_INCLUDED diff --git a/include/internal/catch_test_registry.cpp b/include/internal/catch_test_registry.cpp new file mode 100644 index 00000000..ec50e92c --- /dev/null +++ b/include/internal/catch_test_registry.cpp @@ -0,0 +1,37 @@ +/* + * Created by Martin on 25/07/2017. + * + * Distributed under the Boost Software License, Version 1.0. (See accompanying + * file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) + */ + +#include "catch_test_registry.h" +#include "catch_test_case_registry_impl.h" +#include "catch_interfaces_registry_hub.h" + +namespace Catch { + + auto makeTestInvoker( void(*testAsFunction)() ) noexcept -> ITestInvoker* { + return new(std::nothrow) TestInvokerAsFunction( testAsFunction ); + } + + NameAndTags::NameAndTags( StringRef name_ , StringRef tags_ ) noexcept : name( name_ ), tags( tags_ ) {} + + AutoReg::AutoReg( ITestInvoker* invoker, SourceLineInfo const& lineInfo, StringRef classOrMethod, NameAndTags const& nameAndTags ) noexcept { + try { + getMutableRegistryHub() + .registerTest( + makeTestCase( + invoker, + extractClassName( classOrMethod ), + nameAndTags.name, + nameAndTags.tags, + lineInfo)); + } catch (...) { + // Do not throw when constructing global objects, instead register the exception to be processed later + getMutableRegistryHub().registerStartupException(); + } + } + + AutoReg::~AutoReg() = default; +} diff --git a/include/internal/catch_test_registry.h b/include/internal/catch_test_registry.h new file mode 100644 index 00000000..afba1dbc --- /dev/null +++ b/include/internal/catch_test_registry.h @@ -0,0 +1,100 @@ +/* + * Created by Phil on 18/10/2010. + * Copyright 2010 Two Blue Cubes Ltd. All rights reserved. + * + * Distributed under the Boost Software License, Version 1.0. (See accompanying + * file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) + */ +#ifndef TWOBLUECUBES_CATCH_TEST_REGISTRY_HPP_INCLUDED +#define TWOBLUECUBES_CATCH_TEST_REGISTRY_HPP_INCLUDED + +#include "catch_common.h" +#include "catch_interfaces_testcase.h" +#include "catch_compiler_capabilities.h" +#include "catch_stringref.h" + +namespace Catch { + +template +class TestInvokerAsMethod : public ITestInvoker { + void (C::*m_testAsMethod)(); +public: + TestInvokerAsMethod( void (C::*testAsMethod)() ) noexcept : m_testAsMethod( testAsMethod ) {} + + void invoke() const override { + C obj; + (obj.*m_testAsMethod)(); + } +}; + +auto makeTestInvoker( void(*testAsFunction)() ) noexcept -> ITestInvoker*; + +template +auto makeTestInvoker( void (C::*testAsMethod)() ) noexcept -> ITestInvoker* { + return new(std::nothrow) TestInvokerAsMethod( testAsMethod ); +} + +struct NameAndTags { + NameAndTags( StringRef name_ = "", StringRef tags_ = "" ) noexcept; + StringRef name; + StringRef tags; +}; + +struct AutoReg : NonCopyable { + AutoReg( ITestInvoker* invoker, SourceLineInfo const& lineInfo, StringRef classOrMethod, NameAndTags const& nameAndTags ) noexcept; + ~AutoReg(); +}; + +} // end namespace Catch + +#if defined(CATCH_CONFIG_DISABLE) + #define INTERNAL_CATCH_TESTCASE_NO_REGISTRATION( TestName, ... ) \ + static void TestName() + #define INTERNAL_CATCH_TESTCASE_METHOD_NO_REGISTRATION( TestName, ClassName, ... ) \ + namespace{ \ + struct TestName : ClassName { \ + void test(); \ + }; \ + } \ + void TestName::test() + +#endif + + /////////////////////////////////////////////////////////////////////////////// + #define INTERNAL_CATCH_TESTCASE2( TestName, ... ) \ + static void TestName(); \ + CATCH_INTERNAL_SUPPRESS_GLOBALS_WARNINGS \ + namespace{ Catch::AutoReg INTERNAL_CATCH_UNIQUE_NAME( autoRegistrar )( Catch::makeTestInvoker( &TestName ), CATCH_INTERNAL_LINEINFO, "", Catch::NameAndTags{ __VA_ARGS__ } ); } /* NOLINT */ \ + CATCH_INTERNAL_UNSUPPRESS_GLOBALS_WARNINGS \ + static void TestName() + #define INTERNAL_CATCH_TESTCASE( ... ) \ + INTERNAL_CATCH_TESTCASE2( INTERNAL_CATCH_UNIQUE_NAME( ____C_A_T_C_H____T_E_S_T____ ), __VA_ARGS__ ) + + /////////////////////////////////////////////////////////////////////////////// + #define INTERNAL_CATCH_METHOD_AS_TEST_CASE( QualifiedMethod, ... ) \ + CATCH_INTERNAL_SUPPRESS_GLOBALS_WARNINGS \ + namespace{ Catch::AutoReg INTERNAL_CATCH_UNIQUE_NAME( autoRegistrar )( Catch::makeTestInvoker( &QualifiedMethod ), CATCH_INTERNAL_LINEINFO, "&" #QualifiedMethod, Catch::NameAndTags{ __VA_ARGS__ } ); } /* NOLINT */ \ + CATCH_INTERNAL_UNSUPPRESS_GLOBALS_WARNINGS + + /////////////////////////////////////////////////////////////////////////////// + #define INTERNAL_CATCH_TEST_CASE_METHOD2( TestName, ClassName, ... )\ + CATCH_INTERNAL_SUPPRESS_GLOBALS_WARNINGS \ + namespace{ \ + struct TestName : ClassName{ \ + void test(); \ + }; \ + Catch::AutoReg INTERNAL_CATCH_UNIQUE_NAME( autoRegistrar ) ( Catch::makeTestInvoker( &TestName::test ), CATCH_INTERNAL_LINEINFO, #ClassName, Catch::NameAndTags{ __VA_ARGS__ } ); /* NOLINT */ \ + } \ + CATCH_INTERNAL_UNSUPPRESS_GLOBALS_WARNINGS \ + void TestName::test() + #define INTERNAL_CATCH_TEST_CASE_METHOD( ClassName, ... ) \ + INTERNAL_CATCH_TEST_CASE_METHOD2( INTERNAL_CATCH_UNIQUE_NAME( ____C_A_T_C_H____T_E_S_T____ ), ClassName, __VA_ARGS__ ) + + /////////////////////////////////////////////////////////////////////////////// + #define INTERNAL_CATCH_REGISTER_TESTCASE( Function, ... ) \ + CATCH_INTERNAL_SUPPRESS_GLOBALS_WARNINGS \ + Catch::AutoReg INTERNAL_CATCH_UNIQUE_NAME( autoRegistrar )( Catch::makeTestInvoker( Function ), CATCH_INTERNAL_LINEINFO, "", Catch::NameAndTags{ __VA_ARGS__ } ); /* NOLINT */ \ + CATCH_INTERNAL_UNSUPPRESS_GLOBALS_WARNINGS + + +#endif // TWOBLUECUBES_CATCH_TEST_REGISTRY_HPP_INCLUDED diff --git a/include/internal/catch_test_registry.hpp b/include/internal/catch_test_registry.hpp deleted file mode 100644 index 3a86dfdc..00000000 --- a/include/internal/catch_test_registry.hpp +++ /dev/null @@ -1,162 +0,0 @@ -/* - * Created by Phil on 18/10/2010. - * Copyright 2010 Two Blue Cubes Ltd. All rights reserved. - * - * Distributed under the Boost Software License, Version 1.0. (See accompanying - * file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) - */ -#ifndef TWOBLUECUBES_CATCH_TEST_REGISTRY_HPP_INCLUDED -#define TWOBLUECUBES_CATCH_TEST_REGISTRY_HPP_INCLUDED - -#include "catch_common.h" -#include "catch_interfaces_testcase.h" -#include "catch_compiler_capabilities.h" - -namespace Catch { - -template -class MethodTestCase : public SharedImpl { - -public: - MethodTestCase( void (C::*method)() ) : m_method( method ) {} - - virtual void invoke() const { - C obj; - (obj.*m_method)(); - } - -private: - virtual ~MethodTestCase() {} - - void (C::*m_method)(); -}; - -typedef void(*TestFunction)(); - -struct NameAndDesc { - NameAndDesc( const char* _name = "", const char* _description= "" ) - : name( _name ), description( _description ) - {} - - const char* name; - const char* description; -}; - -void registerTestCase - ( ITestCase* testCase, - char const* className, - NameAndDesc const& nameAndDesc, - SourceLineInfo const& lineInfo ); - -struct AutoReg { - - AutoReg - ( TestFunction function, - SourceLineInfo const& lineInfo, - NameAndDesc const& nameAndDesc ); - - template - AutoReg - ( void (C::*method)(), - char const* className, - NameAndDesc const& nameAndDesc, - SourceLineInfo const& lineInfo ) { - - registerTestCase - ( new MethodTestCase( method ), - className, - nameAndDesc, - lineInfo ); - } - - ~AutoReg(); - -private: - AutoReg( AutoReg const& ); - void operator= ( AutoReg const& ); -}; - -void registerTestCaseFunction - ( TestFunction function, - SourceLineInfo const& lineInfo, - NameAndDesc const& nameAndDesc ); - -} // end namespace Catch - -#ifdef CATCH_CONFIG_VARIADIC_MACROS - /////////////////////////////////////////////////////////////////////////////// - #define INTERNAL_CATCH_TESTCASE2( TestName, ... ) \ - static void TestName(); \ - CATCH_INTERNAL_SUPPRESS_ETD_WARNINGS \ - namespace{ Catch::AutoReg INTERNAL_CATCH_UNIQUE_NAME( autoRegistrar )( &TestName, CATCH_INTERNAL_LINEINFO, Catch::NameAndDesc( __VA_ARGS__ ) ); } /* NOLINT */ \ - CATCH_INTERNAL_UNSUPPRESS_ETD_WARNINGS \ - static void TestName() - #define INTERNAL_CATCH_TESTCASE( ... ) \ - INTERNAL_CATCH_TESTCASE2( INTERNAL_CATCH_UNIQUE_NAME( ____C_A_T_C_H____T_E_S_T____ ), __VA_ARGS__ ) - - /////////////////////////////////////////////////////////////////////////////// - #define INTERNAL_CATCH_METHOD_AS_TEST_CASE( QualifiedMethod, ... ) \ - CATCH_INTERNAL_SUPPRESS_ETD_WARNINGS \ - namespace{ Catch::AutoReg INTERNAL_CATCH_UNIQUE_NAME( autoRegistrar )( &QualifiedMethod, "&" #QualifiedMethod, Catch::NameAndDesc( __VA_ARGS__ ), CATCH_INTERNAL_LINEINFO ); } /* NOLINT */ \ - CATCH_INTERNAL_UNSUPPRESS_ETD_WARNINGS - - /////////////////////////////////////////////////////////////////////////////// - #define INTERNAL_CATCH_TEST_CASE_METHOD2( TestName, ClassName, ... )\ - CATCH_INTERNAL_SUPPRESS_ETD_WARNINGS \ - namespace{ \ - struct TestName : ClassName{ \ - void test(); \ - }; \ - Catch::AutoReg INTERNAL_CATCH_UNIQUE_NAME( autoRegistrar ) ( &TestName::test, #ClassName, Catch::NameAndDesc( __VA_ARGS__ ), CATCH_INTERNAL_LINEINFO ); /* NOLINT */ \ - } \ - CATCH_INTERNAL_UNSUPPRESS_ETD_WARNINGS \ - void TestName::test() - #define INTERNAL_CATCH_TEST_CASE_METHOD( ClassName, ... ) \ - INTERNAL_CATCH_TEST_CASE_METHOD2( INTERNAL_CATCH_UNIQUE_NAME( ____C_A_T_C_H____T_E_S_T____ ), ClassName, __VA_ARGS__ ) - - /////////////////////////////////////////////////////////////////////////////// - #define INTERNAL_CATCH_REGISTER_TESTCASE( Function, ... ) \ - CATCH_INTERNAL_SUPPRESS_ETD_WARNINGS \ - Catch::AutoReg( Function, CATCH_INTERNAL_LINEINFO, Catch::NameAndDesc( __VA_ARGS__ ) ); /* NOLINT */ \ - CATCH_INTERNAL_UNSUPPRESS_ETD_WARNINGS - -#else - /////////////////////////////////////////////////////////////////////////////// - #define INTERNAL_CATCH_TESTCASE2( TestName, Name, Desc ) \ - static void TestName(); \ - CATCH_INTERNAL_SUPPRESS_ETD_WARNINGS \ - namespace{ Catch::AutoReg INTERNAL_CATCH_UNIQUE_NAME( autoRegistrar )( &TestName, CATCH_INTERNAL_LINEINFO, Catch::NameAndDesc( Name, Desc ) ); } /* NOLINT */ \ - CATCH_INTERNAL_UNSUPPRESS_ETD_WARNINGS \ - static void TestName() - #define INTERNAL_CATCH_TESTCASE( Name, Desc ) \ - INTERNAL_CATCH_TESTCASE2( INTERNAL_CATCH_UNIQUE_NAME( ____C_A_T_C_H____T_E_S_T____ ), Name, Desc ) - - /////////////////////////////////////////////////////////////////////////////// - #define INTERNAL_CATCH_METHOD_AS_TEST_CASE( QualifiedMethod, Name, Desc ) \ - CATCH_INTERNAL_SUPPRESS_ETD_WARNINGS \ - namespace{ Catch::AutoReg INTERNAL_CATCH_UNIQUE_NAME( autoRegistrar )( &QualifiedMethod, "&" #QualifiedMethod, Catch::NameAndDesc( Name, Desc ), CATCH_INTERNAL_LINEINFO ); } /* NOLINT */ \ - CATCH_INTERNAL_UNSUPPRESS_ETD_WARNINGS - - /////////////////////////////////////////////////////////////////////////////// - #define INTERNAL_CATCH_TEST_CASE_METHOD2( TestCaseName, ClassName, TestName, Desc )\ - CATCH_INTERNAL_SUPPRESS_ETD_WARNINGS \ - namespace{ \ - struct TestCaseName : ClassName{ \ - void test(); \ - }; \ - Catch::AutoReg INTERNAL_CATCH_UNIQUE_NAME( autoRegistrar ) ( &TestCaseName::test, #ClassName, Catch::NameAndDesc( TestName, Desc ), CATCH_INTERNAL_LINEINFO ); /* NOLINT */ \ - } \ - CATCH_INTERNAL_UNSUPPRESS_ETD_WARNINGS \ - void TestCaseName::test() - #define INTERNAL_CATCH_TEST_CASE_METHOD( ClassName, TestName, Desc )\ - INTERNAL_CATCH_TEST_CASE_METHOD2( INTERNAL_CATCH_UNIQUE_NAME( ____C_A_T_C_H____T_E_S_T____ ), ClassName, TestName, Desc ) - - /////////////////////////////////////////////////////////////////////////////// - #define INTERNAL_CATCH_REGISTER_TESTCASE( Function, Name, Desc ) \ - CATCH_INTERNAL_SUPPRESS_ETD_WARNINGS \ - Catch::AutoReg( Function, CATCH_INTERNAL_LINEINFO, Catch::NameAndDesc( Name, Desc ) ); /* NOLINT */ \ - CATCH_INTERNAL_UNSUPPRESS_ETD_WARNINGS - -#endif - -#endif // TWOBLUECUBES_CATCH_TEST_REGISTRY_HPP_INCLUDED diff --git a/include/internal/catch_test_spec.cpp b/include/internal/catch_test_spec.cpp new file mode 100644 index 00000000..d9c149d5 --- /dev/null +++ b/include/internal/catch_test_spec.cpp @@ -0,0 +1,59 @@ +/* + * Created by Martin on 19/07/2017. + * + * Distributed under the Boost Software License, Version 1.0. (See accompanying + * file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) + */ + +#include "catch_test_spec.h" +#include "catch_string_manip.h" + +#include +#include +#include +#include + +namespace Catch { + + TestSpec::Pattern::~Pattern() = default; + TestSpec::NamePattern::~NamePattern() = default; + TestSpec::TagPattern::~TagPattern() = default; + TestSpec::ExcludedPattern::~ExcludedPattern() = default; + + TestSpec::NamePattern::NamePattern( std::string const& name ) + : m_wildcardPattern( toLower( name ), CaseSensitive::No ) + {} + bool TestSpec::NamePattern::matches( TestCaseInfo const& testCase ) const { + return m_wildcardPattern.matches( toLower( testCase.name ) ); + } + + TestSpec::TagPattern::TagPattern( std::string const& tag ) : m_tag( toLower( tag ) ) {} + bool TestSpec::TagPattern::matches( TestCaseInfo const& testCase ) const { + return std::find(begin(testCase.lcaseTags), + end(testCase.lcaseTags), + m_tag) != end(testCase.lcaseTags); + } + + TestSpec::ExcludedPattern::ExcludedPattern( PatternPtr const& underlyingPattern ) : m_underlyingPattern( underlyingPattern ) {} + bool TestSpec::ExcludedPattern::matches( TestCaseInfo const& testCase ) const { return !m_underlyingPattern->matches( testCase ); } + + bool TestSpec::Filter::matches( TestCaseInfo const& testCase ) const { + // All patterns in a filter must match for the filter to be a match + for( auto const& pattern : m_patterns ) { + if( !pattern->matches( testCase ) ) + return false; + } + return true; + } + + bool TestSpec::hasFilters() const { + return !m_filters.empty(); + } + bool TestSpec::matches( TestCaseInfo const& testCase ) const { + // A TestSpec matches if any filter matches + for( auto const& filter : m_filters ) + if( filter.matches( testCase ) ) + return true; + return false; + } +} diff --git a/include/internal/catch_test_spec.h b/include/internal/catch_test_spec.h new file mode 100644 index 00000000..baf8b019 --- /dev/null +++ b/include/internal/catch_test_spec.h @@ -0,0 +1,80 @@ +/* + * Created by Phil on 14/8/2012. + * Copyright 2010 Two Blue Cubes Ltd. All rights reserved. + * + * Distributed under the Boost Software License, Version 1.0. (See accompanying + * file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) + */ +#ifndef TWOBLUECUBES_CATCH_TEST_SPEC_HPP_INCLUDED +#define TWOBLUECUBES_CATCH_TEST_SPEC_HPP_INCLUDED + +#ifdef __clang__ +#pragma clang diagnostic push +#pragma clang diagnostic ignored "-Wpadded" +#endif + +#include "catch_wildcard_pattern.h" +#include "catch_test_case_info.h" + +#include +#include +#include + +namespace Catch { + + class TestSpec { + struct Pattern { + virtual ~Pattern(); + virtual bool matches( TestCaseInfo const& testCase ) const = 0; + }; + using PatternPtr = std::shared_ptr; + + class NamePattern : public Pattern { + public: + NamePattern( std::string const& name ); + virtual ~NamePattern(); + virtual bool matches( TestCaseInfo const& testCase ) const override; + private: + WildcardPattern m_wildcardPattern; + }; + + class TagPattern : public Pattern { + public: + TagPattern( std::string const& tag ); + virtual ~TagPattern(); + virtual bool matches( TestCaseInfo const& testCase ) const override; + private: + std::string m_tag; + }; + + class ExcludedPattern : public Pattern { + public: + ExcludedPattern( PatternPtr const& underlyingPattern ); + virtual ~ExcludedPattern(); + virtual bool matches( TestCaseInfo const& testCase ) const override; + private: + PatternPtr m_underlyingPattern; + }; + + struct Filter { + std::vector m_patterns; + + bool matches( TestCaseInfo const& testCase ) const; + }; + + public: + bool hasFilters() const; + bool matches( TestCaseInfo const& testCase ) const; + + private: + std::vector m_filters; + + friend class TestSpecParser; + }; +} + +#ifdef __clang__ +#pragma clang diagnostic pop +#endif + +#endif // TWOBLUECUBES_CATCH_TEST_SPEC_HPP_INCLUDED diff --git a/include/internal/catch_test_spec.hpp b/include/internal/catch_test_spec.hpp deleted file mode 100644 index 3ec59b55..00000000 --- a/include/internal/catch_test_spec.hpp +++ /dev/null @@ -1,98 +0,0 @@ -/* - * Created by Phil on 14/8/2012. - * Copyright 2010 Two Blue Cubes Ltd. All rights reserved. - * - * Distributed under the Boost Software License, Version 1.0. (See accompanying - * file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) - */ -#ifndef TWOBLUECUBES_CATCH_TEST_SPEC_HPP_INCLUDED -#define TWOBLUECUBES_CATCH_TEST_SPEC_HPP_INCLUDED - -#ifdef __clang__ -#pragma clang diagnostic push -#pragma clang diagnostic ignored "-Wpadded" -#endif - -#include "catch_wildcard_pattern.hpp" -#include "catch_test_case_info.h" - -#include -#include - -namespace Catch { - - class TestSpec { - struct Pattern : SharedImpl<> { - virtual ~Pattern(); - virtual bool matches( TestCaseInfo const& testCase ) const = 0; - }; - class NamePattern : public Pattern { - public: - NamePattern( std::string const& name ) - : m_wildcardPattern( toLower( name ), CaseSensitive::No ) - {} - virtual ~NamePattern(); - virtual bool matches( TestCaseInfo const& testCase ) const { - return m_wildcardPattern.matches( toLower( testCase.name ) ); - } - private: - WildcardPattern m_wildcardPattern; - }; - - class TagPattern : public Pattern { - public: - TagPattern( std::string const& tag ) : m_tag( toLower( tag ) ) {} - virtual ~TagPattern(); - virtual bool matches( TestCaseInfo const& testCase ) const { - return testCase.lcaseTags.find( m_tag ) != testCase.lcaseTags.end(); - } - private: - std::string m_tag; - }; - - class ExcludedPattern : public Pattern { - public: - ExcludedPattern( Ptr const& underlyingPattern ) : m_underlyingPattern( underlyingPattern ) {} - virtual ~ExcludedPattern(); - virtual bool matches( TestCaseInfo const& testCase ) const { return !m_underlyingPattern->matches( testCase ); } - private: - Ptr m_underlyingPattern; - }; - - struct Filter { - std::vector > m_patterns; - - bool matches( TestCaseInfo const& testCase ) const { - // All patterns in a filter must match for the filter to be a match - for( std::vector >::const_iterator it = m_patterns.begin(), itEnd = m_patterns.end(); it != itEnd; ++it ) { - if( !(*it)->matches( testCase ) ) - return false; - } - return true; - } - }; - - public: - bool hasFilters() const { - return !m_filters.empty(); - } - bool matches( TestCaseInfo const& testCase ) const { - // A TestSpec matches if any filter matches - for( std::vector::const_iterator it = m_filters.begin(), itEnd = m_filters.end(); it != itEnd; ++it ) - if( it->matches( testCase ) ) - return true; - return false; - } - - private: - std::vector m_filters; - - friend class TestSpecParser; - }; -} - -#ifdef __clang__ -#pragma clang diagnostic pop -#endif - -#endif // TWOBLUECUBES_CATCH_TEST_SPEC_HPP_INCLUDED diff --git a/include/internal/catch_test_spec_parser.cpp b/include/internal/catch_test_spec_parser.cpp new file mode 100644 index 00000000..61c9e4df --- /dev/null +++ b/include/internal/catch_test_spec_parser.cpp @@ -0,0 +1,87 @@ +/* + * Created by Martin on 19/07/2017. + * + * Distributed under the Boost Software License, Version 1.0. (See accompanying + * file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) + */ + +#include "catch_test_spec_parser.h" + +namespace Catch { + + TestSpecParser::TestSpecParser( ITagAliasRegistry const& tagAliases ) : m_tagAliases( &tagAliases ) {} + + TestSpecParser& TestSpecParser::parse( std::string const& arg ) { + m_mode = None; + m_exclusion = false; + m_start = std::string::npos; + m_arg = m_tagAliases->expandAliases( arg ); + m_escapeChars.clear(); + for( m_pos = 0; m_pos < m_arg.size(); ++m_pos ) + visitChar( m_arg[m_pos] ); + if( m_mode == Name ) + addPattern(); + return *this; + } + TestSpec TestSpecParser::testSpec() { + addFilter(); + return m_testSpec; + } + + void TestSpecParser::visitChar( char c ) { + if( m_mode == None ) { + switch( c ) { + case ' ': return; + case '~': m_exclusion = true; return; + case '[': return startNewMode( Tag, ++m_pos ); + case '"': return startNewMode( QuotedName, ++m_pos ); + case '\\': return escape(); + default: startNewMode( Name, m_pos ); break; + } + } + if( m_mode == Name ) { + if( c == ',' ) { + addPattern(); + addFilter(); + } + else if( c == '[' ) { + if( subString() == "exclude:" ) + m_exclusion = true; + else + addPattern(); + startNewMode( Tag, ++m_pos ); + } + else if( c == '\\' ) + escape(); + } + else if( m_mode == EscapedName ) + m_mode = Name; + else if( m_mode == QuotedName && c == '"' ) + addPattern(); + else if( m_mode == Tag && c == ']' ) + addPattern(); + } + void TestSpecParser::startNewMode( Mode mode, std::size_t start ) { + m_mode = mode; + m_start = start; + } + void TestSpecParser::escape() { + if( m_mode == None ) + m_start = m_pos; + m_mode = EscapedName; + m_escapeChars.push_back( m_pos ); + } + std::string TestSpecParser::subString() const { return m_arg.substr( m_start, m_pos - m_start ); } + + void TestSpecParser::addFilter() { + if( !m_currentFilter.m_patterns.empty() ) { + m_testSpec.m_filters.push_back( m_currentFilter ); + m_currentFilter = TestSpec::Filter(); + } + } + + TestSpec parseTestSpec( std::string const& arg ) { + return TestSpecParser( ITagAliasRegistry::get() ).parse( arg ).testSpec(); + } + +} // namespace Catch diff --git a/include/internal/catch_test_spec_parser.h b/include/internal/catch_test_spec_parser.h new file mode 100644 index 00000000..79ce8898 --- /dev/null +++ b/include/internal/catch_test_spec_parser.h @@ -0,0 +1,75 @@ +/* + * Created by Phil on 15/5/2013. + * Copyright 2014 Two Blue Cubes Ltd. All rights reserved. + * + * Distributed under the Boost Software License, Version 1.0. (See accompanying + * file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) + */ +#ifndef TWOBLUECUBES_CATCH_TEST_SPEC_PARSER_HPP_INCLUDED +#define TWOBLUECUBES_CATCH_TEST_SPEC_PARSER_HPP_INCLUDED + +#ifdef __clang__ +#pragma clang diagnostic push +#pragma clang diagnostic ignored "-Wpadded" +#endif + +#include "catch_test_spec.h" +#include "catch_string_manip.h" +#include "catch_interfaces_tag_alias_registry.h" + +namespace Catch { + + class TestSpecParser { + enum Mode{ None, Name, QuotedName, Tag, EscapedName }; + Mode m_mode = None; + bool m_exclusion = false; + std::size_t m_start = std::string::npos, m_pos = 0; + std::string m_arg; + std::vector m_escapeChars; + TestSpec::Filter m_currentFilter; + TestSpec m_testSpec; + ITagAliasRegistry const* m_tagAliases = nullptr; + + public: + TestSpecParser( ITagAliasRegistry const& tagAliases ); + + TestSpecParser& parse( std::string const& arg ); + TestSpec testSpec(); + + private: + void visitChar( char c ); + void startNewMode( Mode mode, std::size_t start ); + void escape(); + std::string subString() const; + + template + void addPattern() { + std::string token = subString(); + for( std::size_t i = 0; i < m_escapeChars.size(); ++i ) + token = token.substr( 0, m_escapeChars[i]-m_start-i ) + token.substr( m_escapeChars[i]-m_start-i+1 ); + m_escapeChars.clear(); + if( startsWith( token, "exclude:" ) ) { + m_exclusion = true; + token = token.substr( 8 ); + } + if( !token.empty() ) { + TestSpec::PatternPtr pattern = std::make_shared( token ); + if( m_exclusion ) + pattern = std::make_shared( pattern ); + m_currentFilter.m_patterns.push_back( pattern ); + } + m_exclusion = false; + m_mode = None; + } + + void addFilter(); + }; + TestSpec parseTestSpec( std::string const& arg ); + +} // namespace Catch + +#ifdef __clang__ +#pragma clang diagnostic pop +#endif + +#endif // TWOBLUECUBES_CATCH_TEST_SPEC_PARSER_HPP_INCLUDED diff --git a/include/internal/catch_test_spec_parser.hpp b/include/internal/catch_test_spec_parser.hpp deleted file mode 100644 index 055bf1dc..00000000 --- a/include/internal/catch_test_spec_parser.hpp +++ /dev/null @@ -1,132 +0,0 @@ -/* - * Created by Phil on 15/5/2013. - * Copyright 2014 Two Blue Cubes Ltd. All rights reserved. - * - * Distributed under the Boost Software License, Version 1.0. (See accompanying - * file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) - */ -#ifndef TWOBLUECUBES_CATCH_TEST_SPEC_PARSER_HPP_INCLUDED -#define TWOBLUECUBES_CATCH_TEST_SPEC_PARSER_HPP_INCLUDED - -#ifdef __clang__ -#pragma clang diagnostic push -#pragma clang diagnostic ignored "-Wpadded" -#endif - -#include "catch_test_spec.hpp" -#include "catch_interfaces_tag_alias_registry.h" - -namespace Catch { - - class TestSpecParser { - enum Mode{ None, Name, QuotedName, Tag, EscapedName }; - Mode m_mode; - bool m_exclusion; - std::size_t m_start, m_pos; - std::string m_arg; - std::vector m_escapeChars; - TestSpec::Filter m_currentFilter; - TestSpec m_testSpec; - ITagAliasRegistry const* m_tagAliases; - - public: - TestSpecParser( ITagAliasRegistry const& tagAliases ) :m_mode(None), m_exclusion(false), m_start(0), m_pos(0), m_tagAliases( &tagAliases ) {} - - TestSpecParser& parse( std::string const& arg ) { - m_mode = None; - m_exclusion = false; - m_start = std::string::npos; - m_arg = m_tagAliases->expandAliases( arg ); - m_escapeChars.clear(); - for( m_pos = 0; m_pos < m_arg.size(); ++m_pos ) - visitChar( m_arg[m_pos] ); - if( m_mode == Name ) - addPattern(); - return *this; - } - TestSpec testSpec() { - addFilter(); - return m_testSpec; - } - private: - void visitChar( char c ) { - if( m_mode == None ) { - switch( c ) { - case ' ': return; - case '~': m_exclusion = true; return; - case '[': return startNewMode( Tag, ++m_pos ); - case '"': return startNewMode( QuotedName, ++m_pos ); - case '\\': return escape(); - default: startNewMode( Name, m_pos ); break; - } - } - if( m_mode == Name ) { - if( c == ',' ) { - addPattern(); - addFilter(); - } - else if( c == '[' ) { - if( subString() == "exclude:" ) - m_exclusion = true; - else - addPattern(); - startNewMode( Tag, ++m_pos ); - } - else if( c == '\\' ) - escape(); - } - else if( m_mode == EscapedName ) - m_mode = Name; - else if( m_mode == QuotedName && c == '"' ) - addPattern(); - else if( m_mode == Tag && c == ']' ) - addPattern(); - } - void startNewMode( Mode mode, std::size_t start ) { - m_mode = mode; - m_start = start; - } - void escape() { - if( m_mode == None ) - m_start = m_pos; - m_mode = EscapedName; - m_escapeChars.push_back( m_pos ); - } - std::string subString() const { return m_arg.substr( m_start, m_pos - m_start ); } - template - void addPattern() { - std::string token = subString(); - for( size_t i = 0; i < m_escapeChars.size(); ++i ) - token = token.substr( 0, m_escapeChars[i]-m_start-i ) + token.substr( m_escapeChars[i]-m_start-i+1 ); - m_escapeChars.clear(); - if( startsWith( token, "exclude:" ) ) { - m_exclusion = true; - token = token.substr( 8 ); - } - if( !token.empty() ) { - Ptr pattern = new T( token ); - if( m_exclusion ) - pattern = new TestSpec::ExcludedPattern( pattern ); - m_currentFilter.m_patterns.push_back( pattern ); - } - m_exclusion = false; - m_mode = None; - } - void addFilter() { - if( !m_currentFilter.m_patterns.empty() ) { - m_testSpec.m_filters.push_back( m_currentFilter ); - m_currentFilter = TestSpec::Filter(); - } - } - }; - inline TestSpec parseTestSpec( std::string const& arg ) { - return TestSpecParser( ITagAliasRegistry::get() ).parse( arg ).testSpec(); - } - -} // namespace Catch - -#ifdef __clang__ -#pragma clang diagnostic pop -#endif - -#endif // TWOBLUECUBES_CATCH_TEST_SPEC_PARSER_HPP_INCLUDED diff --git a/include/internal/catch_text.h b/include/internal/catch_text.h index b66751f3..eeafe8e2 100644 --- a/include/internal/catch_text.h +++ b/include/internal/catch_text.h @@ -8,17 +8,10 @@ #ifndef TWOBLUECUBES_CATCH_TEXT_H_INCLUDED #define TWOBLUECUBES_CATCH_TEXT_H_INCLUDED -#include "catch_config.hpp" - -#define TBC_TEXT_FORMAT_CONSOLE_WIDTH CATCH_CONFIG_CONSOLE_WIDTH - -#define CLICHE_TBC_TEXT_FORMAT_OUTER_NAMESPACE Catch -#include "../external/tbc_text_format.h" -#undef CLICHE_TBC_TEXT_FORMAT_OUTER_NAMESPACE +#include "catch_clara.h" namespace Catch { - using Tbc::Text; - using Tbc::TextAttributes; + using namespace clara::TextFlow; } #endif // TWOBLUECUBES_CATCH_TEXT_H_INCLUDED diff --git a/include/internal/catch_timer.cpp b/include/internal/catch_timer.cpp new file mode 100644 index 00000000..a6f37e8e --- /dev/null +++ b/include/internal/catch_timer.cpp @@ -0,0 +1,62 @@ +/* + * Created by Phil on 05/08/2013. + * Copyright 2013 Two Blue Cubes Ltd. All rights reserved. + * + * Distributed under the Boost Software License, Version 1.0. (See accompanying + * file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) + */ + +#include "catch_timer.h" + +#include + +namespace Catch { + + auto getCurrentNanosecondsSinceEpoch() -> uint64_t { + return std::chrono::duration_cast( std::chrono::high_resolution_clock::now().time_since_epoch() ).count(); + } + + auto estimateClockResolution() -> uint64_t { + uint64_t sum = 0; + static const uint64_t iterations = 1000000; + + for( std::size_t i = 0; i < iterations; ++i ) { + + uint64_t ticks; + uint64_t baseTicks = getCurrentNanosecondsSinceEpoch(); + do { + ticks = getCurrentNanosecondsSinceEpoch(); + } + while( ticks == baseTicks ); + + auto delta = ticks - baseTicks; + sum += delta; + } + + // We're just taking the mean, here. To do better we could take the std. dev and exclude outliers + // - and potentially do more iterations if there's a high variance. + return sum/iterations; + } + auto getEstimatedClockResolution() -> uint64_t { + static auto s_resolution = estimateClockResolution(); + return s_resolution; + } + + void Timer::start() { + m_nanoseconds = getCurrentNanosecondsSinceEpoch(); + } + auto Timer::getElapsedNanoseconds() const -> unsigned int { + return static_cast(getCurrentNanosecondsSinceEpoch() - m_nanoseconds); + } + auto Timer::getElapsedMicroseconds() const -> unsigned int { + return static_cast(getElapsedNanoseconds()/1000); + } + auto Timer::getElapsedMilliseconds() const -> unsigned int { + return static_cast(getElapsedMicroseconds()/1000); + } + auto Timer::getElapsedSeconds() const -> double { + return getElapsedMicroseconds()/1000000.0; + } + + +} // namespace Catch diff --git a/include/internal/catch_timer.h b/include/internal/catch_timer.h index 607aa6bb..28e1f9db 100644 --- a/include/internal/catch_timer.h +++ b/include/internal/catch_timer.h @@ -8,32 +8,21 @@ #ifndef TWOBLUECUBES_CATCH_TIMER_H_INCLUDED #define TWOBLUECUBES_CATCH_TIMER_H_INCLUDED -#include "catch_platform.h" - -#ifdef _MSC_VER +#include namespace Catch { - typedef unsigned long long UInt64; -} -#else -#include -namespace Catch { - typedef uint64_t UInt64; -} -#endif + auto getCurrentNanosecondsSinceEpoch() -> uint64_t; + auto getEstimatedClockResolution() -> uint64_t; -namespace Catch { class Timer { + uint64_t m_nanoseconds = 0; public: - Timer() : m_ticks( 0 ) {} void start(); - unsigned int getElapsedMicroseconds() const; - unsigned int getElapsedMilliseconds() const; - double getElapsedSeconds() const; - - private: - UInt64 m_ticks; + auto getElapsedNanoseconds() const -> unsigned int; + auto getElapsedMicroseconds() const -> unsigned int; + auto getElapsedMilliseconds() const -> unsigned int; + auto getElapsedSeconds() const -> double; }; } // namespace Catch diff --git a/include/internal/catch_timer.hpp b/include/internal/catch_timer.hpp deleted file mode 100644 index ed9e9a94..00000000 --- a/include/internal/catch_timer.hpp +++ /dev/null @@ -1,67 +0,0 @@ -/* - * Created by Phil on 05/08/2013. - * Copyright 2013 Two Blue Cubes Ltd. All rights reserved. - * - * Distributed under the Boost Software License, Version 1.0. (See accompanying - * file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) - */ - -#include "catch_timer.h" -#include "catch_platform.h" - -#ifdef __clang__ -#pragma clang diagnostic push -#pragma clang diagnostic ignored "-Wc++11-long-long" -#endif - -#ifdef CATCH_PLATFORM_WINDOWS - -# include "catch_windows_h_proxy.h" - -#else - -#include - -#endif - -namespace Catch { - - namespace { -#ifdef CATCH_PLATFORM_WINDOWS - UInt64 getCurrentTicks() { - static UInt64 hz=0, hzo=0; - if (!hz) { - QueryPerformanceFrequency( reinterpret_cast( &hz ) ); - QueryPerformanceCounter( reinterpret_cast( &hzo ) ); - } - UInt64 t; - QueryPerformanceCounter( reinterpret_cast( &t ) ); - return ((t-hzo)*1000000)/hz; - } -#else - UInt64 getCurrentTicks() { - timeval t; - gettimeofday(&t,CATCH_NULL); - return static_cast( t.tv_sec ) * 1000000ull + static_cast( t.tv_usec ); - } -#endif - } - - void Timer::start() { - m_ticks = getCurrentTicks(); - } - unsigned int Timer::getElapsedMicroseconds() const { - return static_cast(getCurrentTicks() - m_ticks); - } - unsigned int Timer::getElapsedMilliseconds() const { - return static_cast(getElapsedMicroseconds()/1000); - } - double Timer::getElapsedSeconds() const { - return getElapsedMicroseconds()/1000000.0; - } - -} // namespace Catch - -#ifdef __clang__ -#pragma clang diagnostic pop -#endif diff --git a/include/internal/catch_tostring.cpp b/include/internal/catch_tostring.cpp new file mode 100644 index 00000000..1b47178d --- /dev/null +++ b/include/internal/catch_tostring.cpp @@ -0,0 +1,225 @@ +/* + * Created by Phil on 23/4/2014. + * Copyright 2014 Two Blue Cubes Ltd. All rights reserved. + * + * Distributed under the Boost Software License, Version 1.0. (See accompanying + * file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) + */ + +#if defined(__clang__) +# pragma clang diagnostic push +# pragma clang diagnostic ignored "-Wexit-time-destructors" +# pragma clang diagnostic ignored "-Wglobal-constructors" +#endif + + +#include "catch_tostring.h" +#include "catch_interfaces_config.h" +#include "catch_context.h" + +#include + +namespace Catch { + +namespace Detail { + + const std::string unprintableString = "{?}"; + + namespace { + const int hexThreshold = 255; + + struct Endianness { + enum Arch { Big, Little }; + + static Arch which() { + union _{ + int asInt; + char asChar[sizeof (int)]; + } u; + + u.asInt = 1; + return ( u.asChar[sizeof(int)-1] == 1 ) ? Big : Little; + } + }; + } + + std::string rawMemoryToString( const void *object, std::size_t size ) { + // Reverse order for little endian architectures + int i = 0, end = static_cast( size ), inc = 1; + if( Endianness::which() == Endianness::Little ) { + i = end-1; + end = inc = -1; + } + + unsigned char const *bytes = static_cast(object); + std::ostringstream os; + os << "0x" << std::setfill('0') << std::hex; + for( ; i != end; i += inc ) + os << std::setw(2) << static_cast(bytes[i]); + return os.str(); + } +} + + +template +std::string fpToString( T value, int precision ) { + std::ostringstream oss; + oss << std::setprecision( precision ) + << std::fixed + << value; + std::string d = oss.str(); + std::size_t i = d.find_last_not_of( '0' ); + if( i != std::string::npos && i != d.size()-1 ) { + if( d[i] == '.' ) + i++; + d = d.substr( 0, i+1 ); + } + return d; +} + + +//// ======================================================= //// +// +// Out-of-line defs for full specialization of StringMaker +// +//// ======================================================= //// + +std::string StringMaker::convert(const std::string& str) { + if (!getCurrentContext().getConfig()->showInvisibles()) { + return '"' + str + '"'; + } + + std::string s("\""); + for (char c : str) { + switch (c) { + case '\n': + s.append("\\n"); + break; + case '\t': + s.append("\\t"); + break; + default: + s.push_back(c); + break; + } + } + s.append("\""); + return s; +} + +std::string StringMaker::convert(const std::wstring& wstr) { + std::string s; + s.reserve(wstr.size()); + for (auto c : wstr) { + s += (c <= 0xff) ? static_cast(c) : '?'; + } + return ::Catch::Detail::stringify(s); +} + +std::string StringMaker::convert(char const* str) { + if (str) { + return ::Catch::Detail::stringify(std::string{ str }); + } else { + return{ "{null string}" }; + } +} +std::string StringMaker::convert(char* str) { + if (str) { + return ::Catch::Detail::stringify(std::string{ str }); + } else { + return{ "{null string}" }; + } +} +std::string StringMaker::convert(wchar_t const * str) { + if (str) { + return ::Catch::Detail::stringify(std::wstring{ str }); + } else { + return{ "{null string}" }; + } +} +std::string StringMaker::convert(wchar_t * str) { + if (str) { + return ::Catch::Detail::stringify(std::wstring{ str }); + } else { + return{ "{null string}" }; + } +} + + +std::string StringMaker::convert(int value) { + return ::Catch::Detail::stringify(static_cast(value)); +} +std::string StringMaker::convert(long value) { + return ::Catch::Detail::stringify(static_cast(value)); +} +std::string StringMaker::convert(long long value) { + std::ostringstream oss; + oss << value; + if (value > Detail::hexThreshold) { + oss << " (0x" << std::hex << value << ')'; + } + return oss.str(); +} + +std::string StringMaker::convert(unsigned int value) { + return ::Catch::Detail::stringify(static_cast(value)); +} +std::string StringMaker::convert(unsigned long value) { + return ::Catch::Detail::stringify(static_cast(value)); +} +std::string StringMaker::convert(unsigned long long value) { + std::ostringstream oss; + oss << value; + if (value > Detail::hexThreshold) { + oss << " (0x" << std::hex << value << ')'; + } + return oss.str(); +} + + +std::string StringMaker::convert(bool b) { + return b ? "true" : "false"; +} + +std::string StringMaker::convert(char value) { + if (value == '\r') { + return "'\\r'"; + } else if (value == '\f') { + return "'\\f'"; + } else if (value == '\n') { + return "'\\n'"; + } else if (value == '\t') { + return "'\\t'"; + } else if ('\0' <= value && value < ' ') { + return ::Catch::Detail::stringify(static_cast(value)); + } else { + char chstr[] = "' '"; + chstr[1] = value; + return chstr; + } +} +std::string StringMaker::convert(signed char c) { + return ::Catch::Detail::stringify(static_cast(c)); +} +std::string StringMaker::convert(unsigned char c) { + return ::Catch::Detail::stringify(static_cast(c)); +} + +std::string StringMaker::convert(std::nullptr_t) { + return "nullptr"; +} + +std::string StringMaker::convert(float value) { + return fpToString(value, 5) + 'f'; +} +std::string StringMaker::convert(double value) { + return fpToString(value, 10); +} + + +} // end namespace Catch + +#if defined(__clang__) +# pragma clang diagnostic pop +#endif + diff --git a/include/internal/catch_tostring.h b/include/internal/catch_tostring.h index 01d518a1..8348c825 100644 --- a/include/internal/catch_tostring.h +++ b/include/internal/catch_tostring.h @@ -8,277 +8,462 @@ #ifndef TWOBLUECUBES_CATCH_TOSTRING_H_INCLUDED #define TWOBLUECUBES_CATCH_TOSTRING_H_INCLUDED -#include "catch_common.h" #include -#include -#include #include #include +#include +#include #ifdef __OBJC__ #include "catch_objc_arc.hpp" #endif -#ifdef CATCH_CONFIG_CPP11_TUPLE -#include +#ifdef _MSC_VER +#pragma warning(push) +#pragma warning(disable:4180) // We attempt to stream a function (address) by const&, which MSVC complains about but is harmless #endif -#ifdef CATCH_CONFIG_CPP11_IS_ENUM -#include -#endif + +// We need a dummy global operator<< so we can bring it into Catch namespace later +struct Catch_global_namespace_dummy; +std::ostream& operator<<(std::ostream&, Catch_global_namespace_dummy); namespace Catch { + // Bring in operator<< from global namespace into Catch namespace + using ::operator<<; -// Why we're here. -template -std::string toString( T const& value ); + namespace Detail { -// Built in overloads + extern const std::string unprintableString; -std::string toString( std::string const& value ); -std::string toString( std::wstring const& value ); -std::string toString( const char* const value ); -std::string toString( char* const value ); -std::string toString( const wchar_t* const value ); -std::string toString( wchar_t* const value ); -std::string toString( int value ); -std::string toString( unsigned long value ); -std::string toString( unsigned int value ); -std::string toString( const double value ); -std::string toString( const float value ); -std::string toString( bool value ); -std::string toString( char value ); -std::string toString( signed char value ); -std::string toString( unsigned char value ); + std::string rawMemoryToString( const void *object, std::size_t size ); -#ifdef CATCH_CONFIG_CPP11_LONG_LONG -std::string toString( long long value ); -std::string toString( unsigned long long value ); -#endif + template + std::string rawMemoryToString( const T& object ) { + return rawMemoryToString( &object, sizeof(object) ); + } -#ifdef CATCH_CONFIG_CPP11_NULLPTR -std::string toString( std::nullptr_t ); -#endif + template + class IsStreamInsertable { + template + static auto test(int) + -> decltype(std::declval() << std::declval(), std::true_type()); -#ifdef __OBJC__ - std::string toString( NSString const * const& nsstring ); - std::string toString( NSString * CATCH_ARC_STRONG & nsstring ); - std::string toString( NSObject* const& nsObject ); -#endif + template + static auto test(...)->std::false_type; + public: + static const bool value = decltype(test(0))::value; + }; -namespace Detail { + } // namespace Detail - extern const std::string unprintableString; + // If we decide for C++14, change these to enable_if_ts + template + struct StringMaker { + template + static + typename std::enable_if<::Catch::Detail::IsStreamInsertable::value, std::string>::type + convert(const Fake& t) { + std::ostringstream sstr; + sstr << t; + return sstr.str(); + } - #if !defined(CATCH_CONFIG_CPP11_STREAM_INSERTABLE_CHECK) - struct BorgType { - template BorgType( T const& ); - }; - - struct TrueType { char sizer[1]; }; - struct FalseType { char sizer[2]; }; - - TrueType& testStreamable( std::ostream& ); - FalseType testStreamable( FalseType ); - - FalseType operator<<( std::ostream const&, BorgType const& ); - - template - struct IsStreamInsertable { - static std::ostream &s; - static T const&t; - enum { value = sizeof( testStreamable(s << t) ) == sizeof( TrueType ) }; - }; -#else - template - class IsStreamInsertable { - template - static auto test(int) - -> decltype( std::declval() << std::declval(), std::true_type() ); - - template - static auto test(...) -> std::false_type; - - public: - static const bool value = decltype(test(0))::value; - }; -#endif - -#if defined(CATCH_CONFIG_CPP11_IS_ENUM) - template::value - > - struct EnumStringMaker - { - static std::string convert( T const& ) { return unprintableString; } - }; - - template - struct EnumStringMaker - { - static std::string convert( T const& v ) - { - return ::Catch::toString( - static_cast::type>(v) - ); + template + static + typename std::enable_if::value, std::string>::type + convert(const Fake&) { + return Detail::unprintableString; } }; -#endif - template - struct StringMakerBase { -#if defined(CATCH_CONFIG_CPP11_IS_ENUM) - template - static std::string convert( T const& v ) - { - return EnumStringMaker::convert( v ); + + namespace Detail { + + // This function dispatches all stringification requests inside of Catch. + // Should be preferably called fully qualified, like ::Catch::Detail::stringify + template + std::string stringify(const T& e) { + return ::Catch::StringMaker::type>::type>::convert(e); } -#else - template - static std::string convert( T const& ) { return unprintableString; } -#endif + + } // namespace Detail + + + // Some predefined specializations + + template<> + struct StringMaker { + static std::string convert(const std::string& str); + }; + template<> + struct StringMaker { + static std::string convert(const std::wstring& wstr); }; template<> - struct StringMakerBase { - template - static std::string convert( T const& _value ) { + struct StringMaker { + static std::string convert(char const * str); + }; + template<> + struct StringMaker { + static std::string convert(char * str); + }; + template<> + struct StringMaker { + static std::string convert(wchar_t const * str); + }; + template<> + struct StringMaker { + static std::string convert(wchar_t * str); + }; + + template + struct StringMaker { + static std::string convert(const char* str) { + return ::Catch::Detail::stringify(std::string{ str }); + } + }; + template + struct StringMaker { + static std::string convert(const char* str) { + return ::Catch::Detail::stringify(std::string{ str }); + } + }; + template + struct StringMaker { + static std::string convert(const char* str) { + return ::Catch::Detail::stringify(std::string{ str }); + } + }; + + template<> + struct StringMaker { + static std::string convert(int value); + }; + template<> + struct StringMaker { + static std::string convert(long value); + }; + template<> + struct StringMaker { + static std::string convert(long long value); + }; + template<> + struct StringMaker { + static std::string convert(unsigned int value); + }; + template<> + struct StringMaker { + static std::string convert(unsigned long value); + }; + template<> + struct StringMaker { + static std::string convert(unsigned long long value); + }; + + template<> + struct StringMaker { + static std::string convert(bool b); + }; + + template<> + struct StringMaker { + static std::string convert(char c); + }; + template<> + struct StringMaker { + static std::string convert(signed char c); + }; + template<> + struct StringMaker { + static std::string convert(unsigned char c); + }; + + template<> + struct StringMaker { + static std::string convert(std::nullptr_t); + }; + + template<> + struct StringMaker { + static std::string convert(float value); + }; + template<> + struct StringMaker { + static std::string convert(double value); + }; + + template + struct StringMaker { + template + static std::string convert(U* p) { + if (p) { + return ::Catch::Detail::rawMemoryToString(p); + } else { + return "nullptr"; + } + } + }; + + template + struct StringMaker { + static std::string convert(R C::* p) { + if (p) { + return ::Catch::Detail::rawMemoryToString(p); + } else { + return "nullptr"; + } + } + }; + + namespace Detail { + template + std::string rangeToString(InputIterator first, InputIterator last) { std::ostringstream oss; - oss << _value; + oss << "{ "; + if (first != last) { + oss << ::Catch::Detail::stringify(*first); + for (++first; first != last; ++first) + oss << ", " << ::Catch::Detail::stringify(*first); + } + oss << " }"; + return oss.str(); + } + } + + template + struct StringMaker > { + static std::string convert( std::vector const& v ) { + return ::Catch::Detail::rangeToString( v.begin(), v.end() ); + } + }; + + template + struct EnumStringMaker { + static std::string convert(const T& t) { + return ::Catch::Detail::stringify(static_cast::type>(t)); + } + }; + +#ifdef __OBJC__ + template<> + struct StringMaker { + static std::string convert(NSString * nsstring) { + if (!nsstring) + return "nil"; + return std::string("@") + [nsstring UTF8String]; + } + }; + template<> + struct StringMaker { + static std::string convert(NSObject* nsObject) { + return ::Catch::Detail::stringify([nsObject description]); + } + + }; + namespace Detail { + inline std::string stringify( NSString* nsstring ) { + return StringMaker::convert( nsstring ); + } + + } // namespace Detail +#endif // __OBJC__ + +} // namespace Catch + +////////////////////////////////////////////////////// +// Separate std-lib types stringification, so it can be selectively enabled +// This means that we do not bring in + +#if defined(CATCH_CONFIG_ENABLE_ALL_STRINGMAKERS) +# define CATCH_CONFIG_ENABLE_PAIR_STRINGMAKER +# define CATCH_CONFIG_ENABLE_TUPLE_STRINGMAKER +# define CATCH_CONFIG_ENABLE_CHRONO_STRINGMAKER +#endif + +// Separate std::pair specialization +#if defined(CATCH_CONFIG_ENABLE_PAIR_STRINGMAKER) +#include +namespace Catch { + template + struct StringMaker > { + static std::string convert(const std::pair& pair) { + std::ostringstream oss; + oss << "{ " + << ::Catch::Detail::stringify(pair.first) + << ", " + << ::Catch::Detail::stringify(pair.second) + << " }"; + return oss.str(); + } + }; +} +#endif // CATCH_CONFIG_ENABLE_PAIR_STRINGMAKER + +// Separate std::tuple specialization +#if defined(CATCH_CONFIG_ENABLE_TUPLE_STRINGMAKER) +#include +namespace Catch { + namespace Detail { + template< + typename Tuple, + std::size_t N = 0, + bool = (N < std::tuple_size::value) + > + struct TupleElementPrinter { + static void print(const Tuple& tuple, std::ostream& os) { + os << (N ? ", " : " ") + << ::Catch::Detail::stringify(std::get(tuple)); + TupleElementPrinter::print(tuple, os); + } + }; + + template< + typename Tuple, + std::size_t N + > + struct TupleElementPrinter { + static void print(const Tuple&, std::ostream&) {} + }; + + } + + + template + struct StringMaker> { + static std::string convert(const std::tuple& tuple) { + std::ostringstream os; + os << '{'; + Detail::TupleElementPrinter>::print(tuple, os); + os << " }"; + return os.str(); + } + }; +} +#endif // CATCH_CONFIG_ENABLE_TUPLE_STRINGMAKER + + +// Separate std::chrono::duration specialization +#if defined(CATCH_CONFIG_ENABLE_CHRONO_STRINGMAKER) +#include +#include +#include + +template +struct ratio_string { + static std::string symbol(); +}; + +template +std::string ratio_string::symbol() { + std::ostringstream oss; + oss << '[' << Ratio::num << '/' + << Ratio::den << ']'; + return oss.str(); +} +template <> +struct ratio_string { + static std::string symbol() { return "a"; } +}; +template <> +struct ratio_string { + static std::string symbol() { return "f"; } +}; +template <> +struct ratio_string { + static std::string symbol() { return "p"; } +}; +template <> +struct ratio_string { + static std::string symbol() { return "n"; } +}; +template <> +struct ratio_string { + static std::string symbol() { return "u"; } +}; +template <> +struct ratio_string { + static std::string symbol() { return "m"; } +}; + +namespace Catch { + //////////// + // std::chrono::duration specializations + template + struct StringMaker> { + static std::string convert(std::chrono::duration const& duration) { + std::ostringstream oss; + oss << duration.count() << ' ' << ratio_string::symbol() << 's'; + return oss.str(); + } + }; + template + struct StringMaker>> { + static std::string convert(std::chrono::duration> const& duration) { + std::ostringstream oss; + oss << duration.count() << " s"; + return oss.str(); + } + }; + template + struct StringMaker>> { + static std::string convert(std::chrono::duration> const& duration) { + std::ostringstream oss; + oss << duration.count() << " m"; + return oss.str(); + } + }; + template + struct StringMaker>> { + static std::string convert(std::chrono::duration> const& duration) { + std::ostringstream oss; + oss << duration.count() << " h"; return oss.str(); } }; - std::string rawMemoryToString( const void *object, std::size_t size ); - - template - std::string rawMemoryToString( const T& object ) { - return rawMemoryToString( &object, sizeof(object) ); - } - -} // end namespace Detail - -template -struct StringMaker : - Detail::StringMakerBase::value> {}; - -template -struct StringMaker { - template - static std::string convert( U* p ) { - if( !p ) - return "NULL"; - else - return Detail::rawMemoryToString( p ); - } -}; - -template -struct StringMaker { - static std::string convert( R C::* p ) { - if( !p ) - return "NULL"; - else - return Detail::rawMemoryToString( p ); - } -}; - -namespace Detail { - template - std::string rangeToString( InputIterator first, InputIterator last ); -} - -//template -//struct StringMaker > { -// static std::string convert( std::vector const& v ) { -// return Detail::rangeToString( v.begin(), v.end() ); -// } -//}; - -template -std::string toString( std::vector const& v ) { - return Detail::rangeToString( v.begin(), v.end() ); -} - - -#ifdef CATCH_CONFIG_CPP11_TUPLE - -// toString for tuples -namespace TupleDetail { - template< - typename Tuple, - std::size_t N = 0, - bool = (N < std::tuple_size::value) - > - struct ElementPrinter { - static void print( const Tuple& tuple, std::ostream& os ) - { - os << ( N ? ", " : " " ) - << Catch::toString(std::get(tuple)); - ElementPrinter::print(tuple,os); - } - }; - - template< - typename Tuple, - std::size_t N - > - struct ElementPrinter { - static void print( const Tuple&, std::ostream& ) {} - }; - -} - -template -struct StringMaker> { - - static std::string convert( const std::tuple& tuple ) - { - std::ostringstream os; - os << '{'; - TupleDetail::ElementPrinter>::print( tuple, os ); - os << " }"; - return os.str(); - } -}; -#endif // CATCH_CONFIG_CPP11_TUPLE - -namespace Detail { - template - std::string makeString( T const& value ) { - return StringMaker::convert( value ); - } -} // end namespace Detail - -/// \brief converts any type to a string -/// -/// The default template forwards on to ostringstream - except when an -/// ostringstream overload does not exist - in which case it attempts to detect -/// that and writes {?}. -/// Overload (not specialise) this template for custom typs that you don't want -/// to provide an ostream overload for. -template -std::string toString( T const& value ) { - return StringMaker::convert( value ); -} - - - namespace Detail { - template - std::string rangeToString( InputIterator first, InputIterator last ) { - std::ostringstream oss; - oss << "{ "; - if( first != last ) { - oss << Catch::toString( *first ); - for( ++first ; first != last ; ++first ) - oss << ", " << Catch::toString( *first ); + //////////// + // std::chrono::time_point specialization + // Generic time_point cannot be specialized, only std::chrono::time_point + template + struct StringMaker> { + static std::string convert(std::chrono::time_point const& time_point) { + return ::Catch::Detail::stringify(time_point.time_since_epoch()) + " since epoch"; } - oss << " }"; - return oss.str(); - } -} + }; + // std::chrono::time_point specialization + template + struct StringMaker> { + static std::string convert(std::chrono::time_point const& time_point) { + auto converted = std::chrono::system_clock::to_time_t(time_point); -} // end namespace Catch +#ifdef _MSC_VER + std::tm timeInfo = {}; + gmtime_s(&timeInfo, &converted); +#else + std::tm* timeInfo = std::gmtime(&converted); +#endif + + auto const timeStampSize = sizeof("2017-01-16T17:06:45Z"); + char timeStamp[timeStampSize]; + const char * const fmt = "%Y-%m-%dT%H:%M:%SZ"; + +#ifdef _MSC_VER + std::strftime(timeStamp, timeStampSize, fmt, &timeInfo); +#else + std::strftime(timeStamp, timeStampSize, fmt, timeInfo); +#endif + return std::string(timeStamp); + } + }; +} +#endif // CATCH_CONFIG_ENABLE_CHRONO_STRINGMAKER + + +#ifdef _MSC_VER +#pragma warning(pop) +#endif #endif // TWOBLUECUBES_CATCH_TOSTRING_H_INCLUDED diff --git a/include/internal/catch_tostring.hpp b/include/internal/catch_tostring.hpp deleted file mode 100644 index 602e639d..00000000 --- a/include/internal/catch_tostring.hpp +++ /dev/null @@ -1,213 +0,0 @@ -/* - * Created by Phil on 23/4/2014. - * Copyright 2014 Two Blue Cubes Ltd. All rights reserved. - * - * Distributed under the Boost Software License, Version 1.0. (See accompanying - * file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) - */ -#ifndef TWOBLUECUBES_CATCH_TOSTRING_HPP_INCLUDED -#define TWOBLUECUBES_CATCH_TOSTRING_HPP_INCLUDED - -#include "catch_tostring.h" -#include "catch_interfaces_config.h" - -namespace Catch { - -namespace Detail { - - const std::string unprintableString = "{?}"; - - namespace { - const int hexThreshold = 255; - - struct Endianness { - enum Arch { Big, Little }; - - static Arch which() { - union _{ - int asInt; - char asChar[sizeof (int)]; - } u; - - u.asInt = 1; - return ( u.asChar[sizeof(int)-1] == 1 ) ? Big : Little; - } - }; - } - - std::string rawMemoryToString( const void *object, std::size_t size ) - { - // Reverse order for little endian architectures - int i = 0, end = static_cast( size ), inc = 1; - if( Endianness::which() == Endianness::Little ) { - i = end-1; - end = inc = -1; - } - - unsigned char const *bytes = static_cast(object); - std::ostringstream os; - os << "0x" << std::setfill('0') << std::hex; - for( ; i != end; i += inc ) - os << std::setw(2) << static_cast(bytes[i]); - return os.str(); - } -} - -std::string toString( std::string const& value ) { - std::string s = value; - if( getCurrentContext().getConfig()->showInvisibles() ) { - for(size_t i = 0; i < s.size(); ++i ) { - std::string subs; - switch( s[i] ) { - case '\n': subs = "\\n"; break; - case '\t': subs = "\\t"; break; - default: break; - } - if( !subs.empty() ) { - s = s.substr( 0, i ) + subs + s.substr( i+1 ); - ++i; - } - } - } - return '"' + s + '"'; -} -std::string toString( std::wstring const& value ) { - - std::string s; - s.reserve( value.size() ); - for(size_t i = 0; i < value.size(); ++i ) - s += value[i] <= 0xff ? static_cast( value[i] ) : '?'; - return Catch::toString( s ); -} - -std::string toString( const char* const value ) { - return value ? Catch::toString( std::string( value ) ) : std::string( "{null string}" ); -} - -std::string toString( char* const value ) { - return Catch::toString( static_cast( value ) ); -} - -std::string toString( const wchar_t* const value ) -{ - return value ? Catch::toString( std::wstring(value) ) : std::string( "{null string}" ); -} - -std::string toString( wchar_t* const value ) -{ - return Catch::toString( static_cast( value ) ); -} - -std::string toString( int value ) { - std::ostringstream oss; - oss << value; - if( value > Detail::hexThreshold ) - oss << " (0x" << std::hex << value << ')'; - return oss.str(); -} - -std::string toString( unsigned long value ) { - std::ostringstream oss; - oss << value; - if( value > Detail::hexThreshold ) - oss << " (0x" << std::hex << value << ')'; - return oss.str(); -} - -std::string toString( unsigned int value ) { - return Catch::toString( static_cast( value ) ); -} - -template -std::string fpToString( T value, int precision ) { - std::ostringstream oss; - oss << std::setprecision( precision ) - << std::fixed - << value; - std::string d = oss.str(); - std::size_t i = d.find_last_not_of( '0' ); - if( i != std::string::npos && i != d.size()-1 ) { - if( d[i] == '.' ) - i++; - d = d.substr( 0, i+1 ); - } - return d; -} - -std::string toString( const double value ) { - return fpToString( value, 10 ); -} -std::string toString( const float value ) { - return fpToString( value, 5 ) + 'f'; -} - -std::string toString( bool value ) { - return value ? "true" : "false"; -} - -std::string toString( char value ) { - if ( value == '\r' ) - return "'\\r'"; - if ( value == '\f' ) - return "'\\f'"; - if ( value == '\n' ) - return "'\\n'"; - if ( value == '\t' ) - return "'\\t'"; - if ( '\0' <= value && value < ' ' ) - return toString( static_cast( value ) ); - char chstr[] = "' '"; - chstr[1] = value; - return chstr; -} - -std::string toString( signed char value ) { - return toString( static_cast( value ) ); -} - -std::string toString( unsigned char value ) { - return toString( static_cast( value ) ); -} - -#ifdef CATCH_CONFIG_CPP11_LONG_LONG -std::string toString( long long value ) { - std::ostringstream oss; - oss << value; - if( value > Detail::hexThreshold ) - oss << " (0x" << std::hex << value << ')'; - return oss.str(); -} -std::string toString( unsigned long long value ) { - std::ostringstream oss; - oss << value; - if( value > Detail::hexThreshold ) - oss << " (0x" << std::hex << value << ')'; - return oss.str(); -} -#endif - -#ifdef CATCH_CONFIG_CPP11_NULLPTR -std::string toString( std::nullptr_t ) { - return "nullptr"; -} -#endif - -#ifdef __OBJC__ - std::string toString( NSString const * const& nsstring ) { - if( !nsstring ) - return "nil"; - return "@" + toString([nsstring UTF8String]); - } - std::string toString( NSString * CATCH_ARC_STRONG & nsstring ) { - if( !nsstring ) - return "nil"; - return "@" + toString([nsstring UTF8String]); - } - std::string toString( NSObject* const& nsObject ) { - return toString( [nsObject description] ); - } -#endif - -} // end namespace Catch - -#endif // TWOBLUECUBES_CATCH_TOSTRING_HPP_INCLUDED diff --git a/include/internal/catch_totals.cpp b/include/internal/catch_totals.cpp new file mode 100644 index 00000000..0391fe82 --- /dev/null +++ b/include/internal/catch_totals.cpp @@ -0,0 +1,61 @@ +/* + * Created by Martin on 19/07/2017. + * + * Distributed under the Boost Software License, Version 1.0. (See accompanying + * file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) + */ + +#include "catch_totals.h" + +namespace Catch { + + Counts Counts::operator - ( Counts const& other ) const { + Counts diff; + diff.passed = passed - other.passed; + diff.failed = failed - other.failed; + diff.failedButOk = failedButOk - other.failedButOk; + return diff; + } + + Counts& Counts::operator += ( Counts const& other ) { + passed += other.passed; + failed += other.failed; + failedButOk += other.failedButOk; + return *this; + } + + std::size_t Counts::total() const { + return passed + failed + failedButOk; + } + bool Counts::allPassed() const { + return failed == 0 && failedButOk == 0; + } + bool Counts::allOk() const { + return failed == 0; + } + + Totals Totals::operator - ( Totals const& other ) const { + Totals diff; + diff.assertions = assertions - other.assertions; + diff.testCases = testCases - other.testCases; + return diff; + } + + Totals& Totals::operator += ( Totals const& other ) { + assertions += other.assertions; + testCases += other.testCases; + return *this; + } + + Totals Totals::delta( Totals const& prevTotals ) const { + Totals diff = *this - prevTotals; + if( diff.assertions.failed > 0 ) + ++diff.testCases.failed; + else if( diff.assertions.failedButOk > 0 ) + ++diff.testCases.failedButOk; + else + ++diff.testCases.passed; + return diff; + } + +} diff --git a/include/internal/catch_totals.h b/include/internal/catch_totals.h new file mode 100644 index 00000000..9507582d --- /dev/null +++ b/include/internal/catch_totals.h @@ -0,0 +1,41 @@ +/* + * Created by Phil Nash on 23/02/2012. + * Copyright (c) 2012 Two Blue Cubes Ltd. All rights reserved. + * + * Distributed under the Boost Software License, Version 1.0. (See accompanying + * file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) + */ +#ifndef TWOBLUECUBES_CATCH_TOTALS_HPP_INCLUDED +#define TWOBLUECUBES_CATCH_TOTALS_HPP_INCLUDED + +#include + +namespace Catch { + + struct Counts { + Counts operator - ( Counts const& other ) const; + Counts& operator += ( Counts const& other ); + + std::size_t total() const; + bool allPassed() const; + bool allOk() const; + + std::size_t passed = 0; + std::size_t failed = 0; + std::size_t failedButOk = 0; + }; + + struct Totals { + + Totals operator - ( Totals const& other ) const; + Totals& operator += ( Totals const& other ); + + Totals delta( Totals const& prevTotals ) const; + + + Counts assertions; + Counts testCases; + }; +} + +#endif // TWOBLUECUBES_CATCH_TOTALS_HPP_INCLUDED diff --git a/include/internal/catch_totals.hpp b/include/internal/catch_totals.hpp deleted file mode 100644 index 551e2947..00000000 --- a/include/internal/catch_totals.hpp +++ /dev/null @@ -1,78 +0,0 @@ -/* - * Created by Phil Nash on 23/02/2012. - * Copyright (c) 2012 Two Blue Cubes Ltd. All rights reserved. - * - * Distributed under the Boost Software License, Version 1.0. (See accompanying - * file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) - */ -#ifndef TWOBLUECUBES_CATCH_TOTALS_HPP_INCLUDED -#define TWOBLUECUBES_CATCH_TOTALS_HPP_INCLUDED - -#include - -namespace Catch { - - struct Counts { - Counts() : passed( 0 ), failed( 0 ), failedButOk( 0 ) {} - - Counts operator - ( Counts const& other ) const { - Counts diff; - diff.passed = passed - other.passed; - diff.failed = failed - other.failed; - diff.failedButOk = failedButOk - other.failedButOk; - return diff; - } - Counts& operator += ( Counts const& other ) { - passed += other.passed; - failed += other.failed; - failedButOk += other.failedButOk; - return *this; - } - - std::size_t total() const { - return passed + failed + failedButOk; - } - bool allPassed() const { - return failed == 0 && failedButOk == 0; - } - bool allOk() const { - return failed == 0; - } - - std::size_t passed; - std::size_t failed; - std::size_t failedButOk; - }; - - struct Totals { - - Totals operator - ( Totals const& other ) const { - Totals diff; - diff.assertions = assertions - other.assertions; - diff.testCases = testCases - other.testCases; - return diff; - } - - Totals delta( Totals const& prevTotals ) const { - Totals diff = *this - prevTotals; - if( diff.assertions.failed > 0 ) - ++diff.testCases.failed; - else if( diff.assertions.failedButOk > 0 ) - ++diff.testCases.failedButOk; - else - ++diff.testCases.passed; - return diff; - } - - Totals& operator += ( Totals const& other ) { - assertions += other.assertions; - testCases += other.testCases; - return *this; - } - - Counts assertions; - Counts testCases; - }; -} - -#endif // TWOBLUECUBES_CATCH_TOTALS_HPP_INCLUDED diff --git a/include/internal/catch_type_traits.hpp b/include/internal/catch_type_traits.hpp deleted file mode 100644 index 2094ba5b..00000000 --- a/include/internal/catch_type_traits.hpp +++ /dev/null @@ -1,47 +0,0 @@ -/* - * Created by Martin on 08/02/2017. - * - * Distributed under the Boost Software License, Version 1.0. (See accompanying - * file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) - */ -#ifndef TWOBLUECUBES_CATCH_TYPE_TRAITS_HPP_INCLUDED -#define TWOBLUECUBES_CATCH_TYPE_TRAITS_HPP_INCLUDED - -#if defined(CATCH_CONFIG_CPP11_TYPE_TRAITS) -#include -#endif - - -namespace Catch { - -#if defined(CATCH_CONFIG_CPP11_TYPE_TRAITS) - - template - using add_lvalue_reference = std::add_lvalue_reference; - - template - using add_const = std::add_const; - -#else - - template - struct add_const { - typedef const T type; - }; - - template - struct add_lvalue_reference { - typedef T& type; - }; - template - struct add_lvalue_reference { - typedef T& type; - }; - // No && overload, because that is C++11, in which case we have - // proper type_traits implementation from the standard library - -#endif - -} - -#endif // TWOBLUECUBES_CATCH_TYPE_TRAITS_HPP_INCLUDED diff --git a/include/internal/catch_version.hpp b/include/internal/catch_version.cpp similarity index 83% rename from include/internal/catch_version.hpp rename to include/internal/catch_version.cpp index 8049e3fb..a9dc64e6 100644 --- a/include/internal/catch_version.hpp +++ b/include/internal/catch_version.cpp @@ -5,10 +5,9 @@ * Distributed under the Boost Software License, Version 1.0. (See accompanying * file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) */ -#ifndef TWOBLUECUBES_CATCH_VERSION_HPP_INCLUDED -#define TWOBLUECUBES_CATCH_VERSION_HPP_INCLUDED #include "catch_version.h" +#include namespace Catch { @@ -37,11 +36,9 @@ namespace Catch { return os; } - inline Version libraryVersion() { - static Version version( 1, 11, 0, "", 0 ); + Version const& libraryVersion() { + static Version version( 2, 0, 1, "", 0 ); return version; } } - -#endif // TWOBLUECUBES_CATCH_VERSION_HPP_INCLUDED diff --git a/include/internal/catch_version.h b/include/internal/catch_version.h index 9f6bc283..018397f4 100644 --- a/include/internal/catch_version.h +++ b/include/internal/catch_version.h @@ -8,10 +8,14 @@ #ifndef TWOBLUECUBES_CATCH_VERSION_H_INCLUDED #define TWOBLUECUBES_CATCH_VERSION_H_INCLUDED +#include + namespace Catch { // Versioning information struct Version { + Version( Version const& ) = delete; + Version& operator=( Version const& ) = delete; Version( unsigned int _majorVersion, unsigned int _minorVersion, unsigned int _patchNumber, @@ -27,12 +31,9 @@ namespace Catch { unsigned int const buildNumber; friend std::ostream& operator << ( std::ostream& os, Version const& version ); - - private: - void operator=( Version const& ); }; - inline Version libraryVersion(); + Version const& libraryVersion(); } #endif // TWOBLUECUBES_CATCH_VERSION_H_INCLUDED diff --git a/include/internal/catch_wildcard_pattern.cpp b/include/internal/catch_wildcard_pattern.cpp new file mode 100644 index 00000000..f7303568 --- /dev/null +++ b/include/internal/catch_wildcard_pattern.cpp @@ -0,0 +1,48 @@ +/* + * Created by Martin on 19/07/2017. + * + * Distributed under the Boost Software License, Version 1.0. (See accompanying + * file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) + */ + +#include "catch_wildcard_pattern.h" +#include "catch_enforce.h" +#include "catch_string_manip.h" + + +namespace Catch { + + WildcardPattern::WildcardPattern( std::string const& pattern, + CaseSensitive::Choice caseSensitivity ) + : m_caseSensitivity( caseSensitivity ), + m_pattern( adjustCase( pattern ) ) + { + if( startsWith( m_pattern, '*' ) ) { + m_pattern = m_pattern.substr( 1 ); + m_wildcard = WildcardAtStart; + } + if( endsWith( m_pattern, '*' ) ) { + m_pattern = m_pattern.substr( 0, m_pattern.size()-1 ); + m_wildcard = static_cast( m_wildcard | WildcardAtEnd ); + } + } + + bool WildcardPattern::matches( std::string const& str ) const { + switch( m_wildcard ) { + case NoWildcard: + return m_pattern == adjustCase( str ); + case WildcardAtStart: + return endsWith( adjustCase( str ), m_pattern ); + case WildcardAtEnd: + return startsWith( adjustCase( str ), m_pattern ); + case WildcardAtBothEnds: + return contains( adjustCase( str ), m_pattern ); + default: + CATCH_INTERNAL_ERROR( "Unknown enum" ); + } + } + + std::string WildcardPattern::adjustCase( std::string const& str ) const { + return m_caseSensitivity == CaseSensitive::No ? toLower( str ) : str; + } +} diff --git a/include/internal/catch_wildcard_pattern.h b/include/internal/catch_wildcard_pattern.h new file mode 100644 index 00000000..4dae7171 --- /dev/null +++ b/include/internal/catch_wildcard_pattern.h @@ -0,0 +1,38 @@ +/* + * Created by Phil on 13/7/2015. + * Copyright 2015 Two Blue Cubes Ltd. All rights reserved. + * + * Distributed under the Boost Software License, Version 1.0. (See accompanying + * file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) + */ +#ifndef TWOBLUECUBES_CATCH_WILDCARD_PATTERN_HPP_INCLUDED +#define TWOBLUECUBES_CATCH_WILDCARD_PATTERN_HPP_INCLUDED + +#include "catch_common.h" + + +namespace Catch +{ + class WildcardPattern { + enum WildcardPosition { + NoWildcard = 0, + WildcardAtStart = 1, + WildcardAtEnd = 2, + WildcardAtBothEnds = WildcardAtStart | WildcardAtEnd + }; + + public: + + WildcardPattern( std::string const& pattern, CaseSensitive::Choice caseSensitivity ); + virtual ~WildcardPattern() = default; + virtual bool matches( std::string const& str ) const; + + private: + std::string adjustCase( std::string const& str ) const; + CaseSensitive::Choice m_caseSensitivity; + WildcardPosition m_wildcard = NoWildcard; + std::string m_pattern; + }; +} + +#endif // TWOBLUECUBES_CATCH_WILDCARD_PATTERN_HPP_INCLUDED diff --git a/include/internal/catch_wildcard_pattern.hpp b/include/internal/catch_wildcard_pattern.hpp deleted file mode 100644 index a922912d..00000000 --- a/include/internal/catch_wildcard_pattern.hpp +++ /dev/null @@ -1,74 +0,0 @@ -/* - * Created by Phil on 13/7/2015. - * Copyright 2015 Two Blue Cubes Ltd. All rights reserved. - * - * Distributed under the Boost Software License, Version 1.0. (See accompanying - * file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) - */ -#ifndef TWOBLUECUBES_CATCH_WILDCARD_PATTERN_HPP_INCLUDED -#define TWOBLUECUBES_CATCH_WILDCARD_PATTERN_HPP_INCLUDED - -#include "catch_common.h" - -#include - - -namespace Catch -{ - class WildcardPattern { - enum WildcardPosition { - NoWildcard = 0, - WildcardAtStart = 1, - WildcardAtEnd = 2, - WildcardAtBothEnds = WildcardAtStart | WildcardAtEnd - }; - - public: - - WildcardPattern( std::string const& pattern, CaseSensitive::Choice caseSensitivity ) - : m_caseSensitivity( caseSensitivity ), - m_wildcard( NoWildcard ), - m_pattern( adjustCase( pattern ) ) - { - if( startsWith( m_pattern, '*' ) ) { - m_pattern = m_pattern.substr( 1 ); - m_wildcard = WildcardAtStart; - } - if( endsWith( m_pattern, '*' ) ) { - m_pattern = m_pattern.substr( 0, m_pattern.size()-1 ); - m_wildcard = static_cast( m_wildcard | WildcardAtEnd ); - } - } - virtual ~WildcardPattern(); - virtual bool matches( std::string const& str ) const { - switch( m_wildcard ) { - case NoWildcard: - return m_pattern == adjustCase( str ); - case WildcardAtStart: - return endsWith( adjustCase( str ), m_pattern ); - case WildcardAtEnd: - return startsWith( adjustCase( str ), m_pattern ); - case WildcardAtBothEnds: - return contains( adjustCase( str ), m_pattern ); - } - -#ifdef __clang__ -#pragma clang diagnostic push -#pragma clang diagnostic ignored "-Wunreachable-code" -#endif - throw std::logic_error( "Unknown enum" ); -#ifdef __clang__ -#pragma clang diagnostic pop -#endif - } - private: - std::string adjustCase( std::string const& str ) const { - return m_caseSensitivity == CaseSensitive::No ? toLower( str ) : str; - } - CaseSensitive::Choice m_caseSensitivity; - WildcardPosition m_wildcard; - std::string m_pattern; - }; -} - -#endif // TWOBLUECUBES_CATCH_WILDCARD_PATTERN_HPP_INCLUDED diff --git a/include/internal/catch_windows_h_proxy.h b/include/internal/catch_windows_h_proxy.h index 4f059b4a..a7a19c8e 100644 --- a/include/internal/catch_windows_h_proxy.h +++ b/include/internal/catch_windows_h_proxy.h @@ -8,10 +8,16 @@ #ifndef TWOBLUECUBES_CATCH_WINDOWS_H_PROXY_H_INCLUDED #define TWOBLUECUBES_CATCH_WINDOWS_H_PROXY_H_INCLUDED -#ifdef CATCH_DEFINES_NOMINMAX +#include "catch_platform.h" + +#if defined(CATCH_PLATFORM_WINDOWS) + +#if !defined(NOMINMAX) && !defined(CATCH_CONFIG_NO_NOMINMAX) +# define CATCH_DEFINED_NOMINMAX # define NOMINMAX #endif -#ifdef CATCH_DEFINES_WIN32_LEAN_AND_MEAN +#if !defined(WIN32_LEAN_AND_MEAN) && !defined(CATCH_CONFIG_NO_WIN32_LEAN_AND_MEAN) +# define CATCH_DEFINED_WIN32_LEAN_AND_MEAN # define WIN32_LEAN_AND_MEAN #endif @@ -21,12 +27,13 @@ #include #endif -#ifdef CATCH_DEFINES_NOMINMAX +#ifdef CATCH_DEFINED_NOMINMAX # undef NOMINMAX #endif -#ifdef CATCH_DEFINES_WIN32_LEAN_AND_MEAN +#ifdef CATCH_DEFINED_WIN32_LEAN_AND_MEAN # undef WIN32_LEAN_AND_MEAN #endif +#endif // defined(CATCH_PLATFORM_WINDOWS) #endif // TWOBLUECUBES_CATCH_WINDOWS_H_PROXY_H_INCLUDED diff --git a/include/internal/catch_xmlwriter.cpp b/include/internal/catch_xmlwriter.cpp new file mode 100644 index 00000000..a3316f46 --- /dev/null +++ b/include/internal/catch_xmlwriter.cpp @@ -0,0 +1,190 @@ +/* + * Created by Phil on 19/07/2017. + * + * Distributed under the Boost Software License, Version 1.0. (See accompanying + * file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) + */ + +#include "catch_xmlwriter.h" + +#include + +namespace Catch { + + XmlEncode::XmlEncode( std::string const& str, ForWhat forWhat ) + : m_str( str ), + m_forWhat( forWhat ) + {} + + void XmlEncode::encodeTo( std::ostream& os ) const { + + // Apostrophe escaping not necessary if we always use " to write attributes + // (see: http://www.w3.org/TR/xml/#syntax) + + for( std::size_t i = 0; i < m_str.size(); ++ i ) { + char c = m_str[i]; + switch( c ) { + case '<': os << "<"; break; + case '&': os << "&"; break; + + case '>': + // See: http://www.w3.org/TR/xml/#syntax + if( i > 2 && m_str[i-1] == ']' && m_str[i-2] == ']' ) + os << ">"; + else + os << c; + break; + + case '\"': + if( m_forWhat == ForAttributes ) + os << """; + else + os << c; + break; + + default: + // Escape control chars - based on contribution by @espenalb in PR #465 and + // by @mrpi PR #588 + if ( ( c >= 0 && c < '\x09' ) || ( c > '\x0D' && c < '\x20') || c=='\x7F' ) { + // see http://stackoverflow.com/questions/404107/why-are-control-characters-illegal-in-xml-1-0 + os << "\\x" << std::uppercase << std::hex << std::setfill('0') << std::setw(2) + << static_cast( c ); + } + else + os << c; + } + } + } + + std::ostream& operator << ( std::ostream& os, XmlEncode const& xmlEncode ) { + xmlEncode.encodeTo( os ); + return os; + } + + XmlWriter::ScopedElement::ScopedElement( XmlWriter* writer ) + : m_writer( writer ) + {} + + XmlWriter::ScopedElement::ScopedElement( ScopedElement&& other ) noexcept + : m_writer( other.m_writer ){ + other.m_writer = nullptr; + } + XmlWriter::ScopedElement& XmlWriter::ScopedElement::operator=( ScopedElement&& other ) noexcept { + if ( m_writer ) { + m_writer->endElement(); + } + m_writer = other.m_writer; + other.m_writer = nullptr; + return *this; + } + + + XmlWriter::ScopedElement::~ScopedElement() { + if( m_writer ) + m_writer->endElement(); + } + + XmlWriter::ScopedElement& XmlWriter::ScopedElement::writeText( std::string const& text, bool indent ) { + m_writer->writeText( text, indent ); + return *this; + } + + XmlWriter::XmlWriter( std::ostream& os ) : m_os( os ) + { + writeDeclaration(); + } + + XmlWriter::~XmlWriter() { + while( !m_tags.empty() ) + endElement(); + } + + XmlWriter& XmlWriter::startElement( std::string const& name ) { + ensureTagClosed(); + newlineIfNecessary(); + m_os << m_indent << '<' << name; + m_tags.push_back( name ); + m_indent += " "; + m_tagIsOpen = true; + return *this; + } + + XmlWriter::ScopedElement XmlWriter::scopedElement( std::string const& name ) { + ScopedElement scoped( this ); + startElement( name ); + return scoped; + } + + XmlWriter& XmlWriter::endElement() { + newlineIfNecessary(); + m_indent = m_indent.substr( 0, m_indent.size()-2 ); + if( m_tagIsOpen ) { + m_os << "/>"; + m_tagIsOpen = false; + } + else { + m_os << m_indent << ""; + } + m_os << std::endl; + m_tags.pop_back(); + return *this; + } + + XmlWriter& XmlWriter::writeAttribute( std::string const& name, std::string const& attribute ) { + if( !name.empty() && !attribute.empty() ) + m_os << ' ' << name << "=\"" << XmlEncode( attribute, XmlEncode::ForAttributes ) << '"'; + return *this; + } + + XmlWriter& XmlWriter::writeAttribute( std::string const& name, bool attribute ) { + m_os << ' ' << name << "=\"" << ( attribute ? "true" : "false" ) << '"'; + return *this; + } + + XmlWriter& XmlWriter::writeText( std::string const& text, bool indent ) { + if( !text.empty() ){ + bool tagWasOpen = m_tagIsOpen; + ensureTagClosed(); + if( tagWasOpen && indent ) + m_os << m_indent; + m_os << XmlEncode( text ); + m_needsNewline = true; + } + return *this; + } + + XmlWriter& XmlWriter::writeComment( std::string const& text ) { + ensureTagClosed(); + m_os << m_indent << ""; + m_needsNewline = true; + return *this; + } + + void XmlWriter::writeStylesheetRef( std::string const& url ) { + m_os << "\n"; + } + + XmlWriter& XmlWriter::writeBlankLine() { + ensureTagClosed(); + m_os << '\n'; + return *this; + } + + void XmlWriter::ensureTagClosed() { + if( m_tagIsOpen ) { + m_os << ">" << std::endl; + m_tagIsOpen = false; + } + } + + void XmlWriter::writeDeclaration() { + m_os << "\n"; + } + + void XmlWriter::newlineIfNecessary() { + if( m_needsNewline ) { + m_os << std::endl; + m_needsNewline = false; + } + } +} diff --git a/include/internal/catch_xmlwriter.h b/include/internal/catch_xmlwriter.h new file mode 100644 index 00000000..2570ec12 --- /dev/null +++ b/include/internal/catch_xmlwriter.h @@ -0,0 +1,108 @@ +/* + * Created by Phil on 09/12/2010. + * Copyright 2010 Two Blue Cubes Ltd. All rights reserved. + * + * Distributed under the Boost Software License, Version 1.0. (See accompanying + * file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) + */ +#ifndef TWOBLUECUBES_CATCH_XMLWRITER_HPP_INCLUDED +#define TWOBLUECUBES_CATCH_XMLWRITER_HPP_INCLUDED + +#include "catch_stream.h" +#include "catch_compiler_capabilities.h" + +#include +#include + +namespace Catch { + + class XmlEncode { + public: + enum ForWhat { ForTextNodes, ForAttributes }; + + XmlEncode( std::string const& str, ForWhat forWhat = ForTextNodes ); + + void encodeTo( std::ostream& os ) const; + + friend std::ostream& operator << ( std::ostream& os, XmlEncode const& xmlEncode ); + + private: + std::string m_str; + ForWhat m_forWhat; + }; + + class XmlWriter { + public: + + class ScopedElement { + public: + ScopedElement( XmlWriter* writer ); + + ScopedElement( ScopedElement&& other ) noexcept; + ScopedElement& operator=( ScopedElement&& other ) noexcept; + + ~ScopedElement(); + + ScopedElement& writeText( std::string const& text, bool indent = true ); + + template + ScopedElement& writeAttribute( std::string const& name, T const& attribute ) { + m_writer->writeAttribute( name, attribute ); + return *this; + } + + private: + mutable XmlWriter* m_writer = nullptr; + }; + + XmlWriter( std::ostream& os = Catch::cout() ); + ~XmlWriter(); + + XmlWriter( XmlWriter const& ) = delete; + XmlWriter& operator=( XmlWriter const& ) = delete; + + XmlWriter& startElement( std::string const& name ); + + ScopedElement scopedElement( std::string const& name ); + + XmlWriter& endElement(); + + XmlWriter& writeAttribute( std::string const& name, std::string const& attribute ); + + XmlWriter& writeAttribute( std::string const& name, bool attribute ); + + template + XmlWriter& writeAttribute( std::string const& name, T const& attribute ) { + m_oss.clear(); + m_oss.str(std::string()); + m_oss << attribute; + return writeAttribute( name, m_oss.str() ); + } + + XmlWriter& writeText( std::string const& text, bool indent = true ); + + XmlWriter& writeComment( std::string const& text ); + + void writeStylesheetRef( std::string const& url ); + + XmlWriter& writeBlankLine(); + + void ensureTagClosed(); + + private: + + void writeDeclaration(); + + void newlineIfNecessary(); + + bool m_tagIsOpen = false; + bool m_needsNewline = false; + std::vector m_tags; + std::string m_indent; + std::ostream& m_os; + std::ostringstream m_oss; + }; + +} + +#endif // TWOBLUECUBES_CATCH_XMLWRITER_HPP_INCLUDED diff --git a/include/internal/catch_xmlwriter.hpp b/include/internal/catch_xmlwriter.hpp deleted file mode 100644 index 224c23a5..00000000 --- a/include/internal/catch_xmlwriter.hpp +++ /dev/null @@ -1,244 +0,0 @@ -/* - * Created by Phil on 09/12/2010. - * Copyright 2010 Two Blue Cubes Ltd. All rights reserved. - * - * Distributed under the Boost Software License, Version 1.0. (See accompanying - * file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) - */ -#ifndef TWOBLUECUBES_CATCH_XMLWRITER_HPP_INCLUDED -#define TWOBLUECUBES_CATCH_XMLWRITER_HPP_INCLUDED - -#include "catch_stream.h" -#include "catch_compiler_capabilities.h" - -#include -#include -#include -#include - -namespace Catch { - - class XmlEncode { - public: - enum ForWhat { ForTextNodes, ForAttributes }; - - XmlEncode( std::string const& str, ForWhat forWhat = ForTextNodes ) - : m_str( str ), - m_forWhat( forWhat ) - {} - - void encodeTo( std::ostream& os ) const { - - // Apostrophe escaping not necessary if we always use " to write attributes - // (see: http://www.w3.org/TR/xml/#syntax) - - for( std::size_t i = 0; i < m_str.size(); ++ i ) { - char c = m_str[i]; - switch( c ) { - case '<': os << "<"; break; - case '&': os << "&"; break; - - case '>': - // See: http://www.w3.org/TR/xml/#syntax - if( i > 2 && m_str[i-1] == ']' && m_str[i-2] == ']' ) - os << ">"; - else - os << c; - break; - - case '\"': - if( m_forWhat == ForAttributes ) - os << """; - else - os << c; - break; - - default: - // Escape control chars - based on contribution by @espenalb in PR #465 and - // by @mrpi PR #588 - if ( ( c >= 0 && c < '\x09' ) || ( c > '\x0D' && c < '\x20') || c=='\x7F' ) { - // see http://stackoverflow.com/questions/404107/why-are-control-characters-illegal-in-xml-1-0 - os << "\\x" << std::uppercase << std::hex << std::setfill('0') << std::setw(2) - << static_cast( c ); - } - else - os << c; - } - } - } - - friend std::ostream& operator << ( std::ostream& os, XmlEncode const& xmlEncode ) { - xmlEncode.encodeTo( os ); - return os; - } - - private: - std::string m_str; - ForWhat m_forWhat; - }; - - class XmlWriter { - public: - - class ScopedElement { - public: - ScopedElement( XmlWriter* writer ) - : m_writer( writer ) - {} - - ScopedElement( ScopedElement const& other ) - : m_writer( other.m_writer ){ - other.m_writer = CATCH_NULL; - } - - ~ScopedElement() { - if( m_writer ) - m_writer->endElement(); - } - - ScopedElement& writeText( std::string const& text, bool indent = true ) { - m_writer->writeText( text, indent ); - return *this; - } - - template - ScopedElement& writeAttribute( std::string const& name, T const& attribute ) { - m_writer->writeAttribute( name, attribute ); - return *this; - } - - private: - mutable XmlWriter* m_writer; - }; - - XmlWriter() - : m_tagIsOpen( false ), - m_needsNewline( false ), - m_os( Catch::cout() ) - { - writeDeclaration(); - } - - XmlWriter( std::ostream& os ) - : m_tagIsOpen( false ), - m_needsNewline( false ), - m_os( os ) - { - writeDeclaration(); - } - - ~XmlWriter() { - while( !m_tags.empty() ) - endElement(); - } - - XmlWriter& startElement( std::string const& name ) { - ensureTagClosed(); - newlineIfNecessary(); - m_os << m_indent << '<' << name; - m_tags.push_back( name ); - m_indent += " "; - m_tagIsOpen = true; - return *this; - } - - ScopedElement scopedElement( std::string const& name ) { - ScopedElement scoped( this ); - startElement( name ); - return scoped; - } - - XmlWriter& endElement() { - newlineIfNecessary(); - m_indent = m_indent.substr( 0, m_indent.size()-2 ); - if( m_tagIsOpen ) { - m_os << "/>"; - m_tagIsOpen = false; - } - else { - m_os << m_indent << ""; - } - m_os << std::endl; - m_tags.pop_back(); - return *this; - } - - XmlWriter& writeAttribute( std::string const& name, std::string const& attribute ) { - if( !name.empty() && !attribute.empty() ) - m_os << ' ' << name << "=\"" << XmlEncode( attribute, XmlEncode::ForAttributes ) << '"'; - return *this; - } - - XmlWriter& writeAttribute( std::string const& name, bool attribute ) { - m_os << ' ' << name << "=\"" << ( attribute ? "true" : "false" ) << '"'; - return *this; - } - - template - XmlWriter& writeAttribute( std::string const& name, T const& attribute ) { - std::ostringstream oss; - oss << attribute; - return writeAttribute( name, oss.str() ); - } - - XmlWriter& writeText( std::string const& text, bool indent = true ) { - if( !text.empty() ){ - bool tagWasOpen = m_tagIsOpen; - ensureTagClosed(); - if( tagWasOpen && indent ) - m_os << m_indent; - m_os << XmlEncode( text ); - m_needsNewline = true; - } - return *this; - } - - XmlWriter& writeComment( std::string const& text ) { - ensureTagClosed(); - m_os << m_indent << ""; - m_needsNewline = true; - return *this; - } - - void writeStylesheetRef( std::string const& url ) { - m_os << "\n"; - } - - XmlWriter& writeBlankLine() { - ensureTagClosed(); - m_os << '\n'; - return *this; - } - - void ensureTagClosed() { - if( m_tagIsOpen ) { - m_os << ">" << std::endl; - m_tagIsOpen = false; - } - } - - private: - XmlWriter( XmlWriter const& ); - void operator=( XmlWriter const& ); - - void writeDeclaration() { - m_os << "\n"; - } - - void newlineIfNecessary() { - if( m_needsNewline ) { - m_os << std::endl; - m_needsNewline = false; - } - } - - bool m_tagIsOpen; - bool m_needsNewline; - std::vector m_tags; - std::string m_indent; - std::ostream& m_os; - }; - -} - -#endif // TWOBLUECUBES_CATCH_XMLWRITER_HPP_INCLUDED diff --git a/include/reporters/catch_reporter_automake.hpp b/include/reporters/catch_reporter_automake.hpp index c267d8a9..dbebe975 100644 --- a/include/reporters/catch_reporter_automake.hpp +++ b/include/reporters/catch_reporter_automake.hpp @@ -16,22 +16,22 @@ namespace Catch { - struct AutomakeReporter : StreamingReporterBase { + struct AutomakeReporter : StreamingReporterBase { AutomakeReporter( ReporterConfig const& _config ) : StreamingReporterBase( _config ) {} - virtual ~AutomakeReporter(); + ~AutomakeReporter() override; static std::string getDescription() { return "Reports test results in the format of Automake .trs files"; } - virtual void assertionStarting( AssertionInfo const& ) CATCH_OVERRIDE {} + void assertionStarting( AssertionInfo const& ) override {} - virtual bool assertionEnded( AssertionStats const& /*_assertionStats*/ ) CATCH_OVERRIDE { return true; } + bool assertionEnded( AssertionStats const& /*_assertionStats*/ ) override { return true; } - virtual void testCaseEnded( TestCaseStats const& _testCaseStats ) CATCH_OVERRIDE { + void testCaseEnded( TestCaseStats const& _testCaseStats ) override { // Possible values to emit are PASS, XFAIL, SKIP, FAIL, XPASS and ERROR. stream << ":test-result: "; if (_testCaseStats.totals.assertions.allPassed()) { @@ -45,7 +45,7 @@ namespace Catch { StreamingReporterBase::testCaseEnded( _testCaseStats ); } - virtual void skipTest( TestCaseInfo const& testInfo ) CATCH_OVERRIDE { + void skipTest( TestCaseInfo const& testInfo ) override { stream << ":test-result: SKIP " << testInfo.name << '\n'; } @@ -55,7 +55,7 @@ namespace Catch { AutomakeReporter::~AutomakeReporter() {} #endif - INTERNAL_CATCH_REGISTER_REPORTER( "automake", AutomakeReporter) + CATCH_REGISTER_REPORTER( "automake", AutomakeReporter) } // end namespace Catch diff --git a/include/reporters/catch_reporter_bases.cpp b/include/reporters/catch_reporter_bases.cpp new file mode 100644 index 00000000..c2059b5a --- /dev/null +++ b/include/reporters/catch_reporter_bases.cpp @@ -0,0 +1,55 @@ +/* + * Created by Phil on 27/11/2013. + * Copyright 2013 Two Blue Cubes Ltd. All rights reserved. + * + * Distributed under the Boost Software License, Version 1.0. (See accompanying + * file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) + */ + +#include "../internal/catch_interfaces_reporter.h" +#include "../internal/catch_errno_guard.h" +#include "catch_reporter_bases.hpp" + +#include +#include +#include +#include +#include + +namespace Catch { + void prepareExpandedExpression(AssertionResult& result) { + result.getExpandedExpression(); + } + + // Because formatting using c++ streams is stateful, drop down to C is required + // Alternatively we could use stringstream, but its performance is... not good. + std::string getFormattedDuration( double duration ) { + // Max exponent + 1 is required to represent the whole part + // + 1 for decimal point + // + 3 for the 3 decimal places + // + 1 for null terminator + const std::size_t maxDoubleSize = DBL_MAX_10_EXP + 1 + 1 + 3 + 1; + char buffer[maxDoubleSize]; + + // Save previous errno, to prevent sprintf from overwriting it + ErrnoGuard guard; +#ifdef _MSC_VER + sprintf_s(buffer, "%.3f", duration); +#else + sprintf(buffer, "%.3f", duration); +#endif + return std::string(buffer); + } + + + TestEventListenerBase::TestEventListenerBase(ReporterConfig const & _config) + :StreamingReporterBase(_config) {} + + void TestEventListenerBase::assertionStarting(AssertionInfo const &) {} + + bool TestEventListenerBase::assertionEnded(AssertionStats const &) { + return false; + } + + +} // end namespace Catch diff --git a/include/reporters/catch_reporter_bases.hpp b/include/reporters/catch_reporter_bases.hpp index 9df096c7..1c7efb64 100644 --- a/include/reporters/catch_reporter_bases.hpp +++ b/include/reporters/catch_reporter_bases.hpp @@ -8,91 +8,81 @@ #ifndef TWOBLUECUBES_CATCH_REPORTER_BASES_HPP_INCLUDED #define TWOBLUECUBES_CATCH_REPORTER_BASES_HPP_INCLUDED +#include "../internal/catch_enforce.h" #include "../internal/catch_interfaces_reporter.h" -#include "../internal/catch_errno_guard.hpp" + +#include #include #include #include #include +#include namespace Catch { + void prepareExpandedExpression(AssertionResult& result); - namespace { - // Because formatting using c++ streams is stateful, drop down to C is required - // Alternatively we could use stringstream, but its performance is... not good. - std::string getFormattedDuration( double duration ) { - // Max exponent + 1 is required to represent the whole part - // + 1 for decimal point - // + 3 for the 3 decimal places - // + 1 for null terminator - const size_t maxDoubleSize = DBL_MAX_10_EXP + 1 + 1 + 3 + 1; - char buffer[maxDoubleSize]; + // Returns double formatted as %.3f (format expected on output) + std::string getFormattedDuration( double duration ); - // Save previous errno, to prevent sprintf from overwriting it - ErrnoGuard guard; -#ifdef _MSC_VER - sprintf_s(buffer, "%.3f", duration); -#else - sprintf(buffer, "%.3f", duration); -#endif - return std::string(buffer); - } - } - - - struct StreamingReporterBase : SharedImpl { + template + struct StreamingReporterBase : IStreamingReporter { StreamingReporterBase( ReporterConfig const& _config ) : m_config( _config.fullConfig() ), stream( _config.stream() ) { m_reporterPrefs.shouldRedirectStdOut = false; + CATCH_ENFORCE( DerivedT::getSupportedVerbosities().count( m_config->verbosity() ), "Verbosity level not supported by this reporter" ); } - virtual ReporterPreferences getPreferences() const CATCH_OVERRIDE { + ReporterPreferences getPreferences() const override { return m_reporterPrefs; } - virtual ~StreamingReporterBase() CATCH_OVERRIDE; + static std::set getSupportedVerbosities() { + return { Verbosity::Normal }; + } - virtual void noMatchingTestCases( std::string const& ) CATCH_OVERRIDE {} + ~StreamingReporterBase() override = default; - virtual void testRunStarting( TestRunInfo const& _testRunInfo ) CATCH_OVERRIDE { + void noMatchingTestCases(std::string const&) override {} + + void testRunStarting(TestRunInfo const& _testRunInfo) override { currentTestRunInfo = _testRunInfo; } - virtual void testGroupStarting( GroupInfo const& _groupInfo ) CATCH_OVERRIDE { + void testGroupStarting(GroupInfo const& _groupInfo) override { currentGroupInfo = _groupInfo; } - virtual void testCaseStarting( TestCaseInfo const& _testInfo ) CATCH_OVERRIDE { + void testCaseStarting(TestCaseInfo const& _testInfo) override { currentTestCaseInfo = _testInfo; } - virtual void sectionStarting( SectionInfo const& _sectionInfo ) CATCH_OVERRIDE { - m_sectionStack.push_back( _sectionInfo ); + void sectionStarting(SectionInfo const& _sectionInfo) override { + m_sectionStack.push_back(_sectionInfo); } - virtual void sectionEnded( SectionStats const& /* _sectionStats */ ) CATCH_OVERRIDE { + void sectionEnded(SectionStats const& /* _sectionStats */) override { m_sectionStack.pop_back(); } - virtual void testCaseEnded( TestCaseStats const& /* _testCaseStats */ ) CATCH_OVERRIDE { + void testCaseEnded(TestCaseStats const& /* _testCaseStats */) override { currentTestCaseInfo.reset(); } - virtual void testGroupEnded( TestGroupStats const& /* _testGroupStats */ ) CATCH_OVERRIDE { + void testGroupEnded(TestGroupStats const& /* _testGroupStats */) override { currentGroupInfo.reset(); } - virtual void testRunEnded( TestRunStats const& /* _testRunStats */ ) CATCH_OVERRIDE { + void testRunEnded(TestRunStats const& /* _testRunStats */) override { currentTestCaseInfo.reset(); currentGroupInfo.reset(); currentTestRunInfo.reset(); } - virtual void skipTest( TestCaseInfo const& ) CATCH_OVERRIDE { + void skipTest(TestCaseInfo const&) override { // Don't do anything with this by default. // It can optionally be overridden in the derived class. } - Ptr m_config; + IConfigPtr m_config; std::ostream& stream; LazyStat currentTestRunInfo; @@ -103,30 +93,31 @@ namespace Catch { ReporterPreferences m_reporterPrefs; }; - struct CumulativeReporterBase : SharedImpl { + template + struct CumulativeReporterBase : IStreamingReporter { template - struct Node : SharedImpl<> { + struct Node { explicit Node( T const& _value ) : value( _value ) {} virtual ~Node() {} - typedef std::vector > ChildNodes; + using ChildNodes = std::vector>; T value; ChildNodes children; }; - struct SectionNode : SharedImpl<> { - explicit SectionNode( SectionStats const& _stats ) : stats( _stats ) {} - virtual ~SectionNode(); + struct SectionNode { + explicit SectionNode(SectionStats const& _stats) : stats(_stats) {} + virtual ~SectionNode() = default; - bool operator == ( SectionNode const& other ) const { + bool operator == (SectionNode const& other) const { return stats.sectionInfo.lineInfo == other.stats.sectionInfo.lineInfo; } - bool operator == ( Ptr const& other ) const { - return operator==( *other ); + bool operator == (std::shared_ptr const& other) const { + return operator==(*other); } SectionStats stats; - typedef std::vector > ChildSections; - typedef std::vector Assertions; + using ChildSections = std::vector>; + using Assertions = std::vector; ChildSections childSections; Assertions assertions; std::string stdOut; @@ -136,129 +127,127 @@ namespace Catch { struct BySectionInfo { BySectionInfo( SectionInfo const& other ) : m_other( other ) {} BySectionInfo( BySectionInfo const& other ) : m_other( other.m_other ) {} - bool operator() ( Ptr const& node ) const { + bool operator() (std::shared_ptr const& node) const { return ((node->stats.sectionInfo.name == m_other.name) && (node->stats.sectionInfo.lineInfo == m_other.lineInfo)); } + void operator=(BySectionInfo const&) = delete; + private: - void operator=( BySectionInfo const& ); SectionInfo const& m_other; }; - typedef Node TestCaseNode; - typedef Node TestGroupNode; - typedef Node TestRunNode; + using TestCaseNode = Node; + using TestGroupNode = Node; + using TestRunNode = Node; CumulativeReporterBase( ReporterConfig const& _config ) : m_config( _config.fullConfig() ), stream( _config.stream() ) { m_reporterPrefs.shouldRedirectStdOut = false; + CATCH_ENFORCE( DerivedT::getSupportedVerbosities().count( m_config->verbosity() ), "Verbosity level not supported by this reporter" ); } - ~CumulativeReporterBase(); + ~CumulativeReporterBase() override = default; - virtual ReporterPreferences getPreferences() const CATCH_OVERRIDE { + ReporterPreferences getPreferences() const override { return m_reporterPrefs; } - virtual void testRunStarting( TestRunInfo const& ) CATCH_OVERRIDE {} - virtual void testGroupStarting( GroupInfo const& ) CATCH_OVERRIDE {} + static std::set getSupportedVerbosities() { + return { Verbosity::Normal }; + } - virtual void testCaseStarting( TestCaseInfo const& ) CATCH_OVERRIDE {} + void testRunStarting( TestRunInfo const& ) override {} + void testGroupStarting( GroupInfo const& ) override {} - virtual void sectionStarting( SectionInfo const& sectionInfo ) CATCH_OVERRIDE { + void testCaseStarting( TestCaseInfo const& ) override {} + + void sectionStarting( SectionInfo const& sectionInfo ) override { SectionStats incompleteStats( sectionInfo, Counts(), 0, false ); - Ptr node; + std::shared_ptr node; if( m_sectionStack.empty() ) { if( !m_rootSection ) - m_rootSection = new SectionNode( incompleteStats ); + m_rootSection = std::make_shared( incompleteStats ); node = m_rootSection; } else { SectionNode& parentNode = *m_sectionStack.back(); - SectionNode::ChildSections::const_iterator it = + auto it = std::find_if( parentNode.childSections.begin(), parentNode.childSections.end(), BySectionInfo( sectionInfo ) ); if( it == parentNode.childSections.end() ) { - node = new SectionNode( incompleteStats ); + node = std::make_shared( incompleteStats ); parentNode.childSections.push_back( node ); } else node = *it; } m_sectionStack.push_back( node ); - m_deepestSection = node; + m_deepestSection = std::move(node); } - virtual void assertionStarting( AssertionInfo const& ) CATCH_OVERRIDE {} + void assertionStarting(AssertionInfo const&) override {} - virtual bool assertionEnded( AssertionStats const& assertionStats ) CATCH_OVERRIDE { - assert( !m_sectionStack.empty() ); - SectionNode& sectionNode = *m_sectionStack.back(); - sectionNode.assertions.push_back( assertionStats ); + bool assertionEnded(AssertionStats const& assertionStats) override { + assert(!m_sectionStack.empty()); // AssertionResult holds a pointer to a temporary DecomposedExpression, // which getExpandedExpression() calls to build the expression string. // Our section stack copy of the assertionResult will likely outlive the // temporary, so it must be expanded or discarded now to avoid calling // a destroyed object later. - prepareExpandedExpression( sectionNode.assertions.back().assertionResult ); + prepareExpandedExpression(const_cast( assertionStats.assertionResult ) ); + SectionNode& sectionNode = *m_sectionStack.back(); + sectionNode.assertions.push_back(assertionStats); return true; } - virtual void sectionEnded( SectionStats const& sectionStats ) CATCH_OVERRIDE { - assert( !m_sectionStack.empty() ); + void sectionEnded(SectionStats const& sectionStats) override { + assert(!m_sectionStack.empty()); SectionNode& node = *m_sectionStack.back(); node.stats = sectionStats; m_sectionStack.pop_back(); } - virtual void testCaseEnded( TestCaseStats const& testCaseStats ) CATCH_OVERRIDE { - Ptr node = new TestCaseNode( testCaseStats ); - assert( m_sectionStack.size() == 0 ); - node->children.push_back( m_rootSection ); - m_testCases.push_back( node ); + void testCaseEnded(TestCaseStats const& testCaseStats) override { + auto node = std::make_shared(testCaseStats); + assert(m_sectionStack.size() == 0); + node->children.push_back(m_rootSection); + m_testCases.push_back(node); m_rootSection.reset(); - - assert( m_deepestSection ); + + assert(m_deepestSection); m_deepestSection->stdOut = testCaseStats.stdOut; m_deepestSection->stdErr = testCaseStats.stdErr; } - virtual void testGroupEnded( TestGroupStats const& testGroupStats ) CATCH_OVERRIDE { - Ptr node = new TestGroupNode( testGroupStats ); - node->children.swap( m_testCases ); - m_testGroups.push_back( node ); + void testGroupEnded(TestGroupStats const& testGroupStats) override { + auto node = std::make_shared(testGroupStats); + node->children.swap(m_testCases); + m_testGroups.push_back(node); } - virtual void testRunEnded( TestRunStats const& testRunStats ) CATCH_OVERRIDE { - Ptr node = new TestRunNode( testRunStats ); - node->children.swap( m_testGroups ); - m_testRuns.push_back( node ); + void testRunEnded(TestRunStats const& testRunStats) override { + auto node = std::make_shared(testRunStats); + node->children.swap(m_testGroups); + m_testRuns.push_back(node); testRunEndedCumulative(); } virtual void testRunEndedCumulative() = 0; - virtual void skipTest( TestCaseInfo const& ) CATCH_OVERRIDE {} + void skipTest(TestCaseInfo const&) override {} - virtual void prepareExpandedExpression( AssertionResult& result ) const { - if( result.isOk() ) - result.discardDecomposedExpression(); - else - result.expandDecomposedExpression(); - } - - Ptr m_config; + IConfigPtr m_config; std::ostream& stream; std::vector m_assertions; - std::vector > > m_sections; - std::vector > m_testCases; - std::vector > m_testGroups; + std::vector>> m_sections; + std::vector> m_testCases; + std::vector> m_testGroups; - std::vector > m_testRuns; + std::vector> m_testRuns; - Ptr m_rootSection; - Ptr m_deepestSection; - std::vector > m_sectionStack; + std::shared_ptr m_rootSection; + std::shared_ptr m_deepestSection; + std::vector> m_sectionStack; ReporterPreferences m_reporterPrefs; - }; template @@ -272,15 +261,11 @@ namespace Catch { } - struct TestEventListenerBase : StreamingReporterBase { - TestEventListenerBase( ReporterConfig const& _config ) - : StreamingReporterBase( _config ) - {} + struct TestEventListenerBase : StreamingReporterBase { + TestEventListenerBase( ReporterConfig const& _config ); - virtual void assertionStarting( AssertionInfo const& ) CATCH_OVERRIDE {} - virtual bool assertionEnded( AssertionStats const& ) CATCH_OVERRIDE { - return false; - } + void assertionStarting(AssertionInfo const&) override; + bool assertionEnded(AssertionStats const&) override; }; } // end namespace Catch diff --git a/include/reporters/catch_reporter_compact.hpp b/include/reporters/catch_reporter_compact.cpp similarity index 86% rename from include/reporters/catch_reporter_compact.hpp rename to include/reporters/catch_reporter_compact.cpp index 8d6f9085..74a09e4b 100644 --- a/include/reporters/catch_reporter_compact.hpp +++ b/include/reporters/catch_reporter_compact.cpp @@ -5,41 +5,56 @@ * Distributed under the Boost Software License, Version 1.0. (See accompanying * file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) */ -#ifndef TWOBLUECUBES_CATCH_REPORTER_COMPACT_HPP_INCLUDED -#define TWOBLUECUBES_CATCH_REPORTER_COMPACT_HPP_INCLUDED #include "catch_reporter_bases.hpp" #include "../internal/catch_reporter_registrars.hpp" -#include "../internal/catch_console_colour.hpp" +#include "internal/catch_console_colour.h" + +namespace { + +#ifdef CATCH_PLATFORM_MAC + const char* failedString() { return "FAILED"; } + const char* passedString() { return "PASSED"; } +#else + const char* failedString() { return "failed"; } + const char* passedString() { return "passed"; } +#endif + + // Colour::LightGrey + Catch::Colour::Code dimColour() { return Catch::Colour::FileName; } + + std::string bothOrAll( std::size_t count ) { + return count == 1 ? std::string() : + count == 2 ? "both " : "all " ; + } +} namespace Catch { - struct CompactReporter : StreamingReporterBase { + struct CompactReporter : StreamingReporterBase { - CompactReporter( ReporterConfig const& _config ) - : StreamingReporterBase( _config ) - {} + using StreamingReporterBase::StreamingReporterBase; - virtual ~CompactReporter(); + ~CompactReporter() override; static std::string getDescription() { return "Reports test results on a single line, suitable for IDEs"; } - virtual ReporterPreferences getPreferences() const { + ReporterPreferences getPreferences() const override { ReporterPreferences prefs; prefs.shouldRedirectStdOut = false; return prefs; } - virtual void noMatchingTestCases( std::string const& spec ) { + void noMatchingTestCases( std::string const& spec ) override { stream << "No test cases matched '" << spec << '\'' << std::endl; } - virtual void assertionStarting( AssertionInfo const& ) {} + void assertionStarting( AssertionInfo const& ) override {} - virtual bool assertionEnded( AssertionStats const& _assertionStats ) { + bool assertionEnded( AssertionStats const& _assertionStats ) override { AssertionResult const& result = _assertionStats.assertionResult; bool printInfoMessages = true; @@ -58,13 +73,13 @@ namespace Catch { return true; } - virtual void sectionEnded(SectionStats const& _sectionStats) CATCH_OVERRIDE { + void sectionEnded(SectionStats const& _sectionStats) override { if (m_config->showDurations() == ShowDurations::Always) { stream << getFormattedDuration(_sectionStats.durationInSeconds) << " s: " << _sectionStats.sectionInfo.name << std::endl; } } - virtual void testRunEnded( TestRunStats const& _testRunStats ) { + void testRunEnded( TestRunStats const& _testRunStats ) override { printTotals( _testRunStats.totals ); stream << '\n' << std::endl; StreamingReporterBase::testRunEnded( _testRunStats ); @@ -72,11 +87,11 @@ namespace Catch { private: class AssertionPrinter { - void operator= ( AssertionPrinter const& ); public: + AssertionPrinter& operator= ( AssertionPrinter const& ) = delete; + AssertionPrinter( AssertionPrinter const& ) = delete; AssertionPrinter( std::ostream& _stream, AssertionStats const& _stats, bool _printInfoMessages ) : stream( _stream ) - , stats( _stats ) , result( _stats.assertionResult ) , messages( _stats.infoMessages ) , itMessage( _stats.infoMessages.begin() ) @@ -152,18 +167,6 @@ namespace Catch { } private: - // Colour::LightGrey - - static Colour::Code dimColour() { return Colour::FileName; } - -#ifdef CATCH_PLATFORM_MAC - static const char* failedString() { return "FAILED"; } - static const char* passedString() { return "PASSED"; } -#else - static const char* failedString() { return "failed"; } - static const char* passedString() { return "passed"; } -#endif - void printSourceInfo() const { Colour colourGuard( Colour::FileName ); stream << result.getSourceInfo() << ':'; @@ -221,7 +224,7 @@ namespace Catch { if ( itMessage == messages.end() ) return; - // using messages.end() directly yields compilation error: + // using messages.end() directly yields (or auto) compilation error: std::vector::const_iterator itEnd = messages.end(); const std::size_t N = static_cast( std::distance( itMessage, itEnd ) ); @@ -244,7 +247,6 @@ namespace Catch { private: std::ostream& stream; - AssertionStats const& stats; AssertionResult const& result; std::vector messages; std::vector::const_iterator itMessage; @@ -258,10 +260,6 @@ namespace Catch { // - red: Failed N tests cases, failed M assertions. // - green: Passed [both/all] N tests cases with M assertions. - std::string bothOrAll( std::size_t count ) const { - return count == 1 ? std::string() : count == 2 ? "both " : "all " ; - } - void printTotals( const Totals& totals ) const { if( totals.testCases.total() == 0 ) { stream << "No tests ran."; @@ -299,8 +297,8 @@ namespace Catch { } }; - INTERNAL_CATCH_REGISTER_REPORTER( "compact", CompactReporter ) + CompactReporter::~CompactReporter() {} + + CATCH_REGISTER_REPORTER( "compact", CompactReporter ) } // end namespace Catch - -#endif // TWOBLUECUBES_CATCH_REPORTER_COMPACT_HPP_INCLUDED diff --git a/include/reporters/catch_reporter_console.hpp b/include/reporters/catch_reporter_console.cpp similarity index 61% rename from include/reporters/catch_reporter_console.hpp rename to include/reporters/catch_reporter_console.cpp index 5f181241..68eb3b94 100644 --- a/include/reporters/catch_reporter_console.hpp +++ b/include/reporters/catch_reporter_console.cpp @@ -5,39 +5,225 @@ * Distributed under the Boost Software License, Version 1.0. (See accompanying * file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) */ -#ifndef TWOBLUECUBES_CATCH_REPORTER_CONSOLE_HPP_INCLUDED -#define TWOBLUECUBES_CATCH_REPORTER_CONSOLE_HPP_INCLUDED #include "catch_reporter_bases.hpp" #include "../internal/catch_reporter_registrars.hpp" -#include "../internal/catch_console_colour.hpp" +#include "internal/catch_console_colour.h" +#include "../internal/catch_version.h" +#include "../internal/catch_text.h" +#include "../internal/catch_stringref.h" #include #include +#if defined(_MSC_VER) +#pragma warning(push) +#pragma warning(disable:4061) // Not all labels are EXPLICITLY handled in switch + // Note that 4062 (not all labels are handled + // and default is missing) is enabled +#endif + + namespace Catch { + namespace { + std::size_t makeRatio( std::size_t number, std::size_t total ) { + std::size_t ratio = total > 0 ? CATCH_CONFIG_CONSOLE_WIDTH * number/ total : 0; + return ( ratio == 0 && number > 0 ) ? 1 : ratio; + } - struct ConsoleReporter : StreamingReporterBase { - ConsoleReporter( ReporterConfig const& _config ) - : StreamingReporterBase( _config ), - m_headerPrinted( false ) + std::size_t& findMax( std::size_t& i, std::size_t& j, std::size_t& k ) { + if( i > j && i > k ) + return i; + else if( j > k ) + return j; + else + return k; + } + + struct ColumnInfo { + enum Justification { Left, Right }; + std::string name; + int width; + Justification justification; + }; + struct ColumnBreak {}; + struct RowBreak {}; + + class TablePrinter { + std::ostream& m_os; + std::vector m_columnInfos; + std::ostringstream m_oss; + int m_currentColumn = -1; + bool m_isOpen = false; + + public: + TablePrinter( std::ostream& os, std::vector const& columnInfos ) + : m_os( os ), + m_columnInfos( columnInfos ) + {} + + auto columnInfos() const -> std::vector const& { + return m_columnInfos; + } + + void open() { + if( !m_isOpen ) { + m_isOpen = true; + *this << RowBreak(); + for( auto const& info : m_columnInfos ) + *this << info.name << ColumnBreak(); + *this << RowBreak(); + m_os << Catch::getLineOfChars<'-'>() << "\n"; + } + } + void close() { + if( m_isOpen ) { + *this << RowBreak(); + m_os << std::endl; + m_isOpen = false; + } + } + + template + friend TablePrinter& operator << ( TablePrinter& tp, T const& value ) { + tp.m_oss << value; + return tp; + } + + friend TablePrinter& operator << ( TablePrinter& tp, ColumnBreak ) { + auto colStr = tp.m_oss.str(); + // This takes account of utf8 encodings + auto strSize = Catch::StringRef( colStr ).numberOfCharacters(); + tp.m_oss.str(""); + tp.open(); + if( tp.m_currentColumn == static_cast(tp.m_columnInfos.size()-1) ) { + tp.m_currentColumn = -1; + tp.m_os << "\n"; + } + tp.m_currentColumn++; + + auto colInfo = tp.m_columnInfos[tp.m_currentColumn]; + auto padding = ( strSize+2 < static_cast( colInfo.width ) ) + ? std::string( colInfo.width-(strSize+2), ' ' ) + : std::string(); + if( colInfo.justification == ColumnInfo::Left ) + tp.m_os << colStr << padding << " "; + else + tp.m_os << padding << colStr << " "; + return tp; + } + + friend TablePrinter& operator << ( TablePrinter& tp, RowBreak ) { + if( tp.m_currentColumn > 0 ) { + tp.m_os << "\n"; + tp.m_currentColumn = -1; + } + return tp; + } + }; + + class Duration { + enum class Unit { + Auto, + Nanoseconds, + Microseconds, + Milliseconds, + Seconds, + Minutes + }; + static const uint64_t s_nanosecondsInAMicrosecond = 1000; + static const uint64_t s_nanosecondsInAMillisecond = 1000*s_nanosecondsInAMicrosecond; + static const uint64_t s_nanosecondsInASecond = 1000*s_nanosecondsInAMillisecond; + static const uint64_t s_nanosecondsInAMinute = 60*s_nanosecondsInASecond; + + uint64_t m_inNanoseconds; + Unit m_units; + + public: + Duration( uint64_t inNanoseconds, Unit units = Unit::Auto ) + : m_inNanoseconds( inNanoseconds ), + m_units( units ) + { + if( m_units == Unit::Auto ) { + if( m_inNanoseconds < s_nanosecondsInAMicrosecond ) + m_units = Unit::Nanoseconds; + else if( m_inNanoseconds < s_nanosecondsInAMillisecond ) + m_units = Unit::Microseconds; + else if( m_inNanoseconds < s_nanosecondsInASecond ) + m_units = Unit::Milliseconds; + else if( m_inNanoseconds < s_nanosecondsInAMinute ) + m_units = Unit::Seconds; + else + m_units = Unit::Minutes; + } + + } + + auto value() const -> double { + switch( m_units ) { + case Unit::Microseconds: + return m_inNanoseconds / static_cast( s_nanosecondsInAMicrosecond ); + case Unit::Milliseconds: + return m_inNanoseconds / static_cast( s_nanosecondsInAMillisecond ); + case Unit::Seconds: + return m_inNanoseconds / static_cast( s_nanosecondsInASecond ); + case Unit::Minutes: + return m_inNanoseconds / static_cast( s_nanosecondsInAMinute ); + default: + return static_cast( m_inNanoseconds ); + } + } + auto unitsAsString() const -> std::string { + switch( m_units ) { + case Unit::Nanoseconds: + return "ns"; + case Unit::Microseconds: + return "µs"; + case Unit::Milliseconds: + return "ms"; + case Unit::Seconds: + return "s"; + case Unit::Minutes: + return "m"; + default: + return "** internal error **"; + } + + } + friend auto operator << ( std::ostream& os, Duration const& duration ) -> std::ostream& { + return os << duration.value() << " " << duration.unitsAsString(); + } + }; + } // end anon namespace + + struct ConsoleReporter : StreamingReporterBase { + TablePrinter m_tablePrinter; + + ConsoleReporter( ReporterConfig const& config ) + : StreamingReporterBase( config ), + m_tablePrinter( config.stream(), + { + { "benchmark name", CATCH_CONFIG_CONSOLE_WIDTH-32, ColumnInfo::Left }, + { "iters", 8, ColumnInfo::Right }, + { "elapsed ns", 14, ColumnInfo::Right }, + { "average", 14, ColumnInfo::Right } + } ) {} - - virtual ~ConsoleReporter() CATCH_OVERRIDE; + ~ConsoleReporter() override; static std::string getDescription() { return "Reports test results as plain lines of text"; } - virtual void noMatchingTestCases( std::string const& spec ) CATCH_OVERRIDE { + void noMatchingTestCases( std::string const& spec ) override { stream << "No test cases matched '" << spec << '\'' << std::endl; } - virtual void assertionStarting( AssertionInfo const& ) CATCH_OVERRIDE { + void assertionStarting( AssertionInfo const& ) override { } - virtual bool assertionEnded( AssertionStats const& _assertionStats ) CATCH_OVERRIDE { + bool assertionEnded( AssertionStats const& _assertionStats ) override { AssertionResult const& result = _assertionStats.assertionResult; bool includeResults = m_config->includeSuccessfulResults() || !result.isOk(); @@ -54,11 +240,12 @@ namespace Catch { return true; } - virtual void sectionStarting( SectionInfo const& _sectionInfo ) CATCH_OVERRIDE { + void sectionStarting( SectionInfo const& _sectionInfo ) override { m_headerPrinted = false; StreamingReporterBase::sectionStarting( _sectionInfo ); } - virtual void sectionEnded( SectionStats const& _sectionStats ) CATCH_OVERRIDE { + void sectionEnded( SectionStats const& _sectionStats ) override { + m_tablePrinter.close(); if( _sectionStats.missingAssertions ) { lazyPrint(); Colour colour( Colour::ResultError ); @@ -77,11 +264,36 @@ namespace Catch { StreamingReporterBase::sectionEnded( _sectionStats ); } - virtual void testCaseEnded( TestCaseStats const& _testCaseStats ) CATCH_OVERRIDE { + + void benchmarkStarting( BenchmarkInfo const& info ) override { + lazyPrintWithoutClosingBenchmarkTable(); + + auto nameCol = Column( info.name ).width( m_tablePrinter.columnInfos()[0].width-2 ); + + bool firstLine = true; + for( auto line : nameCol ) { + if( !firstLine ) + m_tablePrinter << ColumnBreak() << ColumnBreak() << ColumnBreak(); + else + firstLine = false; + + m_tablePrinter << line << ColumnBreak(); + } + } + void benchmarkEnded( BenchmarkStats const& stats ) override { + Duration average( stats.elapsedTimeInNanoseconds/stats.iterations ); + m_tablePrinter + << stats.iterations << ColumnBreak() + << stats.elapsedTimeInNanoseconds << ColumnBreak() + << average << ColumnBreak(); + } + + void testCaseEnded( TestCaseStats const& _testCaseStats ) override { + m_tablePrinter.close(); StreamingReporterBase::testCaseEnded( _testCaseStats ); m_headerPrinted = false; } - virtual void testGroupEnded( TestGroupStats const& _testGroupStats ) CATCH_OVERRIDE { + void testGroupEnded( TestGroupStats const& _testGroupStats ) override { if( currentGroupInfo.used ) { printSummaryDivider(); stream << "Summary for group '" << _testGroupStats.groupInfo.name << "':\n"; @@ -90,7 +302,7 @@ namespace Catch { } StreamingReporterBase::testGroupEnded( _testGroupStats ); } - virtual void testRunEnded( TestRunStats const& _testRunStats ) CATCH_OVERRIDE { + void testRunEnded( TestRunStats const& _testRunStats ) override { printTotalsDivider( _testRunStats.totals ); printTotals( _testRunStats.totals ); stream << std::endl; @@ -100,8 +312,9 @@ namespace Catch { private: class AssertionPrinter { - void operator= ( AssertionPrinter const& ); public: + AssertionPrinter& operator= ( AssertionPrinter const& ) = delete; + AssertionPrinter( AssertionPrinter const& ) = delete; AssertionPrinter( std::ostream& _stream, AssertionStats const& _stats, bool _printInfoMessages ) : stream( _stream ), stats( _stats ), @@ -212,18 +425,16 @@ namespace Catch { if( result.hasExpandedExpression() ) { stream << "with expansion:\n"; Colour colourGuard( Colour::ReconstructedExpression ); - stream << Text( result.getExpandedExpression(), TextAttributes().setIndent(2) ) << '\n'; + stream << Column( result.getExpandedExpression() ).indent(2) << '\n'; } } void printMessage() const { if( !messageLabel.empty() ) stream << messageLabel << ':' << '\n'; - for( std::vector::const_iterator it = messages.begin(), itEnd = messages.end(); - it != itEnd; - ++it ) { + for( auto const& msg : messages ) { // If this assertion is a warning ignore any INFO messages - if( printInfoMessages || it->type != ResultWas::Info ) - stream << Text( it->message, TextAttributes().setIndent(2) ) << '\n'; + if( printInfoMessages || msg.type != ResultWas::Info ) + stream << Column( msg.message ).indent(2) << '\n'; } } void printSourceInfo() const { @@ -244,6 +455,12 @@ namespace Catch { void lazyPrint() { + m_tablePrinter.close(); + lazyPrintWithoutClosingBenchmarkTable(); + } + + void lazyPrintWithoutClosingBenchmarkTable() { + if( !currentTestRunInfo.used ) lazyPrintRunInfo(); if( !currentGroupInfo.used ) @@ -279,7 +496,7 @@ namespace Catch { if( m_sectionStack.size() > 1 ) { Colour colourGuard( Colour::Headers ); - std::vector::const_iterator + auto it = m_sectionStack.begin()+1, // Skip first section (test case) itEnd = m_sectionStack.end(); for( ; it != itEnd; ++it ) @@ -316,9 +533,7 @@ namespace Catch { i+=2; else i = 0; - stream << Text( _string, TextAttributes() - .setIndent( indent+i) - .setInitialIndent( indent ) ) << '\n'; + stream << Column( _string ).indent( indent+i ).initialIndent( indent ) << '\n'; } struct SummaryColumn { @@ -331,10 +546,10 @@ namespace Catch { std::ostringstream oss; oss << count; std::string row = oss.str(); - for( std::vector::iterator it = rows.begin(); it != rows.end(); ++it ) { - while( it->size() < row.size() ) - *it = ' ' + *it; - while( it->size() > row.size() ) + for( auto& oldRow : rows ) { + while( oldRow.size() < row.size() ) + oldRow = ' ' + oldRow; + while( oldRow.size() > row.size() ) row = ' ' + row; } rows.push_back( row ); @@ -379,9 +594,9 @@ namespace Catch { } } void printSummaryRow( std::string const& label, std::vector const& cols, std::size_t row ) { - for( std::vector::const_iterator it = cols.begin(); it != cols.end(); ++it ) { - std::string value = it->rows[row]; - if( it->label.empty() ) { + for( auto col : cols ) { + std::string value = col.rows[row]; + if( col.label.empty() ) { stream << label << ": "; if( value != "0" ) stream << value; @@ -390,26 +605,13 @@ namespace Catch { } else if( value != "0" ) { stream << Colour( Colour::LightGrey ) << " | "; - stream << Colour( it->colour ) - << value << ' ' << it->label; + stream << Colour( col.colour ) + << value << ' ' << col.label; } } stream << '\n'; } - static std::size_t makeRatio( std::size_t number, std::size_t total ) { - std::size_t ratio = total > 0 ? CATCH_CONFIG_CONSOLE_WIDTH * number/ total : 0; - return ( ratio == 0 && number > 0 ) ? 1 : ratio; - } - static std::size_t& findMax( std::size_t& i, std::size_t& j, std::size_t& k ) { - if( i > j && i > k ) - return i; - else if( j > k ) - return j; - else - return k; - } - void printTotalsDivider( Totals const& totals ) { if( totals.testCases.total() > 0 ) { std::size_t failedRatio = makeRatio( totals.testCases.failed, totals.testCases.total() ); @@ -437,11 +639,15 @@ namespace Catch { } private: - bool m_headerPrinted; + bool m_headerPrinted = false; }; - INTERNAL_CATCH_REGISTER_REPORTER( "console", ConsoleReporter ) + CATCH_REGISTER_REPORTER( "console", ConsoleReporter ) + + ConsoleReporter::~ConsoleReporter() {} } // end namespace Catch -#endif // TWOBLUECUBES_CATCH_REPORTER_CONSOLE_HPP_INCLUDED +#if defined(_MSC_VER) +#pragma warning(pop) +#endif diff --git a/include/reporters/catch_reporter_junit.hpp b/include/reporters/catch_reporter_junit.cpp similarity index 74% rename from include/reporters/catch_reporter_junit.hpp rename to include/reporters/catch_reporter_junit.cpp index 28a671cd..bb7f6d9c 100644 --- a/include/reporters/catch_reporter_junit.hpp +++ b/include/reporters/catch_reporter_junit.cpp @@ -5,17 +5,19 @@ * Distributed under the Boost Software License, Version 1.0. (See accompanying * file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) */ -#ifndef TWOBLUECUBES_CATCH_REPORTER_JUNIT_HPP_INCLUDED -#define TWOBLUECUBES_CATCH_REPORTER_JUNIT_HPP_INCLUDED #include "catch_reporter_bases.hpp" #include "../internal/catch_tostring.h" #include "../internal/catch_reporter_registrars.hpp" -#include "../internal/catch_xmlwriter.hpp" +#include "internal/catch_xmlwriter.h" +#include "../internal/catch_timer.h" #include +#include +#include + namespace Catch { namespace { @@ -24,7 +26,7 @@ namespace Catch { // Also, UTC only, again because of backward compatibility (%z is C++11) time_t rawtime; std::time(&rawtime); - const size_t timeStampSize = sizeof("2017-01-16T17:06:45Z"); + auto const timeStampSize = sizeof("2017-01-16T17:06:45Z"); #ifdef _MSC_VER std::tm timeInfo = {}; @@ -45,33 +47,39 @@ namespace Catch { return std::string(timeStamp); } + std::string fileNameTag(const std::vector &tags) { + auto it = std::find_if(begin(tags), + end(tags), + [] (std::string const& tag) {return tag.front() == '#'; }); + if (it != tags.end()) + return it->substr(1); + return std::string(); + } } - class JunitReporter : public CumulativeReporterBase { + class JunitReporter : public CumulativeReporterBase { public: JunitReporter( ReporterConfig const& _config ) : CumulativeReporterBase( _config ), - xml( _config.stream() ), - unexpectedExceptions( 0 ), - m_okToFail( false ) + xml( _config.stream() ) { m_reporterPrefs.shouldRedirectStdOut = true; } - virtual ~JunitReporter() CATCH_OVERRIDE; + ~JunitReporter() override; static std::string getDescription() { return "Reports test results in an XML format that looks like Ant's junitreport target"; } - virtual void noMatchingTestCases( std::string const& /*spec*/ ) CATCH_OVERRIDE {} + void noMatchingTestCases( std::string const& /*spec*/ ) override {} - virtual void testRunStarting( TestRunInfo const& runInfo ) CATCH_OVERRIDE { + void testRunStarting( TestRunInfo const& runInfo ) override { CumulativeReporterBase::testRunStarting( runInfo ); xml.startElement( "testsuites" ); } - virtual void testGroupStarting( GroupInfo const& groupInfo ) CATCH_OVERRIDE { + void testGroupStarting( GroupInfo const& groupInfo ) override { suiteTimer.start(); stdOutForSuite.str(""); stdErrForSuite.str(""); @@ -79,28 +87,28 @@ namespace Catch { CumulativeReporterBase::testGroupStarting( groupInfo ); } - virtual void testCaseStarting( TestCaseInfo const& testCaseInfo ) CATCH_OVERRIDE { + void testCaseStarting( TestCaseInfo const& testCaseInfo ) override { m_okToFail = testCaseInfo.okToFail(); } - virtual bool assertionEnded( AssertionStats const& assertionStats ) CATCH_OVERRIDE { + bool assertionEnded( AssertionStats const& assertionStats ) override { if( assertionStats.assertionResult.getResultType() == ResultWas::ThrewException && !m_okToFail ) unexpectedExceptions++; return CumulativeReporterBase::assertionEnded( assertionStats ); } - virtual void testCaseEnded( TestCaseStats const& testCaseStats ) CATCH_OVERRIDE { + void testCaseEnded( TestCaseStats const& testCaseStats ) override { stdOutForSuite << testCaseStats.stdOut; stdErrForSuite << testCaseStats.stdErr; CumulativeReporterBase::testCaseEnded( testCaseStats ); } - virtual void testGroupEnded( TestGroupStats const& testGroupStats ) CATCH_OVERRIDE { + void testGroupEnded( TestGroupStats const& testGroupStats ) override { double suiteTime = suiteTimer.getElapsedSeconds(); CumulativeReporterBase::testGroupEnded( testGroupStats ); writeGroup( *m_testGroups.back(), suiteTime ); } - virtual void testRunEndedCumulative() CATCH_OVERRIDE { + void testRunEndedCumulative() override { xml.endElement(); } @@ -119,11 +127,8 @@ namespace Catch { xml.writeAttribute( "timestamp", getCurrentTimestamp() ); // Write test cases - for( TestGroupNode::ChildNodes::const_iterator - it = groupNode.children.begin(), itEnd = groupNode.children.end(); - it != itEnd; - ++it ) - writeTestCase( **it ); + for( auto const& child : groupNode.children ) + writeTestCase( *child ); xml.scopedElement( "system-out" ).writeText( trim( stdOutForSuite.str() ), false ); xml.scopedElement( "system-err" ).writeText( trim( stdErrForSuite.str() ), false ); @@ -140,9 +145,14 @@ namespace Catch { std::string className = stats.testInfo.className; if( className.empty() ) { - if( rootSection.childSections.empty() ) + className = fileNameTag(stats.testInfo.tags); + if ( className.empty() ) className = "global"; } + + if ( !m_config->name().empty() ) + className = m_config->name() + "." + className; + writeSection( className, "", rootSection ); } @@ -165,7 +175,7 @@ namespace Catch { xml.writeAttribute( "classname", className ); xml.writeAttribute( "name", name ); } - xml.writeAttribute( "time", Catch::toString( sectionNode.stats.durationInSeconds ) ); + xml.writeAttribute( "time", ::Catch::Detail::stringify( sectionNode.stats.durationInSeconds ) ); writeAssertions( sectionNode ); @@ -174,23 +184,16 @@ namespace Catch { if( !sectionNode.stdErr.empty() ) xml.scopedElement( "system-err" ).writeText( trim( sectionNode.stdErr ), false ); } - for( SectionNode::ChildSections::const_iterator - it = sectionNode.childSections.begin(), - itEnd = sectionNode.childSections.end(); - it != itEnd; - ++it ) + for( auto const& childNode : sectionNode.childSections ) if( className.empty() ) - writeSection( name, "", **it ); + writeSection( name, "", *childNode ); else - writeSection( className, name, **it ); + writeSection( className, name, *childNode ); } void writeAssertions( SectionNode const& sectionNode ) { - for( SectionNode::Assertions::const_iterator - it = sectionNode.assertions.begin(), itEnd = sectionNode.assertions.end(); - it != itEnd; - ++it ) - writeAssertion( *it ); + for( auto const& assertion : sectionNode.assertions ) + writeAssertion( assertion ); } void writeAssertion( AssertionStats const& stats ) { AssertionResult const& result = stats.assertionResult; @@ -230,13 +233,9 @@ namespace Catch { std::ostringstream oss; if( !result.getMessage().empty() ) oss << result.getMessage() << '\n'; - for( std::vector::const_iterator - it = stats.infoMessages.begin(), - itEnd = stats.infoMessages.end(); - it != itEnd; - ++it ) - if( it->type == ResultWas::Info ) - oss << it->message << '\n'; + for( auto const& msg : stats.infoMessages ) + if( msg.type == ResultWas::Info ) + oss << msg.message << '\n'; oss << "at " << result.getSourceInfo(); xml.writeText( oss.str(), false ); @@ -247,12 +246,11 @@ namespace Catch { Timer suiteTimer; std::ostringstream stdOutForSuite; std::ostringstream stdErrForSuite; - unsigned int unexpectedExceptions; - bool m_okToFail; + unsigned int unexpectedExceptions = 0; + bool m_okToFail = false; }; - INTERNAL_CATCH_REGISTER_REPORTER( "junit", JunitReporter ) + JunitReporter::~JunitReporter() {} + CATCH_REGISTER_REPORTER( "junit", JunitReporter ) } // end namespace Catch - -#endif // TWOBLUECUBES_CATCH_REPORTER_JUNIT_HPP_INCLUDED diff --git a/include/reporters/catch_reporter_multi.cpp b/include/reporters/catch_reporter_multi.cpp new file mode 100644 index 00000000..23bb1603 --- /dev/null +++ b/include/reporters/catch_reporter_multi.cpp @@ -0,0 +1,104 @@ +/* + * Created by Phil on 5/08/2015. + * Copyright 2015 Two Blue Cubes Ltd. All rights reserved. + * + * Distributed under the Boost Software License, Version 1.0. (See accompanying + * file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) + */ + + #include "catch_reporter_multi.h" + +namespace Catch { + + void MultipleReporters::add( IStreamingReporterPtr&& reporter ) { + m_reporters.push_back( std::move( reporter ) ); + } + + ReporterPreferences MultipleReporters::getPreferences() const { + return m_reporters[0]->getPreferences(); + } + + std::set MultipleReporters::getSupportedVerbosities() { + return std::set{ }; + } + + + void MultipleReporters::noMatchingTestCases( std::string const& spec ) { + for( auto const& reporter : m_reporters ) + reporter->noMatchingTestCases( spec ); + } + + void MultipleReporters::benchmarkStarting( BenchmarkInfo const& benchmarkInfo ) { + for( auto const& reporter : m_reporters ) + reporter->benchmarkStarting( benchmarkInfo ); + } + void MultipleReporters::benchmarkEnded( BenchmarkStats const& benchmarkStats ) { + for( auto const& reporter : m_reporters ) + reporter->benchmarkEnded( benchmarkStats ); + } + + void MultipleReporters::testRunStarting( TestRunInfo const& testRunInfo ) { + for( auto const& reporter : m_reporters ) + reporter->testRunStarting( testRunInfo ); + } + + void MultipleReporters::testGroupStarting( GroupInfo const& groupInfo ) { + for( auto const& reporter : m_reporters ) + reporter->testGroupStarting( groupInfo ); + } + + + void MultipleReporters::testCaseStarting( TestCaseInfo const& testInfo ) { + for( auto const& reporter : m_reporters ) + reporter->testCaseStarting( testInfo ); + } + + void MultipleReporters::sectionStarting( SectionInfo const& sectionInfo ) { + for( auto const& reporter : m_reporters ) + reporter->sectionStarting( sectionInfo ); + } + + void MultipleReporters::assertionStarting( AssertionInfo const& assertionInfo ) { + for( auto const& reporter : m_reporters ) + reporter->assertionStarting( assertionInfo ); + } + + // The return value indicates if the messages buffer should be cleared: + bool MultipleReporters::assertionEnded( AssertionStats const& assertionStats ) { + bool clearBuffer = false; + for( auto const& reporter : m_reporters ) + clearBuffer |= reporter->assertionEnded( assertionStats ); + return clearBuffer; + } + + void MultipleReporters::sectionEnded( SectionStats const& sectionStats ) { + for( auto const& reporter : m_reporters ) + reporter->sectionEnded( sectionStats ); + } + + void MultipleReporters::testCaseEnded( TestCaseStats const& testCaseStats ) { + for( auto const& reporter : m_reporters ) + reporter->testCaseEnded( testCaseStats ); + } + + void MultipleReporters::testGroupEnded( TestGroupStats const& testGroupStats ) { + for( auto const& reporter : m_reporters ) + reporter->testGroupEnded( testGroupStats ); + } + + void MultipleReporters::testRunEnded( TestRunStats const& testRunStats ) { + for( auto const& reporter : m_reporters ) + reporter->testRunEnded( testRunStats ); + } + + + void MultipleReporters::skipTest( TestCaseInfo const& testInfo ) { + for( auto const& reporter : m_reporters ) + reporter->skipTest( testInfo ); + } + + bool MultipleReporters::isMulti() const { + return true; + } + +} // end namespace Catch diff --git a/include/reporters/catch_reporter_multi.h b/include/reporters/catch_reporter_multi.h new file mode 100644 index 00000000..985d442f --- /dev/null +++ b/include/reporters/catch_reporter_multi.h @@ -0,0 +1,52 @@ +/* + * Created by Martin on 19/07/2017. + * + * Distributed under the Boost Software License, Version 1.0. (See accompanying + * file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) + */ +#ifndef TWOBLUECUBES_CATCH_MULTI_REPORTER_H_INCLUDED +#define TWOBLUECUBES_CATCH_MULTI_REPORTER_H_INCLUDED + +#include "../internal/catch_interfaces_reporter.h" + +namespace Catch { + + class MultipleReporters : public IStreamingReporter { + using Reporters = std::vector; + Reporters m_reporters; + + public: + void add( IStreamingReporterPtr&& reporter ); + + public: // IStreamingReporter + + ReporterPreferences getPreferences() const override; + + void noMatchingTestCases( std::string const& spec ) override; + + static std::set getSupportedVerbosities(); + + void benchmarkStarting( BenchmarkInfo const& benchmarkInfo ) override; + void benchmarkEnded( BenchmarkStats const& benchmarkStats ) override; + + void testRunStarting( TestRunInfo const& testRunInfo ) override; + void testGroupStarting( GroupInfo const& groupInfo ) override; + void testCaseStarting( TestCaseInfo const& testInfo ) override; + void sectionStarting( SectionInfo const& sectionInfo ) override; + void assertionStarting( AssertionInfo const& assertionInfo ) override; + + // The return value indicates if the messages buffer should be cleared: + bool assertionEnded( AssertionStats const& assertionStats ) override; + void sectionEnded( SectionStats const& sectionStats ) override; + void testCaseEnded( TestCaseStats const& testCaseStats ) override; + void testGroupEnded( TestGroupStats const& testGroupStats ) override; + void testRunEnded( TestRunStats const& testRunStats ) override; + + void skipTest( TestCaseInfo const& testInfo ) override; + bool isMulti() const override; + + }; + +} // end namespace Catch + +#endif // TWOBLUECUBES_CATCH_MULTI_REPORTER_H_INCLUDED diff --git a/include/reporters/catch_reporter_multi.hpp b/include/reporters/catch_reporter_multi.hpp deleted file mode 100644 index 0e06892f..00000000 --- a/include/reporters/catch_reporter_multi.hpp +++ /dev/null @@ -1,152 +0,0 @@ -/* - * Created by Phil on 5/08/2015. - * Copyright 2015 Two Blue Cubes Ltd. All rights reserved. - * - * Distributed under the Boost Software License, Version 1.0. (See accompanying - * file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) - */ -#ifndef TWOBLUECUBES_CATCH_REPORTER_MULTI_HPP_INCLUDED -#define TWOBLUECUBES_CATCH_REPORTER_MULTI_HPP_INCLUDED - -#include "../internal/catch_interfaces_reporter.h" - -namespace Catch { - -class MultipleReporters : public SharedImpl { - typedef std::vector > Reporters; - Reporters m_reporters; - -public: - void add( Ptr const& reporter ) { - m_reporters.push_back( reporter ); - } - -public: // IStreamingReporter - - virtual ReporterPreferences getPreferences() const CATCH_OVERRIDE { - return m_reporters[0]->getPreferences(); - } - - virtual void noMatchingTestCases( std::string const& spec ) CATCH_OVERRIDE { - for( Reporters::const_iterator it = m_reporters.begin(), itEnd = m_reporters.end(); - it != itEnd; - ++it ) - (*it)->noMatchingTestCases( spec ); - } - - - virtual void testRunStarting( TestRunInfo const& testRunInfo ) CATCH_OVERRIDE { - for( Reporters::const_iterator it = m_reporters.begin(), itEnd = m_reporters.end(); - it != itEnd; - ++it ) - (*it)->testRunStarting( testRunInfo ); - } - - virtual void testGroupStarting( GroupInfo const& groupInfo ) CATCH_OVERRIDE { - for( Reporters::const_iterator it = m_reporters.begin(), itEnd = m_reporters.end(); - it != itEnd; - ++it ) - (*it)->testGroupStarting( groupInfo ); - } - - - virtual void testCaseStarting( TestCaseInfo const& testInfo ) CATCH_OVERRIDE { - for( Reporters::const_iterator it = m_reporters.begin(), itEnd = m_reporters.end(); - it != itEnd; - ++it ) - (*it)->testCaseStarting( testInfo ); - } - - virtual void sectionStarting( SectionInfo const& sectionInfo ) CATCH_OVERRIDE { - for( Reporters::const_iterator it = m_reporters.begin(), itEnd = m_reporters.end(); - it != itEnd; - ++it ) - (*it)->sectionStarting( sectionInfo ); - } - - - virtual void assertionStarting( AssertionInfo const& assertionInfo ) CATCH_OVERRIDE { - for( Reporters::const_iterator it = m_reporters.begin(), itEnd = m_reporters.end(); - it != itEnd; - ++it ) - (*it)->assertionStarting( assertionInfo ); - } - - - // The return value indicates if the messages buffer should be cleared: - virtual bool assertionEnded( AssertionStats const& assertionStats ) CATCH_OVERRIDE { - bool clearBuffer = false; - for( Reporters::const_iterator it = m_reporters.begin(), itEnd = m_reporters.end(); - it != itEnd; - ++it ) - clearBuffer |= (*it)->assertionEnded( assertionStats ); - return clearBuffer; - } - - virtual void sectionEnded( SectionStats const& sectionStats ) CATCH_OVERRIDE { - for( Reporters::const_iterator it = m_reporters.begin(), itEnd = m_reporters.end(); - it != itEnd; - ++it ) - (*it)->sectionEnded( sectionStats ); - } - - virtual void testCaseEnded( TestCaseStats const& testCaseStats ) CATCH_OVERRIDE { - for( Reporters::const_iterator it = m_reporters.begin(), itEnd = m_reporters.end(); - it != itEnd; - ++it ) - (*it)->testCaseEnded( testCaseStats ); - } - - virtual void testGroupEnded( TestGroupStats const& testGroupStats ) CATCH_OVERRIDE { - for( Reporters::const_iterator it = m_reporters.begin(), itEnd = m_reporters.end(); - it != itEnd; - ++it ) - (*it)->testGroupEnded( testGroupStats ); - } - - virtual void testRunEnded( TestRunStats const& testRunStats ) CATCH_OVERRIDE { - for( Reporters::const_iterator it = m_reporters.begin(), itEnd = m_reporters.end(); - it != itEnd; - ++it ) - (*it)->testRunEnded( testRunStats ); - } - - - virtual void skipTest( TestCaseInfo const& testInfo ) CATCH_OVERRIDE { - for( Reporters::const_iterator it = m_reporters.begin(), itEnd = m_reporters.end(); - it != itEnd; - ++it ) - (*it)->skipTest( testInfo ); - } - - virtual MultipleReporters* tryAsMulti() CATCH_OVERRIDE { - return this; - } - -}; - -Ptr addReporter( Ptr const& existingReporter, Ptr const& additionalReporter ) { - Ptr resultingReporter; - - if( existingReporter ) { - MultipleReporters* multi = existingReporter->tryAsMulti(); - if( !multi ) { - multi = new MultipleReporters; - resultingReporter = Ptr( multi ); - if( existingReporter ) - multi->add( existingReporter ); - } - else - resultingReporter = existingReporter; - multi->add( additionalReporter ); - } - else - resultingReporter = additionalReporter; - - return resultingReporter; -} - - -} // end namespace Catch - -#endif // TWOBLUECUBES_CATCH_REPORTER_MULTI_HPP_INCLUDED diff --git a/include/reporters/catch_reporter_tap.hpp b/include/reporters/catch_reporter_tap.hpp index 8f8896a6..edb75b5a 100644 --- a/include/reporters/catch_reporter_tap.hpp +++ b/include/reporters/catch_reporter_tap.hpp @@ -19,32 +19,29 @@ namespace Catch { - struct TAPReporter : StreamingReporterBase { + struct TAPReporter : StreamingReporterBase { - TAPReporter( ReporterConfig const& _config ) - : StreamingReporterBase( _config ), - counter(0) - {} + using StreamingReporterBase::StreamingReporterBase; - virtual ~TAPReporter(); + ~TAPReporter() override; static std::string getDescription() { return "Reports test results in TAP format, suitable for test harneses"; } - virtual ReporterPreferences getPreferences() const { + ReporterPreferences getPreferences() const override { ReporterPreferences prefs; prefs.shouldRedirectStdOut = false; return prefs; } - virtual void noMatchingTestCases( std::string const& spec ) { + void noMatchingTestCases( std::string const& spec ) override { stream << "# No test cases matched '" << spec << "'" << std::endl; } - virtual void assertionStarting( AssertionInfo const& ) {} + void assertionStarting( AssertionInfo const& ) override {} - virtual bool assertionEnded( AssertionStats const& _assertionStats ) { + bool assertionEnded( AssertionStats const& _assertionStats ) override { ++counter; AssertionPrinter printer( stream, _assertionStats, counter ); @@ -55,25 +52,25 @@ namespace Catch { return true; } - virtual void testRunEnded( TestRunStats const& _testRunStats ) { + void testRunEnded( TestRunStats const& _testRunStats ) override { printTotals( _testRunStats.totals ); stream << "\n" << std::endl; StreamingReporterBase::testRunEnded( _testRunStats ); } private: - size_t counter; + std::size_t counter = 0; class AssertionPrinter { - void operator= ( AssertionPrinter const& ); public: - AssertionPrinter( std::ostream& _stream, AssertionStats const& _stats, size_t counter ) + AssertionPrinter& operator= ( AssertionPrinter const& ) = delete; + AssertionPrinter( AssertionPrinter const& ) = delete; + AssertionPrinter( std::ostream& _stream, AssertionStats const& _stats, std::size_t _counter ) : stream( _stream ) - , stats( _stats ) , result( _stats.assertionResult ) , messages( _stats.infoMessages ) , itMessage( _stats.infoMessages.begin() ) , printInfoMessages( true ) - , counter(counter) + , counter(_counter) {} void print() { @@ -208,7 +205,7 @@ namespace Catch { return; } - // using messages.end() directly yields compilation error: + // using messages.end() directly (or auto) yields compilation error: std::vector::const_iterator itEnd = messages.end(); const std::size_t N = static_cast( std::distance( itMessage, itEnd ) ); @@ -231,12 +228,11 @@ namespace Catch { private: std::ostream& stream; - AssertionStats const& stats; AssertionResult const& result; std::vector messages; std::vector::const_iterator itMessage; bool printInfoMessages; - size_t counter; + std::size_t counter; }; void printTotals( const Totals& totals ) const { @@ -252,7 +248,7 @@ namespace Catch { TAPReporter::~TAPReporter() {} #endif - INTERNAL_CATCH_REGISTER_REPORTER( "tap", TAPReporter ) + CATCH_REGISTER_REPORTER( "tap", TAPReporter ) } // end namespace Catch diff --git a/include/reporters/catch_reporter_teamcity.hpp b/include/reporters/catch_reporter_teamcity.hpp index af3505bd..6c7b3d18 100644 --- a/include/reporters/catch_reporter_teamcity.hpp +++ b/include/reporters/catch_reporter_teamcity.hpp @@ -19,16 +19,13 @@ #ifdef __clang__ # pragma clang diagnostic push # pragma clang diagnostic ignored "-Wpadded" -# pragma clang diagnostic ignored "-Wc++98-compat" -# pragma clang diagnostic ignored "-Wc++98-compat-pedantic" #endif namespace Catch { - struct TeamCityReporter : StreamingReporterBase { + struct TeamCityReporter : StreamingReporterBase { TeamCityReporter( ReporterConfig const& _config ) - : StreamingReporterBase( _config ), - m_headerPrintedForThisSection( false ) + : StreamingReporterBase( _config ) { m_reporterPrefs.shouldRedirectStdOut = true; } @@ -43,33 +40,32 @@ namespace Catch { replaceInPlace( escaped, "]", "|]" ); return escaped; } - virtual ~TeamCityReporter() CATCH_OVERRIDE; + ~TeamCityReporter() override; static std::string getDescription() { return "Reports test results as TeamCity service messages"; } - virtual void skipTest( TestCaseInfo const& /* testInfo */ ) CATCH_OVERRIDE { + void skipTest( TestCaseInfo const& /* testInfo */ ) override { } - virtual void noMatchingTestCases( std::string const& /* spec */ ) CATCH_OVERRIDE {} + void noMatchingTestCases( std::string const& /* spec */ ) override {} - virtual void testGroupStarting( GroupInfo const& groupInfo ) CATCH_OVERRIDE { + void testGroupStarting( GroupInfo const& groupInfo ) override { StreamingReporterBase::testGroupStarting( groupInfo ); stream << "##teamcity[testSuiteStarted name='" << escape( groupInfo.name ) << "']\n"; } - virtual void testGroupEnded( TestGroupStats const& testGroupStats ) CATCH_OVERRIDE { + void testGroupEnded( TestGroupStats const& testGroupStats ) override { StreamingReporterBase::testGroupEnded( testGroupStats ); stream << "##teamcity[testSuiteFinished name='" << escape( testGroupStats.groupInfo.name ) << "']\n"; } - virtual void assertionStarting( AssertionInfo const& ) CATCH_OVERRIDE { - } + void assertionStarting( AssertionInfo const& ) override {} - virtual bool assertionEnded( AssertionStats const& assertionStats ) CATCH_OVERRIDE { + bool assertionEnded( AssertionStats const& assertionStats ) override { AssertionResult const& result = assertionStats.assertionResult; if( !result.isOk() ) { @@ -101,23 +97,19 @@ namespace Catch { case ResultWas::Ok: case ResultWas::Info: case ResultWas::Warning: - + CATCH_ERROR( "Internal error in TeamCity reporter" ); // These cases are here to prevent compiler warnings case ResultWas::Unknown: case ResultWas::FailureBit: case ResultWas::Exception: - CATCH_NOT_IMPLEMENTED; + CATCH_ERROR( "Not implemented" ); } if( assertionStats.infoMessages.size() == 1 ) msg << " with message:"; if( assertionStats.infoMessages.size() > 1 ) msg << " with messages:"; - for( std::vector::const_iterator - it = assertionStats.infoMessages.begin(), - itEnd = assertionStats.infoMessages.end(); - it != itEnd; - ++it ) - msg << "\n \"" << it->message << "\""; + for( auto const& messageInfo : assertionStats.infoMessages ) + msg << "\n \"" << messageInfo.message << "\""; if( result.hasExpression() ) { @@ -145,12 +137,12 @@ namespace Catch { return true; } - virtual void sectionStarting( SectionInfo const& sectionInfo ) CATCH_OVERRIDE { + void sectionStarting( SectionInfo const& sectionInfo ) override { m_headerPrintedForThisSection = false; StreamingReporterBase::sectionStarting( sectionInfo ); } - virtual void testCaseStarting( TestCaseInfo const& testInfo ) CATCH_OVERRIDE { + void testCaseStarting( TestCaseInfo const& testInfo ) override { m_testTimer.start(); StreamingReporterBase::testCaseStarting( testInfo ); stream << "##teamcity[testStarted name='" @@ -158,7 +150,7 @@ namespace Catch { stream.flush(); } - virtual void testCaseEnded( TestCaseStats const& testCaseStats ) CATCH_OVERRIDE { + void testCaseEnded( TestCaseStats const& testCaseStats ) override { StreamingReporterBase::testCaseEnded( testCaseStats ); if( !testCaseStats.stdOut.empty() ) stream << "##teamcity[testStdOut name='" @@ -198,18 +190,18 @@ namespace Catch { // if string has a : in first line will set indent to follow it on // subsequent lines - void printHeaderString( std::ostream& os, std::string const& _string, std::size_t indent = 0 ) { + static void printHeaderString( std::ostream& os, std::string const& _string, std::size_t indent = 0 ) { std::size_t i = _string.find( ": " ); if( i != std::string::npos ) i+=2; else i = 0; - os << Text( _string, TextAttributes() - .setIndent( indent+i) - .setInitialIndent( indent ) ) << "\n"; + os << Column( _string ) + .indent( indent+i) + .initialIndent( indent ) << "\n"; } private: - bool m_headerPrintedForThisSection; + bool m_headerPrintedForThisSection = false; Timer m_testTimer; }; @@ -217,7 +209,7 @@ namespace Catch { TeamCityReporter::~TeamCityReporter() {} #endif - INTERNAL_CATCH_REGISTER_REPORTER( "teamcity", TeamCityReporter ) + CATCH_REGISTER_REPORTER( "teamcity", TeamCityReporter ) } // end namespace Catch diff --git a/include/reporters/catch_reporter_xml.hpp b/include/reporters/catch_reporter_xml.cpp similarity index 80% rename from include/reporters/catch_reporter_xml.hpp rename to include/reporters/catch_reporter_xml.cpp index 55c19564..36f169fd 100644 --- a/include/reporters/catch_reporter_xml.hpp +++ b/include/reporters/catch_reporter_xml.cpp @@ -5,28 +5,32 @@ * Distributed under the Boost Software License, Version 1.0. (See accompanying * file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) */ -#ifndef TWOBLUECUBES_CATCH_REPORTER_XML_HPP_INCLUDED -#define TWOBLUECUBES_CATCH_REPORTER_XML_HPP_INCLUDED #include "catch_reporter_bases.hpp" #include "../internal/catch_capture.hpp" #include "../internal/catch_reporter_registrars.hpp" -#include "../internal/catch_xmlwriter.hpp" +#include "internal/catch_xmlwriter.h" #include "../internal/catch_timer.h" +#if defined(_MSC_VER) +#pragma warning(push) +#pragma warning(disable:4061) // Not all labels are EXPLICITLY handled in switch + // Note that 4062 (not all labels are handled + // and default is missing) is enabled +#endif + namespace Catch { - class XmlReporter : public StreamingReporterBase { + class XmlReporter : public StreamingReporterBase { public: XmlReporter( ReporterConfig const& _config ) : StreamingReporterBase( _config ), - m_xml(_config.stream()), - m_sectionDepth( 0 ) + m_xml(_config.stream()) { m_reporterPrefs.shouldRedirectStdOut = true; } - virtual ~XmlReporter() CATCH_OVERRIDE; + ~XmlReporter() override; static std::string getDescription() { return "Reports test results as an XML document"; @@ -44,11 +48,11 @@ namespace Catch { public: // StreamingReporterBase - virtual void noMatchingTestCases( std::string const& s ) CATCH_OVERRIDE { + void noMatchingTestCases( std::string const& s ) override { StreamingReporterBase::noMatchingTestCases( s ); } - virtual void testRunStarting( TestRunInfo const& testInfo ) CATCH_OVERRIDE { + void testRunStarting( TestRunInfo const& testInfo ) override { StreamingReporterBase::testRunStarting( testInfo ); std::string stylesheetRef = getStylesheetRef(); if( !stylesheetRef.empty() ) @@ -58,18 +62,18 @@ namespace Catch { m_xml.writeAttribute( "name", m_config->name() ); } - virtual void testGroupStarting( GroupInfo const& groupInfo ) CATCH_OVERRIDE { + void testGroupStarting( GroupInfo const& groupInfo ) override { StreamingReporterBase::testGroupStarting( groupInfo ); m_xml.startElement( "Group" ) .writeAttribute( "name", groupInfo.name ); } - virtual void testCaseStarting( TestCaseInfo const& testInfo ) CATCH_OVERRIDE { + void testCaseStarting( TestCaseInfo const& testInfo ) override { StreamingReporterBase::testCaseStarting(testInfo); m_xml.startElement( "TestCase" ) .writeAttribute( "name", trim( testInfo.name ) ) .writeAttribute( "description", testInfo.description ) - .writeAttribute( "tags", testInfo.tagsAsString ); + .writeAttribute( "tags", testInfo.tagsAsString() ); writeSourceInfo( testInfo.lineInfo ); @@ -78,7 +82,7 @@ namespace Catch { m_xml.ensureTagClosed(); } - virtual void sectionStarting( SectionInfo const& sectionInfo ) CATCH_OVERRIDE { + void sectionStarting( SectionInfo const& sectionInfo ) override { StreamingReporterBase::sectionStarting( sectionInfo ); if( m_sectionDepth++ > 0 ) { m_xml.startElement( "Section" ) @@ -89,9 +93,9 @@ namespace Catch { } } - virtual void assertionStarting( AssertionInfo const& ) CATCH_OVERRIDE { } + void assertionStarting( AssertionInfo const& ) override { } - virtual bool assertionEnded( AssertionStats const& assertionStats ) CATCH_OVERRIDE { + bool assertionEnded( AssertionStats const& assertionStats ) override { AssertionResult const& result = assertionStats.assertionResult; @@ -99,15 +103,13 @@ namespace Catch { if( includeResults ) { // Print any info messages in tags. - for( std::vector::const_iterator it = assertionStats.infoMessages.begin(), itEnd = assertionStats.infoMessages.end(); - it != itEnd; - ++it ) { - if( it->type == ResultWas::Info ) { + for( auto const& msg : assertionStats.infoMessages ) { + if( msg.type == ResultWas::Info ) { m_xml.scopedElement( "Info" ) - .writeText( it->message ); - } else if ( it->type == ResultWas::Warning ) { + .writeText( msg.message ); + } else if ( msg.type == ResultWas::Warning ) { m_xml.scopedElement( "Warning" ) - .writeText( it->message ); + .writeText( msg.message ); } } } @@ -168,7 +170,7 @@ namespace Catch { return true; } - virtual void sectionEnded( SectionStats const& sectionStats ) CATCH_OVERRIDE { + void sectionEnded( SectionStats const& sectionStats ) override { StreamingReporterBase::sectionEnded( sectionStats ); if( --m_sectionDepth > 0 ) { XmlWriter::ScopedElement e = m_xml.scopedElement( "OverallResults" ); @@ -183,7 +185,7 @@ namespace Catch { } } - virtual void testCaseEnded( TestCaseStats const& testCaseStats ) CATCH_OVERRIDE { + void testCaseEnded( TestCaseStats const& testCaseStats ) override { StreamingReporterBase::testCaseEnded( testCaseStats ); XmlWriter::ScopedElement e = m_xml.scopedElement( "OverallResult" ); e.writeAttribute( "success", testCaseStats.totals.assertions.allOk() ); @@ -199,7 +201,7 @@ namespace Catch { m_xml.endElement(); } - virtual void testGroupEnded( TestGroupStats const& testGroupStats ) CATCH_OVERRIDE { + void testGroupEnded( TestGroupStats const& testGroupStats ) override { StreamingReporterBase::testGroupEnded( testGroupStats ); // TODO: Check testGroupStats.aborting and act accordingly. m_xml.scopedElement( "OverallResults" ) @@ -209,7 +211,7 @@ namespace Catch { m_xml.endElement(); } - virtual void testRunEnded( TestRunStats const& testRunStats ) CATCH_OVERRIDE { + void testRunEnded( TestRunStats const& testRunStats ) override { StreamingReporterBase::testRunEnded( testRunStats ); m_xml.scopedElement( "OverallResults" ) .writeAttribute( "successes", testRunStats.totals.assertions.passed ) @@ -221,11 +223,14 @@ namespace Catch { private: Timer m_testCaseTimer; XmlWriter m_xml; - int m_sectionDepth; + int m_sectionDepth = 0; }; - INTERNAL_CATCH_REGISTER_REPORTER( "xml", XmlReporter ) + XmlReporter::~XmlReporter() {} + CATCH_REGISTER_REPORTER( "xml", XmlReporter ) } // end namespace Catch -#endif // TWOBLUECUBES_CATCH_REPORTER_XML_HPP_INCLUDED +#if defined(_MSC_VER) +#pragma warning(pop) +#endif diff --git a/projects/Benchmark/StringificationBench.cpp b/projects/Benchmark/StringificationBench.cpp index 1c14939b..765b4e96 100644 --- a/projects/Benchmark/StringificationBench.cpp +++ b/projects/Benchmark/StringificationBench.cpp @@ -11,11 +11,11 @@ /////////////////////////////////////////////////////////////////////////////// TEST_CASE("Successful tests -- REQUIRE", "[Success]") { - const size_t sz = 1 * 1024 * 1024; + const std::size_t sz = 1 * 1024 * 1024; - std::vector vec; vec.reserve(sz); - for (size_t i = 0; i < sz; ++i){ + std::vector vec; vec.reserve(sz); + for (std::size_t i = 0; i < sz; ++i){ vec.push_back(i); REQUIRE(vec.back() == i); } @@ -23,11 +23,11 @@ TEST_CASE("Successful tests -- REQUIRE", "[Success]") { /////////////////////////////////////////////////////////////////////////////// TEST_CASE("Successful tests -- CHECK", "[Success]") { - const size_t sz = 1 * 1024 * 1024; + const std::size_t sz = 1 * 1024 * 1024; - std::vector vec; vec.reserve(sz); - for (size_t i = 0; i < sz; ++i){ + std::vector vec; vec.reserve(sz); + for (std::size_t i = 0; i < sz; ++i){ vec.push_back(i); CHECK(vec.back() == i); } @@ -35,11 +35,11 @@ TEST_CASE("Successful tests -- CHECK", "[Success]") { /////////////////////////////////////////////////////////////////////////////// TEST_CASE("Unsuccessful tests -- CHECK", "[Failure]") { - const size_t sz = 1024 * 1024; + const std::size_t sz = 1024 * 1024; - std::vector vec; vec.reserve(sz); - for (size_t i = 0; i < sz; ++i){ + std::vector vec; vec.reserve(sz); + for (std::size_t i = 0; i < sz; ++i){ vec.push_back(i); CHECK(vec.size() == i); } diff --git a/projects/SelfTest/ApproxTests.cpp b/projects/SelfTest/ApproxTests.cpp index 65ba52fb..09008e5a 100644 --- a/projects/SelfTest/ApproxTests.cpp +++ b/projects/SelfTest/ApproxTests.cpp @@ -8,6 +8,8 @@ #include "catch.hpp" +#include + /////////////////////////////////////////////////////////////////////////////// TEST_CASE ( @@ -25,7 +27,7 @@ TEST_CASE REQUIRE( Approx( d ) != 1.22 ); REQUIRE( Approx( d ) != 1.24 ); - REQUIRE( 0 == Approx(0) ); + REQUIRE(INFINITY == Approx(INFINITY)); } /////////////////////////////////////////////////////////////////////////////// @@ -106,7 +108,7 @@ TEST_CASE REQUIRE( 1.0f == Approx( 1 ) ); REQUIRE( 0 == Approx( dZero) ); - REQUIRE( 0 == Approx( dSmall ).epsilon( 0.001 ) ); + REQUIRE( 0 == Approx( dSmall ).margin( 0.001 ) ); REQUIRE( 1.234f == Approx( dMedium ) ); REQUIRE( dMedium == Approx( 1.234f ) ); } @@ -120,7 +122,7 @@ TEST_CASE { double d = 1.23; - Approx approx = Approx::custom().epsilon( 0.005 ); + Approx approx = Approx::custom().epsilon( 0.01 ); REQUIRE( d == approx( 1.23 ) ); REQUIRE( d == approx( 1.22 ) ); @@ -164,9 +166,32 @@ TEST_CASE("Approx with exactly-representable margin", "[Approx]") { CHECK( 245.5f == Approx(245.25f).margin(0.25f) ); } -//////////////////////////////////////////////////////////////////////////////// +TEST_CASE("Approx setters validate their arguments", "[Approx]") { + REQUIRE_NOTHROW(Approx(0).margin(0)); + REQUIRE_NOTHROW(Approx(0).margin(1234656)); + + REQUIRE_THROWS_AS(Approx(0).margin(-2), std::domain_error); + + REQUIRE_NOTHROW(Approx(0).epsilon(0)); + REQUIRE_NOTHROW(Approx(0).epsilon(1)); + + REQUIRE_THROWS_AS(Approx(0).epsilon(-0.001), std::domain_error); + REQUIRE_THROWS_AS(Approx(0).epsilon(1.0001), std::domain_error); +} + +TEST_CASE("Default scale is invisible to comparison", "[Approx]") { + REQUIRE(101.000001 != Approx(100).epsilon(0.01)); + REQUIRE(std::pow(10, -5) != Approx(std::pow(10, -7))); +} + +TEST_CASE("Epsilon only applies to Approx's value", "[Approx]") { + REQUIRE(101.01 != Approx(100).epsilon(0.01)); +} + +TEST_CASE("Assorted miscellaneous tests", "[Approx]") { + REQUIRE(INFINITY == Approx(INFINITY)); +} -#if defined(CATCH_CONFIG_CPP11_TYPE_TRAITS) class StrongDoubleTypedef { double d_ = 0.0; @@ -180,11 +205,7 @@ inline std::ostream& operator<<( std::ostream& os, StrongDoubleTypedef td ) { return os << "StrongDoubleTypedef(" << static_cast(td) << ")"; } -TEST_CASE -( - "Comparison with explicitly convertible types", - "[Approx][c++11]" -) +TEST_CASE( "Comparison with explicitly convertible types", "[Approx]" ) { StrongDoubleTypedef td(10.0); @@ -205,6 +226,3 @@ TEST_CASE REQUIRE(Approx(11.0) >= td); } -#endif - -//////////////////////////////////////////////////////////////////////////////// diff --git a/projects/SelfTest/Baselines/console.std.approved.txt b/projects/SelfTest/Baselines/console.std.approved.txt index 2bf57d56..f0791caf 100644 --- a/projects/SelfTest/Baselines/console.std.approved.txt +++ b/projects/SelfTest/Baselines/console.std.approved.txt @@ -274,6 +274,55 @@ MatchersTests.cpp:: FAILED: with expansion: "this string contains 'abc' as a substring" equals: "something else" +------------------------------------------------------------------------------- +Exception matchers that fail + No exception +------------------------------------------------------------------------------- +MatchersTests.cpp: +............................................................................... + +MatchersTests.cpp:: FAILED: + CHECK_THROWS_MATCHES( doesNotThrow(), SpecialException, ExceptionMatcher{ 1 } ) +because no exception was thrown where one was expected: + +MatchersTests.cpp:: FAILED: + REQUIRE_THROWS_MATCHES( doesNotThrow(), SpecialException, ExceptionMatcher{ 1 } ) +because no exception was thrown where one was expected: + +------------------------------------------------------------------------------- +Exception matchers that fail + Type mismatch +------------------------------------------------------------------------------- +MatchersTests.cpp: +............................................................................... + +MatchersTests.cpp:: FAILED: + CHECK_THROWS_MATCHES( throwsAsInt(1), SpecialException, ExceptionMatcher{ 1 } ) +due to unexpected exception with message: + Unknown exception + +MatchersTests.cpp:: FAILED: + REQUIRE_THROWS_MATCHES( throwsAsInt(1), SpecialException, ExceptionMatcher{ 1 } ) +due to unexpected exception with message: + Unknown exception + +------------------------------------------------------------------------------- +Exception matchers that fail + Contents are wrong +------------------------------------------------------------------------------- +MatchersTests.cpp: +............................................................................... + +MatchersTests.cpp:: FAILED: + CHECK_THROWS_MATCHES( throws(3), SpecialException, ExceptionMatcher{ 1 } ) +with expansion: + {?} special exception has value of 1 + +MatchersTests.cpp:: FAILED: + REQUIRE_THROWS_MATCHES( throws(4), SpecialException, ExceptionMatcher{ 1 } ) +with expansion: + {?} special exception has value of 1 + ------------------------------------------------------------------------------- Expected exceptions that don't throw or unexpected exceptions fail the test ------------------------------------------------------------------------------- @@ -373,6 +422,20 @@ with messages: this message should be logged and this, but later +------------------------------------------------------------------------------- +INFO is reset for each loop +------------------------------------------------------------------------------- +MessageTests.cpp: +............................................................................... + +MessageTests.cpp:: FAILED: + REQUIRE( i < 10 ) +with expansion: + 10 < 10 +with messages: + current counter 10 + i := 10 + ------------------------------------------------------------------------------- Inequality checks that should fail ------------------------------------------------------------------------------- @@ -436,7 +499,7 @@ ExceptionTests.cpp: ExceptionTests.cpp:: FAILED: REQUIRE_THROWS_WITH( thisThrows(), "should fail" ) with expansion: - expected exception + "expected exception" equals: "should fail" ------------------------------------------------------------------------------- Nice descriptive name @@ -581,20 +644,6 @@ MessageTests.cpp:: FAILED: explicitly with message: Message from section two -------------------------------------------------------------------------------- -Pointers can be converted to strings -------------------------------------------------------------------------------- -MessageTests.cpp: -............................................................................... - -MessageTests.cpp:: -warning: - actual address of p: 0x - -MessageTests.cpp:: -warning: - toString(p): 0x - ------------------------------------------------------------------------------- Reconstruction should be based on stringification: #914 ------------------------------------------------------------------------------- @@ -606,25 +655,8 @@ DecompositionTests.cpp:: FAILED: with expansion: Hey, its truthy! -------------------------------------------------------------------------------- -SCOPED_INFO is reset for each loop -------------------------------------------------------------------------------- -MessageTests.cpp: -............................................................................... - -MessageTests.cpp:: FAILED: - REQUIRE( i < 10 ) -with expansion: - 10 < 10 -with messages: - current counter 10 - i := 10 - A string sent directly to stdout A string sent directly to stderr -Write to std::cerr -Write to std::clog -Interleaved writes to error streams Message from section one Message from section two ------------------------------------------------------------------------------- @@ -638,8 +670,6 @@ MatchersTests.cpp:: FAILED: with expansion: "this string contains 'abc' as a substring" starts with: "string" -hello -hello ------------------------------------------------------------------------------- Tabs and newlines show in output ------------------------------------------------------------------------------- @@ -838,7 +868,8 @@ MiscTests.cpp:: FAILED: with expansion: false -spanner------------------------------------------------------------------------------- +loose text artifact +------------------------------------------------------------------------------- just failure ------------------------------------------------------------------------------- MessageTests.cpp: @@ -955,7 +986,23 @@ TrickyTests.cpp:: FAILED: with expansion: "first" == "second" -=============================================================================== -test cases: 171 | 122 passed | 45 failed | 4 failed as expected -assertions: 980 | 871 passed | 88 failed | 21 failed as expected +------------------------------------------------------------------------------- +toString(enum class) +------------------------------------------------------------------------------- +EnumToString.cpp: +............................................................................... + +EnumToString.cpp:: FAILED: + CHECK( ::Catch::Detail::stringify(e0) == "0" ) +with expansion: + "{?}" == "0" + +EnumToString.cpp:: FAILED: + CHECK( ::Catch::Detail::stringify(e1) == "1" ) +with expansion: + "{?}" == "1" + +=============================================================================== +test cases: 185 | 134 passed | 47 failed | 4 failed as expected +assertions: 904 | 787 passed | 96 failed | 21 failed as expected diff --git a/projects/SelfTest/Baselines/console.sw.approved.txt b/projects/SelfTest/Baselines/console.sw.approved.txt index 9c76fa4e..2c205275 100644 --- a/projects/SelfTest/Baselines/console.sw.approved.txt +++ b/projects/SelfTest/Baselines/console.sw.approved.txt @@ -14,6 +14,25 @@ PASSED: with message: yay +------------------------------------------------------------------------------- +#1005: Comparing pointer to int and long (NULL can be either on various + systems) +------------------------------------------------------------------------------- +DecompositionTests.cpp: +............................................................................... + +DecompositionTests.cpp:: +PASSED: + REQUIRE( fptr == 0 ) +with expansion: + 0 == 0 + +DecompositionTests.cpp:: +PASSED: + REQUIRE( fptr == 0l ) +with expansion: + 0 == 0 + ------------------------------------------------------------------------------- #748 - captures with unexpected exceptions outside assertions @@ -88,7 +107,7 @@ PASSED: CompilationTests.cpp:: PASSED: - CHECK_THROWS_AS( throws_int(true), const int& ) + CHECK_THROWS_AS( throws_int(true), int ) CompilationTests.cpp:: PASSED: @@ -123,6 +142,20 @@ PASSED: with expansion: 1 == 1 +------------------------------------------------------------------------------- +#872 +------------------------------------------------------------------------------- +CompilationTests.cpp: +............................................................................... + +CompilationTests.cpp:: +PASSED: + REQUIRE( x == 4 ) +with expansion: + {?} == 4 +with message: + dummy := 0 + ------------------------------------------------------------------------------- #961 -- Dynamically created sections should all be reported Looped section 0 @@ -481,19 +514,6 @@ PASSED: with expansion: 100.3 == Approx( 100.0 ) -------------------------------------------------------------------------------- -AllOf matcher -------------------------------------------------------------------------------- -MatchersTests.cpp: -............................................................................... - -MatchersTests.cpp:: -PASSED: - CHECK_THAT( testStringForMatching(), AllOf( Catch::Contains( "string" ), Catch::Contains( "abc" ) ) ) -with expansion: - "this string contains 'abc' as a substring" ( contains: "string" and - contains: "abc" ) - ------------------------------------------------------------------------------- An expression with side-effects should only be evaluated once ------------------------------------------------------------------------------- @@ -539,24 +559,38 @@ with message: anonymous test case ------------------------------------------------------------------------------- -AnyOf matcher +Approx setters validate their arguments ------------------------------------------------------------------------------- -MatchersTests.cpp: +ApproxTests.cpp: ............................................................................... -MatchersTests.cpp:: +ApproxTests.cpp:: PASSED: - CHECK_THAT( testStringForMatching(), AnyOf( Catch::Contains( "string" ), Catch::Contains( "not there" ) ) ) -with expansion: - "this string contains 'abc' as a substring" ( contains: "string" or contains: - "not there" ) + REQUIRE_NOTHROW( Approx(0).margin(0) ) -MatchersTests.cpp:: +ApproxTests.cpp:: PASSED: - CHECK_THAT( testStringForMatching(), AnyOf( Catch::Contains( "not there" ), Catch::Contains( "string" ) ) ) -with expansion: - "this string contains 'abc' as a substring" ( contains: "not there" or - contains: "string" ) + REQUIRE_NOTHROW( Approx(0).margin(1234656) ) + +ApproxTests.cpp:: +PASSED: + REQUIRE_THROWS_AS( Approx(0).margin(-2), std::domain_error ) + +ApproxTests.cpp:: +PASSED: + REQUIRE_NOTHROW( Approx(0).epsilon(0) ) + +ApproxTests.cpp:: +PASSED: + REQUIRE_NOTHROW( Approx(0).epsilon(1) ) + +ApproxTests.cpp:: +PASSED: + REQUIRE_THROWS_AS( Approx(0).epsilon(-0.001), std::domain_error ) + +ApproxTests.cpp:: +PASSED: + REQUIRE_THROWS_AS( Approx(0).epsilon(1.0001), std::domain_error ) ------------------------------------------------------------------------------- Approx with exactly-representable margin @@ -686,7 +720,7 @@ with expansion: ApproxTests.cpp:: PASSED: - REQUIRE( 0 == Approx( dSmall ).epsilon( 0.001 ) ) + REQUIRE( 0 == Approx( dSmall ).margin( 0.001 ) ) with expansion: 0 == Approx( 0.00001 ) @@ -780,6 +814,36 @@ PASSED: with expansion: true +------------------------------------------------------------------------------- +Assorted miscellaneous tests +------------------------------------------------------------------------------- +ApproxTests.cpp: +............................................................................... + +ApproxTests.cpp:: +PASSED: + REQUIRE( INFINITY == Approx(INFINITY) ) +with expansion: + inff == Approx( inf ) + +------------------------------------------------------------------------------- +Bitfields can be captured (#1027) +------------------------------------------------------------------------------- +TrickyTests.cpp: +............................................................................... + +TrickyTests.cpp:: +PASSED: + REQUIRE( y.v == 0 ) +with expansion: + 0 == 0 + +TrickyTests.cpp:: +PASSED: + REQUIRE( 0 == y.v ) +with expansion: + 0 == 0 + ------------------------------------------------------------------------------- Capture and info messages Capture should stringify like assertions @@ -911,6 +975,74 @@ PASSED: with expansion: 5 == 5 +------------------------------------------------------------------------------- +Commas in various macros are allowed +------------------------------------------------------------------------------- +TrickyTests.cpp: +............................................................................... + +TrickyTests.cpp:: +PASSED: + REQUIRE_THROWS( std::vector{constructor_throws{}, constructor_throws{}} ) + +TrickyTests.cpp:: +PASSED: + CHECK_THROWS( std::vector{constructor_throws{}, constructor_throws{}} ) + +TrickyTests.cpp:: +PASSED: + REQUIRE_NOTHROW( std::vector{1, 2, 3} == std::vector{1, 2, 3} ) + +TrickyTests.cpp:: +PASSED: + CHECK_NOTHROW( std::vector{1, 2, 3} == std::vector{1, 2, 3} ) + +TrickyTests.cpp:: +PASSED: + REQUIRE( std::vector{1, 2} == std::vector{1, 2} ) +with expansion: + { 1, 2 } == { 1, 2 } + +TrickyTests.cpp:: +PASSED: + CHECK( std::vector{1, 2} == std::vector{1, 2} ) +with expansion: + { 1, 2 } == { 1, 2 } + +TrickyTests.cpp:: +PASSED: + REQUIRE_FALSE( std::vector{1, 2} == std::vector{1, 2, 3} ) +with expansion: + !({ 1, 2 } == { 1, 2, 3 }) + +TrickyTests.cpp:: +PASSED: + CHECK_FALSE( std::vector{1, 2} == std::vector{1, 2, 3} ) +with expansion: + !({ 1, 2 } == { 1, 2, 3 }) + +TrickyTests.cpp:: +PASSED: + CHECK_NOFAIL( std::vector{1, 2} == std::vector{1, 2} ) +with expansion: + { 1, 2 } == { 1, 2 } + +TrickyTests.cpp:: +PASSED: + CHECKED_IF( std::vector{1, 2} == std::vector{1, 2} ) +with expansion: + { 1, 2 } == { 1, 2 } + +TrickyTests.cpp:: +PASSED: + REQUIRE( true ) + +TrickyTests.cpp:: +PASSED: + CHECKED_ELSE( std::vector{1, 2} == std::vector{1, 2} ) +with expansion: + { 1, 2 } == { 1, 2 } + ------------------------------------------------------------------------------- Comparing function pointers ------------------------------------------------------------------------------- @@ -930,18 +1062,82 @@ with expansion: 0x == 0x ------------------------------------------------------------------------------- -Comparing member function pointers +Comparison with explicitly convertible types ------------------------------------------------------------------------------- -TrickyTests.cpp: +ApproxTests.cpp: ............................................................................... -TrickyTests.cpp:: +ApproxTests.cpp:: PASSED: - CHECK( m == &S::f ) + REQUIRE( td == Approx(10.0) ) with expansion: - 0x - == - 0x + StrongDoubleTypedef(10) == Approx( 10.0 ) + +ApproxTests.cpp:: +PASSED: + REQUIRE( Approx(10.0) == td ) +with expansion: + Approx( 10.0 ) == StrongDoubleTypedef(10) + +ApproxTests.cpp:: +PASSED: + REQUIRE( td != Approx(11.0) ) +with expansion: + StrongDoubleTypedef(10) != Approx( 11.0 ) + +ApproxTests.cpp:: +PASSED: + REQUIRE( Approx(11.0) != td ) +with expansion: + Approx( 11.0 ) != StrongDoubleTypedef(10) + +ApproxTests.cpp:: +PASSED: + REQUIRE( td <= Approx(10.0) ) +with expansion: + StrongDoubleTypedef(10) <= Approx( 10.0 ) + +ApproxTests.cpp:: +PASSED: + REQUIRE( td <= Approx(11.0) ) +with expansion: + StrongDoubleTypedef(10) <= Approx( 11.0 ) + +ApproxTests.cpp:: +PASSED: + REQUIRE( Approx(10.0) <= td ) +with expansion: + Approx( 10.0 ) <= StrongDoubleTypedef(10) + +ApproxTests.cpp:: +PASSED: + REQUIRE( Approx(9.0) <= td ) +with expansion: + Approx( 9.0 ) <= StrongDoubleTypedef(10) + +ApproxTests.cpp:: +PASSED: + REQUIRE( td >= Approx(9.0) ) +with expansion: + StrongDoubleTypedef(10) >= Approx( 9.0 ) + +ApproxTests.cpp:: +PASSED: + REQUIRE( td >= Approx(10.0) ) +with expansion: + StrongDoubleTypedef(10) >= Approx( 10.0 ) + +ApproxTests.cpp:: +PASSED: + REQUIRE( Approx(10.0) >= td ) +with expansion: + Approx( 10.0 ) >= StrongDoubleTypedef(10) + +ApproxTests.cpp:: +PASSED: + REQUIRE( Approx(11.0) >= td ) +with expansion: + Approx( 11.0 ) >= StrongDoubleTypedef(10) ------------------------------------------------------------------------------- Comparisons between ints where one side is computed @@ -1078,11 +1274,9 @@ with expansion: ConditionTests.cpp:: PASSED: - REQUIRE( (std::numeric_limits::max)() > ul ) + REQUIRE( (std::numeric_limits::max)() > ul ) with expansion: - 18446744073709551615 (0x) - > - 4 + 4294967295 (0x) > 4 ------------------------------------------------------------------------------- Contains string matcher @@ -1128,16 +1322,22 @@ due to unexpected exception with message: custom std exception ------------------------------------------------------------------------------- -Demonstrate that a non-const == is not used +Default scale is invisible to comparison ------------------------------------------------------------------------------- -TrickyTests.cpp: +ApproxTests.cpp: ............................................................................... -TrickyTests.cpp:: +ApproxTests.cpp:: PASSED: - REQUIRE( t == 1u ) + REQUIRE( 101.000001 != Approx(100).epsilon(0.01) ) with expansion: - {?} == 1 + 101.000001 != Approx( 100.0 ) + +ApproxTests.cpp:: +PASSED: + REQUIRE( std::pow(10, -5) != Approx(std::pow(10, -7)) ) +with expansion: + 0.00001 != Approx( 0.0000001 ) ------------------------------------------------------------------------------- EndsWith string matcher @@ -1150,6 +1350,18 @@ MatchersTests.cpp:: FAILED: with expansion: "this string contains 'abc' as a substring" ends with: "this" +------------------------------------------------------------------------------- +Epsilon only applies to Approx's value +------------------------------------------------------------------------------- +ApproxTests.cpp: +............................................................................... + +ApproxTests.cpp:: +PASSED: + REQUIRE( 101.01 != Approx(100).epsilon(0.01) ) +with expansion: + 101.01 != Approx( 100.0 ) + ------------------------------------------------------------------------------- Equality checks that should fail ------------------------------------------------------------------------------- @@ -1293,6 +1505,73 @@ MatchersTests.cpp:: FAILED: with expansion: "this string contains 'abc' as a substring" equals: "something else" +------------------------------------------------------------------------------- +Exception matchers that fail + No exception +------------------------------------------------------------------------------- +MatchersTests.cpp: +............................................................................... + +MatchersTests.cpp:: FAILED: + CHECK_THROWS_MATCHES( doesNotThrow(), SpecialException, ExceptionMatcher{ 1 } ) +because no exception was thrown where one was expected: + +MatchersTests.cpp:: FAILED: + REQUIRE_THROWS_MATCHES( doesNotThrow(), SpecialException, ExceptionMatcher{ 1 } ) +because no exception was thrown where one was expected: + +------------------------------------------------------------------------------- +Exception matchers that fail + Type mismatch +------------------------------------------------------------------------------- +MatchersTests.cpp: +............................................................................... + +MatchersTests.cpp:: FAILED: + CHECK_THROWS_MATCHES( throwsAsInt(1), SpecialException, ExceptionMatcher{ 1 } ) +due to unexpected exception with message: + Unknown exception + +MatchersTests.cpp:: FAILED: + REQUIRE_THROWS_MATCHES( throwsAsInt(1), SpecialException, ExceptionMatcher{ 1 } ) +due to unexpected exception with message: + Unknown exception + +------------------------------------------------------------------------------- +Exception matchers that fail + Contents are wrong +------------------------------------------------------------------------------- +MatchersTests.cpp: +............................................................................... + +MatchersTests.cpp:: FAILED: + CHECK_THROWS_MATCHES( throws(3), SpecialException, ExceptionMatcher{ 1 } ) +with expansion: + {?} special exception has value of 1 + +MatchersTests.cpp:: FAILED: + REQUIRE_THROWS_MATCHES( throws(4), SpecialException, ExceptionMatcher{ 1 } ) +with expansion: + {?} special exception has value of 1 + +------------------------------------------------------------------------------- +Exception matchers that succeed +------------------------------------------------------------------------------- +MatchersTests.cpp: +............................................................................... + +MatchersTests.cpp:: +PASSED: + CHECK_THROWS_MATCHES( throws(1), SpecialException, ExceptionMatcher{ 1 } ) +with expansion: + {?} special exception has value of 1 + +MatchersTests.cpp:: +PASSED: + REQUIRE_THROWS_MATCHES( throws(2), SpecialException, ExceptionMatcher{ 2 } ) +with expansion: + {?} special exception has value of 2 + ------------------------------------------------------------------------------- Exception messages can be tested for exact match @@ -1303,6 +1582,8 @@ ExceptionTests.cpp: ExceptionTests.cpp:: PASSED: REQUIRE_THROWS_WITH( thisThrows(), "expected exception" ) +with expansion: + "expected exception" equals: "expected exception" ------------------------------------------------------------------------------- Exception messages can be tested for @@ -1314,6 +1595,8 @@ ExceptionTests.cpp: ExceptionTests.cpp:: PASSED: REQUIRE_THROWS_WITH( thisThrows(), Equals( "expecteD Exception", Catch::CaseSensitive::No ) ) +with expansion: + "expected exception" equals: "expected exception" (case insensitive) ------------------------------------------------------------------------------- Exception messages can be tested for @@ -1325,18 +1608,26 @@ ExceptionTests.cpp: ExceptionTests.cpp:: PASSED: REQUIRE_THROWS_WITH( thisThrows(), StartsWith( "expected" ) ) +with expansion: + "expected exception" starts with: "expected" ExceptionTests.cpp:: PASSED: REQUIRE_THROWS_WITH( thisThrows(), EndsWith( "exception" ) ) +with expansion: + "expected exception" ends with: "exception" ExceptionTests.cpp:: PASSED: REQUIRE_THROWS_WITH( thisThrows(), Contains( "except" ) ) +with expansion: + "expected exception" contains: "except" ExceptionTests.cpp:: PASSED: REQUIRE_THROWS_WITH( thisThrows(), Contains( "exCept", Catch::CaseSensitive::No ) ) +with expansion: + "expected exception" contains: "except" (case insensitive) ------------------------------------------------------------------------------- Expected exceptions that don't throw or unexpected exceptions fail the test @@ -1426,1326 +1717,6 @@ PASSED: with expansion: 3628800 (0x) == 3628800 (0x) -------------------------------------------------------------------------------- -Generator over a range of pairs -------------------------------------------------------------------------------- -GeneratorTests.cpp: -............................................................................... - -GeneratorTests.cpp:: -PASSED: - CATCH_REQUIRE( i->first == i->second-1 ) -with expansion: - 0 == 0 - -------------------------------------------------------------------------------- -Generator over a range of pairs -------------------------------------------------------------------------------- -GeneratorTests.cpp: -............................................................................... - -GeneratorTests.cpp:: -PASSED: - CATCH_REQUIRE( i->first == i->second-1 ) -with expansion: - 2 == 2 - -------------------------------------------------------------------------------- -Generators over two ranges -------------------------------------------------------------------------------- -GeneratorTests.cpp: -............................................................................... - -GeneratorTests.cpp:: -PASSED: - CATCH_REQUIRE( multiply( i, 2 ) == i*2 ) -with expansion: - 2 == 2 - -GeneratorTests.cpp:: -PASSED: - CATCH_REQUIRE( multiply( j, 2 ) == j*2 ) -with expansion: - 200 == 200 - -------------------------------------------------------------------------------- -Generators over two ranges -------------------------------------------------------------------------------- -GeneratorTests.cpp: -............................................................................... - -GeneratorTests.cpp:: -PASSED: - CATCH_REQUIRE( multiply( i, 2 ) == i*2 ) -with expansion: - 4 == 4 - -GeneratorTests.cpp:: -PASSED: - CATCH_REQUIRE( multiply( j, 2 ) == j*2 ) -with expansion: - 200 == 200 - -------------------------------------------------------------------------------- -Generators over two ranges -------------------------------------------------------------------------------- -GeneratorTests.cpp: -............................................................................... - -GeneratorTests.cpp:: -PASSED: - CATCH_REQUIRE( multiply( i, 2 ) == i*2 ) -with expansion: - 6 == 6 - -GeneratorTests.cpp:: -PASSED: - CATCH_REQUIRE( multiply( j, 2 ) == j*2 ) -with expansion: - 200 == 200 - -------------------------------------------------------------------------------- -Generators over two ranges -------------------------------------------------------------------------------- -GeneratorTests.cpp: -............................................................................... - -GeneratorTests.cpp:: -PASSED: - CATCH_REQUIRE( multiply( i, 2 ) == i*2 ) -with expansion: - 8 == 8 - -GeneratorTests.cpp:: -PASSED: - CATCH_REQUIRE( multiply( j, 2 ) == j*2 ) -with expansion: - 200 == 200 - -------------------------------------------------------------------------------- -Generators over two ranges -------------------------------------------------------------------------------- -GeneratorTests.cpp: -............................................................................... - -GeneratorTests.cpp:: -PASSED: - CATCH_REQUIRE( multiply( i, 2 ) == i*2 ) -with expansion: - 10 == 10 - -GeneratorTests.cpp:: -PASSED: - CATCH_REQUIRE( multiply( j, 2 ) == j*2 ) -with expansion: - 200 == 200 - -------------------------------------------------------------------------------- -Generators over two ranges -------------------------------------------------------------------------------- -GeneratorTests.cpp: -............................................................................... - -GeneratorTests.cpp:: -PASSED: - CATCH_REQUIRE( multiply( i, 2 ) == i*2 ) -with expansion: - 30 == 30 - -GeneratorTests.cpp:: -PASSED: - CATCH_REQUIRE( multiply( j, 2 ) == j*2 ) -with expansion: - 200 == 200 - -------------------------------------------------------------------------------- -Generators over two ranges -------------------------------------------------------------------------------- -GeneratorTests.cpp: -............................................................................... - -GeneratorTests.cpp:: -PASSED: - CATCH_REQUIRE( multiply( i, 2 ) == i*2 ) -with expansion: - 40 == 40 - -GeneratorTests.cpp:: -PASSED: - CATCH_REQUIRE( multiply( j, 2 ) == j*2 ) -with expansion: - 200 == 200 - -------------------------------------------------------------------------------- -Generators over two ranges -------------------------------------------------------------------------------- -GeneratorTests.cpp: -............................................................................... - -GeneratorTests.cpp:: -PASSED: - CATCH_REQUIRE( multiply( i, 2 ) == i*2 ) -with expansion: - 42 == 42 - -GeneratorTests.cpp:: -PASSED: - CATCH_REQUIRE( multiply( j, 2 ) == j*2 ) -with expansion: - 200 == 200 - -------------------------------------------------------------------------------- -Generators over two ranges -------------------------------------------------------------------------------- -GeneratorTests.cpp: -............................................................................... - -GeneratorTests.cpp:: -PASSED: - CATCH_REQUIRE( multiply( i, 2 ) == i*2 ) -with expansion: - 72 == 72 - -GeneratorTests.cpp:: -PASSED: - CATCH_REQUIRE( multiply( j, 2 ) == j*2 ) -with expansion: - 200 == 200 - -------------------------------------------------------------------------------- -Generators over two ranges -------------------------------------------------------------------------------- -GeneratorTests.cpp: -............................................................................... - -GeneratorTests.cpp:: -PASSED: - CATCH_REQUIRE( multiply( i, 2 ) == i*2 ) -with expansion: - 2 == 2 - -GeneratorTests.cpp:: -PASSED: - CATCH_REQUIRE( multiply( j, 2 ) == j*2 ) -with expansion: - 202 == 202 - -------------------------------------------------------------------------------- -Generators over two ranges -------------------------------------------------------------------------------- -GeneratorTests.cpp: -............................................................................... - -GeneratorTests.cpp:: -PASSED: - CATCH_REQUIRE( multiply( i, 2 ) == i*2 ) -with expansion: - 4 == 4 - -GeneratorTests.cpp:: -PASSED: - CATCH_REQUIRE( multiply( j, 2 ) == j*2 ) -with expansion: - 202 == 202 - -------------------------------------------------------------------------------- -Generators over two ranges -------------------------------------------------------------------------------- -GeneratorTests.cpp: -............................................................................... - -GeneratorTests.cpp:: -PASSED: - CATCH_REQUIRE( multiply( i, 2 ) == i*2 ) -with expansion: - 6 == 6 - -GeneratorTests.cpp:: -PASSED: - CATCH_REQUIRE( multiply( j, 2 ) == j*2 ) -with expansion: - 202 == 202 - -------------------------------------------------------------------------------- -Generators over two ranges -------------------------------------------------------------------------------- -GeneratorTests.cpp: -............................................................................... - -GeneratorTests.cpp:: -PASSED: - CATCH_REQUIRE( multiply( i, 2 ) == i*2 ) -with expansion: - 8 == 8 - -GeneratorTests.cpp:: -PASSED: - CATCH_REQUIRE( multiply( j, 2 ) == j*2 ) -with expansion: - 202 == 202 - -------------------------------------------------------------------------------- -Generators over two ranges -------------------------------------------------------------------------------- -GeneratorTests.cpp: -............................................................................... - -GeneratorTests.cpp:: -PASSED: - CATCH_REQUIRE( multiply( i, 2 ) == i*2 ) -with expansion: - 10 == 10 - -GeneratorTests.cpp:: -PASSED: - CATCH_REQUIRE( multiply( j, 2 ) == j*2 ) -with expansion: - 202 == 202 - -------------------------------------------------------------------------------- -Generators over two ranges -------------------------------------------------------------------------------- -GeneratorTests.cpp: -............................................................................... - -GeneratorTests.cpp:: -PASSED: - CATCH_REQUIRE( multiply( i, 2 ) == i*2 ) -with expansion: - 30 == 30 - -GeneratorTests.cpp:: -PASSED: - CATCH_REQUIRE( multiply( j, 2 ) == j*2 ) -with expansion: - 202 == 202 - -------------------------------------------------------------------------------- -Generators over two ranges -------------------------------------------------------------------------------- -GeneratorTests.cpp: -............................................................................... - -GeneratorTests.cpp:: -PASSED: - CATCH_REQUIRE( multiply( i, 2 ) == i*2 ) -with expansion: - 40 == 40 - -GeneratorTests.cpp:: -PASSED: - CATCH_REQUIRE( multiply( j, 2 ) == j*2 ) -with expansion: - 202 == 202 - -------------------------------------------------------------------------------- -Generators over two ranges -------------------------------------------------------------------------------- -GeneratorTests.cpp: -............................................................................... - -GeneratorTests.cpp:: -PASSED: - CATCH_REQUIRE( multiply( i, 2 ) == i*2 ) -with expansion: - 42 == 42 - -GeneratorTests.cpp:: -PASSED: - CATCH_REQUIRE( multiply( j, 2 ) == j*2 ) -with expansion: - 202 == 202 - -------------------------------------------------------------------------------- -Generators over two ranges -------------------------------------------------------------------------------- -GeneratorTests.cpp: -............................................................................... - -GeneratorTests.cpp:: -PASSED: - CATCH_REQUIRE( multiply( i, 2 ) == i*2 ) -with expansion: - 72 == 72 - -GeneratorTests.cpp:: -PASSED: - CATCH_REQUIRE( multiply( j, 2 ) == j*2 ) -with expansion: - 202 == 202 - -------------------------------------------------------------------------------- -Generators over two ranges -------------------------------------------------------------------------------- -GeneratorTests.cpp: -............................................................................... - -GeneratorTests.cpp:: -PASSED: - CATCH_REQUIRE( multiply( i, 2 ) == i*2 ) -with expansion: - 2 == 2 - -GeneratorTests.cpp:: -PASSED: - CATCH_REQUIRE( multiply( j, 2 ) == j*2 ) -with expansion: - 204 == 204 - -------------------------------------------------------------------------------- -Generators over two ranges -------------------------------------------------------------------------------- -GeneratorTests.cpp: -............................................................................... - -GeneratorTests.cpp:: -PASSED: - CATCH_REQUIRE( multiply( i, 2 ) == i*2 ) -with expansion: - 4 == 4 - -GeneratorTests.cpp:: -PASSED: - CATCH_REQUIRE( multiply( j, 2 ) == j*2 ) -with expansion: - 204 == 204 - -------------------------------------------------------------------------------- -Generators over two ranges -------------------------------------------------------------------------------- -GeneratorTests.cpp: -............................................................................... - -GeneratorTests.cpp:: -PASSED: - CATCH_REQUIRE( multiply( i, 2 ) == i*2 ) -with expansion: - 6 == 6 - -GeneratorTests.cpp:: -PASSED: - CATCH_REQUIRE( multiply( j, 2 ) == j*2 ) -with expansion: - 204 == 204 - -------------------------------------------------------------------------------- -Generators over two ranges -------------------------------------------------------------------------------- -GeneratorTests.cpp: -............................................................................... - -GeneratorTests.cpp:: -PASSED: - CATCH_REQUIRE( multiply( i, 2 ) == i*2 ) -with expansion: - 8 == 8 - -GeneratorTests.cpp:: -PASSED: - CATCH_REQUIRE( multiply( j, 2 ) == j*2 ) -with expansion: - 204 == 204 - -------------------------------------------------------------------------------- -Generators over two ranges -------------------------------------------------------------------------------- -GeneratorTests.cpp: -............................................................................... - -GeneratorTests.cpp:: -PASSED: - CATCH_REQUIRE( multiply( i, 2 ) == i*2 ) -with expansion: - 10 == 10 - -GeneratorTests.cpp:: -PASSED: - CATCH_REQUIRE( multiply( j, 2 ) == j*2 ) -with expansion: - 204 == 204 - -------------------------------------------------------------------------------- -Generators over two ranges -------------------------------------------------------------------------------- -GeneratorTests.cpp: -............................................................................... - -GeneratorTests.cpp:: -PASSED: - CATCH_REQUIRE( multiply( i, 2 ) == i*2 ) -with expansion: - 30 == 30 - -GeneratorTests.cpp:: -PASSED: - CATCH_REQUIRE( multiply( j, 2 ) == j*2 ) -with expansion: - 204 == 204 - -------------------------------------------------------------------------------- -Generators over two ranges -------------------------------------------------------------------------------- -GeneratorTests.cpp: -............................................................................... - -GeneratorTests.cpp:: -PASSED: - CATCH_REQUIRE( multiply( i, 2 ) == i*2 ) -with expansion: - 40 == 40 - -GeneratorTests.cpp:: -PASSED: - CATCH_REQUIRE( multiply( j, 2 ) == j*2 ) -with expansion: - 204 == 204 - -------------------------------------------------------------------------------- -Generators over two ranges -------------------------------------------------------------------------------- -GeneratorTests.cpp: -............................................................................... - -GeneratorTests.cpp:: -PASSED: - CATCH_REQUIRE( multiply( i, 2 ) == i*2 ) -with expansion: - 42 == 42 - -GeneratorTests.cpp:: -PASSED: - CATCH_REQUIRE( multiply( j, 2 ) == j*2 ) -with expansion: - 204 == 204 - -------------------------------------------------------------------------------- -Generators over two ranges -------------------------------------------------------------------------------- -GeneratorTests.cpp: -............................................................................... - -GeneratorTests.cpp:: -PASSED: - CATCH_REQUIRE( multiply( i, 2 ) == i*2 ) -with expansion: - 72 == 72 - -GeneratorTests.cpp:: -PASSED: - CATCH_REQUIRE( multiply( j, 2 ) == j*2 ) -with expansion: - 204 == 204 - -------------------------------------------------------------------------------- -Generators over two ranges -------------------------------------------------------------------------------- -GeneratorTests.cpp: -............................................................................... - -GeneratorTests.cpp:: -PASSED: - CATCH_REQUIRE( multiply( i, 2 ) == i*2 ) -with expansion: - 2 == 2 - -GeneratorTests.cpp:: -PASSED: - CATCH_REQUIRE( multiply( j, 2 ) == j*2 ) -with expansion: - 206 == 206 - -------------------------------------------------------------------------------- -Generators over two ranges -------------------------------------------------------------------------------- -GeneratorTests.cpp: -............................................................................... - -GeneratorTests.cpp:: -PASSED: - CATCH_REQUIRE( multiply( i, 2 ) == i*2 ) -with expansion: - 4 == 4 - -GeneratorTests.cpp:: -PASSED: - CATCH_REQUIRE( multiply( j, 2 ) == j*2 ) -with expansion: - 206 == 206 - -------------------------------------------------------------------------------- -Generators over two ranges -------------------------------------------------------------------------------- -GeneratorTests.cpp: -............................................................................... - -GeneratorTests.cpp:: -PASSED: - CATCH_REQUIRE( multiply( i, 2 ) == i*2 ) -with expansion: - 6 == 6 - -GeneratorTests.cpp:: -PASSED: - CATCH_REQUIRE( multiply( j, 2 ) == j*2 ) -with expansion: - 206 == 206 - -------------------------------------------------------------------------------- -Generators over two ranges -------------------------------------------------------------------------------- -GeneratorTests.cpp: -............................................................................... - -GeneratorTests.cpp:: -PASSED: - CATCH_REQUIRE( multiply( i, 2 ) == i*2 ) -with expansion: - 8 == 8 - -GeneratorTests.cpp:: -PASSED: - CATCH_REQUIRE( multiply( j, 2 ) == j*2 ) -with expansion: - 206 == 206 - -------------------------------------------------------------------------------- -Generators over two ranges -------------------------------------------------------------------------------- -GeneratorTests.cpp: -............................................................................... - -GeneratorTests.cpp:: -PASSED: - CATCH_REQUIRE( multiply( i, 2 ) == i*2 ) -with expansion: - 10 == 10 - -GeneratorTests.cpp:: -PASSED: - CATCH_REQUIRE( multiply( j, 2 ) == j*2 ) -with expansion: - 206 == 206 - -------------------------------------------------------------------------------- -Generators over two ranges -------------------------------------------------------------------------------- -GeneratorTests.cpp: -............................................................................... - -GeneratorTests.cpp:: -PASSED: - CATCH_REQUIRE( multiply( i, 2 ) == i*2 ) -with expansion: - 30 == 30 - -GeneratorTests.cpp:: -PASSED: - CATCH_REQUIRE( multiply( j, 2 ) == j*2 ) -with expansion: - 206 == 206 - -------------------------------------------------------------------------------- -Generators over two ranges -------------------------------------------------------------------------------- -GeneratorTests.cpp: -............................................................................... - -GeneratorTests.cpp:: -PASSED: - CATCH_REQUIRE( multiply( i, 2 ) == i*2 ) -with expansion: - 40 == 40 - -GeneratorTests.cpp:: -PASSED: - CATCH_REQUIRE( multiply( j, 2 ) == j*2 ) -with expansion: - 206 == 206 - -------------------------------------------------------------------------------- -Generators over two ranges -------------------------------------------------------------------------------- -GeneratorTests.cpp: -............................................................................... - -GeneratorTests.cpp:: -PASSED: - CATCH_REQUIRE( multiply( i, 2 ) == i*2 ) -with expansion: - 42 == 42 - -GeneratorTests.cpp:: -PASSED: - CATCH_REQUIRE( multiply( j, 2 ) == j*2 ) -with expansion: - 206 == 206 - -------------------------------------------------------------------------------- -Generators over two ranges -------------------------------------------------------------------------------- -GeneratorTests.cpp: -............................................................................... - -GeneratorTests.cpp:: -PASSED: - CATCH_REQUIRE( multiply( i, 2 ) == i*2 ) -with expansion: - 72 == 72 - -GeneratorTests.cpp:: -PASSED: - CATCH_REQUIRE( multiply( j, 2 ) == j*2 ) -with expansion: - 206 == 206 - -------------------------------------------------------------------------------- -Generators over two ranges -------------------------------------------------------------------------------- -GeneratorTests.cpp: -............................................................................... - -GeneratorTests.cpp:: -PASSED: - CATCH_REQUIRE( multiply( i, 2 ) == i*2 ) -with expansion: - 2 == 2 - -GeneratorTests.cpp:: -PASSED: - CATCH_REQUIRE( multiply( j, 2 ) == j*2 ) -with expansion: - 208 == 208 - -------------------------------------------------------------------------------- -Generators over two ranges -------------------------------------------------------------------------------- -GeneratorTests.cpp: -............................................................................... - -GeneratorTests.cpp:: -PASSED: - CATCH_REQUIRE( multiply( i, 2 ) == i*2 ) -with expansion: - 4 == 4 - -GeneratorTests.cpp:: -PASSED: - CATCH_REQUIRE( multiply( j, 2 ) == j*2 ) -with expansion: - 208 == 208 - -------------------------------------------------------------------------------- -Generators over two ranges -------------------------------------------------------------------------------- -GeneratorTests.cpp: -............................................................................... - -GeneratorTests.cpp:: -PASSED: - CATCH_REQUIRE( multiply( i, 2 ) == i*2 ) -with expansion: - 6 == 6 - -GeneratorTests.cpp:: -PASSED: - CATCH_REQUIRE( multiply( j, 2 ) == j*2 ) -with expansion: - 208 == 208 - -------------------------------------------------------------------------------- -Generators over two ranges -------------------------------------------------------------------------------- -GeneratorTests.cpp: -............................................................................... - -GeneratorTests.cpp:: -PASSED: - CATCH_REQUIRE( multiply( i, 2 ) == i*2 ) -with expansion: - 8 == 8 - -GeneratorTests.cpp:: -PASSED: - CATCH_REQUIRE( multiply( j, 2 ) == j*2 ) -with expansion: - 208 == 208 - -------------------------------------------------------------------------------- -Generators over two ranges -------------------------------------------------------------------------------- -GeneratorTests.cpp: -............................................................................... - -GeneratorTests.cpp:: -PASSED: - CATCH_REQUIRE( multiply( i, 2 ) == i*2 ) -with expansion: - 10 == 10 - -GeneratorTests.cpp:: -PASSED: - CATCH_REQUIRE( multiply( j, 2 ) == j*2 ) -with expansion: - 208 == 208 - -------------------------------------------------------------------------------- -Generators over two ranges -------------------------------------------------------------------------------- -GeneratorTests.cpp: -............................................................................... - -GeneratorTests.cpp:: -PASSED: - CATCH_REQUIRE( multiply( i, 2 ) == i*2 ) -with expansion: - 30 == 30 - -GeneratorTests.cpp:: -PASSED: - CATCH_REQUIRE( multiply( j, 2 ) == j*2 ) -with expansion: - 208 == 208 - -------------------------------------------------------------------------------- -Generators over two ranges -------------------------------------------------------------------------------- -GeneratorTests.cpp: -............................................................................... - -GeneratorTests.cpp:: -PASSED: - CATCH_REQUIRE( multiply( i, 2 ) == i*2 ) -with expansion: - 40 == 40 - -GeneratorTests.cpp:: -PASSED: - CATCH_REQUIRE( multiply( j, 2 ) == j*2 ) -with expansion: - 208 == 208 - -------------------------------------------------------------------------------- -Generators over two ranges -------------------------------------------------------------------------------- -GeneratorTests.cpp: -............................................................................... - -GeneratorTests.cpp:: -PASSED: - CATCH_REQUIRE( multiply( i, 2 ) == i*2 ) -with expansion: - 42 == 42 - -GeneratorTests.cpp:: -PASSED: - CATCH_REQUIRE( multiply( j, 2 ) == j*2 ) -with expansion: - 208 == 208 - -------------------------------------------------------------------------------- -Generators over two ranges -------------------------------------------------------------------------------- -GeneratorTests.cpp: -............................................................................... - -GeneratorTests.cpp:: -PASSED: - CATCH_REQUIRE( multiply( i, 2 ) == i*2 ) -with expansion: - 72 == 72 - -GeneratorTests.cpp:: -PASSED: - CATCH_REQUIRE( multiply( j, 2 ) == j*2 ) -with expansion: - 208 == 208 - -------------------------------------------------------------------------------- -Generators over two ranges -------------------------------------------------------------------------------- -GeneratorTests.cpp: -............................................................................... - -GeneratorTests.cpp:: -PASSED: - CATCH_REQUIRE( multiply( i, 2 ) == i*2 ) -with expansion: - 2 == 2 - -GeneratorTests.cpp:: -PASSED: - CATCH_REQUIRE( multiply( j, 2 ) == j*2 ) -with expansion: - 210 == 210 - -------------------------------------------------------------------------------- -Generators over two ranges -------------------------------------------------------------------------------- -GeneratorTests.cpp: -............................................................................... - -GeneratorTests.cpp:: -PASSED: - CATCH_REQUIRE( multiply( i, 2 ) == i*2 ) -with expansion: - 4 == 4 - -GeneratorTests.cpp:: -PASSED: - CATCH_REQUIRE( multiply( j, 2 ) == j*2 ) -with expansion: - 210 == 210 - -------------------------------------------------------------------------------- -Generators over two ranges -------------------------------------------------------------------------------- -GeneratorTests.cpp: -............................................................................... - -GeneratorTests.cpp:: -PASSED: - CATCH_REQUIRE( multiply( i, 2 ) == i*2 ) -with expansion: - 6 == 6 - -GeneratorTests.cpp:: -PASSED: - CATCH_REQUIRE( multiply( j, 2 ) == j*2 ) -with expansion: - 210 == 210 - -------------------------------------------------------------------------------- -Generators over two ranges -------------------------------------------------------------------------------- -GeneratorTests.cpp: -............................................................................... - -GeneratorTests.cpp:: -PASSED: - CATCH_REQUIRE( multiply( i, 2 ) == i*2 ) -with expansion: - 8 == 8 - -GeneratorTests.cpp:: -PASSED: - CATCH_REQUIRE( multiply( j, 2 ) == j*2 ) -with expansion: - 210 == 210 - -------------------------------------------------------------------------------- -Generators over two ranges -------------------------------------------------------------------------------- -GeneratorTests.cpp: -............................................................................... - -GeneratorTests.cpp:: -PASSED: - CATCH_REQUIRE( multiply( i, 2 ) == i*2 ) -with expansion: - 10 == 10 - -GeneratorTests.cpp:: -PASSED: - CATCH_REQUIRE( multiply( j, 2 ) == j*2 ) -with expansion: - 210 == 210 - -------------------------------------------------------------------------------- -Generators over two ranges -------------------------------------------------------------------------------- -GeneratorTests.cpp: -............................................................................... - -GeneratorTests.cpp:: -PASSED: - CATCH_REQUIRE( multiply( i, 2 ) == i*2 ) -with expansion: - 30 == 30 - -GeneratorTests.cpp:: -PASSED: - CATCH_REQUIRE( multiply( j, 2 ) == j*2 ) -with expansion: - 210 == 210 - -------------------------------------------------------------------------------- -Generators over two ranges -------------------------------------------------------------------------------- -GeneratorTests.cpp: -............................................................................... - -GeneratorTests.cpp:: -PASSED: - CATCH_REQUIRE( multiply( i, 2 ) == i*2 ) -with expansion: - 40 == 40 - -GeneratorTests.cpp:: -PASSED: - CATCH_REQUIRE( multiply( j, 2 ) == j*2 ) -with expansion: - 210 == 210 - -------------------------------------------------------------------------------- -Generators over two ranges -------------------------------------------------------------------------------- -GeneratorTests.cpp: -............................................................................... - -GeneratorTests.cpp:: -PASSED: - CATCH_REQUIRE( multiply( i, 2 ) == i*2 ) -with expansion: - 42 == 42 - -GeneratorTests.cpp:: -PASSED: - CATCH_REQUIRE( multiply( j, 2 ) == j*2 ) -with expansion: - 210 == 210 - -------------------------------------------------------------------------------- -Generators over two ranges -------------------------------------------------------------------------------- -GeneratorTests.cpp: -............................................................................... - -GeneratorTests.cpp:: -PASSED: - CATCH_REQUIRE( multiply( i, 2 ) == i*2 ) -with expansion: - 72 == 72 - -GeneratorTests.cpp:: -PASSED: - CATCH_REQUIRE( multiply( j, 2 ) == j*2 ) -with expansion: - 210 == 210 - -------------------------------------------------------------------------------- -Generators over two ranges -------------------------------------------------------------------------------- -GeneratorTests.cpp: -............................................................................... - -GeneratorTests.cpp:: -PASSED: - CATCH_REQUIRE( multiply( i, 2 ) == i*2 ) -with expansion: - 2 == 2 - -GeneratorTests.cpp:: -PASSED: - CATCH_REQUIRE( multiply( j, 2 ) == j*2 ) -with expansion: - 212 == 212 - -------------------------------------------------------------------------------- -Generators over two ranges -------------------------------------------------------------------------------- -GeneratorTests.cpp: -............................................................................... - -GeneratorTests.cpp:: -PASSED: - CATCH_REQUIRE( multiply( i, 2 ) == i*2 ) -with expansion: - 4 == 4 - -GeneratorTests.cpp:: -PASSED: - CATCH_REQUIRE( multiply( j, 2 ) == j*2 ) -with expansion: - 212 == 212 - -------------------------------------------------------------------------------- -Generators over two ranges -------------------------------------------------------------------------------- -GeneratorTests.cpp: -............................................................................... - -GeneratorTests.cpp:: -PASSED: - CATCH_REQUIRE( multiply( i, 2 ) == i*2 ) -with expansion: - 6 == 6 - -GeneratorTests.cpp:: -PASSED: - CATCH_REQUIRE( multiply( j, 2 ) == j*2 ) -with expansion: - 212 == 212 - -------------------------------------------------------------------------------- -Generators over two ranges -------------------------------------------------------------------------------- -GeneratorTests.cpp: -............................................................................... - -GeneratorTests.cpp:: -PASSED: - CATCH_REQUIRE( multiply( i, 2 ) == i*2 ) -with expansion: - 8 == 8 - -GeneratorTests.cpp:: -PASSED: - CATCH_REQUIRE( multiply( j, 2 ) == j*2 ) -with expansion: - 212 == 212 - -------------------------------------------------------------------------------- -Generators over two ranges -------------------------------------------------------------------------------- -GeneratorTests.cpp: -............................................................................... - -GeneratorTests.cpp:: -PASSED: - CATCH_REQUIRE( multiply( i, 2 ) == i*2 ) -with expansion: - 10 == 10 - -GeneratorTests.cpp:: -PASSED: - CATCH_REQUIRE( multiply( j, 2 ) == j*2 ) -with expansion: - 212 == 212 - -------------------------------------------------------------------------------- -Generators over two ranges -------------------------------------------------------------------------------- -GeneratorTests.cpp: -............................................................................... - -GeneratorTests.cpp:: -PASSED: - CATCH_REQUIRE( multiply( i, 2 ) == i*2 ) -with expansion: - 30 == 30 - -GeneratorTests.cpp:: -PASSED: - CATCH_REQUIRE( multiply( j, 2 ) == j*2 ) -with expansion: - 212 == 212 - -------------------------------------------------------------------------------- -Generators over two ranges -------------------------------------------------------------------------------- -GeneratorTests.cpp: -............................................................................... - -GeneratorTests.cpp:: -PASSED: - CATCH_REQUIRE( multiply( i, 2 ) == i*2 ) -with expansion: - 40 == 40 - -GeneratorTests.cpp:: -PASSED: - CATCH_REQUIRE( multiply( j, 2 ) == j*2 ) -with expansion: - 212 == 212 - -------------------------------------------------------------------------------- -Generators over two ranges -------------------------------------------------------------------------------- -GeneratorTests.cpp: -............................................................................... - -GeneratorTests.cpp:: -PASSED: - CATCH_REQUIRE( multiply( i, 2 ) == i*2 ) -with expansion: - 42 == 42 - -GeneratorTests.cpp:: -PASSED: - CATCH_REQUIRE( multiply( j, 2 ) == j*2 ) -with expansion: - 212 == 212 - -------------------------------------------------------------------------------- -Generators over two ranges -------------------------------------------------------------------------------- -GeneratorTests.cpp: -............................................................................... - -GeneratorTests.cpp:: -PASSED: - CATCH_REQUIRE( multiply( i, 2 ) == i*2 ) -with expansion: - 72 == 72 - -GeneratorTests.cpp:: -PASSED: - CATCH_REQUIRE( multiply( j, 2 ) == j*2 ) -with expansion: - 212 == 212 - -------------------------------------------------------------------------------- -Generators over two ranges -------------------------------------------------------------------------------- -GeneratorTests.cpp: -............................................................................... - -GeneratorTests.cpp:: -PASSED: - CATCH_REQUIRE( multiply( i, 2 ) == i*2 ) -with expansion: - 2 == 2 - -GeneratorTests.cpp:: -PASSED: - CATCH_REQUIRE( multiply( j, 2 ) == j*2 ) -with expansion: - 214 == 214 - -------------------------------------------------------------------------------- -Generators over two ranges -------------------------------------------------------------------------------- -GeneratorTests.cpp: -............................................................................... - -GeneratorTests.cpp:: -PASSED: - CATCH_REQUIRE( multiply( i, 2 ) == i*2 ) -with expansion: - 4 == 4 - -GeneratorTests.cpp:: -PASSED: - CATCH_REQUIRE( multiply( j, 2 ) == j*2 ) -with expansion: - 214 == 214 - -------------------------------------------------------------------------------- -Generators over two ranges -------------------------------------------------------------------------------- -GeneratorTests.cpp: -............................................................................... - -GeneratorTests.cpp:: -PASSED: - CATCH_REQUIRE( multiply( i, 2 ) == i*2 ) -with expansion: - 6 == 6 - -GeneratorTests.cpp:: -PASSED: - CATCH_REQUIRE( multiply( j, 2 ) == j*2 ) -with expansion: - 214 == 214 - -------------------------------------------------------------------------------- -Generators over two ranges -------------------------------------------------------------------------------- -GeneratorTests.cpp: -............................................................................... - -GeneratorTests.cpp:: -PASSED: - CATCH_REQUIRE( multiply( i, 2 ) == i*2 ) -with expansion: - 8 == 8 - -GeneratorTests.cpp:: -PASSED: - CATCH_REQUIRE( multiply( j, 2 ) == j*2 ) -with expansion: - 214 == 214 - -------------------------------------------------------------------------------- -Generators over two ranges -------------------------------------------------------------------------------- -GeneratorTests.cpp: -............................................................................... - -GeneratorTests.cpp:: -PASSED: - CATCH_REQUIRE( multiply( i, 2 ) == i*2 ) -with expansion: - 10 == 10 - -GeneratorTests.cpp:: -PASSED: - CATCH_REQUIRE( multiply( j, 2 ) == j*2 ) -with expansion: - 214 == 214 - -------------------------------------------------------------------------------- -Generators over two ranges -------------------------------------------------------------------------------- -GeneratorTests.cpp: -............................................................................... - -GeneratorTests.cpp:: -PASSED: - CATCH_REQUIRE( multiply( i, 2 ) == i*2 ) -with expansion: - 30 == 30 - -GeneratorTests.cpp:: -PASSED: - CATCH_REQUIRE( multiply( j, 2 ) == j*2 ) -with expansion: - 214 == 214 - -------------------------------------------------------------------------------- -Generators over two ranges -------------------------------------------------------------------------------- -GeneratorTests.cpp: -............................................................................... - -GeneratorTests.cpp:: -PASSED: - CATCH_REQUIRE( multiply( i, 2 ) == i*2 ) -with expansion: - 40 == 40 - -GeneratorTests.cpp:: -PASSED: - CATCH_REQUIRE( multiply( j, 2 ) == j*2 ) -with expansion: - 214 == 214 - -------------------------------------------------------------------------------- -Generators over two ranges -------------------------------------------------------------------------------- -GeneratorTests.cpp: -............................................................................... - -GeneratorTests.cpp:: -PASSED: - CATCH_REQUIRE( multiply( i, 2 ) == i*2 ) -with expansion: - 42 == 42 - -GeneratorTests.cpp:: -PASSED: - CATCH_REQUIRE( multiply( j, 2 ) == j*2 ) -with expansion: - 214 == 214 - -------------------------------------------------------------------------------- -Generators over two ranges -------------------------------------------------------------------------------- -GeneratorTests.cpp: -............................................................................... - -GeneratorTests.cpp:: -PASSED: - CATCH_REQUIRE( multiply( i, 2 ) == i*2 ) -with expansion: - 72 == 72 - -GeneratorTests.cpp:: -PASSED: - CATCH_REQUIRE( multiply( j, 2 ) == j*2 ) -with expansion: - 214 == 214 - ------------------------------------------------------------------------------- Greater-than inequalities with different epsilons ------------------------------------------------------------------------------- @@ -2843,6 +1814,110 @@ with messages: and this, but later but not this +------------------------------------------------------------------------------- +INFO is reset for each loop +------------------------------------------------------------------------------- +MessageTests.cpp: +............................................................................... + +MessageTests.cpp:: +PASSED: + REQUIRE( i < 10 ) +with expansion: + 0 < 10 +with messages: + current counter 0 + i := 0 + +MessageTests.cpp:: +PASSED: + REQUIRE( i < 10 ) +with expansion: + 1 < 10 +with messages: + current counter 1 + i := 1 + +MessageTests.cpp:: +PASSED: + REQUIRE( i < 10 ) +with expansion: + 2 < 10 +with messages: + current counter 2 + i := 2 + +MessageTests.cpp:: +PASSED: + REQUIRE( i < 10 ) +with expansion: + 3 < 10 +with messages: + current counter 3 + i := 3 + +MessageTests.cpp:: +PASSED: + REQUIRE( i < 10 ) +with expansion: + 4 < 10 +with messages: + current counter 4 + i := 4 + +MessageTests.cpp:: +PASSED: + REQUIRE( i < 10 ) +with expansion: + 5 < 10 +with messages: + current counter 5 + i := 5 + +MessageTests.cpp:: +PASSED: + REQUIRE( i < 10 ) +with expansion: + 6 < 10 +with messages: + current counter 6 + i := 6 + +MessageTests.cpp:: +PASSED: + REQUIRE( i < 10 ) +with expansion: + 7 < 10 +with messages: + current counter 7 + i := 7 + +MessageTests.cpp:: +PASSED: + REQUIRE( i < 10 ) +with expansion: + 8 < 10 +with messages: + current counter 8 + i := 8 + +MessageTests.cpp:: +PASSED: + REQUIRE( i < 10 ) +with expansion: + 9 < 10 +with messages: + current counter 9 + i := 9 + +MessageTests.cpp:: FAILED: + REQUIRE( i < 10 ) +with expansion: + 10 < 10 +with messages: + current counter 10 + i := 10 + ------------------------------------------------------------------------------- Inequality checks that should fail ------------------------------------------------------------------------------- @@ -2976,1556 +2051,6 @@ PASSED: with expansion: 1.23 <= Approx( 1.22 ) -------------------------------------------------------------------------------- -Long strings can be wrapped - plain string - No wrapping -------------------------------------------------------------------------------- -TestMain.cpp: -............................................................................... - -TestMain.cpp:: -PASSED: - CHECK( Text( testString, TextAttributes().setWidth( 80 ) ).toString() == testString ) -with expansion: - "one two three four" - == - "one two three four" - -TestMain.cpp:: -PASSED: - CHECK( Text( testString, TextAttributes().setWidth( 18 ) ).toString() == testString ) -with expansion: - "one two three four" - == - "one two three four" - -------------------------------------------------------------------------------- -Long strings can be wrapped - plain string - Wrapped once -------------------------------------------------------------------------------- -TestMain.cpp: -............................................................................... - -TestMain.cpp:: -PASSED: - CHECK( Text( testString, TextAttributes().setWidth( 17 ) ).toString() == "one two three\nfour" ) -with expansion: - "one two three - four" - == - "one two three - four" - -TestMain.cpp:: -PASSED: - CHECK( Text( testString, TextAttributes().setWidth( 16 ) ).toString() == "one two three\nfour" ) -with expansion: - "one two three - four" - == - "one two three - four" - -TestMain.cpp:: -PASSED: - CHECK( Text( testString, TextAttributes().setWidth( 14 ) ).toString() == "one two three\nfour" ) -with expansion: - "one two three - four" - == - "one two three - four" - -TestMain.cpp:: -PASSED: - CHECK( Text( testString, TextAttributes().setWidth( 13 ) ).toString() == "one two three\nfour" ) -with expansion: - "one two three - four" - == - "one two three - four" - -TestMain.cpp:: -PASSED: - CHECK( Text( testString, TextAttributes().setWidth( 12 ) ).toString() == "one two\nthree four" ) -with expansion: - "one two - three four" - == - "one two - three four" - -------------------------------------------------------------------------------- -Long strings can be wrapped - plain string - Wrapped twice -------------------------------------------------------------------------------- -TestMain.cpp: -............................................................................... - -TestMain.cpp:: -PASSED: - CHECK( Text( testString, TextAttributes().setWidth( 9 ) ).toString() == "one two\nthree\nfour" ) -with expansion: - "one two - three - four" - == - "one two - three - four" - -TestMain.cpp:: -PASSED: - CHECK( Text( testString, TextAttributes().setWidth( 8 ) ).toString() == "one two\nthree\nfour" ) -with expansion: - "one two - three - four" - == - "one two - three - four" - -TestMain.cpp:: -PASSED: - CHECK( Text( testString, TextAttributes().setWidth( 7 ) ).toString() == "one two\nthree\nfour" ) -with expansion: - "one two - three - four" - == - "one two - three - four" - -------------------------------------------------------------------------------- -Long strings can be wrapped - plain string - Wrapped three times -------------------------------------------------------------------------------- -TestMain.cpp: -............................................................................... - -TestMain.cpp:: -PASSED: - CHECK( Text( testString, TextAttributes().setWidth( 6 ) ).toString() == "one\ntwo\nthree\nfour" ) -with expansion: - "one - two - three - four" - == - "one - two - three - four" - -TestMain.cpp:: -PASSED: - CHECK( Text( testString, TextAttributes().setWidth( 5 ) ).toString() == "one\ntwo\nthree\nfour" ) -with expansion: - "one - two - three - four" - == - "one - two - three - four" - -------------------------------------------------------------------------------- -Long strings can be wrapped - plain string - Short wrap -------------------------------------------------------------------------------- -TestMain.cpp: -............................................................................... - -TestMain.cpp:: -PASSED: - CHECK( Text( "abcdef", TextAttributes().setWidth( 4 ) ).toString() == "abc-\ndef" ) -with expansion: - "abc- - def" - == - "abc- - def" - -TestMain.cpp:: -PASSED: - CHECK( Text( "abcdefg", TextAttributes().setWidth( 4 ) ).toString() == "abc-\ndefg" ) -with expansion: - "abc- - defg" - == - "abc- - defg" - -TestMain.cpp:: -PASSED: - CHECK( Text( "abcdefgh", TextAttributes().setWidth( 4 ) ).toString() == "abc-\ndef-\ngh" ) -with expansion: - "abc- - def- - gh" - == - "abc- - def- - gh" - -TestMain.cpp:: -PASSED: - CHECK( Text( testString, TextAttributes().setWidth( 4 ) ).toString() == "one\ntwo\nthr-\nee\nfour" ) -with expansion: - "one - two - thr- - ee - four" - == - "one - two - thr- - ee - four" - -TestMain.cpp:: -PASSED: - CHECK( Text( testString, TextAttributes().setWidth( 3 ) ).toString() == "one\ntwo\nth-\nree\nfo-\nur" ) -with expansion: - "one - two - th- - ree - fo- - ur" - == - "one - two - th- - ree - fo- - ur" - -------------------------------------------------------------------------------- -Long strings can be wrapped - plain string - As container -------------------------------------------------------------------------------- -TestMain.cpp: -............................................................................... - -TestMain.cpp:: -PASSED: - REQUIRE( text.size() == 4 ) -with expansion: - 4 == 4 - -TestMain.cpp:: -PASSED: - CHECK( text[0] == "one" ) -with expansion: - "one" == "one" - -TestMain.cpp:: -PASSED: - CHECK( text[1] == "two" ) -with expansion: - "two" == "two" - -TestMain.cpp:: -PASSED: - CHECK( text[2] == "three" ) -with expansion: - "three" == "three" - -TestMain.cpp:: -PASSED: - CHECK( text[3] == "four" ) -with expansion: - "four" == "four" - -------------------------------------------------------------------------------- -Long strings can be wrapped - plain string - Indent first line differently -------------------------------------------------------------------------------- -TestMain.cpp: -............................................................................... - -TestMain.cpp:: -PASSED: - CHECK( text.toString() == " one two\n three\n four" ) -with expansion: - " one two - three - four" - == - " one two - three - four" - -------------------------------------------------------------------------------- -Long strings can be wrapped - With newlines - No wrapping -------------------------------------------------------------------------------- -TestMain.cpp: -............................................................................... - -TestMain.cpp:: -PASSED: - CHECK( Text( testString, TextAttributes().setWidth( 80 ) ).toString() == testString ) -with expansion: - "one two - three four" - == - "one two - three four" - -TestMain.cpp:: -PASSED: - CHECK( Text( testString, TextAttributes().setWidth( 18 ) ).toString() == testString ) -with expansion: - "one two - three four" - == - "one two - three four" - -TestMain.cpp:: -PASSED: - CHECK( Text( testString, TextAttributes().setWidth( 10 ) ).toString() == testString ) -with expansion: - "one two - three four" - == - "one two - three four" - -------------------------------------------------------------------------------- -Long strings can be wrapped - With newlines - Trailing newline -------------------------------------------------------------------------------- -TestMain.cpp: -............................................................................... - -TestMain.cpp:: -PASSED: - CHECK( Text( "abcdef\n", TextAttributes().setWidth( 10 ) ).toString() == "abcdef" ) -with expansion: - "abcdef" == "abcdef" - -TestMain.cpp:: -PASSED: - CHECK( Text( "abcdef", TextAttributes().setWidth( 6 ) ).toString() == "abcdef" ) -with expansion: - "abcdef" == "abcdef" - -TestMain.cpp:: -PASSED: - CHECK( Text( "abcdef\n", TextAttributes().setWidth( 6 ) ).toString() == "abcdef" ) -with expansion: - "abcdef" == "abcdef" - -TestMain.cpp:: -PASSED: - CHECK( Text( "abcdef\n", TextAttributes().setWidth( 5 ) ).toString() == "abcd-\nef" ) -with expansion: - "abcd- - ef" - == - "abcd- - ef" - -------------------------------------------------------------------------------- -Long strings can be wrapped - With newlines - Wrapped once -------------------------------------------------------------------------------- -TestMain.cpp: -............................................................................... - -TestMain.cpp:: -PASSED: - CHECK( Text( testString, TextAttributes().setWidth( 9 ) ).toString() == "one two\nthree\nfour" ) -with expansion: - "one two - three - four" - == - "one two - three - four" - -TestMain.cpp:: -PASSED: - CHECK( Text( testString, TextAttributes().setWidth( 8 ) ).toString() == "one two\nthree\nfour" ) -with expansion: - "one two - three - four" - == - "one two - three - four" - -TestMain.cpp:: -PASSED: - CHECK( Text( testString, TextAttributes().setWidth( 7 ) ).toString() == "one two\nthree\nfour" ) -with expansion: - "one two - three - four" - == - "one two - three - four" - -------------------------------------------------------------------------------- -Long strings can be wrapped - With newlines - Wrapped twice -------------------------------------------------------------------------------- -TestMain.cpp: -............................................................................... - -TestMain.cpp:: -PASSED: - CHECK( Text( testString, TextAttributes().setWidth( 6 ) ).toString() == "one\ntwo\nthree\nfour" ) -with expansion: - "one - two - three - four" - == - "one - two - three - four" - -------------------------------------------------------------------------------- -Long strings can be wrapped - With wrap-before/ after characters - No wrapping -------------------------------------------------------------------------------- -TestMain.cpp: -............................................................................... - -TestMain.cpp:: -PASSED: - CHECK( Text( testString, TextAttributes().setWidth( 80 ) ).toString() == testString ) -with expansion: - "one,two(three) " - == - "one,two(three) " - -TestMain.cpp:: -PASSED: - CHECK( Text( testString, TextAttributes().setWidth( 24 ) ).toString() == testString ) -with expansion: - "one,two(three) " - == - "one,two(three) " - -------------------------------------------------------------------------------- -Long strings can be wrapped - With wrap-before/ after characters - Wrap before -------------------------------------------------------------------------------- -TestMain.cpp: -............................................................................... - -TestMain.cpp:: -PASSED: - CHECK( Text( testString, TextAttributes().setWidth( 11 ) ).toString() == "one,two\n(three)\n" ) -with expansion: - "one,two - (three) - " - == - "one,two - (three) - " - -------------------------------------------------------------------------------- -Long strings can be wrapped - With wrap-before/ after characters - Wrap after -------------------------------------------------------------------------------- -TestMain.cpp: -............................................................................... - -TestMain.cpp:: -PASSED: - CHECK( Text( testString, TextAttributes().setWidth( 6 ) ).toString() == "one,\ntwo\n(thre-\ne)\n" ) -with expansion: - "one, - two - (thre- - e) - " - == - "one, - two - (thre- - e) - " - -TestMain.cpp:: -PASSED: - CHECK( Text( testString, TextAttributes().setWidth( 5 ) ).toString() == "one,\ntwo\n(thr-\nee)\n" ) -with expansion: - "one, - two - (thr- - ee) - " - == - "one, - two - (thr- - ee) - " - -TestMain.cpp:: -PASSED: - CHECK( Text( testString, TextAttributes().setWidth( 4 ) ).toString() == "one,\ntwo\n(th-\nree)\n" ) -with expansion: - "one, - two - (th- - ree) - " - == - "one, - two - (th- - ree) - " - -------------------------------------------------------------------------------- -Long text is truncated -------------------------------------------------------------------------------- -TestMain.cpp: -............................................................................... - -TestMain.cpp:: -PASSED: - CHECK_THAT( t.toString(), EndsWith( "... message truncated due to excessive size" ) ) -with expansion: - "***************************************************************************- - ***- - ****************************************************************************- - **- - ************************ - ****************************************************************************- - **- - ****************************************************************************- - **- - ************************ - ****************************************************************************- - **- - ****************************************************************************- - **- - ************************ - ****************************************************************************- - **- - ****************************************************************************- - **- - ************************ - ****************************************************************************- - **- - ****************************************************************************- - **- - ************************ - ****************************************************************************- - **- - ****************************************************************************- - **- - ************************ - ****************************************************************************- - **- - ****************************************************************************- - **- - ************************ - ****************************************************************************- - **- - ****************************************************************************- - **- - ************************ - ****************************************************************************- - **- - ****************************************************************************- - **- - ************************ - ****************************************************************************- - **- - ****************************************************************************- - **- - ************************ - ****************************************************************************- - **- - ****************************************************************************- - **- - ************************ - ****************************************************************************- - **- - ****************************************************************************- - **- - ************************ - ****************************************************************************- - **- - ****************************************************************************- - **- - ************************ - ****************************************************************************- - **- - ****************************************************************************- - **- - ************************ - ****************************************************************************- - **- - ****************************************************************************- - **- - ************************ - ****************************************************************************- - **- - ****************************************************************************- - **- - ************************ - ****************************************************************************- - **- - ****************************************************************************- - **- - ************************ - ****************************************************************************- - **- - ****************************************************************************- - **- - ************************ - ****************************************************************************- - **- - ****************************************************************************- - **- - ************************ - ****************************************************************************- - **- - ****************************************************************************- - **- - ************************ - ****************************************************************************- - **- - ****************************************************************************- - **- - ************************ - ****************************************************************************- - **- - ****************************************************************************- - **- - ************************ - ****************************************************************************- - **- - ****************************************************************************- - **- - ************************ - ****************************************************************************- - **- - ****************************************************************************- - **- - ************************ - ****************************************************************************- - **- - ****************************************************************************- - **- - ************************ - ****************************************************************************- - **- - ****************************************************************************- - **- - ************************ - ****************************************************************************- - **- - ****************************************************************************- - **- - ************************ - ****************************************************************************- - **- - ****************************************************************************- - **- - ************************ - ****************************************************************************- - **- - ****************************************************************************- - **- - ************************ - ****************************************************************************- - **- - ****************************************************************************- - **- - ************************ - ****************************************************************************- - **- - ****************************************************************************- - **- - ************************ - ****************************************************************************- - **- - ****************************************************************************- - **- - ************************ - ****************************************************************************- - **- - ****************************************************************************- - **- - ************************ - ****************************************************************************- - **- - ****************************************************************************- - **- - ************************ - ****************************************************************************- - **- - ****************************************************************************- - **- - ************************ - ****************************************************************************- - **- - ****************************************************************************- - **- - ************************ - ****************************************************************************- - **- - ****************************************************************************- - **- - ************************ - ****************************************************************************- - **- - ****************************************************************************- - **- - ************************ - ****************************************************************************- - **- - ****************************************************************************- - **- - ************************ - ****************************************************************************- - **- - ****************************************************************************- - **- - ************************ - ****************************************************************************- - **- - ****************************************************************************- - **- - ************************ - ****************************************************************************- - **- - ****************************************************************************- - **- - ************************ - ****************************************************************************- - **- - ****************************************************************************- - **- - ************************ - ****************************************************************************- - **- - ****************************************************************************- - **- - ************************ - ****************************************************************************- - **- - ****************************************************************************- - **- - ************************ - ****************************************************************************- - **- - ****************************************************************************- - **- - ************************ - ****************************************************************************- - **- - ****************************************************************************- - **- - ************************ - ****************************************************************************- - **- - ****************************************************************************- - **- - ************************ - ****************************************************************************- - **- - ****************************************************************************- - **- - ************************ - ****************************************************************************- - **- - ****************************************************************************- - **- - ************************ - ****************************************************************************- - **- - ****************************************************************************- - **- - ************************ - ****************************************************************************- - **- - ****************************************************************************- - **- - ************************ - ****************************************************************************- - **- - ****************************************************************************- - **- - ************************ - ****************************************************************************- - **- - ****************************************************************************- - **- - ************************ - ****************************************************************************- - **- - ****************************************************************************- - **- - ************************ - ****************************************************************************- - **- - ****************************************************************************- - **- - ************************ - ****************************************************************************- - **- - ****************************************************************************- - **- - ************************ - ****************************************************************************- - **- - ****************************************************************************- - **- - ************************ - ****************************************************************************- - **- - ****************************************************************************- - **- - ************************ - ****************************************************************************- - **- - ****************************************************************************- - **- - ************************ - ****************************************************************************- - **- - ****************************************************************************- - **- - ************************ - ****************************************************************************- - **- - ****************************************************************************- - **- - ************************ - ****************************************************************************- - **- - ****************************************************************************- - **- - ************************ - ****************************************************************************- - **- - ****************************************************************************- - **- - ************************ - ****************************************************************************- - **- - ****************************************************************************- - **- - ************************ - ****************************************************************************- - **- - ****************************************************************************- - **- - ************************ - ****************************************************************************- - **- - ****************************************************************************- - **- - ************************ - ****************************************************************************- - **- - ****************************************************************************- - **- - ************************ - ****************************************************************************- - **- - ****************************************************************************- - **- - ************************ - ****************************************************************************- - **- - ****************************************************************************- - **- - ************************ - ****************************************************************************- - **- - ****************************************************************************- - **- - ************************ - ****************************************************************************- - **- - ****************************************************************************- - **- - ************************ - ****************************************************************************- - **- - ****************************************************************************- - **- - ************************ - ****************************************************************************- - **- - ****************************************************************************- - **- - ************************ - ****************************************************************************- - **- - ****************************************************************************- - **- - ************************ - ****************************************************************************- - **- - ****************************************************************************- - **- - ************************ - ****************************************************************************- - **- - ****************************************************************************- - **- - ************************ - ****************************************************************************- - **- - ****************************************************************************- - **- - ************************ - ****************************************************************************- - **- - ****************************************************************************- - **- - ************************ - ****************************************************************************- - **- - ****************************************************************************- - **- - ************************ - ****************************************************************************- - **- - ****************************************************************************- - **- - ************************ - ****************************************************************************- - **- - ****************************************************************************- - **- - ************************ - ****************************************************************************- - **- - ****************************************************************************- - **- - ************************ - ****************************************************************************- - **- - ****************************************************************************- - **- - ************************ - ****************************************************************************- - **- - ****************************************************************************- - **- - ************************ - ****************************************************************************- - **- - ****************************************************************************- - **- - ************************ - ****************************************************************************- - **- - ****************************************************************************- - **- - ************************ - ****************************************************************************- - **- - ****************************************************************************- - **- - ************************ - ****************************************************************************- - **- - ****************************************************************************- - **- - ************************ - ****************************************************************************- - **- - ****************************************************************************- - **- - ************************ - ****************************************************************************- - **- - ****************************************************************************- - **- - ************************ - ****************************************************************************- - **- - ****************************************************************************- - **- - ************************ - ****************************************************************************- - **- - ****************************************************************************- - **- - ************************ - ****************************************************************************- - **- - ****************************************************************************- - **- - ************************ - ****************************************************************************- - **- - ****************************************************************************- - **- - ************************ - ****************************************************************************- - **- - ****************************************************************************- - **- - ************************ - ****************************************************************************- - **- - ****************************************************************************- - **- - ************************ - ****************************************************************************- - **- - ****************************************************************************- - **- - ************************ - ****************************************************************************- - **- - ****************************************************************************- - **- - ************************ - ****************************************************************************- - **- - ****************************************************************************- - **- - ************************ - ****************************************************************************- - **- - ****************************************************************************- - **- - ************************ - ****************************************************************************- - **- - ****************************************************************************- - **- - ************************ - ****************************************************************************- - **- - ****************************************************************************- - **- - ************************ - ****************************************************************************- - **- - ****************************************************************************- - **- - ************************ - ****************************************************************************- - **- - ****************************************************************************- - **- - ************************ - ****************************************************************************- - **- - ****************************************************************************- - **- - ************************ - ****************************************************************************- - **- - ****************************************************************************- - **- - ************************ - ****************************************************************************- - **- - ****************************************************************************- - **- - ************************ - ****************************************************************************- - **- - ****************************************************************************- - **- - ************************ - ****************************************************************************- - **- - ****************************************************************************- - **- - ************************ - ****************************************************************************- - **- - ****************************************************************************- - **- - ************************ - ****************************************************************************- - **- - ****************************************************************************- - **- - ************************ - ****************************************************************************- - **- - ****************************************************************************- - **- - ************************ - ****************************************************************************- - **- - ****************************************************************************- - **- - ************************ - ****************************************************************************- - **- - ****************************************************************************- - **- - ************************ - ****************************************************************************- - **- - ****************************************************************************- - **- - ************************ - ****************************************************************************- - **- - ****************************************************************************- - **- - ************************ - ****************************************************************************- - **- - ****************************************************************************- - **- - ************************ - ****************************************************************************- - **- - ****************************************************************************- - **- - ************************ - ****************************************************************************- - **- - ****************************************************************************- - **- - ************************ - ****************************************************************************- - **- - ****************************************************************************- - **- - ************************ - ****************************************************************************- - **- - ****************************************************************************- - **- - ************************ - ****************************************************************************- - **- - ****************************************************************************- - **- - ************************ - ****************************************************************************- - **- - ****************************************************************************- - **- - ************************ - ****************************************************************************- - **- - ****************************************************************************- - **- - ************************ - ****************************************************************************- - **- - ****************************************************************************- - **- - ************************ - ****************************************************************************- - **- - ****************************************************************************- - **- - ************************ - ****************************************************************************- - **- - ****************************************************************************- - **- - ************************ - ****************************************************************************- - **- - ****************************************************************************- - **- - ************************ - ****************************************************************************- - **- - ****************************************************************************- - **- - ************************ - ****************************************************************************- - **- - ****************************************************************************- - **- - ************************ - ****************************************************************************- - **- - ****************************************************************************- - **- - ************************ - ****************************************************************************- - **- - ****************************************************************************- - **- - ************************ - ****************************************************************************- - **- - ****************************************************************************- - **- - ************************ - ****************************************************************************- - **- - ****************************************************************************- - **- - ************************ - ****************************************************************************- - **- - ****************************************************************************- - **- - ************************ - ****************************************************************************- - **- - ****************************************************************************- - **- - ************************ - ****************************************************************************- - **- - ****************************************************************************- - **- - ************************ - ****************************************************************************- - **- - ****************************************************************************- - **- - ************************ - ****************************************************************************- - **- - ****************************************************************************- - **- - ************************ - ****************************************************************************- - **- - ****************************************************************************- - **- - ************************ - ****************************************************************************- - **- - ****************************************************************************- - **- - ************************ - ****************************************************************************- - **- - ****************************************************************************- - **- - ************************ - ****************************************************************************- - **- - ****************************************************************************- - **- - ************************ - ****************************************************************************- - **- - ****************************************************************************- - **- - ************************ - ****************************************************************************- - **- - ****************************************************************************- - **- - ************************ - ****************************************************************************- - **- - ****************************************************************************- - **- - ************************ - ****************************************************************************- - **- - ****************************************************************************- - **- - ************************ - ****************************************************************************- - **- - ****************************************************************************- - **- - ************************ - ****************************************************************************- - **- - ****************************************************************************- - **- - ************************ - ****************************************************************************- - **- - ****************************************************************************- - **- - ************************ - ****************************************************************************- - **- - ****************************************************************************- - **- - ************************ - ****************************************************************************- - **- - ****************************************************************************- - **- - ************************ - ****************************************************************************- - **- - ****************************************************************************- - **- - ************************ - ****************************************************************************- - **- - ****************************************************************************- - **- - ************************ - ****************************************************************************- - **- - ****************************************************************************- - **- - ************************ - ****************************************************************************- - **- - ****************************************************************************- - **- - ************************ - ****************************************************************************- - **- - ****************************************************************************- - **- - ************************ - ****************************************************************************- - **- - ****************************************************************************- - **- - ************************ - ****************************************************************************- - **- - ****************************************************************************- - **- - ************************ - ****************************************************************************- - **- - ****************************************************************************- - **- - ************************ - ****************************************************************************- - **- - ****************************************************************************- - **- - ************************ - ****************************************************************************- - **- - ****************************************************************************- - **- - ************************ - ****************************************************************************- - **- - ****************************************************************************- - **- - ************************ - ****************************************************************************- - **- - ****************************************************************************- - **- - ************************ - ****************************************************************************- - **- - ****************************************************************************- - **- - ************************ - ****************************************************************************- - **- - ****************************************************************************- - **- - ************************ - ****************************************************************************- - **- - ****************************************************************************- - **- - ************************ - ****************************************************************************- - **- - ****************************************************************************- - **- - ************************ - ****************************************************************************- - **- - ****************************************************************************- - **- - ************************ - ****************************************************************************- - **- - ****************************************************************************- - **- - ************************ - ****************************************************************************- - **- - ****************************************************************************- - **- - ************************ - ****************************************************************************- - **- - ****************************************************************************- - **- - ************************ - ****************************************************************************- - **- - ****************************************************************************- - **- - ************************ - ****************************************************************************- - **- - ****************************************************************************- - **- - ************************ - ****************************************************************************- - **- - ****************************************************************************- - **- - ************************ - ****************************************************************************- - **- - ****************************************************************************- - **- - ************************ - ****************************************************************************- - **- - ****************************************************************************- - **- - ************************ - ****************************************************************************- - **- - ****************************************************************************- - **- - ************************ - ****************************************************************************- - **- - ****************************************************************************- - **- - ************************ - ****************************************************************************- - **- - ****************************************************************************- - **- - ************************ - ****************************************************************************- - **- - ****************************************************************************- - **- - ************************ - ****************************************************************************- - **- - ****************************************************************************- - **- - ************************ - ****************************************************************************- - **- - ****************************************************************************- - **- - ************************ - ****************************************************************************- - **- - ****************************************************************************- - **- - ************************ - ****************************************************************************- - **- - ****************************************************************************- - **- - ************************ - ****************************************************************************- - **- - ****************************************************************************- - **- - ************************ - ****************************************************************************- - **- - ****************************************************************************- - **- - ************************ - ****************************************************************************- - **- - ****************************************************************************- - **- - ************************ - ****************************************************************************- - **- - ****************************************************************************- - **- - ************************ - ****************************************************************************- - **- - ****************************************************************************- - **- - ************************ - ****************************************************************************- - **- - ****************************************************************************- - **- - ************************ - ****************************************************************************- - **- - ****************************************************************************- - **- - ************************ - ****************************************************************************- - **- - ****************************************************************************- - **- - ************************ - ****************************************************************************- - **- - ****************************************************************************- - **- - ************************ - ****************************************************************************- - **- - ****************************************************************************- - **- - ************************ - ****************************************************************************- - **- - ****************************************************************************- - **- - ************************ - ****************************************************************************- - **- - ****************************************************************************- - **- - ************************ - ****************************************************************************- - **- - ****************************************************************************- - **- - ************************ - ****************************************************************************- - **- - ****************************************************************************- - **- - ************************ -... message truncated due to excessive size - ------------------------------------------------------------------------------- ManuallyRegistered ------------------------------------------------------------------------------- @@ -4627,11 +2152,13 @@ ExceptionTests.cpp: ExceptionTests.cpp:: PASSED: REQUIRE_THROWS_WITH( thisThrows(), "expected exception" ) +with expansion: + "expected exception" equals: "expected exception" ExceptionTests.cpp:: FAILED: REQUIRE_THROWS_WITH( thisThrows(), "should fail" ) with expansion: - expected exception + "expected exception" equals: "should fail" ------------------------------------------------------------------------------- Nice descriptive name @@ -4653,16 +2180,6 @@ ExceptionTests.cpp:: FAILED: due to unexpected exception with message: custom exception -------------------------------------------------------------------------------- -NotImplemented exception -------------------------------------------------------------------------------- -ExceptionTests.cpp: -............................................................................... - -ExceptionTests.cpp:: -PASSED: - REQUIRE_THROWS( thisFunctionNotImplemented( 7 ) ) - ------------------------------------------------------------------------------- Objects that evaluated in boolean contexts can be checked ------------------------------------------------------------------------------- @@ -4673,7 +2190,7 @@ TrickyTests.cpp:: PASSED: CHECK( True ) with expansion: - 1 + {?} TrickyTests.cpp:: PASSED: @@ -4685,19 +2202,7 @@ TrickyTests.cpp:: PASSED: CHECK_FALSE( False ) with expansion: - !0 - -------------------------------------------------------------------------------- -Operators at different namespace levels not hijacked by Koenig lookup -------------------------------------------------------------------------------- -TrickyTests.cpp: -............................................................................... - -TrickyTests.cpp:: -PASSED: - REQUIRE( 0x == o ) -with expansion: - 3221225472 (0x) == {?} + !{?} ------------------------------------------------------------------------------- Ordering comparison checks that should fail @@ -5967,7 +3472,7 @@ TrickyTests.cpp:: PASSED: REQUIRE( (std::pair( 1, 2 )) == aNicePair ) with expansion: - std::pair( 1, 2 ) == std::pair( 1, 2 ) + {?} == {?} ------------------------------------------------------------------------------- Pointers can be compared to null @@ -5979,13 +3484,13 @@ ConditionTests.cpp:: PASSED: REQUIRE( p == 0 ) with expansion: - NULL == 0 + 0 == 0 ConditionTests.cpp:: PASSED: REQUIRE( p == pNULL ) with expansion: - NULL == NULL + 0 == 0 ConditionTests.cpp:: PASSED: @@ -6023,20 +3528,6 @@ PASSED: with expansion: 0 != 0x -------------------------------------------------------------------------------- -Pointers can be converted to strings -------------------------------------------------------------------------------- -MessageTests.cpp: -............................................................................... - -MessageTests.cpp:: -warning: - actual address of p: 0x - -MessageTests.cpp:: -warning: - toString(p): 0x - ------------------------------------------------------------------------------- Process can be configured on command line empty args don't cause a crash @@ -6046,7 +3537,9 @@ TestMain.cpp: TestMain.cpp:: PASSED: - CHECK_NOTHROW( parser.parseInto( std::vector(), config ) ) + CHECK( result ) +with expansion: + {?} TestMain.cpp:: PASSED: @@ -6063,7 +3556,9 @@ TestMain.cpp: TestMain.cpp:: PASSED: - CHECK_NOTHROW( parseIntoConfig( argv, config ) ) + CHECK( result ) +with expansion: + {?} TestMain.cpp:: PASSED: @@ -6105,17 +3600,19 @@ TestMain.cpp: TestMain.cpp:: PASSED: - CHECK_NOTHROW( parseIntoConfig( argv, config ) ) + CHECK( result ) +with expansion: + {?} TestMain.cpp:: PASSED: - REQUIRE( cfg.testSpec().matches( fakeTestCase( "notIncluded" ) ) == false ) + REQUIRE( cfg.testSpec().matches(fakeTestCase("notIncluded")) == false ) with expansion: false == false TestMain.cpp:: PASSED: - REQUIRE( cfg.testSpec().matches( fakeTestCase( "test1" ) ) ) + REQUIRE( cfg.testSpec().matches(fakeTestCase("test1")) ) with expansion: true @@ -6129,17 +3626,19 @@ TestMain.cpp: TestMain.cpp:: PASSED: - CHECK_NOTHROW( parseIntoConfig( argv, config ) ) + CHECK( result ) +with expansion: + {?} TestMain.cpp:: PASSED: - REQUIRE( cfg.testSpec().matches( fakeTestCase( "test1" ) ) == false ) + REQUIRE( cfg.testSpec().matches(fakeTestCase("test1")) == false ) with expansion: false == false TestMain.cpp:: PASSED: - REQUIRE( cfg.testSpec().matches( fakeTestCase( "alwaysIncluded" ) ) ) + REQUIRE( cfg.testSpec().matches(fakeTestCase("alwaysIncluded")) ) with expansion: true @@ -6153,17 +3652,19 @@ TestMain.cpp: TestMain.cpp:: PASSED: - CHECK_NOTHROW( parseIntoConfig( argv, config ) ) + CHECK( result ) +with expansion: + {?} TestMain.cpp:: PASSED: - REQUIRE( cfg.testSpec().matches( fakeTestCase( "test1" ) ) == false ) + REQUIRE( cfg.testSpec().matches(fakeTestCase("test1")) == false ) with expansion: false == false TestMain.cpp:: PASSED: - REQUIRE( cfg.testSpec().matches( fakeTestCase( "alwaysIncluded" ) ) ) + REQUIRE( cfg.testSpec().matches(fakeTestCase("alwaysIncluded")) ) with expansion: true @@ -6177,7 +3678,9 @@ TestMain.cpp: TestMain.cpp:: PASSED: - CHECK_NOTHROW( parseIntoConfig( argv, config ) ) + CHECK( cli.parse({"test", "-r", "console"}) ) +with expansion: + {?} TestMain.cpp:: PASSED: @@ -6195,7 +3698,9 @@ TestMain.cpp: TestMain.cpp:: PASSED: - CHECK_NOTHROW( parseIntoConfig( argv, config ) ) + CHECK( cli.parse({"test", "-r", "xml"}) ) +with expansion: + {?} TestMain.cpp:: PASSED: @@ -6213,7 +3718,9 @@ TestMain.cpp: TestMain.cpp:: PASSED: - CHECK_NOTHROW( parseIntoConfig( argv, config ) ) + CHECK( cli.parse({"test", "-r", "xml", "-r", "junit"}) ) +with expansion: + {?} TestMain.cpp:: PASSED: @@ -6243,7 +3750,9 @@ TestMain.cpp: TestMain.cpp:: PASSED: - CHECK_NOTHROW( parseIntoConfig( argv, config ) ) + CHECK( cli.parse({"test", "--reporter", "junit"}) ) +with expansion: + {?} TestMain.cpp:: PASSED: @@ -6261,7 +3770,9 @@ TestMain.cpp: TestMain.cpp:: PASSED: - CHECK_NOTHROW( parseIntoConfig( argv, config ) ) + CHECK( cli.parse({"test", "-b"}) ) +with expansion: + {?} TestMain.cpp:: PASSED: @@ -6279,7 +3790,9 @@ TestMain.cpp: TestMain.cpp:: PASSED: - CHECK_NOTHROW( parseIntoConfig( argv, config ) ) + CHECK( cli.parse({"test", "--break"}) ) +with expansion: + {?} TestMain.cpp:: PASSED: @@ -6297,7 +3810,9 @@ TestMain.cpp: TestMain.cpp:: PASSED: - CHECK_NOTHROW( parseIntoConfig( argv, config ) ) + CHECK( cli.parse({"test", "-a"}) ) +with expansion: + {?} TestMain.cpp:: PASSED: @@ -6315,7 +3830,9 @@ TestMain.cpp: TestMain.cpp:: PASSED: - CHECK_NOTHROW( parseIntoConfig( argv, config ) ) + CHECK( cli.parse({"test", "-x", "2"}) ) +with expansion: + {?} TestMain.cpp:: PASSED: @@ -6323,21 +3840,6 @@ PASSED: with expansion: 2 == 2 -------------------------------------------------------------------------------- -Process can be configured on command line - abort - -x must be greater than zero -------------------------------------------------------------------------------- -TestMain.cpp: -............................................................................... - -TestMain.cpp:: -PASSED: - REQUIRE_THAT( parseIntoConfigAndReturnError( argv, config ), Contains( "greater than zero" ) ) -with expansion: - "Value after -x or --abortAfter must be greater than zero - - while parsing: (-x, --abortx )" contains: "greater than zero" - ------------------------------------------------------------------------------- Process can be configured on command line abort @@ -6348,10 +3850,16 @@ TestMain.cpp: TestMain.cpp:: PASSED: - REQUIRE_THAT( parseIntoConfigAndReturnError( argv, config ), Contains( "-x" ) ) + CHECK( !result ) with expansion: - "Unable to convert oops to destination type - - while parsing: (-x, --abortx )" contains: "-x" + true + +TestMain.cpp:: +PASSED: + REQUIRE_THAT( result.errorMessage(), Contains("convert") && Contains("oops") ) +with expansion: + "Unable to convert 'oops' to destination type" ( contains: "convert" and + contains: "oops" ) ------------------------------------------------------------------------------- Process can be configured on command line @@ -6363,13 +3871,15 @@ TestMain.cpp: TestMain.cpp:: PASSED: - CHECK_NOTHROW( parseIntoConfig( argv, config ) ) + CHECK( cli.parse({"test", "-e"}) ) +with expansion: + {?} TestMain.cpp:: PASSED: - REQUIRE( config.noThrow == true ) + REQUIRE( config.noThrow ) with expansion: - true == true + true ------------------------------------------------------------------------------- Process can be configured on command line @@ -6381,13 +3891,15 @@ TestMain.cpp: TestMain.cpp:: PASSED: - CHECK_NOTHROW( parseIntoConfig( argv, config ) ) + CHECK( cli.parse({"test", "--nothrow"}) ) +with expansion: + {?} TestMain.cpp:: PASSED: - REQUIRE( config.noThrow == true ) + REQUIRE( config.noThrow ) with expansion: - true == true + true ------------------------------------------------------------------------------- Process can be configured on command line @@ -6399,7 +3911,9 @@ TestMain.cpp: TestMain.cpp:: PASSED: - CHECK_NOTHROW( parseIntoConfig( argv, config ) ) + CHECK( cli.parse({"test", "-o", "filename.ext"}) ) +with expansion: + {?} TestMain.cpp:: PASSED: @@ -6417,7 +3931,9 @@ TestMain.cpp: TestMain.cpp:: PASSED: - CHECK_NOTHROW( parseIntoConfig( argv, config ) ) + CHECK( cli.parse({"test", "--out", "filename.ext"}) ) +with expansion: + {?} TestMain.cpp:: PASSED: @@ -6435,7 +3951,9 @@ TestMain.cpp: TestMain.cpp:: PASSED: - CHECK_NOTHROW( parseIntoConfig( argv, config ) ) + CHECK( cli.parse({"test", "-abe"}) ) +with expansion: + {?} TestMain.cpp:: PASSED: @@ -6465,7 +3983,9 @@ TestMain.cpp: TestMain.cpp:: PASSED: - CHECK_NOTHROW( parseIntoConfig( argv, config ) ) + CHECK( cli.parse({"test"}) ) +with expansion: + {?} TestMain.cpp:: PASSED: @@ -6483,7 +4003,9 @@ TestMain.cpp: TestMain.cpp:: PASSED: - CHECK_NOTHROW( parseIntoConfig( argv, config ) ) + CHECK( cli.parse({"test", "--use-colour", "auto"}) ) +with expansion: + {?} TestMain.cpp:: PASSED: @@ -6501,7 +4023,9 @@ TestMain.cpp: TestMain.cpp:: PASSED: - CHECK_NOTHROW( parseIntoConfig( argv, config ) ) + CHECK( cli.parse({"test", "--use-colour", "yes"}) ) +with expansion: + {?} TestMain.cpp:: PASSED: @@ -6519,7 +4043,9 @@ TestMain.cpp: TestMain.cpp:: PASSED: - CHECK_NOTHROW( parseIntoConfig( argv, config ) ) + CHECK( cli.parse({"test", "--use-colour", "no"}) ) +with expansion: + {?} TestMain.cpp:: PASSED: @@ -6537,7 +4063,16 @@ TestMain.cpp: TestMain.cpp:: PASSED: - REQUIRE_THROWS_WITH( parseIntoConfig( argv, config ), Contains( "colour mode must be one of" ) ) + CHECK( !result ) +with expansion: + true + +TestMain.cpp:: +PASSED: + CHECK_THAT( result.errorMessage(), Contains( "colour mode must be one of" ) ) +with expansion: + "colour mode must be one of: auto, yes or no. 'wrong' not recognised" + contains: "colour mode must be one of" ------------------------------------------------------------------------------- Reconstruction should be based on stringification: #914 @@ -6550,110 +4085,6 @@ DecompositionTests.cpp:: FAILED: with expansion: Hey, its truthy! -------------------------------------------------------------------------------- -SCOPED_INFO is reset for each loop -------------------------------------------------------------------------------- -MessageTests.cpp: -............................................................................... - -MessageTests.cpp:: -PASSED: - REQUIRE( i < 10 ) -with expansion: - 0 < 10 -with messages: - current counter 0 - i := 0 - -MessageTests.cpp:: -PASSED: - REQUIRE( i < 10 ) -with expansion: - 1 < 10 -with messages: - current counter 1 - i := 1 - -MessageTests.cpp:: -PASSED: - REQUIRE( i < 10 ) -with expansion: - 2 < 10 -with messages: - current counter 2 - i := 2 - -MessageTests.cpp:: -PASSED: - REQUIRE( i < 10 ) -with expansion: - 3 < 10 -with messages: - current counter 3 - i := 3 - -MessageTests.cpp:: -PASSED: - REQUIRE( i < 10 ) -with expansion: - 4 < 10 -with messages: - current counter 4 - i := 4 - -MessageTests.cpp:: -PASSED: - REQUIRE( i < 10 ) -with expansion: - 5 < 10 -with messages: - current counter 5 - i := 5 - -MessageTests.cpp:: -PASSED: - REQUIRE( i < 10 ) -with expansion: - 6 < 10 -with messages: - current counter 6 - i := 6 - -MessageTests.cpp:: -PASSED: - REQUIRE( i < 10 ) -with expansion: - 7 < 10 -with messages: - current counter 7 - i := 7 - -MessageTests.cpp:: -PASSED: - REQUIRE( i < 10 ) -with expansion: - 8 < 10 -with messages: - current counter 8 - i := 8 - -MessageTests.cpp:: -PASSED: - REQUIRE( i < 10 ) -with expansion: - 9 < 10 -with messages: - current counter 9 - i := 9 - -MessageTests.cpp:: FAILED: - REQUIRE( i < 10 ) -with expansion: - 10 < 10 -with messages: - current counter 10 - i := 10 - ------------------------------------------------------------------------------- SUCCEED counts as a test pass ------------------------------------------------------------------------------- @@ -6890,42 +4321,9 @@ with expansion: ApproxTests.cpp:: PASSED: - REQUIRE( 0 == Approx(0) ) + REQUIRE( INFINITY == Approx(INFINITY) ) with expansion: - 0 == Approx( 0.0 ) - -Write to std::cerr -------------------------------------------------------------------------------- -Standard error is reported and redirected - std::cerr -------------------------------------------------------------------------------- -MessageTests.cpp: -............................................................................... - - -No assertions in section 'std::cerr' - -Write to std::clog -------------------------------------------------------------------------------- -Standard error is reported and redirected - std::clog -------------------------------------------------------------------------------- -MessageTests.cpp: -............................................................................... - - -No assertions in section 'std::clog' - -Interleaved writes to error streams -------------------------------------------------------------------------------- -Standard error is reported and redirected - Interleaved writes to cerr and clog -------------------------------------------------------------------------------- -MessageTests.cpp: -............................................................................... - - -No assertions in section 'Interleaved writes to cerr and clog' + inff == Approx( inf ) Message from section one ------------------------------------------------------------------------------- @@ -6990,8 +4388,429 @@ PASSED: with expansion: "this string contains 'abc' as a substring" ends with: "substring" -hello -hello +------------------------------------------------------------------------------- +StringRef + Empty string +------------------------------------------------------------------------------- +StringRef.tests.cpp: +............................................................................... + +StringRef.tests.cpp:: +PASSED: + REQUIRE( empty.empty() ) +with expansion: + true + +StringRef.tests.cpp:: +PASSED: + REQUIRE( empty.size() == 0 ) +with expansion: + 0 == 0 + +StringRef.tests.cpp:: +PASSED: + REQUIRE( std::strcmp( empty.c_str(), "" ) == 0 ) +with expansion: + 0 == 0 + +------------------------------------------------------------------------------- +StringRef + From string literal +------------------------------------------------------------------------------- +StringRef.tests.cpp: +............................................................................... + +StringRef.tests.cpp:: +PASSED: + REQUIRE( s.empty() == false ) +with expansion: + false == false + +StringRef.tests.cpp:: +PASSED: + REQUIRE( s.size() == 5 ) +with expansion: + 5 == 5 + +StringRef.tests.cpp:: +PASSED: + REQUIRE( isSubstring( s ) == false ) +with expansion: + false == false + +StringRef.tests.cpp:: +PASSED: + REQUIRE( std::strcmp( rawChars, "hello" ) == 0 ) +with expansion: + 0 == 0 + +------------------------------------------------------------------------------- +StringRef + From string literal + c_str() does not cause copy +------------------------------------------------------------------------------- +StringRef.tests.cpp: +............................................................................... + +StringRef.tests.cpp:: +PASSED: + REQUIRE( isOwned( s ) == false ) +with expansion: + false == false + +StringRef.tests.cpp:: +PASSED: + REQUIRE( s.c_str() == rawChars ) +with expansion: + "hello" == "hello" + +StringRef.tests.cpp:: +PASSED: + REQUIRE( isOwned( s ) == false ) +with expansion: + false == false + +------------------------------------------------------------------------------- +StringRef + From sub-string +------------------------------------------------------------------------------- +StringRef.tests.cpp: +............................................................................... + +StringRef.tests.cpp:: +PASSED: + REQUIRE( original == "original" ) + +StringRef.tests.cpp:: FAILED: + REQUIRE( isSubstring( original ) ) +with expansion: + false + +------------------------------------------------------------------------------- +StringRef + Substrings + zero-based substring +------------------------------------------------------------------------------- +StringRef.tests.cpp: +............................................................................... + +StringRef.tests.cpp:: +PASSED: + REQUIRE( ss.empty() == false ) +with expansion: + false == false + +StringRef.tests.cpp:: +PASSED: + REQUIRE( ss.size() == 5 ) +with expansion: + 5 == 5 + +StringRef.tests.cpp:: +PASSED: + REQUIRE( std::strcmp( ss.c_str(), "hello" ) == 0 ) +with expansion: + 0 == 0 + +StringRef.tests.cpp:: +PASSED: + REQUIRE( ss == "hello" ) +with expansion: + hello == "hello" + +------------------------------------------------------------------------------- +StringRef + Substrings + c_str() causes copy +------------------------------------------------------------------------------- +StringRef.tests.cpp: +............................................................................... + +StringRef.tests.cpp:: +PASSED: + REQUIRE( isSubstring( ss ) ) +with expansion: + true + +StringRef.tests.cpp:: +PASSED: + REQUIRE( isOwned( ss ) == false ) +with expansion: + false == false + +StringRef.tests.cpp:: +PASSED: + REQUIRE( rawChars == data( s ) ) +with expansion: + "hello world!" == "hello world!" + +StringRef.tests.cpp:: +PASSED: + REQUIRE( ss.c_str() != rawChars ) +with expansion: + "hello" != "hello world!" + +StringRef.tests.cpp:: +PASSED: + REQUIRE( isSubstring( ss ) == false ) +with expansion: + false == false + +StringRef.tests.cpp:: +PASSED: + REQUIRE( isOwned( ss ) ) +with expansion: + true + +StringRef.tests.cpp:: +PASSED: + REQUIRE( data( ss ) != data( s ) ) +with expansion: + "hello" != "hello world!" + +------------------------------------------------------------------------------- +StringRef + Substrings + non-zero-based substring +------------------------------------------------------------------------------- +StringRef.tests.cpp: +............................................................................... + +StringRef.tests.cpp:: +PASSED: + REQUIRE( ss.size() == 6 ) +with expansion: + 6 == 6 + +StringRef.tests.cpp:: +PASSED: + REQUIRE( std::strcmp( ss.c_str(), "world!" ) == 0 ) +with expansion: + 0 == 0 + +------------------------------------------------------------------------------- +StringRef + Substrings + Pointer values of full refs should match +------------------------------------------------------------------------------- +StringRef.tests.cpp: +............................................................................... + +StringRef.tests.cpp:: +PASSED: + REQUIRE( s.c_str() == s2.c_str() ) +with expansion: + "hello world!" == "hello world!" + +------------------------------------------------------------------------------- +StringRef + Substrings + Pointer values of substring refs should not match +------------------------------------------------------------------------------- +StringRef.tests.cpp: +............................................................................... + +StringRef.tests.cpp:: +PASSED: + REQUIRE( s.c_str() != ss.c_str() ) +with expansion: + "hello world!" != "hello" + +------------------------------------------------------------------------------- +StringRef + Comparisons +------------------------------------------------------------------------------- +StringRef.tests.cpp: +............................................................................... + +StringRef.tests.cpp:: +PASSED: + REQUIRE( StringRef("hello") == StringRef("hello") ) +with expansion: + hello == hello + +StringRef.tests.cpp:: +PASSED: + REQUIRE( StringRef("hello") != StringRef("cello") ) +with expansion: + hello != cello + +------------------------------------------------------------------------------- +StringRef + from std::string + implicitly constructed +------------------------------------------------------------------------------- +StringRef.tests.cpp: +............................................................................... + +StringRef.tests.cpp:: +PASSED: + REQUIRE( sr == "a standard string" ) +with expansion: + a standard string == "a standard string" + +StringRef.tests.cpp:: +PASSED: + REQUIRE( sr.size() == stdStr.size() ) +with expansion: + 17 == 17 + +------------------------------------------------------------------------------- +StringRef + from std::string + explicitly constructed +------------------------------------------------------------------------------- +StringRef.tests.cpp: +............................................................................... + +StringRef.tests.cpp:: +PASSED: + REQUIRE( sr == "a standard string" ) +with expansion: + a standard string == "a standard string" + +StringRef.tests.cpp:: +PASSED: + REQUIRE( sr.size() == stdStr.size() ) +with expansion: + 17 == 17 + +------------------------------------------------------------------------------- +StringRef + from std::string + assigned +------------------------------------------------------------------------------- +StringRef.tests.cpp: +............................................................................... + +StringRef.tests.cpp:: +PASSED: + REQUIRE( sr == "a standard string" ) +with expansion: + a standard string == "a standard string" + +StringRef.tests.cpp:: +PASSED: + REQUIRE( sr.size() == stdStr.size() ) +with expansion: + 17 == 17 + +------------------------------------------------------------------------------- +StringRef + to std::string + implicitly constructed +------------------------------------------------------------------------------- +StringRef.tests.cpp: +............................................................................... + +StringRef.tests.cpp:: +PASSED: + REQUIRE( stdStr == "a stringref" ) +with expansion: + "a stringref" == "a stringref" + +StringRef.tests.cpp:: +PASSED: + REQUIRE( stdStr.size() == sr.size() ) +with expansion: + 11 == 11 + +------------------------------------------------------------------------------- +StringRef + to std::string + explicitly constructed +------------------------------------------------------------------------------- +StringRef.tests.cpp: +............................................................................... + +StringRef.tests.cpp:: +PASSED: + REQUIRE( stdStr == "a stringref" ) +with expansion: + "a stringref" == "a stringref" + +StringRef.tests.cpp:: +PASSED: + REQUIRE( stdStr.size() == sr.size() ) +with expansion: + 11 == 11 + +------------------------------------------------------------------------------- +StringRef + to std::string + assigned +------------------------------------------------------------------------------- +StringRef.tests.cpp: +............................................................................... + +StringRef.tests.cpp:: +PASSED: + REQUIRE( stdStr == "a stringref" ) +with expansion: + "a stringref" == "a stringref" + +StringRef.tests.cpp:: +PASSED: + REQUIRE( stdStr.size() == sr.size() ) +with expansion: + 11 == 11 + +------------------------------------------------------------------------------- +Stringifying std::chrono::duration helpers +------------------------------------------------------------------------------- +ToStringChrono.cpp: +............................................................................... + +ToStringChrono.cpp:: +PASSED: + REQUIRE( minute == seconds ) +with expansion: + 1 m == 60 s + +ToStringChrono.cpp:: +PASSED: + REQUIRE( hour != seconds ) +with expansion: + 1 h != 60 s + +ToStringChrono.cpp:: +PASSED: + REQUIRE( micro != milli ) +with expansion: + 1 us != 1 ms + +ToStringChrono.cpp:: +PASSED: + REQUIRE( nano != micro ) +with expansion: + 1 ns != 1 us + +------------------------------------------------------------------------------- +Stringifying std::chrono::duration with weird ratios +------------------------------------------------------------------------------- +ToStringChrono.cpp: +............................................................................... + +ToStringChrono.cpp:: +PASSED: + REQUIRE( half_minute != femto_second ) +with expansion: + 1 [30/1]s != 1 fs + +------------------------------------------------------------------------------- +Stringifying std::chrono::time_point +------------------------------------------------------------------------------- +ToStringChrono.cpp: +............................................................................... + +ToStringChrono.cpp:: +PASSED: + REQUIRE( now != later ) +with expansion: + {iso8601-timestamp} + != + {iso8601-timestamp} + ------------------------------------------------------------------------------- Tabs and newlines show in output ------------------------------------------------------------------------------- @@ -7021,37 +4840,33 @@ TagAliasTests.cpp:: PASSED: CHECK_THAT( what, Contains( "[@zzz]" ) ) with expansion: - "error: tag alias, "[@zzz]" already registered. - First seen at file:2 - Redefined at file:10 - " contains: "[@zzz]" + "error: tag alias, '[@zzz]' already registered. + First seen at: file:2 + Redefined at: file:10" contains: "[@zzz]" TagAliasTests.cpp:: PASSED: CHECK_THAT( what, Contains( "file" ) ) with expansion: - "error: tag alias, "[@zzz]" already registered. - First seen at file:2 - Redefined at file:10 - " contains: "file" + "error: tag alias, '[@zzz]' already registered. + First seen at: file:2 + Redefined at: file:10" contains: "file" TagAliasTests.cpp:: PASSED: CHECK_THAT( what, Contains( "2" ) ) with expansion: - "error: tag alias, "[@zzz]" already registered. - First seen at file:2 - Redefined at file:10 - " contains: "2" + "error: tag alias, '[@zzz]' already registered. + First seen at: file:2 + Redefined at: file:10" contains: "2" TagAliasTests.cpp:: PASSED: CHECK_THAT( what, Contains( "10" ) ) with expansion: - "error: tag alias, "[@zzz]" already registered. - First seen at file:2 - Redefined at file:10 - " contains: "10" + "error: tag alias, '[@zzz]' already registered. + First seen at: file:2 + Redefined at: file:10" contains: "10" ------------------------------------------------------------------------------- Tag alias can be registered against tag patterns @@ -7099,28 +4914,6 @@ PASSED: with expansion: 3221225472 (0x) == 3221225472 -------------------------------------------------------------------------------- -Text can be formatted using the Text class -------------------------------------------------------------------------------- -TestMain.cpp: -............................................................................... - -TestMain.cpp:: -PASSED: - CHECK( Text( "hi there" ).toString() == "hi there" ) -with expansion: - "hi there" == "hi there" - -TestMain.cpp:: -PASSED: - CHECK( Text( "hi there", narrow ).toString() == "hi\nthere" ) -with expansion: - "hi - there" - == - "hi - there" - ------------------------------------------------------------------------------- The NO_FAIL macro reports a failure but does not fail the test ------------------------------------------------------------------------------- @@ -8789,7 +6582,8 @@ MiscTests.cpp: MiscTests.cpp:: PASSED: -spanner------------------------------------------------------------------------------- +loose text artifact +------------------------------------------------------------------------------- just failure ------------------------------------------------------------------------------- MessageTests.cpp: @@ -8799,6 +6593,20 @@ MessageTests.cpp:: FAILED: explicitly with message: Previous info should not be seen +------------------------------------------------------------------------------- +long long +------------------------------------------------------------------------------- +MiscTests.cpp: +............................................................................... + +MiscTests.cpp:: +PASSED: + REQUIRE( l == std::numeric_limits::max() ) +with expansion: + 9223372036854775807 (0x) + == + 9223372036854775807 (0x) + ------------------------------------------------------------------------------- looped SECTION tests s1 @@ -8961,6 +6769,18 @@ PASSED: with expansion: "7" == "7" +------------------------------------------------------------------------------- +non-copyable objects +------------------------------------------------------------------------------- +TrickyTests.cpp: +............................................................................... + +TrickyTests.cpp:: +PASSED: + CHECK( ti == typeid(int) ) +with expansion: + {?} == {?} + ------------------------------------------------------------------------------- not allowed ------------------------------------------------------------------------------- @@ -8988,6 +6808,18 @@ PASSED: with expansion: {null string} == {null string} +------------------------------------------------------------------------------- +null_ptr +------------------------------------------------------------------------------- +TrickyTests.cpp: +............................................................................... + +TrickyTests.cpp:: +PASSED: + REQUIRE( ptr.get() == 0 ) +with expansion: + 0 == 0 + ------------------------------------------------------------------------------- pair > -> toString ------------------------------------------------------------------------------- @@ -8996,7 +6828,7 @@ ToStringPair.cpp: ToStringPair.cpp:: PASSED: - REQUIRE( Catch::toString( pair ) == "{ { 42, \"Arthur\" }, { \"Ford\", 24 } }" ) + REQUIRE( ::Catch::Detail::stringify( pair ) == "{ { 42, \"Arthur\" }, { \"Ford\", 24 } }" ) with expansion: "{ { 42, "Arthur" }, { "Ford", 24 } }" == @@ -9012,7 +6844,7 @@ TrickyTests.cpp:: PASSED: REQUIRE( p == 0 ) with expansion: - NULL == 0 + 0 == 0 ------------------------------------------------------------------------------- random SECTION tests @@ -9055,7 +6887,7 @@ TestMain.cpp: TestMain.cpp:: PASSED: - CHECK( replaceInPlace( letters, "b", "z" ) ) + CHECK( Catch::replaceInPlace( letters, "b", "z" ) ) with expansion: true @@ -9074,7 +6906,7 @@ TestMain.cpp: TestMain.cpp:: PASSED: - CHECK( replaceInPlace( letters, "c", "z" ) ) + CHECK( Catch::replaceInPlace( letters, "c", "z" ) ) with expansion: true @@ -9093,7 +6925,7 @@ TestMain.cpp: TestMain.cpp:: PASSED: - CHECK( replaceInPlace( letters, "a", "z" ) ) + CHECK( Catch::replaceInPlace( letters, "a", "z" ) ) with expansion: true @@ -9112,7 +6944,7 @@ TestMain.cpp: TestMain.cpp:: PASSED: - CHECK( replaceInPlace( letters, "g", "z" ) ) + CHECK( Catch::replaceInPlace( letters, "g", "z" ) ) with expansion: true @@ -9131,7 +6963,7 @@ TestMain.cpp: TestMain.cpp:: PASSED: - CHECK( replaceInPlace( letters, letters, "replaced" ) ) + CHECK( Catch::replaceInPlace( letters, letters, "replaced" ) ) with expansion: true @@ -9150,7 +6982,7 @@ TestMain.cpp: TestMain.cpp:: PASSED: - CHECK_FALSE( replaceInPlace( letters, "x", "z" ) ) + CHECK_FALSE( Catch::replaceInPlace( letters, "x", "z" ) ) with expansion: !false @@ -9169,7 +7001,7 @@ TestMain.cpp: TestMain.cpp:: PASSED: - CHECK( replaceInPlace( s, "'", "|'" ) ) + CHECK( Catch::replaceInPlace( s, "'", "|'" ) ) with expansion: true @@ -9210,7 +7042,7 @@ ToStringPair.cpp: ToStringPair.cpp:: PASSED: - REQUIRE( Catch::toString(value) == "{ 34, \"xyzzy\" }" ) + REQUIRE( ::Catch::Detail::stringify(value) == "{ 34, \"xyzzy\" }" ) with expansion: "{ 34, "xyzzy" }" == "{ 34, "xyzzy" }" @@ -9222,7 +7054,7 @@ ToStringPair.cpp: ToStringPair.cpp:: PASSED: - REQUIRE( Catch::toString( value ) == "{ 34, \"xyzzy\" }" ) + REQUIRE( ::Catch::Detail::stringify( value ) == "{ 34, \"xyzzy\" }" ) with expansion: "{ 34, "xyzzy" }" == "{ 34, "xyzzy" }" @@ -9234,7 +7066,7 @@ ToStringPair.cpp: ToStringPair.cpp:: PASSED: - REQUIRE( Catch::toString( pr ) == "{ { \"green\", 55 } }" ) + REQUIRE( ::Catch::Detail::stringify( pr ) == "{ { \"green\", 55 } }" ) with expansion: "{ { "green", 55 } }" == @@ -9251,6 +7083,48 @@ TrickyTests.cpp:: FAILED: with expansion: "first" == "second" +------------------------------------------------------------------------------- +stringify( has_maker ) +------------------------------------------------------------------------------- +ToStringWhich.cpp: +............................................................................... + +ToStringWhich.cpp:: +PASSED: + REQUIRE( ::Catch::Detail::stringify( item ) == "StringMaker" ) +with expansion: + "StringMaker" + == + "StringMaker" + +------------------------------------------------------------------------------- +stringify( has_maker_and_toString ) +------------------------------------------------------------------------------- +ToStringWhich.cpp: +............................................................................... + +ToStringWhich.cpp:: +PASSED: + REQUIRE( ::Catch::Detail::stringify( item ) == "StringMaker" ) +with expansion: + "StringMaker" + == + "StringMaker" + +------------------------------------------------------------------------------- +stringify( has_operator ) +------------------------------------------------------------------------------- +ToStringWhich.cpp: +............................................................................... + +ToStringWhich.cpp:: +PASSED: + REQUIRE( ::Catch::Detail::stringify( item ) == "operator<<( has_operator )" ) +with expansion: + "operator<<( has_operator )" + == + "operator<<( has_operator )" + ------------------------------------------------------------------------------- toString on const wchar_t const pointer returns the string contents ------------------------------------------------------------------------------- @@ -9299,48 +7173,6 @@ PASSED: with expansion: ""wide load"" == ""wide load"" -------------------------------------------------------------------------------- -toString( has_maker ) -------------------------------------------------------------------------------- -ToStringWhich.cpp: -............................................................................... - -ToStringWhich.cpp:: -PASSED: - REQUIRE( Catch::toString( item ) == "StringMaker" ) -with expansion: - "StringMaker" - == - "StringMaker" - -------------------------------------------------------------------------------- -toString( has_maker_and_toString ) -------------------------------------------------------------------------------- -ToStringWhich.cpp: -............................................................................... - -ToStringWhich.cpp:: -PASSED: - REQUIRE( Catch::toString( item ) == "toString( has_maker_and_toString )" ) -with expansion: - "toString( has_maker_and_toString )" - == - "toString( has_maker_and_toString )" - -------------------------------------------------------------------------------- -toString( has_toString ) -------------------------------------------------------------------------------- -ToStringWhich.cpp: -............................................................................... - -ToStringWhich.cpp:: -PASSED: - REQUIRE( Catch::toString( item ) == "toString( has_toString )" ) -with expansion: - "toString( has_toString )" - == - "toString( has_toString )" - ------------------------------------------------------------------------------- toString( vectors ToStringWhich.cpp:: PASSED: - REQUIRE( Catch::toString( v ) == "{ StringMaker }" ) + REQUIRE( ::Catch::Detail::stringify( v ) == "{ StringMaker }" ) with expansion: "{ StringMaker }" == "{ StringMaker }" +------------------------------------------------------------------------------- +toString(enum class w/operator<<) +------------------------------------------------------------------------------- +EnumToString.cpp: +............................................................................... + +EnumToString.cpp:: +PASSED: + CHECK( ::Catch::Detail::stringify(e0) == "E2/V0" ) +with expansion: + "E2/V0" == "E2/V0" + +EnumToString.cpp:: +PASSED: + CHECK( ::Catch::Detail::stringify(e1) == "E2/V1" ) +with expansion: + "E2/V1" == "E2/V1" + +EnumToString.cpp:: +PASSED: + CHECK( ::Catch::Detail::stringify(e3) == "Unknown enum value 10" ) +with expansion: + "Unknown enum value 10" + == + "Unknown enum value 10" + +------------------------------------------------------------------------------- +toString(enum class) +------------------------------------------------------------------------------- +EnumToString.cpp: +............................................................................... + +EnumToString.cpp:: FAILED: + CHECK( ::Catch::Detail::stringify(e0) == "0" ) +with expansion: + "{?}" == "0" + +EnumToString.cpp:: FAILED: + CHECK( ::Catch::Detail::stringify(e1) == "1" ) +with expansion: + "{?}" == "1" + ------------------------------------------------------------------------------- toString(enum w/operator<<) ------------------------------------------------------------------------------- @@ -9363,13 +7237,13 @@ EnumToString.cpp: EnumToString.cpp:: PASSED: - CHECK( Catch::toString(e0) == "E2{0}" ) + CHECK( ::Catch::Detail::stringify(e0) == "E2{0}" ) with expansion: "E2{0}" == "E2{0}" EnumToString.cpp:: PASSED: - CHECK( Catch::toString(e1) == "E2{1}" ) + CHECK( ::Catch::Detail::stringify(e1) == "E2{1}" ) with expansion: "E2{1}" == "E2{1}" @@ -9381,16 +7255,150 @@ EnumToString.cpp: EnumToString.cpp:: PASSED: - CHECK( Catch::toString(e0) == "0" ) + CHECK( ::Catch::Detail::stringify(e0) == "0" ) with expansion: "0" == "0" EnumToString.cpp:: PASSED: - CHECK( Catch::toString(e1) == "1" ) + CHECK( ::Catch::Detail::stringify(e1) == "1" ) with expansion: "1" == "1" +------------------------------------------------------------------------------- +tuple<> +------------------------------------------------------------------------------- +ToStringTuple.cpp: +............................................................................... + +ToStringTuple.cpp:: +PASSED: + CHECK( "{ }" == ::Catch::Detail::stringify(type{}) ) +with expansion: + "{ }" == "{ }" + +ToStringTuple.cpp:: +PASSED: + CHECK( "{ }" == ::Catch::Detail::stringify(value) ) +with expansion: + "{ }" == "{ }" + +------------------------------------------------------------------------------- +tuple +------------------------------------------------------------------------------- +ToStringTuple.cpp: +............................................................................... + +ToStringTuple.cpp:: +PASSED: + CHECK( "1.2f" == ::Catch::Detail::stringify(float(1.2)) ) +with expansion: + "1.2f" == "1.2f" + +ToStringTuple.cpp:: +PASSED: + CHECK( "{ 1.2f, 0 }" == ::Catch::Detail::stringify(type{1.2f,0}) ) +with expansion: + "{ 1.2f, 0 }" == "{ 1.2f, 0 }" + +------------------------------------------------------------------------------- +tuple +------------------------------------------------------------------------------- +ToStringTuple.cpp: +............................................................................... + +ToStringTuple.cpp:: +PASSED: + CHECK( "{ 0 }" == ::Catch::Detail::stringify(type{0}) ) +with expansion: + "{ 0 }" == "{ 0 }" + +------------------------------------------------------------------------------- +tuple<0,int,const char *> +------------------------------------------------------------------------------- +ToStringTuple.cpp: +............................................................................... + +ToStringTuple.cpp:: +PASSED: + CHECK( "{ 0, 42, \"Catch me\" }" == ::Catch::Detail::stringify(value) ) +with expansion: + "{ 0, 42, "Catch me" }" + == + "{ 0, 42, "Catch me" }" + +------------------------------------------------------------------------------- +tuple +------------------------------------------------------------------------------- +ToStringTuple.cpp: +............................................................................... + +ToStringTuple.cpp:: +PASSED: + CHECK( "{ \"hello\", \"world\" }" == ::Catch::Detail::stringify(type{"hello","world"}) ) +with expansion: + "{ "hello", "world" }" + == + "{ "hello", "world" }" + +------------------------------------------------------------------------------- +tuple,tuple<>,float> +------------------------------------------------------------------------------- +ToStringTuple.cpp: +............................................................................... + +ToStringTuple.cpp:: +PASSED: + CHECK( "{ { 42 }, { }, 1.2f }" == ::Catch::Detail::stringify(value) ) +with expansion: + "{ { 42 }, { }, 1.2f }" + == + "{ { 42 }, { }, 1.2f }" + +------------------------------------------------------------------------------- +vec> -> toString +------------------------------------------------------------------------------- +ToStringVector.cpp: +............................................................................... + +ToStringVector.cpp:: +PASSED: + REQUIRE( ::Catch::Detail::stringify(v) == "{ }" ) +with expansion: + "{ }" == "{ }" + +ToStringVector.cpp:: +PASSED: + REQUIRE( ::Catch::Detail::stringify(v) == "{ { \"hello\" }, { \"world\" } }" ) +with expansion: + "{ { "hello" }, { "world" } }" + == + "{ { "hello" }, { "world" } }" + +------------------------------------------------------------------------------- +vector -> toString +------------------------------------------------------------------------------- +ToStringVector.cpp: +............................................................................... + +ToStringVector.cpp:: +PASSED: + REQUIRE( ::Catch::Detail::stringify(vv) == "{ }" ) +with expansion: + "{ }" == "{ }" + +ToStringVector.cpp:: +PASSED: + REQUIRE( ::Catch::Detail::stringify(vv) == "{ 42 }" ) +with expansion: + "{ 42 }" == "{ 42 }" + +ToStringVector.cpp:: +PASSED: + REQUIRE( ::Catch::Detail::stringify(vv) == "{ 42, 250 }" ) +with expansion: + "{ 42, 250 }" == "{ 42, 250 }" + ------------------------------------------------------------------------------- vector -> toString ------------------------------------------------------------------------------- @@ -9399,19 +7407,19 @@ ToStringVector.cpp: ToStringVector.cpp:: PASSED: - REQUIRE( Catch::toString(vv) == "{ }" ) + REQUIRE( ::Catch::Detail::stringify(vv) == "{ }" ) with expansion: "{ }" == "{ }" ToStringVector.cpp:: PASSED: - REQUIRE( Catch::toString(vv) == "{ 42 }" ) + REQUIRE( ::Catch::Detail::stringify(vv) == "{ 42 }" ) with expansion: "{ 42 }" == "{ 42 }" ToStringVector.cpp:: PASSED: - REQUIRE( Catch::toString(vv) == "{ 42, 250 }" ) + REQUIRE( ::Catch::Detail::stringify(vv) == "{ 42, 250 }" ) with expansion: "{ 42, 250 }" == "{ 42, 250 }" @@ -9423,19 +7431,19 @@ ToStringVector.cpp: ToStringVector.cpp:: PASSED: - REQUIRE( Catch::toString(vv) == "{ }" ) + REQUIRE( ::Catch::Detail::stringify(vv) == "{ }" ) with expansion: "{ }" == "{ }" ToStringVector.cpp:: PASSED: - REQUIRE( Catch::toString(vv) == "{ \"hello\" }" ) + REQUIRE( ::Catch::Detail::stringify(vv) == "{ \"hello\" }" ) with expansion: "{ "hello" }" == "{ "hello" }" ToStringVector.cpp:: PASSED: - REQUIRE( Catch::toString(vv) == "{ \"hello\", \"world\" }" ) + REQUIRE( ::Catch::Detail::stringify(vv) == "{ \"hello\", \"world\" }" ) with expansion: "{ "hello", "world" }" == @@ -9624,6 +7632,6 @@ MiscTests.cpp:: PASSED: =============================================================================== -test cases: 171 | 120 passed | 47 failed | 4 failed as expected -assertions: 985 | 871 passed | 93 failed | 21 failed as expected +test cases: 185 | 132 passed | 49 failed | 4 failed as expected +assertions: 903 | 783 passed | 99 failed | 21 failed as expected diff --git a/projects/SelfTest/Baselines/console.swa4.approved.txt b/projects/SelfTest/Baselines/console.swa4.approved.txt index a831b8b5..eacf34e1 100644 --- a/projects/SelfTest/Baselines/console.swa4.approved.txt +++ b/projects/SelfTest/Baselines/console.swa4.approved.txt @@ -14,6 +14,25 @@ PASSED: with message: yay +------------------------------------------------------------------------------- +#1005: Comparing pointer to int and long (NULL can be either on various + systems) +------------------------------------------------------------------------------- +DecompositionTests.cpp: +............................................................................... + +DecompositionTests.cpp:: +PASSED: + REQUIRE( fptr == 0 ) +with expansion: + 0 == 0 + +DecompositionTests.cpp:: +PASSED: + REQUIRE( fptr == 0l ) +with expansion: + 0 == 0 + ------------------------------------------------------------------------------- #748 - captures with unexpected exceptions outside assertions @@ -88,7 +107,7 @@ PASSED: CompilationTests.cpp:: PASSED: - CHECK_THROWS_AS( throws_int(true), const int& ) + CHECK_THROWS_AS( throws_int(true), int ) CompilationTests.cpp:: PASSED: @@ -123,6 +142,20 @@ PASSED: with expansion: 1 == 1 +------------------------------------------------------------------------------- +#872 +------------------------------------------------------------------------------- +CompilationTests.cpp: +............................................................................... + +CompilationTests.cpp:: +PASSED: + REQUIRE( x == 4 ) +with expansion: + {?} == 4 +with message: + dummy := 0 + ------------------------------------------------------------------------------- #961 -- Dynamically created sections should all be reported Looped section 0 @@ -206,6 +239,6 @@ with expansion: !true =============================================================================== -test cases: 7 | 4 passed | 1 failed | 2 failed as expected -assertions: 23 | 16 passed | 4 failed | 3 failed as expected +test cases: 9 | 6 passed | 1 failed | 2 failed as expected +assertions: 26 | 19 passed | 4 failed | 3 failed as expected diff --git a/projects/SelfTest/Baselines/junit.sw.approved.txt b/projects/SelfTest/Baselines/junit.sw.approved.txt index e6fa4187..0aba9725 100644 --- a/projects/SelfTest/Baselines/junit.sw.approved.txt +++ b/projects/SelfTest/Baselines/junit.sw.approved.txt @@ -1,35 +1,38 @@ - - - - + + + + + expected exception answer := 42 ExceptionTests.cpp: - + expected exception answer := 42 ExceptionTests.cpp: - - - - + + + + MiscTests.cpp: - - - - - - + + + + + + + ConditionTests.cpp: @@ -55,32 +58,32 @@ ConditionTests.cpp: ConditionTests.cpp: - - - - - - - + + + + + + + ClassTests.cpp: - - + + ClassTests.cpp: - - + + to infinity and beyond MiscTests.cpp: - - + + TrickyTests.cpp: @@ -88,67 +91,70 @@ TrickyTests.cpp: TrickyTests.cpp: - - - - + + + unexpected exception ExceptionTests.cpp: - - - - - - - - - - - - - - - - - - - - - - - + + + + + + + + + + + + + + + + + + + + + + + + + + MatchersTests.cpp: - + custom exception - not std ExceptionTests.cpp: - + custom exception - not std ExceptionTests.cpp: - + custom std exception ExceptionTests.cpp: - - + + MatchersTests.cpp: - + + ConditionTests.cpp: @@ -189,17 +195,44 @@ ConditionTests.cpp: ConditionTests.cpp: - - - + + + MatchersTests.cpp: - - - - + + +MatchersTests.cpp: + + +MatchersTests.cpp: + + + + +Unknown exception +MatchersTests.cpp: + + +Unknown exception +MatchersTests.cpp: + + + + +MatchersTests.cpp: + + +MatchersTests.cpp: + + + + + + + expected exception ExceptionTests.cpp: @@ -212,36 +245,34 @@ expected exception ExceptionTests.cpp: - + This is a failure MessageTests.cpp: - + MessageTests.cpp: - + This is a failure MessageTests.cpp: - - - - - - + + + + this message should be logged so should this MessageTests.cpp: - + this message may be logged later this message should be logged @@ -254,7 +285,14 @@ and this, but later MessageTests.cpp: - + + +current counter 10 +i := 10 +MessageTests.cpp: + + + ConditionTests.cpp: @@ -271,54 +309,37 @@ ConditionTests.cpp: ConditionTests.cpp: - - - - - - - - - - - - - - - - - - - - - - + + + + + + + MatchersTests.cpp: - - + + MatchersTests.cpp: - - + + ExceptionTests.cpp: - - + + custom exception ExceptionTests.cpp: - - - - + + ConditionTests.cpp: @@ -377,102 +398,93 @@ ConditionTests.cpp: ConditionTests.cpp: - - + + Message from section one MessageTests.cpp: - + Message from section two MessageTests.cpp: - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + DecompositionTests.cpp: - - -current counter 10 -i := 10 -MessageTests.cpp: - - - - - - - - - - - - - - + + + + + + + + + + + + A string sent directly to stdout @@ -480,33 +492,43 @@ A string sent directly to stdout A string sent directly to stderr - - - -Write to std::cerr -Write to std::clog -Interleaved writes to error streams - - - + + Message from section one Message from section two - + MatchersTests.cpp: - - - -hello -hello - + + + + + + +StringRef.tests.cpp: + - + + + + + + + + + + + + + + + + - - - - - - - - - - - - - - - - - - - - - - - - + + + + + + + + + + + + + + + + + + + + + + + + 3.14 ExceptionTests.cpp: - - - - - - + + + + + + MatchersTests.cpp: @@ -561,7 +582,7 @@ MatchersTests.cpp: MatchersTests.cpp: - + MatchersTests.cpp: @@ -569,7 +590,7 @@ MatchersTests.cpp: MatchersTests.cpp: - + MatchersTests.cpp: @@ -583,55 +604,55 @@ MatchersTests.cpp: MatchersTests.cpp: - - + + unexpected exception ExceptionTests.cpp: - + expected exception ExceptionTests.cpp: - + expected exception ExceptionTests.cpp: - + expected exception ExceptionTests.cpp: - + unexpected exception ExceptionTests.cpp: - - - - - - - - - - - - - - - - - - + + + + + + + + + + + + + + + + + + MiscTests.cpp: @@ -639,8 +660,8 @@ MiscTests.cpp: MiscTests.cpp: - - + + MiscTests.cpp: @@ -648,23 +669,24 @@ MiscTests.cpp: MiscTests.cpp: - - - - - - + + + + + + Previous info should not be seen MessageTests.cpp: - + + MiscTests.cpp: - + Testing if fib[0] (1) is even MiscTests.cpp: @@ -690,82 +712,96 @@ Testing if fib[7] (21) is even MiscTests.cpp: - + MiscTests.cpp: - - - - - - - - - - - - - - - - - - - + + + + + + + + + + + + + + + + + + + + + 3 MiscTests.cpp: - + hi i := 7 MessageTests.cpp: - - - - + + + + TrickyTests.cpp: - - - - - - - - - - - - - - - - - - - - + + + + + + + + + + + +EnumToString.cpp: + + +EnumToString.cpp: + + + + + + + + + + + + + + + + + + + + + + A string sent directly to stdout Message from section one Message from section two -hello -hello A string sent directly to stderr -Write to std::cerr -Write to std::clog -Interleaved writes to error streams diff --git a/projects/SelfTest/Baselines/xml.sw.approved.txt b/projects/SelfTest/Baselines/xml.sw.approved.txt index 7a9684dd..d3ef33c9 100644 --- a/projects/SelfTest/Baselines/xml.sw.approved.txt +++ b/projects/SelfTest/Baselines/xml.sw.approved.txt @@ -4,7 +4,26 @@ - + + + + fptr == 0 + + + 0 == 0 + + + + + fptr == 0l + + + 0 == 0 + + + + +
answer := 42 @@ -85,10 +104,10 @@ - throws_int(true), const int& + throws_int(true), int - throws_int(true), const int& + throws_int(true), int @@ -117,7 +136,7 @@ - + f() == 0 @@ -136,7 +155,21 @@ - + + + dummy := 0 + + + + x == 4 + + + {?} == 4 + + + + +
@@ -154,7 +187,7 @@
- + false != false @@ -370,7 +403,7 @@ - + s == "world" @@ -392,7 +425,7 @@ - + m_a == 2 @@ -414,7 +447,7 @@ - +
@@ -426,7 +459,7 @@ - + &o1 == &o2 @@ -496,17 +529,6 @@ - - - - testStringForMatching(), AllOf( Catch::Contains( "string" ), Catch::Contains( "abc" ) ) - - - "this string contains 'abc' as a substring" ( contains: "string" and contains: "abc" ) - - - - @@ -529,7 +551,7 @@ - + 1 == 1 @@ -554,21 +576,61 @@ - - + + - testStringForMatching(), AnyOf( Catch::Contains( "string" ), Catch::Contains( "not there" ) ) + Approx(0).margin(0) - "this string contains 'abc' as a substring" ( contains: "string" or contains: "not there" ) + Approx(0).margin(0) - + - testStringForMatching(), AnyOf( Catch::Contains( "not there" ), Catch::Contains( "string" ) ) + Approx(0).margin(1234656) - "this string contains 'abc' as a substring" ( contains: "not there" or contains: "string" ) + Approx(0).margin(1234656) + + + + + Approx(0).margin(-2), std::domain_error + + + Approx(0).margin(-2), std::domain_error + + + + + Approx(0).epsilon(0) + + + Approx(0).epsilon(0) + + + + + Approx(0).epsilon(1) + + + Approx(0).epsilon(1) + + + + + Approx(0).epsilon(-0.001), std::domain_error + + + Approx(0).epsilon(-0.001), std::domain_error + + + + + Approx(0).epsilon(1.0001), std::domain_error + + + Approx(0).epsilon(1.0001), std::domain_error @@ -711,7 +773,7 @@ - 0 == Approx( dSmall ).epsilon( 0.001 ) + 0 == Approx( dSmall ).margin( 0.001 ) 0 == Approx( 0.00001 ) @@ -798,6 +860,36 @@
+ + + + INFINITY == Approx(INFINITY) + + + inff == Approx( inf ) + + + + + + + + y.v == 0 + + + 0 == 0 + + + + + 0 == y.v + + + 0 == 0 + + + +
@@ -953,6 +1045,105 @@
+ + + + std::vector<constructor_throws>{constructor_throws{}, constructor_throws{}} + + + std::vector<constructor_throws>{constructor_throws{}, constructor_throws{}} + + + + + std::vector<constructor_throws>{constructor_throws{}, constructor_throws{}} + + + std::vector<constructor_throws>{constructor_throws{}, constructor_throws{}} + + + + + std::vector<int>{1, 2, 3} == std::vector<int>{1, 2, 3} + + + std::vector<int>{1, 2, 3} == std::vector<int>{1, 2, 3} + + + + + std::vector<int>{1, 2, 3} == std::vector<int>{1, 2, 3} + + + std::vector<int>{1, 2, 3} == std::vector<int>{1, 2, 3} + + + + + std::vector<int>{1, 2} == std::vector<int>{1, 2} + + + { 1, 2 } == { 1, 2 } + + + + + std::vector<int>{1, 2} == std::vector<int>{1, 2} + + + { 1, 2 } == { 1, 2 } + + + + + !(std::vector<int>{1, 2} == std::vector<int>{1, 2, 3}) + + + !({ 1, 2 } == { 1, 2, 3 }) + + + + + !(std::vector<int>{1, 2} == std::vector<int>{1, 2, 3}) + + + !({ 1, 2 } == { 1, 2, 3 }) + + + + + std::vector<int>{1, 2} == std::vector<int>{1, 2} + + + { 1, 2 } == { 1, 2 } + + + + + std::vector<int>{1, 2} == std::vector<int>{1, 2} + + + { 1, 2 } == { 1, 2 } + + + + + true + + + true + + + + + std::vector<int>{1, 2} == std::vector<int>{1, 2} + + + { 1, 2 } == { 1, 2 } + + + + @@ -972,15 +1163,101 @@ - - + + - m == &S::f + td == Approx(10.0) - 0x -== -0x + StrongDoubleTypedef(10) == Approx( 10.0 ) + + + + + Approx(10.0) == td + + + Approx( 10.0 ) == StrongDoubleTypedef(10) + + + + + td != Approx(11.0) + + + StrongDoubleTypedef(10) != Approx( 11.0 ) + + + + + Approx(11.0) != td + + + Approx( 11.0 ) != StrongDoubleTypedef(10) + + + + + td <= Approx(10.0) + + + StrongDoubleTypedef(10) <= Approx( 10.0 ) + + + + + td <= Approx(11.0) + + + StrongDoubleTypedef(10) <= Approx( 11.0 ) + + + + + Approx(10.0) <= td + + + Approx( 10.0 ) <= StrongDoubleTypedef(10) + + + + + Approx(9.0) <= td + + + Approx( 9.0 ) <= StrongDoubleTypedef(10) + + + + + td >= Approx(9.0) + + + StrongDoubleTypedef(10) >= Approx( 9.0 ) + + + + + td >= Approx(10.0) + + + StrongDoubleTypedef(10) >= Approx( 10.0 ) + + + + + Approx(10.0) >= td + + + Approx( 10.0 ) >= StrongDoubleTypedef(10) + + + + + Approx(11.0) >= td + + + Approx( 11.0 ) >= StrongDoubleTypedef(10) @@ -1146,17 +1423,15 @@ - (std::numeric_limits<unsigned long>::max)() > ul + (std::numeric_limits<uint32_t>::max)() > ul - 18446744073709551615 (0x) -> -4 + 4294967295 (0x) > 4 - + testStringForMatching(), Contains( "not there" ) @@ -1167,7 +1442,7 @@ - + throwCustom() @@ -1181,7 +1456,7 @@ - + throwCustom(), std::exception @@ -1195,24 +1470,32 @@ - + custom std exception - - + + - t == 1u + 101.000001 != Approx(100).epsilon(0.01) - {?} == 1 + 101.000001 != Approx( 100.0 ) + + + + + std::pow(10, -5) != Approx(std::pow(10, -7)) + + + 0.00001 != Approx( 0.0000001 ) - + testStringForMatching(), EndsWith( "this" ) @@ -1223,7 +1506,18 @@ - + + + + 101.01 != Approx(100).epsilon(0.01) + + + 101.01 != Approx( 100.0 ) + + + + + data.int_seven == 6 @@ -1400,7 +1694,7 @@ - + testStringForMatching(), Equals( "something else" ) @@ -1411,6 +1705,91 @@ + +
+ + + doesNotThrow(), SpecialException, ExceptionMatcher{ 1 } + + + doesNotThrow(), SpecialException, ExceptionMatcher{ 1 } + + + + + doesNotThrow(), SpecialException, ExceptionMatcher{ 1 } + + + doesNotThrow(), SpecialException, ExceptionMatcher{ 1 } + + + +
+
+ + + throwsAsInt(1), SpecialException, ExceptionMatcher{ 1 } + + + throwsAsInt(1), SpecialException, ExceptionMatcher{ 1 } + + + Unknown exception + + + + + throwsAsInt(1), SpecialException, ExceptionMatcher{ 1 } + + + throwsAsInt(1), SpecialException, ExceptionMatcher{ 1 } + + + Unknown exception + + + +
+
+ + + throws(3), SpecialException, ExceptionMatcher{ 1 } + + + {?} special exception has value of 1 + + + + + throws(4), SpecialException, ExceptionMatcher{ 1 } + + + {?} special exception has value of 1 + + + +
+ +
+ + + + throws(1), SpecialException, ExceptionMatcher{ 1 } + + + {?} special exception has value of 1 + + + + + throws(2), SpecialException, ExceptionMatcher{ 2 } + + + {?} special exception has value of 2 + + + +
@@ -1418,7 +1797,7 @@ thisThrows(), "expected exception" - thisThrows(), "expected exception" + "expected exception" equals: "expected exception" @@ -1429,7 +1808,7 @@ thisThrows(), Equals( "expecteD Exception", Catch::CaseSensitive::No ) - thisThrows(), Equals( "expecteD Exception", Catch::CaseSensitive::No ) + "expected exception" equals: "expected exception" (case insensitive) @@ -1440,7 +1819,7 @@ thisThrows(), StartsWith( "expected" ) - thisThrows(), StartsWith( "expected" ) + "expected exception" starts with: "expected" @@ -1448,7 +1827,7 @@ thisThrows(), EndsWith( "exception" ) - thisThrows(), EndsWith( "exception" ) + "expected exception" ends with: "exception" @@ -1456,7 +1835,7 @@ thisThrows(), Contains( "except" ) - thisThrows(), Contains( "except" ) + "expected exception" contains: "except" @@ -1464,14 +1843,14 @@ thisThrows(), Contains( "exCept", Catch::CaseSensitive::No ) - thisThrows(), Contains( "exCept", Catch::CaseSensitive::No ) + "expected exception" contains: "except" (case insensitive)
- + thisThrows(), std::string @@ -1504,17 +1883,17 @@ - + This is a failure - + - + This is a failure @@ -1566,1180 +1945,6 @@
- - - - i->first == i->second-1 - - - 0 == 0 - - - - - i->first == i->second-1 - - - 2 == 2 - - - - - - - - multiply( i, 2 ) == i*2 - - - 2 == 2 - - - - - multiply( j, 2 ) == j*2 - - - 200 == 200 - - - - - multiply( i, 2 ) == i*2 - - - 4 == 4 - - - - - multiply( j, 2 ) == j*2 - - - 200 == 200 - - - - - multiply( i, 2 ) == i*2 - - - 6 == 6 - - - - - multiply( j, 2 ) == j*2 - - - 200 == 200 - - - - - multiply( i, 2 ) == i*2 - - - 8 == 8 - - - - - multiply( j, 2 ) == j*2 - - - 200 == 200 - - - - - multiply( i, 2 ) == i*2 - - - 10 == 10 - - - - - multiply( j, 2 ) == j*2 - - - 200 == 200 - - - - - multiply( i, 2 ) == i*2 - - - 30 == 30 - - - - - multiply( j, 2 ) == j*2 - - - 200 == 200 - - - - - multiply( i, 2 ) == i*2 - - - 40 == 40 - - - - - multiply( j, 2 ) == j*2 - - - 200 == 200 - - - - - multiply( i, 2 ) == i*2 - - - 42 == 42 - - - - - multiply( j, 2 ) == j*2 - - - 200 == 200 - - - - - multiply( i, 2 ) == i*2 - - - 72 == 72 - - - - - multiply( j, 2 ) == j*2 - - - 200 == 200 - - - - - multiply( i, 2 ) == i*2 - - - 2 == 2 - - - - - multiply( j, 2 ) == j*2 - - - 202 == 202 - - - - - multiply( i, 2 ) == i*2 - - - 4 == 4 - - - - - multiply( j, 2 ) == j*2 - - - 202 == 202 - - - - - multiply( i, 2 ) == i*2 - - - 6 == 6 - - - - - multiply( j, 2 ) == j*2 - - - 202 == 202 - - - - - multiply( i, 2 ) == i*2 - - - 8 == 8 - - - - - multiply( j, 2 ) == j*2 - - - 202 == 202 - - - - - multiply( i, 2 ) == i*2 - - - 10 == 10 - - - - - multiply( j, 2 ) == j*2 - - - 202 == 202 - - - - - multiply( i, 2 ) == i*2 - - - 30 == 30 - - - - - multiply( j, 2 ) == j*2 - - - 202 == 202 - - - - - multiply( i, 2 ) == i*2 - - - 40 == 40 - - - - - multiply( j, 2 ) == j*2 - - - 202 == 202 - - - - - multiply( i, 2 ) == i*2 - - - 42 == 42 - - - - - multiply( j, 2 ) == j*2 - - - 202 == 202 - - - - - multiply( i, 2 ) == i*2 - - - 72 == 72 - - - - - multiply( j, 2 ) == j*2 - - - 202 == 202 - - - - - multiply( i, 2 ) == i*2 - - - 2 == 2 - - - - - multiply( j, 2 ) == j*2 - - - 204 == 204 - - - - - multiply( i, 2 ) == i*2 - - - 4 == 4 - - - - - multiply( j, 2 ) == j*2 - - - 204 == 204 - - - - - multiply( i, 2 ) == i*2 - - - 6 == 6 - - - - - multiply( j, 2 ) == j*2 - - - 204 == 204 - - - - - multiply( i, 2 ) == i*2 - - - 8 == 8 - - - - - multiply( j, 2 ) == j*2 - - - 204 == 204 - - - - - multiply( i, 2 ) == i*2 - - - 10 == 10 - - - - - multiply( j, 2 ) == j*2 - - - 204 == 204 - - - - - multiply( i, 2 ) == i*2 - - - 30 == 30 - - - - - multiply( j, 2 ) == j*2 - - - 204 == 204 - - - - - multiply( i, 2 ) == i*2 - - - 40 == 40 - - - - - multiply( j, 2 ) == j*2 - - - 204 == 204 - - - - - multiply( i, 2 ) == i*2 - - - 42 == 42 - - - - - multiply( j, 2 ) == j*2 - - - 204 == 204 - - - - - multiply( i, 2 ) == i*2 - - - 72 == 72 - - - - - multiply( j, 2 ) == j*2 - - - 204 == 204 - - - - - multiply( i, 2 ) == i*2 - - - 2 == 2 - - - - - multiply( j, 2 ) == j*2 - - - 206 == 206 - - - - - multiply( i, 2 ) == i*2 - - - 4 == 4 - - - - - multiply( j, 2 ) == j*2 - - - 206 == 206 - - - - - multiply( i, 2 ) == i*2 - - - 6 == 6 - - - - - multiply( j, 2 ) == j*2 - - - 206 == 206 - - - - - multiply( i, 2 ) == i*2 - - - 8 == 8 - - - - - multiply( j, 2 ) == j*2 - - - 206 == 206 - - - - - multiply( i, 2 ) == i*2 - - - 10 == 10 - - - - - multiply( j, 2 ) == j*2 - - - 206 == 206 - - - - - multiply( i, 2 ) == i*2 - - - 30 == 30 - - - - - multiply( j, 2 ) == j*2 - - - 206 == 206 - - - - - multiply( i, 2 ) == i*2 - - - 40 == 40 - - - - - multiply( j, 2 ) == j*2 - - - 206 == 206 - - - - - multiply( i, 2 ) == i*2 - - - 42 == 42 - - - - - multiply( j, 2 ) == j*2 - - - 206 == 206 - - - - - multiply( i, 2 ) == i*2 - - - 72 == 72 - - - - - multiply( j, 2 ) == j*2 - - - 206 == 206 - - - - - multiply( i, 2 ) == i*2 - - - 2 == 2 - - - - - multiply( j, 2 ) == j*2 - - - 208 == 208 - - - - - multiply( i, 2 ) == i*2 - - - 4 == 4 - - - - - multiply( j, 2 ) == j*2 - - - 208 == 208 - - - - - multiply( i, 2 ) == i*2 - - - 6 == 6 - - - - - multiply( j, 2 ) == j*2 - - - 208 == 208 - - - - - multiply( i, 2 ) == i*2 - - - 8 == 8 - - - - - multiply( j, 2 ) == j*2 - - - 208 == 208 - - - - - multiply( i, 2 ) == i*2 - - - 10 == 10 - - - - - multiply( j, 2 ) == j*2 - - - 208 == 208 - - - - - multiply( i, 2 ) == i*2 - - - 30 == 30 - - - - - multiply( j, 2 ) == j*2 - - - 208 == 208 - - - - - multiply( i, 2 ) == i*2 - - - 40 == 40 - - - - - multiply( j, 2 ) == j*2 - - - 208 == 208 - - - - - multiply( i, 2 ) == i*2 - - - 42 == 42 - - - - - multiply( j, 2 ) == j*2 - - - 208 == 208 - - - - - multiply( i, 2 ) == i*2 - - - 72 == 72 - - - - - multiply( j, 2 ) == j*2 - - - 208 == 208 - - - - - multiply( i, 2 ) == i*2 - - - 2 == 2 - - - - - multiply( j, 2 ) == j*2 - - - 210 == 210 - - - - - multiply( i, 2 ) == i*2 - - - 4 == 4 - - - - - multiply( j, 2 ) == j*2 - - - 210 == 210 - - - - - multiply( i, 2 ) == i*2 - - - 6 == 6 - - - - - multiply( j, 2 ) == j*2 - - - 210 == 210 - - - - - multiply( i, 2 ) == i*2 - - - 8 == 8 - - - - - multiply( j, 2 ) == j*2 - - - 210 == 210 - - - - - multiply( i, 2 ) == i*2 - - - 10 == 10 - - - - - multiply( j, 2 ) == j*2 - - - 210 == 210 - - - - - multiply( i, 2 ) == i*2 - - - 30 == 30 - - - - - multiply( j, 2 ) == j*2 - - - 210 == 210 - - - - - multiply( i, 2 ) == i*2 - - - 40 == 40 - - - - - multiply( j, 2 ) == j*2 - - - 210 == 210 - - - - - multiply( i, 2 ) == i*2 - - - 42 == 42 - - - - - multiply( j, 2 ) == j*2 - - - 210 == 210 - - - - - multiply( i, 2 ) == i*2 - - - 72 == 72 - - - - - multiply( j, 2 ) == j*2 - - - 210 == 210 - - - - - multiply( i, 2 ) == i*2 - - - 2 == 2 - - - - - multiply( j, 2 ) == j*2 - - - 212 == 212 - - - - - multiply( i, 2 ) == i*2 - - - 4 == 4 - - - - - multiply( j, 2 ) == j*2 - - - 212 == 212 - - - - - multiply( i, 2 ) == i*2 - - - 6 == 6 - - - - - multiply( j, 2 ) == j*2 - - - 212 == 212 - - - - - multiply( i, 2 ) == i*2 - - - 8 == 8 - - - - - multiply( j, 2 ) == j*2 - - - 212 == 212 - - - - - multiply( i, 2 ) == i*2 - - - 10 == 10 - - - - - multiply( j, 2 ) == j*2 - - - 212 == 212 - - - - - multiply( i, 2 ) == i*2 - - - 30 == 30 - - - - - multiply( j, 2 ) == j*2 - - - 212 == 212 - - - - - multiply( i, 2 ) == i*2 - - - 40 == 40 - - - - - multiply( j, 2 ) == j*2 - - - 212 == 212 - - - - - multiply( i, 2 ) == i*2 - - - 42 == 42 - - - - - multiply( j, 2 ) == j*2 - - - 212 == 212 - - - - - multiply( i, 2 ) == i*2 - - - 72 == 72 - - - - - multiply( j, 2 ) == j*2 - - - 212 == 212 - - - - - multiply( i, 2 ) == i*2 - - - 2 == 2 - - - - - multiply( j, 2 ) == j*2 - - - 214 == 214 - - - - - multiply( i, 2 ) == i*2 - - - 4 == 4 - - - - - multiply( j, 2 ) == j*2 - - - 214 == 214 - - - - - multiply( i, 2 ) == i*2 - - - 6 == 6 - - - - - multiply( j, 2 ) == j*2 - - - 214 == 214 - - - - - multiply( i, 2 ) == i*2 - - - 8 == 8 - - - - - multiply( j, 2 ) == j*2 - - - 214 == 214 - - - - - multiply( i, 2 ) == i*2 - - - 10 == 10 - - - - - multiply( j, 2 ) == j*2 - - - 214 == 214 - - - - - multiply( i, 2 ) == i*2 - - - 30 == 30 - - - - - multiply( j, 2 ) == j*2 - - - 214 == 214 - - - - - multiply( i, 2 ) == i*2 - - - 40 == 40 - - - - - multiply( j, 2 ) == j*2 - - - 214 == 214 - - - - - multiply( i, 2 ) == i*2 - - - 42 == 42 - - - - - multiply( j, 2 ) == j*2 - - - 214 == 214 - - - - - multiply( i, 2 ) == i*2 - - - 72 == 72 - - - - - multiply( j, 2 ) == j*2 - - - 214 == 214 - - - - @@ -2775,7 +1980,7 @@ - + this is a message @@ -2784,7 +1989,7 @@ - + this message should be logged @@ -2801,7 +2006,7 @@ - + this message may be logged later @@ -2866,7 +2071,164 @@ - + + + current counter 0 + + + i := 0 + + + + i < 10 + + + 0 < 10 + + + + current counter 1 + + + i := 1 + + + + i < 10 + + + 1 < 10 + + + + current counter 2 + + + i := 2 + + + + i < 10 + + + 2 < 10 + + + + current counter 3 + + + i := 3 + + + + i < 10 + + + 3 < 10 + + + + current counter 4 + + + i := 4 + + + + i < 10 + + + 4 < 10 + + + + current counter 5 + + + i := 5 + + + + i < 10 + + + 5 < 10 + + + + current counter 6 + + + i := 6 + + + + i < 10 + + + 6 < 10 + + + + current counter 7 + + + i := 7 + + + + i < 10 + + + 7 < 10 + + + + current counter 8 + + + i := 8 + + + + i < 10 + + + 8 < 10 + + + + current counter 9 + + + i := 9 + + + + i < 10 + + + 9 < 10 + + + + current counter 10 + + + i := 10 + + + + i < 10 + + + 10 < 10 + + + + + data.int_seven != 7 @@ -3035,1610 +2397,6 @@ - -
-
- - - Text( testString, TextAttributes().setWidth( 80 ) ).toString() == testString - - - "one two three four" -== -"one two three four" - - - - - Text( testString, TextAttributes().setWidth( 18 ) ).toString() == testString - - - "one two three four" -== -"one two three four" - - - -
- -
-
-
- - - Text( testString, TextAttributes().setWidth( 17 ) ).toString() == "one two three\nfour" - - - "one two three -four" -== -"one two three -four" - - - - - Text( testString, TextAttributes().setWidth( 16 ) ).toString() == "one two three\nfour" - - - "one two three -four" -== -"one two three -four" - - - - - Text( testString, TextAttributes().setWidth( 14 ) ).toString() == "one two three\nfour" - - - "one two three -four" -== -"one two three -four" - - - - - Text( testString, TextAttributes().setWidth( 13 ) ).toString() == "one two three\nfour" - - - "one two three -four" -== -"one two three -four" - - - - - Text( testString, TextAttributes().setWidth( 12 ) ).toString() == "one two\nthree four" - - - "one two -three four" -== -"one two -three four" - - - -
- -
-
-
- - - Text( testString, TextAttributes().setWidth( 9 ) ).toString() == "one two\nthree\nfour" - - - "one two -three -four" -== -"one two -three -four" - - - - - Text( testString, TextAttributes().setWidth( 8 ) ).toString() == "one two\nthree\nfour" - - - "one two -three -four" -== -"one two -three -four" - - - - - Text( testString, TextAttributes().setWidth( 7 ) ).toString() == "one two\nthree\nfour" - - - "one two -three -four" -== -"one two -three -four" - - - -
- -
-
-
- - - Text( testString, TextAttributes().setWidth( 6 ) ).toString() == "one\ntwo\nthree\nfour" - - - "one -two -three -four" -== -"one -two -three -four" - - - - - Text( testString, TextAttributes().setWidth( 5 ) ).toString() == "one\ntwo\nthree\nfour" - - - "one -two -three -four" -== -"one -two -three -four" - - - -
- -
-
-
- - - Text( "abcdef", TextAttributes().setWidth( 4 ) ).toString() == "abc-\ndef" - - - "abc- -def" -== -"abc- -def" - - - - - Text( "abcdefg", TextAttributes().setWidth( 4 ) ).toString() == "abc-\ndefg" - - - "abc- -defg" -== -"abc- -defg" - - - - - Text( "abcdefgh", TextAttributes().setWidth( 4 ) ).toString() == "abc-\ndef-\ngh" - - - "abc- -def- -gh" -== -"abc- -def- -gh" - - - - - Text( testString, TextAttributes().setWidth( 4 ) ).toString() == "one\ntwo\nthr-\nee\nfour" - - - "one -two -thr- -ee -four" -== -"one -two -thr- -ee -four" - - - - - Text( testString, TextAttributes().setWidth( 3 ) ).toString() == "one\ntwo\nth-\nree\nfo-\nur" - - - "one -two -th- -ree -fo- -ur" -== -"one -two -th- -ree -fo- -ur" - - - -
- -
-
-
- - - text.size() == 4 - - - 4 == 4 - - - - - text[0] == "one" - - - "one" == "one" - - - - - text[1] == "two" - - - "two" == "two" - - - - - text[2] == "three" - - - "three" == "three" - - - - - text[3] == "four" - - - "four" == "four" - - - -
- -
-
-
- - - text.toString() == " one two\n three\n four" - - - " one two - three - four" -== -" one two - three - four" - - - -
- -
-
-
- - - Text( testString, TextAttributes().setWidth( 80 ) ).toString() == testString - - - "one two -three four" -== -"one two -three four" - - - - - Text( testString, TextAttributes().setWidth( 18 ) ).toString() == testString - - - "one two -three four" -== -"one two -three four" - - - - - Text( testString, TextAttributes().setWidth( 10 ) ).toString() == testString - - - "one two -three four" -== -"one two -three four" - - - -
- -
-
-
- - - Text( "abcdef\n", TextAttributes().setWidth( 10 ) ).toString() == "abcdef" - - - "abcdef" == "abcdef" - - - - - Text( "abcdef", TextAttributes().setWidth( 6 ) ).toString() == "abcdef" - - - "abcdef" == "abcdef" - - - - - Text( "abcdef\n", TextAttributes().setWidth( 6 ) ).toString() == "abcdef" - - - "abcdef" == "abcdef" - - - - - Text( "abcdef\n", TextAttributes().setWidth( 5 ) ).toString() == "abcd-\nef" - - - "abcd- -ef" -== -"abcd- -ef" - - - -
- -
-
-
- - - Text( testString, TextAttributes().setWidth( 9 ) ).toString() == "one two\nthree\nfour" - - - "one two -three -four" -== -"one two -three -four" - - - - - Text( testString, TextAttributes().setWidth( 8 ) ).toString() == "one two\nthree\nfour" - - - "one two -three -four" -== -"one two -three -four" - - - - - Text( testString, TextAttributes().setWidth( 7 ) ).toString() == "one two\nthree\nfour" - - - "one two -three -four" -== -"one two -three -four" - - - -
- -
-
-
- - - Text( testString, TextAttributes().setWidth( 6 ) ).toString() == "one\ntwo\nthree\nfour" - - - "one -two -three -four" -== -"one -two -three -four" - - - -
- -
-
-
- - - Text( testString, TextAttributes().setWidth( 80 ) ).toString() == testString - - - "one,two(three) <here>" -== -"one,two(three) <here>" - - - - - Text( testString, TextAttributes().setWidth( 24 ) ).toString() == testString - - - "one,two(three) <here>" -== -"one,two(three) <here>" - - - -
- -
-
-
- - - Text( testString, TextAttributes().setWidth( 11 ) ).toString() == "one,two\n(three)\n<here>" - - - "one,two -(three) -<here>" -== -"one,two -(three) -<here>" - - - -
- -
-
-
- - - Text( testString, TextAttributes().setWidth( 6 ) ).toString() == "one,\ntwo\n(thre-\ne)\n<here>" - - - "one, -two -(thre- -e) -<here>" -== -"one, -two -(thre- -e) -<here>" - - - - - Text( testString, TextAttributes().setWidth( 5 ) ).toString() == "one,\ntwo\n(thr-\nee)\n<her-\ne>" - - - "one, -two -(thr- -ee) -<her- -e>" -== -"one, -two -(thr- -ee) -<her- -e>" - - - - - Text( testString, TextAttributes().setWidth( 4 ) ).toString() == "one,\ntwo\n(th-\nree)\n<he-\nre>" - - - "one, -two -(th- -ree) -<he- -re>" -== -"one, -two -(th- -ree) -<he- -re>" - - - -
- -
- -
- - - - t.toString(), EndsWith( "... message truncated due to excessive size" ) - - - "******************************************************************************- -******************************************************************************- -************************ -******************************************************************************- -******************************************************************************- -************************ -******************************************************************************- -******************************************************************************- -************************ -******************************************************************************- -******************************************************************************- -************************ -******************************************************************************- -******************************************************************************- -************************ -******************************************************************************- -******************************************************************************- -************************ -******************************************************************************- -******************************************************************************- -************************ -******************************************************************************- -******************************************************************************- -************************ -******************************************************************************- -******************************************************************************- -************************ -******************************************************************************- -******************************************************************************- -************************ -******************************************************************************- -******************************************************************************- -************************ -******************************************************************************- -******************************************************************************- -************************ -******************************************************************************- -******************************************************************************- -************************ -******************************************************************************- -******************************************************************************- -************************ -******************************************************************************- -******************************************************************************- -************************ -******************************************************************************- -******************************************************************************- -************************ -******************************************************************************- -******************************************************************************- -************************ -******************************************************************************- -******************************************************************************- -************************ -******************************************************************************- -******************************************************************************- -************************ -******************************************************************************- -******************************************************************************- -************************ -******************************************************************************- -******************************************************************************- -************************ -******************************************************************************- -******************************************************************************- -************************ -******************************************************************************- -******************************************************************************- -************************ -******************************************************************************- -******************************************************************************- -************************ -******************************************************************************- -******************************************************************************- -************************ -******************************************************************************- -******************************************************************************- -************************ -******************************************************************************- -******************************************************************************- -************************ -******************************************************************************- -******************************************************************************- -************************ -******************************************************************************- -******************************************************************************- -************************ -******************************************************************************- -******************************************************************************- -************************ -******************************************************************************- -******************************************************************************- -************************ -******************************************************************************- -******************************************************************************- -************************ -******************************************************************************- -******************************************************************************- -************************ -******************************************************************************- -******************************************************************************- -************************ -******************************************************************************- -******************************************************************************- -************************ -******************************************************************************- -******************************************************************************- -************************ -******************************************************************************- -******************************************************************************- -************************ -******************************************************************************- -******************************************************************************- -************************ -******************************************************************************- -******************************************************************************- -************************ -******************************************************************************- -******************************************************************************- -************************ -******************************************************************************- -******************************************************************************- -************************ -******************************************************************************- -******************************************************************************- -************************ -******************************************************************************- -******************************************************************************- -************************ -******************************************************************************- -******************************************************************************- -************************ -******************************************************************************- -******************************************************************************- -************************ -******************************************************************************- -******************************************************************************- -************************ -******************************************************************************- -******************************************************************************- -************************ -******************************************************************************- -******************************************************************************- -************************ -******************************************************************************- -******************************************************************************- -************************ -******************************************************************************- -******************************************************************************- -************************ -******************************************************************************- -******************************************************************************- -************************ -******************************************************************************- -******************************************************************************- -************************ -******************************************************************************- -******************************************************************************- -************************ -******************************************************************************- -******************************************************************************- -************************ -******************************************************************************- -******************************************************************************- -************************ -******************************************************************************- -******************************************************************************- -************************ -******************************************************************************- -******************************************************************************- -************************ -******************************************************************************- -******************************************************************************- -************************ -******************************************************************************- -******************************************************************************- -************************ -******************************************************************************- -******************************************************************************- -************************ -******************************************************************************- -******************************************************************************- -************************ -******************************************************************************- -******************************************************************************- -************************ -******************************************************************************- -******************************************************************************- -************************ -******************************************************************************- -******************************************************************************- -************************ -******************************************************************************- -******************************************************************************- -************************ -******************************************************************************- -******************************************************************************- -************************ -******************************************************************************- -******************************************************************************- -************************ -******************************************************************************- -******************************************************************************- -************************ -******************************************************************************- -******************************************************************************- -************************ -******************************************************************************- -******************************************************************************- -************************ -******************************************************************************- -******************************************************************************- -************************ -******************************************************************************- -******************************************************************************- -************************ -******************************************************************************- -******************************************************************************- -************************ -******************************************************************************- -******************************************************************************- -************************ -******************************************************************************- -******************************************************************************- -************************ -******************************************************************************- -******************************************************************************- -************************ -******************************************************************************- -******************************************************************************- -************************ -******************************************************************************- -******************************************************************************- -************************ -******************************************************************************- -******************************************************************************- -************************ -******************************************************************************- -******************************************************************************- -************************ -******************************************************************************- -******************************************************************************- -************************ -******************************************************************************- -******************************************************************************- -************************ -******************************************************************************- -******************************************************************************- -************************ -******************************************************************************- -******************************************************************************- -************************ -******************************************************************************- -******************************************************************************- -************************ -******************************************************************************- -******************************************************************************- -************************ -******************************************************************************- -******************************************************************************- -************************ -******************************************************************************- -******************************************************************************- -************************ -******************************************************************************- -******************************************************************************- -************************ -******************************************************************************- -******************************************************************************- -************************ -******************************************************************************- -******************************************************************************- -************************ -******************************************************************************- -******************************************************************************- -************************ -******************************************************************************- -******************************************************************************- -************************ -******************************************************************************- -******************************************************************************- -************************ -******************************************************************************- -******************************************************************************- -************************ -******************************************************************************- -******************************************************************************- -************************ -******************************************************************************- -******************************************************************************- -************************ -******************************************************************************- -******************************************************************************- -************************ -******************************************************************************- -******************************************************************************- -************************ -******************************************************************************- -******************************************************************************- -************************ -******************************************************************************- -******************************************************************************- -************************ -******************************************************************************- -******************************************************************************- -************************ -******************************************************************************- -******************************************************************************- -************************ -******************************************************************************- -******************************************************************************- -************************ -******************************************************************************- -******************************************************************************- -************************ -******************************************************************************- -******************************************************************************- -************************ -******************************************************************************- -******************************************************************************- -************************ -******************************************************************************- -******************************************************************************- -************************ -******************************************************************************- -******************************************************************************- -************************ -******************************************************************************- -******************************************************************************- -************************ -******************************************************************************- -******************************************************************************- -************************ -******************************************************************************- -******************************************************************************- -************************ -******************************************************************************- -******************************************************************************- -************************ -******************************************************************************- -******************************************************************************- -************************ -******************************************************************************- -******************************************************************************- -************************ -******************************************************************************- -******************************************************************************- -************************ -******************************************************************************- -******************************************************************************- -************************ -******************************************************************************- -******************************************************************************- -************************ -******************************************************************************- -******************************************************************************- -************************ -******************************************************************************- -******************************************************************************- -************************ -******************************************************************************- -******************************************************************************- -************************ -******************************************************************************- -******************************************************************************- -************************ -******************************************************************************- -******************************************************************************- -************************ -******************************************************************************- -******************************************************************************- -************************ -******************************************************************************- -******************************************************************************- -************************ -******************************************************************************- -******************************************************************************- -************************ -******************************************************************************- -******************************************************************************- -************************ -******************************************************************************- -******************************************************************************- -************************ -******************************************************************************- -******************************************************************************- -************************ -******************************************************************************- -******************************************************************************- -************************ -******************************************************************************- -******************************************************************************- -************************ -******************************************************************************- -******************************************************************************- -************************ -******************************************************************************- -******************************************************************************- -************************ -******************************************************************************- -******************************************************************************- -************************ -******************************************************************************- -******************************************************************************- -************************ -******************************************************************************- -******************************************************************************- -************************ -******************************************************************************- -******************************************************************************- -************************ -******************************************************************************- -******************************************************************************- -************************ -******************************************************************************- -******************************************************************************- -************************ -******************************************************************************- -******************************************************************************- -************************ -******************************************************************************- -******************************************************************************- -************************ -******************************************************************************- -******************************************************************************- -************************ -******************************************************************************- -******************************************************************************- -************************ -******************************************************************************- -******************************************************************************- -************************ -******************************************************************************- -******************************************************************************- -************************ -******************************************************************************- -******************************************************************************- -************************ -******************************************************************************- -******************************************************************************- -************************ -******************************************************************************- -******************************************************************************- -************************ -******************************************************************************- -******************************************************************************- -************************ -******************************************************************************- -******************************************************************************- -************************ -******************************************************************************- -******************************************************************************- -************************ -******************************************************************************- -******************************************************************************- -************************ -******************************************************************************- -******************************************************************************- -************************ -******************************************************************************- -******************************************************************************- -************************ -******************************************************************************- -******************************************************************************- -************************ -******************************************************************************- -******************************************************************************- -************************ -******************************************************************************- -******************************************************************************- -************************ -******************************************************************************- -******************************************************************************- -************************ -******************************************************************************- -******************************************************************************- -************************ -******************************************************************************- -******************************************************************************- -************************ -******************************************************************************- -******************************************************************************- -************************ -******************************************************************************- -******************************************************************************- -************************ -******************************************************************************- -******************************************************************************- -************************ -******************************************************************************- -******************************************************************************- -************************ -******************************************************************************- -******************************************************************************- -************************ -******************************************************************************- -******************************************************************************- -************************ -******************************************************************************- -******************************************************************************- -************************ -******************************************************************************- -******************************************************************************- -************************ -******************************************************************************- -******************************************************************************- -************************ -******************************************************************************- -******************************************************************************- -************************ -******************************************************************************- -******************************************************************************- -************************ -******************************************************************************- -******************************************************************************- -************************ -******************************************************************************- -******************************************************************************- -************************ -******************************************************************************- -******************************************************************************- -************************ -******************************************************************************- -******************************************************************************- -************************ -******************************************************************************- -******************************************************************************- -************************ -******************************************************************************- -******************************************************************************- -************************ -******************************************************************************- -******************************************************************************- -************************ -******************************************************************************- -******************************************************************************- -************************ -******************************************************************************- -******************************************************************************- -************************ -******************************************************************************- -******************************************************************************- -************************ -******************************************************************************- -******************************************************************************- -************************ -******************************************************************************- -******************************************************************************- -************************ -******************************************************************************- -******************************************************************************- -************************ -******************************************************************************- -******************************************************************************- -************************ -******************************************************************************- -******************************************************************************- -************************ -******************************************************************************- -******************************************************************************- -************************ -******************************************************************************- -******************************************************************************- -************************ -******************************************************************************- -******************************************************************************- -************************ -******************************************************************************- -******************************************************************************- -************************ -******************************************************************************- -******************************************************************************- -************************ -******************************************************************************- -******************************************************************************- -************************ -******************************************************************************- -******************************************************************************- -************************ -******************************************************************************- -******************************************************************************- -************************ -******************************************************************************- -******************************************************************************- -************************ -******************************************************************************- -******************************************************************************- -************************ -******************************************************************************- -******************************************************************************- -************************ -******************************************************************************- -******************************************************************************- -************************ -******************************************************************************- -******************************************************************************- -************************ -******************************************************************************- -******************************************************************************- -************************ -******************************************************************************- -******************************************************************************- -************************ -******************************************************************************- -******************************************************************************- -************************ -******************************************************************************- -******************************************************************************- -************************ -******************************************************************************- -******************************************************************************- -************************ -******************************************************************************- -******************************************************************************- -************************ -******************************************************************************- -******************************************************************************- -************************ -******************************************************************************- -******************************************************************************- -************************ -******************************************************************************- -******************************************************************************- -************************ -******************************************************************************- -******************************************************************************- -************************ -******************************************************************************- -******************************************************************************- -************************ -******************************************************************************- -******************************************************************************- -************************ -******************************************************************************- -******************************************************************************- -************************ -******************************************************************************- -******************************************************************************- -************************ -******************************************************************************- -******************************************************************************- -************************ -******************************************************************************- -******************************************************************************- -************************ -******************************************************************************- -******************************************************************************- -************************ -******************************************************************************- -******************************************************************************- -************************ -******************************************************************************- -******************************************************************************- -************************ -******************************************************************************- -******************************************************************************- -************************ -******************************************************************************- -******************************************************************************- -************************ -******************************************************************************- -******************************************************************************- -************************ -******************************************************************************- -******************************************************************************- -************************ -******************************************************************************- -******************************************************************************- -************************ -******************************************************************************- -******************************************************************************- -************************ -******************************************************************************- -******************************************************************************- -************************ -******************************************************************************- -******************************************************************************- -************************ -******************************************************************************- -******************************************************************************- -************************ -******************************************************************************- -******************************************************************************- -************************ -******************************************************************************- -******************************************************************************- -************************ -******************************************************************************- -******************************************************************************- -************************ -******************************************************************************- -******************************************************************************- -************************ -******************************************************************************- -******************************************************************************- -************************ -******************************************************************************- -******************************************************************************- -************************ -******************************************************************************- -******************************************************************************- -************************ -******************************************************************************- -******************************************************************************- -************************ -******************************************************************************- -******************************************************************************- -************************ -******************************************************************************- -******************************************************************************- -************************ -******************************************************************************- -******************************************************************************- -************************ -******************************************************************************- -******************************************************************************- -************************ -******************************************************************************- -******************************************************************************- -************************ -******************************************************************************- -******************************************************************************- -************************ -******************************************************************************- -******************************************************************************- -************************ -******************************************************************************- -******************************************************************************- -************************ -******************************************************************************- -******************************************************************************- -************************ -******************************************************************************- -******************************************************************************- -************************ -******************************************************************************- -******************************************************************************- -************************ -******************************************************************************- -******************************************************************************- -************************ -******************************************************************************- -******************************************************************************- -************************ -******************************************************************************- -******************************************************************************- -************************ -******************************************************************************- -******************************************************************************- -************************ -******************************************************************************- -******************************************************************************- -************************ -******************************************************************************- -******************************************************************************- -************************ -******************************************************************************- -******************************************************************************- -************************ -******************************************************************************- -******************************************************************************- -************************ -******************************************************************************- -******************************************************************************- -************************ -******************************************************************************- -******************************************************************************- -************************ -******************************************************************************- -******************************************************************************- -************************ -******************************************************************************- -******************************************************************************- -************************ -******************************************************************************- -******************************************************************************- -************************ -******************************************************************************- -******************************************************************************- -************************ -******************************************************************************- -******************************************************************************- -************************ -******************************************************************************- -******************************************************************************- -************************ -******************************************************************************- -******************************************************************************- -************************ -******************************************************************************- -******************************************************************************- -************************ -******************************************************************************- -******************************************************************************- -************************ -******************************************************************************- -******************************************************************************- -************************ -******************************************************************************- -******************************************************************************- -************************ -******************************************************************************- -******************************************************************************- -************************ -******************************************************************************- -******************************************************************************- -************************ -******************************************************************************- -******************************************************************************- -************************ -******************************************************************************- -******************************************************************************- -************************ -******************************************************************************- -******************************************************************************- -************************ -******************************************************************************- -******************************************************************************- -************************ -******************************************************************************- -******************************************************************************- -************************ -******************************************************************************- -******************************************************************************- -************************ -******************************************************************************- -******************************************************************************- -************************ -******************************************************************************- -******************************************************************************- -************************ -******************************************************************************- -******************************************************************************- -************************ -******************************************************************************- -******************************************************************************- -************************ -******************************************************************************- -******************************************************************************- -************************ -******************************************************************************- -******************************************************************************- -************************ -******************************************************************************- -******************************************************************************- -************************ -******************************************************************************- -******************************************************************************- -************************ -******************************************************************************- -******************************************************************************- -************************ -******************************************************************************- -******************************************************************************- -************************ -******************************************************************************- -******************************************************************************- -************************ -******************************************************************************- -******************************************************************************- -************************ -******************************************************************************- -******************************************************************************- -************************ -******************************************************************************- -******************************************************************************- -************************ -******************************************************************************- -******************************************************************************- -************************ -******************************************************************************- -******************************************************************************- -************************ -******************************************************************************- -******************************************************************************- -************************ -******************************************************************************- -******************************************************************************- -************************ -******************************************************************************- -******************************************************************************- -************************ -******************************************************************************- -******************************************************************************- -************************ -******************************************************************************- -******************************************************************************- -************************ -******************************************************************************- -******************************************************************************- -************************ -******************************************************************************- -******************************************************************************- -************************ -******************************************************************************- -******************************************************************************- -************************ -******************************************************************************- -******************************************************************************- -************************ -******************************************************************************- -******************************************************************************- -************************ -******************************************************************************- -******************************************************************************- -************************ -******************************************************************************- -******************************************************************************- -************************ -******************************************************************************- -******************************************************************************- -************************ -******************************************************************************- -******************************************************************************- -************************ -******************************************************************************- -******************************************************************************- -************************ -******************************************************************************- -******************************************************************************- -************************ -******************************************************************************- -******************************************************************************- -************************ -******************************************************************************- -******************************************************************************- -************************ -******************************************************************************- -******************************************************************************- -************************ -******************************************************************************- -******************************************************************************- -************************ -******************************************************************************- -******************************************************************************- -************************ -******************************************************************************- -******************************************************************************- -************************ -******************************************************************************- -******************************************************************************- -************************ -******************************************************************************- -******************************************************************************- -************************ -******************************************************************************- -******************************************************************************- -************************ -******************************************************************************- -******************************************************************************- -************************ -******************************************************************************- -******************************************************************************- -************************ -******************************************************************************- -******************************************************************************- -************************ -******************************************************************************- -******************************************************************************- -************************ -******************************************************************************- -******************************************************************************- -************************ -******************************************************************************- -******************************************************************************- -************************ -******************************************************************************- -******************************************************************************- -************************ -******************************************************************************- -******************************************************************************- -************************ -******************************************************************************- -******************************************************************************- -************************ -******************************************************************************- -******************************************************************************- -************************ -******************************************************************************- -******************************************************************************- -************************ -******************************************************************************- -******************************************************************************- -************************ -******************************************************************************- -******************************************************************************- -************************ -******************************************************************************- -******************************************************************************- -************************ -******************************************************************************- -******************************************************************************- -************************ -******************************************************************************- -******************************************************************************- -************************ -******************************************************************************- -******************************************************************************- -************************ -******************************************************************************- -... message truncated due to excessive size" ends with: "... message truncated due to excessive size" - - - - @@ -4683,7 +2441,7 @@ re>"
- + testStringForMatching(), ( Contains( "string" ) || Contains( "different" ) ) && Contains( "random" ) @@ -4705,7 +2463,7 @@ re>" - + testStringForMatching(), !Contains( "substring" ) @@ -4716,13 +2474,13 @@ re>" - + thisThrows(), "expected exception" - thisThrows(), "expected exception" + "expected exception" equals: "expected exception" @@ -4730,41 +2488,30 @@ re>" thisThrows(), "should fail" - expected exception + "expected exception" equals: "should fail" - + This one ran - + custom exception - - - - thisFunctionNotImplemented( 7 ) - - - thisFunctionNotImplemented( 7 ) - - - - True - 1 + {?} @@ -4780,23 +2527,12 @@ re>" !(False) - !0 + !{?} - - - - 0x == o - - - 3221225472 (0x) == {?} - - - - - + data.int_seven > 7 @@ -5090,7 +2826,7 @@ re>" - +
Message from section one @@ -6287,7 +4023,7 @@ re>" (std::pair<int, int>( 1, 2 )) == aNicePair - std::pair( 1, 2 ) == std::pair( 1, 2 ) + {?} == {?} @@ -6298,7 +4034,7 @@ re>" p == 0 - NULL == 0 + 0 == 0 @@ -6306,7 +4042,7 @@ re>" p == pNULL - NULL == NULL + 0 == 0 @@ -6359,23 +4095,14 @@ re>" - - - actual address of p: 0x - - - toString(p): 0x - - -
- + - parser.parseInto( std::vector<std::string>(), config ) + result - parser.parseInto( std::vector<std::string>(), config ) + {?} @@ -6389,12 +4116,12 @@ re>"
- + - parseIntoConfig( argv, config ) + result - parseIntoConfig( argv, config ) + {?} @@ -6441,17 +4168,17 @@ re>"
- + - parseIntoConfig( argv, config ) + result - parseIntoConfig( argv, config ) + {?} - cfg.testSpec().matches( fakeTestCase( "notIncluded" ) ) == false + cfg.testSpec().matches(fakeTestCase("notIncluded")) == false false == false @@ -6459,7 +4186,7 @@ re>" - cfg.testSpec().matches( fakeTestCase( "test1" ) ) + cfg.testSpec().matches(fakeTestCase("test1")) true @@ -6471,17 +4198,17 @@ re>"
- + - parseIntoConfig( argv, config ) + result - parseIntoConfig( argv, config ) + {?} - cfg.testSpec().matches( fakeTestCase( "test1" ) ) == false + cfg.testSpec().matches(fakeTestCase("test1")) == false false == false @@ -6489,7 +4216,7 @@ re>" - cfg.testSpec().matches( fakeTestCase( "alwaysIncluded" ) ) + cfg.testSpec().matches(fakeTestCase("alwaysIncluded")) true @@ -6501,17 +4228,17 @@ re>"
- + - parseIntoConfig( argv, config ) + result - parseIntoConfig( argv, config ) + {?} - cfg.testSpec().matches( fakeTestCase( "test1" ) ) == false + cfg.testSpec().matches(fakeTestCase("test1")) == false false == false @@ -6519,7 +4246,7 @@ re>" - cfg.testSpec().matches( fakeTestCase( "alwaysIncluded" ) ) + cfg.testSpec().matches(fakeTestCase("alwaysIncluded")) true @@ -6531,12 +4258,12 @@ re>"
- + - parseIntoConfig( argv, config ) + cli.parse({"test", "-r", "console"}) - parseIntoConfig( argv, config ) + {?} @@ -6553,12 +4280,12 @@ re>"
- + - parseIntoConfig( argv, config ) + cli.parse({"test", "-r", "xml"}) - parseIntoConfig( argv, config ) + {?} @@ -6575,12 +4302,12 @@ re>"
- + - parseIntoConfig( argv, config ) + cli.parse({"test", "-r", "xml", "-r", "junit"}) - parseIntoConfig( argv, config ) + {?} @@ -6613,12 +4340,12 @@ re>"
- + - parseIntoConfig( argv, config ) + cli.parse({"test", "--reporter", "junit"}) - parseIntoConfig( argv, config ) + {?} @@ -6635,12 +4362,12 @@ re>"
- + - parseIntoConfig( argv, config ) + cli.parse({"test", "-b"}) - parseIntoConfig( argv, config ) + {?} @@ -6657,12 +4384,12 @@ re>"
- + - parseIntoConfig( argv, config ) + cli.parse({"test", "--break"}) - parseIntoConfig( argv, config ) + {?} @@ -6679,12 +4406,12 @@ re>"
- + - parseIntoConfig( argv, config ) + cli.parse({"test", "-a"}) - parseIntoConfig( argv, config ) + {?} @@ -6701,12 +4428,12 @@ re>"
- + - parseIntoConfig( argv, config ) + cli.parse({"test", "-x", "2"}) - parseIntoConfig( argv, config ) + {?} @@ -6721,52 +4448,44 @@ re>"
-
-
- - - parseIntoConfigAndReturnError( argv, config ), Contains( "greater than zero" ) - - - "Value after -x or --abortAfter must be greater than zero -- while parsing: (-x, --abortx <no. failures>)" contains: "greater than zero" - - - -
- -
- + - parseIntoConfigAndReturnError( argv, config ), Contains( "-x" ) + !result - "Unable to convert oops to destination type -- while parsing: (-x, --abortx <no. failures>)" contains: "-x" + true - + + + result.errorMessage(), Contains("convert") && Contains("oops") + + + "Unable to convert 'oops' to destination type" ( contains: "convert" and contains: "oops" ) + + +
- +
- + - parseIntoConfig( argv, config ) + cli.parse({"test", "-e"}) - parseIntoConfig( argv, config ) + {?} - config.noThrow == true + config.noThrow - true == true + true @@ -6775,20 +4494,20 @@ re>"
- + - parseIntoConfig( argv, config ) + cli.parse({"test", "--nothrow"}) - parseIntoConfig( argv, config ) + {?} - config.noThrow == true + config.noThrow - true == true + true @@ -6797,12 +4516,12 @@ re>"
- + - parseIntoConfig( argv, config ) + cli.parse({"test", "-o", "filename.ext"}) - parseIntoConfig( argv, config ) + {?} @@ -6819,12 +4538,12 @@ re>"
- + - parseIntoConfig( argv, config ) + cli.parse({"test", "--out", "filename.ext"}) - parseIntoConfig( argv, config ) + {?} @@ -6841,12 +4560,12 @@ re>"
- + - parseIntoConfig( argv, config ) + cli.parse({"test", "-abe"}) - parseIntoConfig( argv, config ) + {?} @@ -6879,12 +4598,12 @@ re>"
- + - parseIntoConfig( argv, config ) + cli.parse({"test"}) - parseIntoConfig( argv, config ) + {?} @@ -6901,12 +4620,12 @@ re>"
- + - parseIntoConfig( argv, config ) + cli.parse({"test", "--use-colour", "auto"}) - parseIntoConfig( argv, config ) + {?} @@ -6923,12 +4642,12 @@ re>"
- + - parseIntoConfig( argv, config ) + cli.parse({"test", "--use-colour", "yes"}) - parseIntoConfig( argv, config ) + {?} @@ -6945,12 +4664,12 @@ re>"
- + - parseIntoConfig( argv, config ) + cli.parse({"test", "--use-colour", "no"}) - parseIntoConfig( argv, config ) + {?} @@ -6967,21 +4686,29 @@ re>"
- + - parseIntoConfig( argv, config ), Contains( "colour mode must be one of" ) + !result - parseIntoConfig( argv, config ), Contains( "colour mode must be one of" ) + true - + + + result.errorMessage(), Contains( "colour mode must be one of" ) + + + "colour mode must be one of: auto, yes or no. 'wrong' not recognised" contains: "colour mode must be one of" + + +
- +
- + truthy(false) @@ -6992,167 +4719,10 @@ re>" - - - current counter 0 - - - i := 0 - - - - i < 10 - - - 0 < 10 - - - - current counter 1 - - - i := 1 - - - - i < 10 - - - 1 < 10 - - - - current counter 2 - - - i := 2 - - - - i < 10 - - - 2 < 10 - - - - current counter 3 - - - i := 3 - - - - i < 10 - - - 3 < 10 - - - - current counter 4 - - - i := 4 - - - - i < 10 - - - 4 < 10 - - - - current counter 5 - - - i := 5 - - - - i < 10 - - - 5 < 10 - - - - current counter 6 - - - i := 6 - - - - i < 10 - - - 6 < 10 - - - - current counter 7 - - - i := 7 - - - - i < 10 - - - 7 < 10 - - - - current counter 8 - - - i := 8 - - - - i < 10 - - - 8 < 10 - - - - current counter 9 - - - i := 9 - - - - i < 10 - - - 9 < 10 - - - - current counter 10 - - - i := 10 - - - - i < 10 - - - 10 < 10 - - - - - + @@ -7317,7 +4887,7 @@ re>"
- + A string sent directly to stdout @@ -7378,33 +4948,15 @@ A string sent directly to stderr - 0 == Approx(0) + INFINITY == Approx(INFINITY) - 0 == Approx( 0.0 ) + inff == Approx( inf ) - -
- -
-
- -
-
- -
- - -Write to std::cerr -Write to std::clog -Interleaved writes to error streams - - -
- +
@@ -7418,7 +4970,7 @@ Message from section two
- + testStringForMatching(), StartsWith( "string" ) @@ -7464,15 +5016,478 @@ Message from section two - - - -hello -hello - - + +
+ + + empty.empty() + + + true + + + + + empty.size() == 0 + + + 0 == 0 + + + + + std::strcmp( empty.c_str(), "" ) == 0 + + + 0 == 0 + + + +
+
+ + + s.empty() == false + + + false == false + + + + + s.size() == 5 + + + 5 == 5 + + + + + isSubstring( s ) == false + + + false == false + + + + + std::strcmp( rawChars, "hello" ) == 0 + + + 0 == 0 + + +
+ + + isOwned( s ) == false + + + false == false + + + + + s.c_str() == rawChars + + + "hello" == "hello" + + + + + isOwned( s ) == false + + + false == false + + + +
+ +
+
+ + + original == "original" + + + original == "original" + + + + + isSubstring( original ) + + + false + + + +
+
+
+ + + ss.empty() == false + + + false == false + + + + + ss.size() == 5 + + + 5 == 5 + + + + + std::strcmp( ss.c_str(), "hello" ) == 0 + + + 0 == 0 + + + + + ss == "hello" + + + hello == "hello" + + + +
+ +
+
+
+ + + isSubstring( ss ) + + + true + + + + + isOwned( ss ) == false + + + false == false + + + + + rawChars == data( s ) + + + "hello world!" == "hello world!" + + + + + ss.c_str() != rawChars + + + "hello" != "hello world!" + + + + + isSubstring( ss ) == false + + + false == false + + + + + isOwned( ss ) + + + true + + + + + data( ss ) != data( s ) + + + "hello" != "hello world!" + + + +
+ +
+
+
+ + + ss.size() == 6 + + + 6 == 6 + + + + + std::strcmp( ss.c_str(), "world!" ) == 0 + + + 0 == 0 + + + +
+ +
+
+
+ + + s.c_str() == s2.c_str() + + + "hello world!" == "hello world!" + + + +
+ +
+
+
+ + + s.c_str() != ss.c_str() + + + "hello world!" != "hello" + + + +
+ +
+
+ + + StringRef("hello") == StringRef("hello") + + + hello == hello + + + + + StringRef("hello") != StringRef("cello") + + + hello != cello + + + +
+
+
+ + + sr == "a standard string" + + + a standard string == "a standard string" + + + + + sr.size() == stdStr.size() + + + 17 == 17 + + + +
+ +
+
+
+ + + sr == "a standard string" + + + a standard string == "a standard string" + + + + + sr.size() == stdStr.size() + + + 17 == 17 + + + +
+ +
+
+
+ + + sr == "a standard string" + + + a standard string == "a standard string" + + + + + sr.size() == stdStr.size() + + + 17 == 17 + + + +
+ +
+
+
+ + + stdStr == "a stringref" + + + "a stringref" == "a stringref" + + + + + stdStr.size() == sr.size() + + + 11 == 11 + + + +
+ +
+
+
+ + + stdStr == "a stringref" + + + "a stringref" == "a stringref" + + + + + stdStr.size() == sr.size() + + + 11 == 11 + + + +
+ +
+
+
+ + + stdStr == "a stringref" + + + "a stringref" == "a stringref" + + + + + stdStr.size() == sr.size() + + + 11 == 11 + + + +
+ +
+
- + + + + minute == seconds + + + 1 m == 60 s + + + + + hour != seconds + + + 1 h != 60 s + + + + + micro != milli + + + 1 us != 1 ms + + + + + nano != micro + + + 1 ns != 1 us + + + + + + + + half_minute != femto_second + + + 1 [30/1]s != 1 fs + + + + + + + + now != later + + + {iso8601-timestamp} +!= +{iso8601-timestamp} + + + + + s1 == s2 @@ -7497,10 +5512,9 @@ hello what, Contains( "[@zzz]" ) - "error: tag alias, "[@zzz]" already registered. - First seen at file:2 - Redefined at file:10 -" contains: "[@zzz]" + "error: tag alias, '[@zzz]' already registered. + First seen at: file:2 + Redefined at: file:10" contains: "[@zzz]" @@ -7508,10 +5522,9 @@ hello what, Contains( "file" ) - "error: tag alias, "[@zzz]" already registered. - First seen at file:2 - Redefined at file:10 -" contains: "file" + "error: tag alias, '[@zzz]' already registered. + First seen at: file:2 + Redefined at: file:10" contains: "file" @@ -7519,10 +5532,9 @@ hello what, Contains( "2" ) - "error: tag alias, "[@zzz]" already registered. - First seen at file:2 - Redefined at file:10 -" contains: "2" + "error: tag alias, '[@zzz]' already registered. + First seen at: file:2 + Redefined at: file:10" contains: "2" @@ -7530,10 +5542,9 @@ hello what, Contains( "10" ) - "error: tag alias, "[@zzz]" already registered. - First seen at file:2 - Redefined at file:10 -" contains: "10" + "error: tag alias, '[@zzz]' already registered. + First seen at: file:2 + Redefined at: file:10" contains: "10" @@ -7589,29 +5600,6 @@ hello - - - - Text( "hi there" ).toString() == "hi there" - - - "hi there" == "hi there" - - - - - Text( "hi there", narrow ).toString() == "hi\nthere" - - - "hi -there" -== -"hi -there" - - - - @@ -7623,7 +5611,7 @@ there" - + @@ -8732,7 +6720,7 @@ there"
- + 3.14 @@ -8895,7 +6883,7 @@ there"
- +
@@ -8998,13 +6986,13 @@ there" - + unexpected exception - + thisThrows() == 0 @@ -9018,7 +7006,7 @@ there" - + thisThrows() == 0 @@ -9032,7 +7020,7 @@ there" - + thisThrows() == 0 @@ -9046,7 +7034,7 @@ there" - +
unexpected exception @@ -9058,13 +7046,13 @@ there" - + Uncomment the code in this test to check that it gives a sensible compiler error - + Uncomment the code in this test to check that it gives a sensible compiler error @@ -9236,7 +7224,7 @@ there" - + flag @@ -9274,7 +7262,7 @@ there" - + flag @@ -9385,9 +7373,10 @@ there" -spanner +loose text artifact + - + Previous info should not be seen @@ -9396,7 +7385,20 @@ spanner - + + + + l == std::numeric_limits<long long>::max() + + + 9223372036854775807 (0x) +== +9223372036854775807 (0x) + + + + +
@@ -9410,7 +7412,7 @@ spanner
- + Testing if fib[0] (1) is even @@ -9501,7 +7503,7 @@ spanner - +
@@ -9546,7 +7548,7 @@ spanner
- +
@@ -9590,6 +7592,17 @@ spanner + + + + ti == typeid(int) + + + {?} == {?} + + + + @@ -9612,10 +7625,21 @@ spanner + + + + ptr.get() == 0 + + + 0 == 0 + + + + - Catch::toString( pair ) == "{ { 42, \"Arthur\" }, { \"Ford\", 24 } }" + ::Catch::Detail::stringify( pair ) == "{ { 42, \"Arthur\" }, { \"Ford\", 24 } }" "{ { 42, "Arthur" }, { "Ford", 24 } }" @@ -9631,12 +7655,12 @@ spanner p == 0 - NULL == 0 + 0 == 0 - +
@@ -9673,7 +7697,7 @@ spanner
- replaceInPlace( letters, "b", "z" ) + Catch::replaceInPlace( letters, "b", "z" ) true @@ -9692,7 +7716,7 @@ spanner
- replaceInPlace( letters, "c", "z" ) + Catch::replaceInPlace( letters, "c", "z" ) true @@ -9711,7 +7735,7 @@ spanner
- replaceInPlace( letters, "a", "z" ) + Catch::replaceInPlace( letters, "a", "z" ) true @@ -9730,7 +7754,7 @@ spanner
- replaceInPlace( letters, "g", "z" ) + Catch::replaceInPlace( letters, "g", "z" ) true @@ -9749,7 +7773,7 @@ spanner
- replaceInPlace( letters, letters, "replaced" ) + Catch::replaceInPlace( letters, letters, "replaced" ) true @@ -9768,7 +7792,7 @@ spanner
- !(replaceInPlace( letters, "x", "z" )) + !(Catch::replaceInPlace( letters, "x", "z" )) !false @@ -9787,7 +7811,7 @@ spanner
- replaceInPlace( s, "'", "|'" ) + Catch::replaceInPlace( s, "'", "|'" ) true @@ -9808,7 +7832,7 @@ spanner - + 3 @@ -9822,7 +7846,7 @@ spanner - + hi @@ -9842,7 +7866,7 @@ spanner - Catch::toString(value) == "{ 34, \"xyzzy\" }" + ::Catch::Detail::stringify(value) == "{ 34, \"xyzzy\" }" "{ 34, "xyzzy" }" == "{ 34, "xyzzy" }" @@ -9853,7 +7877,7 @@ spanner - Catch::toString( value ) == "{ 34, \"xyzzy\" }" + ::Catch::Detail::stringify( value ) == "{ 34, \"xyzzy\" }" "{ 34, "xyzzy" }" == "{ 34, "xyzzy" }" @@ -9864,7 +7888,7 @@ spanner - Catch::toString( pr ) == "{ { \"green\", 55 } }" + ::Catch::Detail::stringify( pr ) == "{ { \"green\", 55 } }" "{ { "green", 55 } }" @@ -9874,7 +7898,7 @@ spanner - + std::string( "first" ) == "second" @@ -9885,6 +7909,45 @@ spanner + + + + ::Catch::Detail::stringify( item ) == "StringMaker<has_maker>" + + + "StringMaker<has_maker>" +== +"StringMaker<has_maker>" + + + + + + + + ::Catch::Detail::stringify( item ) == "StringMaker<has_maker_and_operator>" + + + "StringMaker<has_maker_and_operator>" +== +"StringMaker<has_maker_and_operator>" + + + + + + + + ::Catch::Detail::stringify( item ) == "operator<<( has_operator )" + + + "operator<<( has_operator )" +== +"operator<<( has_operator )" + + + + @@ -9929,49 +7992,10 @@ spanner - - - - Catch::toString( item ) == "StringMaker<has_maker>" - - - "StringMaker<has_maker>" -== -"StringMaker<has_maker>" - - - - - - - - Catch::toString( item ) == "toString( has_maker_and_toString )" - - - "toString( has_maker_and_toString )" -== -"toString( has_maker_and_toString )" - - - - - - - - Catch::toString( item ) == "toString( has_toString )" - - - "toString( has_toString )" -== -"toString( has_toString )" - - - - - Catch::toString( v ) == "{ StringMaker<has_maker> }" + ::Catch::Detail::stringify( v ) == "{ StringMaker<has_maker> }" "{ StringMaker<has_maker> }" @@ -9981,10 +8005,58 @@ spanner + + + + ::Catch::Detail::stringify(e0) == "E2/V0" + + + "E2/V0" == "E2/V0" + + + + + ::Catch::Detail::stringify(e1) == "E2/V1" + + + "E2/V1" == "E2/V1" + + + + + ::Catch::Detail::stringify(e3) == "Unknown enum value 10" + + + "Unknown enum value 10" +== +"Unknown enum value 10" + + + + + + + + ::Catch::Detail::stringify(e0) == "0" + + + "{?}" == "0" + + + + + ::Catch::Detail::stringify(e1) == "1" + + + "{?}" == "1" + + + + - Catch::toString(e0) == "E2{0}" + ::Catch::Detail::stringify(e0) == "E2{0}" "E2{0}" == "E2{0}" @@ -9992,7 +8064,7 @@ spanner - Catch::toString(e1) == "E2{1}" + ::Catch::Detail::stringify(e1) == "E2{1}" "E2{1}" == "E2{1}" @@ -10003,7 +8075,7 @@ spanner - Catch::toString(e0) == "0" + ::Catch::Detail::stringify(e0) == "0" "0" == "0" @@ -10011,7 +8083,7 @@ spanner - Catch::toString(e1) == "1" + ::Catch::Detail::stringify(e1) == "1" "1" == "1" @@ -10019,10 +8091,98 @@ spanner - + + + + "{ }" == ::Catch::Detail::stringify(type{}) + + + "{ }" == "{ }" + + + + + "{ }" == ::Catch::Detail::stringify(value) + + + "{ }" == "{ }" + + + + + + + + "1.2f" == ::Catch::Detail::stringify(float(1.2)) + + + "1.2f" == "1.2f" + + + + + "{ 1.2f, 0 }" == ::Catch::Detail::stringify(type{1.2f,0}) + + + "{ 1.2f, 0 }" == "{ 1.2f, 0 }" + + + + + + + + "{ 0 }" == ::Catch::Detail::stringify(type{0}) + + + "{ 0 }" == "{ 0 }" + + + + + + + + "{ 0, 42, \"Catch me\" }" == ::Catch::Detail::stringify(value) + + + "{ 0, 42, "Catch me" }" +== +"{ 0, 42, "Catch me" }" + + + + + + + + "{ \"hello\", \"world\" }" == ::Catch::Detail::stringify(type{"hello","world"}) + + + "{ "hello", "world" }" +== +"{ "hello", "world" }" + + + + + + + + "{ { 42 }, { }, 1.2f }" == ::Catch::Detail::stringify(value) + + + "{ { 42 }, { }, 1.2f }" +== +"{ { 42 }, { }, 1.2f }" + + + + + - Catch::toString(vv) == "{ }" + ::Catch::Detail::stringify(v) == "{ }" "{ }" == "{ }" @@ -10030,7 +8190,28 @@ spanner - Catch::toString(vv) == "{ 42 }" + ::Catch::Detail::stringify(v) == "{ { \"hello\" }, { \"world\" } }" + + + "{ { "hello" }, { "world" } }" +== +"{ { "hello" }, { "world" } }" + + + + + + + + ::Catch::Detail::stringify(vv) == "{ }" + + + "{ }" == "{ }" + + + + + ::Catch::Detail::stringify(vv) == "{ 42 }" "{ 42 }" == "{ 42 }" @@ -10038,7 +8219,34 @@ spanner - Catch::toString(vv) == "{ 42, 250 }" + ::Catch::Detail::stringify(vv) == "{ 42, 250 }" + + + "{ 42, 250 }" == "{ 42, 250 }" + + + + + + + + ::Catch::Detail::stringify(vv) == "{ }" + + + "{ }" == "{ }" + + + + + ::Catch::Detail::stringify(vv) == "{ 42 }" + + + "{ 42 }" == "{ 42 }" + + + + + ::Catch::Detail::stringify(vv) == "{ 42, 250 }" "{ 42, 250 }" == "{ 42, 250 }" @@ -10049,7 +8257,7 @@ spanner - Catch::toString(vv) == "{ }" + ::Catch::Detail::stringify(vv) == "{ }" "{ }" == "{ }" @@ -10057,7 +8265,7 @@ spanner - Catch::toString(vv) == "{ \"hello\" }" + ::Catch::Detail::stringify(vv) == "{ \"hello\" }" "{ "hello" }" == "{ "hello" }" @@ -10065,7 +8273,7 @@ spanner - Catch::toString(vv) == "{ \"hello\", \"world\" }" + ::Catch::Detail::stringify(vv) == "{ \"hello\", \"world\" }" "{ "hello", "world" }" @@ -10238,7 +8446,7 @@ spanner
- + - + diff --git a/projects/SelfTest/Benchmark.tests.cpp b/projects/SelfTest/Benchmark.tests.cpp new file mode 100644 index 00000000..ddf69504 --- /dev/null +++ b/projects/SelfTest/Benchmark.tests.cpp @@ -0,0 +1,43 @@ +#include "catch.hpp" + +#include + +TEST_CASE( "benchmarked", "[!benchmark]" ) { + + static const int size = 100; + + std::vector v; + std::map m; + + BENCHMARK( "Load up a vector" ) { + v = std::vector(); + for(int i =0; i < size; ++i ) + v.push_back( i ); + } + REQUIRE( v.size() == size ); + + BENCHMARK( "Load up a map" ) { + m = std::map(); + for(int i =0; i < size; ++i ) + m.insert( { i, i+1 } ); + } + REQUIRE( m.size() == size ); + + BENCHMARK( "Reserved vector" ) { + v = std::vector(); + v.reserve(size); + for(int i =0; i < size; ++i ) + v.push_back( i ); + } + REQUIRE( v.size() == size ); + + int array[size]; + BENCHMARK( "A fixed size array that should require no allocations" ) { + for(int i =0; i < size; ++i ) + array[i] = i; + } + int sum = 0; + for(int i =0; i < size; ++i ) + sum += array[i]; + REQUIRE( sum > size ); +} diff --git a/projects/SelfTest/CmdLineTests.cpp b/projects/SelfTest/CmdLineTests.cpp index 83b79fde..c375d41b 100644 --- a/projects/SelfTest/CmdLineTests.cpp +++ b/projects/SelfTest/CmdLineTests.cpp @@ -7,60 +7,60 @@ */ #include "catch.hpp" -#include "internal/catch_test_spec_parser.hpp" +#include "internal/catch_test_spec_parser.h" #ifdef __clang__ # pragma clang diagnostic ignored "-Wc++98-compat" #endif -inline Catch::TestCase fakeTestCase( const char* name, const char* desc = "" ){ return Catch::makeTestCase( CATCH_NULL, "", name, desc, CATCH_INTERNAL_LINEINFO ); } +inline Catch::TestCase fakeTestCase( const char* name, const char* desc = "" ){ return Catch::makeTestCase( nullptr, "", name, desc, CATCH_INTERNAL_LINEINFO ); } -TEST_CASE( "Parse test names and tags", "" ) { +TEST_CASE( "Parse test names and tags" ) { using Catch::parseTestSpec; using Catch::TestSpec; - Catch::TestCase tcA = fakeTestCase( "a", "" ); + Catch::TestCase tcA = fakeTestCase( "a" ); Catch::TestCase tcB = fakeTestCase( "b", "[one][x]" ); Catch::TestCase tcC = fakeTestCase( "longer name with spaces", "[two][three][.][x]" ); - Catch::TestCase tcD = fakeTestCase( "zlonger name with spacesz", "" ); + Catch::TestCase tcD = fakeTestCase( "zlonger name with spacesz" ); - SECTION( "Empty test spec should have no filters", "" ) { + SECTION( "Empty test spec should have no filters" ) { TestSpec spec; CHECK( spec.hasFilters() == false ); CHECK( spec.matches( tcA ) == false ); CHECK( spec.matches( tcB ) == false ); } - SECTION( "Test spec from empty string should have no filters", "" ) { + SECTION( "Test spec from empty string should have no filters" ) { TestSpec spec = parseTestSpec( "" ); CHECK( spec.hasFilters() == false ); CHECK( spec.matches(tcA ) == false ); CHECK( spec.matches( tcB ) == false ); } - SECTION( "Test spec from just a comma should have no filters", "" ) { + SECTION( "Test spec from just a comma should have no filters" ) { TestSpec spec = parseTestSpec( "," ); CHECK( spec.hasFilters() == false ); CHECK( spec.matches( tcA ) == false ); CHECK( spec.matches( tcB ) == false ); } - SECTION( "Test spec from name should have one filter", "" ) { + SECTION( "Test spec from name should have one filter" ) { TestSpec spec = parseTestSpec( "b" ); CHECK( spec.hasFilters() == true ); CHECK( spec.matches( tcA ) == false ); CHECK( spec.matches( tcB ) == true ); } - SECTION( "Test spec from quoted name should have one filter", "" ) { + SECTION( "Test spec from quoted name should have one filter" ) { TestSpec spec = parseTestSpec( "\"b\"" ); CHECK( spec.hasFilters() == true ); CHECK( spec.matches( tcA ) == false ); CHECK( spec.matches( tcB ) == true ); } - SECTION( "Test spec from name should have one filter", "" ) { + SECTION( "Test spec from name should have one filter" ) { TestSpec spec = parseTestSpec( "b" ); CHECK( spec.hasFilters() == true ); CHECK( spec.matches( tcA ) == false ); @@ -68,7 +68,7 @@ TEST_CASE( "Parse test names and tags", "" ) { CHECK( spec.matches( tcC ) == false ); } - SECTION( "Wildcard at the start", "" ) { + SECTION( "Wildcard at the start" ) { TestSpec spec = parseTestSpec( "*spaces" ); CHECK( spec.hasFilters() == true ); CHECK( spec.matches( tcA ) == false ); @@ -77,7 +77,7 @@ TEST_CASE( "Parse test names and tags", "" ) { CHECK( spec.matches( tcD ) == false ); CHECK( parseTestSpec( "*a" ).matches( tcA ) == true ); } - SECTION( "Wildcard at the end", "" ) { + SECTION( "Wildcard at the end" ) { TestSpec spec = parseTestSpec( "long*" ); CHECK( spec.hasFilters() == true ); CHECK( spec.matches( tcA ) == false ); @@ -86,7 +86,7 @@ TEST_CASE( "Parse test names and tags", "" ) { CHECK( spec.matches( tcD ) == false ); CHECK( parseTestSpec( "a*" ).matches( tcA ) == true ); } - SECTION( "Wildcard at both ends", "" ) { + SECTION( "Wildcard at both ends" ) { TestSpec spec = parseTestSpec( "*name*" ); CHECK( spec.hasFilters() == true ); CHECK( spec.matches( tcA ) == false ); @@ -95,25 +95,25 @@ TEST_CASE( "Parse test names and tags", "" ) { CHECK( spec.matches( tcD ) == true ); CHECK( parseTestSpec( "*a*" ).matches( tcA ) == true ); } - SECTION( "Redundant wildcard at the start", "" ) { + SECTION( "Redundant wildcard at the start" ) { TestSpec spec = parseTestSpec( "*a" ); CHECK( spec.hasFilters() == true ); CHECK( spec.matches( tcA ) == true ); CHECK( spec.matches( tcB ) == false ); } - SECTION( "Redundant wildcard at the end", "" ) { + SECTION( "Redundant wildcard at the end" ) { TestSpec spec = parseTestSpec( "a*" ); CHECK( spec.hasFilters() == true ); CHECK( spec.matches( tcA ) == true ); CHECK( spec.matches( tcB ) == false ); } - SECTION( "Redundant wildcard at both ends", "" ) { + SECTION( "Redundant wildcard at both ends" ) { TestSpec spec = parseTestSpec( "*a*" ); CHECK( spec.hasFilters() == true ); CHECK( spec.matches( tcA ) == true ); CHECK( spec.matches( tcB ) == false ); } - SECTION( "Wildcard at both ends, redundant at start", "" ) { + SECTION( "Wildcard at both ends, redundant at start" ) { TestSpec spec = parseTestSpec( "*longer*" ); CHECK( spec.hasFilters() == true ); CHECK( spec.matches( tcA ) == false ); @@ -121,7 +121,7 @@ TEST_CASE( "Parse test names and tags", "" ) { CHECK( spec.matches( tcC ) == true ); CHECK( spec.matches( tcD ) == true ); } - SECTION( "Just wildcard", "" ) { + SECTION( "Just wildcard" ) { TestSpec spec = parseTestSpec( "*" ); CHECK( spec.hasFilters() == true ); CHECK( spec.matches( tcA ) == true ); @@ -130,35 +130,35 @@ TEST_CASE( "Parse test names and tags", "" ) { CHECK( spec.matches( tcD ) == true ); } - SECTION( "Single tag", "" ) { + SECTION( "Single tag" ) { TestSpec spec = parseTestSpec( "[one]" ); CHECK( spec.hasFilters() == true ); CHECK( spec.matches( tcA ) == false ); CHECK( spec.matches( tcB ) == true ); CHECK( spec.matches( tcC ) == false ); } - SECTION( "Single tag, two matches", "" ) { + SECTION( "Single tag, two matches" ) { TestSpec spec = parseTestSpec( "[x]" ); CHECK( spec.hasFilters() == true ); CHECK( spec.matches( tcA ) == false ); CHECK( spec.matches( tcB ) == true ); CHECK( spec.matches( tcC ) == true ); } - SECTION( "Two tags", "" ) { + SECTION( "Two tags" ) { TestSpec spec = parseTestSpec( "[two][x]" ); CHECK( spec.hasFilters() == true ); CHECK( spec.matches( tcA ) == false ); CHECK( spec.matches( tcB ) == false ); CHECK( spec.matches( tcC ) == true ); } - SECTION( "Two tags, spare separated", "" ) { + SECTION( "Two tags, spare separated" ) { TestSpec spec = parseTestSpec( "[two] [x]" ); CHECK( spec.hasFilters() == true ); CHECK( spec.matches( tcA ) == false ); CHECK( spec.matches( tcB ) == false ); CHECK( spec.matches( tcC ) == true ); } - SECTION( "Wildcarded name and tag", "" ) { + SECTION( "Wildcarded name and tag" ) { TestSpec spec = parseTestSpec( "*name*[x]" ); CHECK( spec.hasFilters() == true ); CHECK( spec.matches( tcA ) == false ); @@ -166,21 +166,21 @@ TEST_CASE( "Parse test names and tags", "" ) { CHECK( spec.matches( tcC ) == true ); CHECK( spec.matches( tcD ) == false ); } - SECTION( "Single tag exclusion", "" ) { + SECTION( "Single tag exclusion" ) { TestSpec spec = parseTestSpec( "~[one]" ); CHECK( spec.hasFilters() == true ); CHECK( spec.matches( tcA ) == true ); CHECK( spec.matches( tcB ) == false ); CHECK( spec.matches( tcC ) == true ); } - SECTION( "One tag exclusion and one tag inclusion", "" ) { + SECTION( "One tag exclusion and one tag inclusion" ) { TestSpec spec = parseTestSpec( "~[two][x]" ); CHECK( spec.hasFilters() == true ); CHECK( spec.matches( tcA ) == false ); CHECK( spec.matches( tcB ) == true ); CHECK( spec.matches( tcC ) == false ); } - SECTION( "One tag exclusion and one wldcarded name inclusion", "" ) { + SECTION( "One tag exclusion and one wldcarded name inclusion" ) { TestSpec spec = parseTestSpec( "~[two]*name*" ); CHECK( spec.hasFilters() == true ); CHECK( spec.matches( tcA ) == false ); @@ -188,7 +188,7 @@ TEST_CASE( "Parse test names and tags", "" ) { CHECK( spec.matches( tcC ) == false ); CHECK( spec.matches( tcD ) == true ); } - SECTION( "One tag exclusion, using exclude:, and one wldcarded name inclusion", "" ) { + SECTION( "One tag exclusion, using exclude:, and one wldcarded name inclusion" ) { TestSpec spec = parseTestSpec( "exclude:[two]*name*" ); CHECK( spec.hasFilters() == true ); CHECK( spec.matches( tcA ) == false ); @@ -196,7 +196,7 @@ TEST_CASE( "Parse test names and tags", "" ) { CHECK( spec.matches( tcC ) == false ); CHECK( spec.matches( tcD ) == true ); } - SECTION( "name exclusion", "" ) { + SECTION( "name exclusion" ) { TestSpec spec = parseTestSpec( "~b" ); CHECK( spec.hasFilters() == true ); CHECK( spec.matches( tcA ) == true ); @@ -204,7 +204,7 @@ TEST_CASE( "Parse test names and tags", "" ) { CHECK( spec.matches( tcC ) == true ); CHECK( spec.matches( tcD ) == true ); } - SECTION( "wildcarded name exclusion", "" ) { + SECTION( "wildcarded name exclusion" ) { TestSpec spec = parseTestSpec( "~*name*" ); CHECK( spec.hasFilters() == true ); CHECK( spec.matches( tcA ) == true ); @@ -212,7 +212,7 @@ TEST_CASE( "Parse test names and tags", "" ) { CHECK( spec.matches( tcC ) == false ); CHECK( spec.matches( tcD ) == false ); } - SECTION( "wildcarded name exclusion with tag inclusion", "" ) { + SECTION( "wildcarded name exclusion with tag inclusion" ) { TestSpec spec = parseTestSpec( "~*name*,[three]" ); CHECK( spec.hasFilters() == true ); CHECK( spec.matches( tcA ) == true ); @@ -220,7 +220,7 @@ TEST_CASE( "Parse test names and tags", "" ) { CHECK( spec.matches( tcC ) == true ); CHECK( spec.matches( tcD ) == false ); } - SECTION( "wildcarded name exclusion, using exclude:, with tag inclusion", "" ) { + SECTION( "wildcarded name exclusion, using exclude:, with tag inclusion" ) { TestSpec spec = parseTestSpec( "exclude:*name*,[three]" ); CHECK( spec.hasFilters() == true ); CHECK( spec.matches( tcA ) == true ); @@ -228,7 +228,7 @@ TEST_CASE( "Parse test names and tags", "" ) { CHECK( spec.matches( tcC ) == true ); CHECK( spec.matches( tcD ) == false ); } - SECTION( "two wildcarded names", "" ) { + SECTION( "two wildcarded names" ) { TestSpec spec = parseTestSpec( "\"longer*\"\"*spaces\"" ); CHECK( spec.hasFilters() == true ); CHECK( spec.matches( tcA ) == false ); @@ -236,7 +236,7 @@ TEST_CASE( "Parse test names and tags", "" ) { CHECK( spec.matches( tcC ) == true ); CHECK( spec.matches( tcD ) == false ); } - SECTION( "empty tag", "" ) { + SECTION( "empty tag" ) { TestSpec spec = parseTestSpec( "[]" ); CHECK( spec.hasFilters() == false ); CHECK( spec.matches( tcA ) == false ); @@ -244,7 +244,7 @@ TEST_CASE( "Parse test names and tags", "" ) { CHECK( spec.matches( tcC ) == false ); CHECK( spec.matches( tcD ) == false ); } - SECTION( "empty quoted name", "" ) { + SECTION( "empty quoted name" ) { TestSpec spec = parseTestSpec( "\"\"" ); CHECK( spec.hasFilters() == false ); CHECK( spec.matches( tcA ) == false ); @@ -252,7 +252,7 @@ TEST_CASE( "Parse test names and tags", "" ) { CHECK( spec.matches( tcC ) == false ); CHECK( spec.matches( tcD ) == false ); } - SECTION( "quoted string followed by tag exclusion", "" ) { + SECTION( "quoted string followed by tag exclusion" ) { TestSpec spec = parseTestSpec( "\"*name*\"~[.]" ); CHECK( spec.hasFilters() == true ); CHECK( spec.matches( tcA ) == false ); diff --git a/projects/SelfTest/CompilationTests.cpp b/projects/SelfTest/CompilationTests.cpp index ed1978f3..4dd260f8 100644 --- a/projects/SelfTest/CompilationTests.cpp +++ b/projects/SelfTest/CompilationTests.cpp @@ -41,9 +41,11 @@ bool templated_tests(T t) { REQUIRE(a == t); CHECK(a == t); REQUIRE_THROWS(throws_int(true)); - CHECK_THROWS_AS(throws_int(true), const int&); + CHECK_THROWS_AS(throws_int(true), int); REQUIRE_NOTHROW(throws_int(false)); +#ifndef CATCH_CONFIG_DISABLE_MATCHERS REQUIRE_THAT("aaa", Catch::EndsWith("aaa")); +#endif return true; } @@ -51,8 +53,17 @@ TEST_CASE("#833") { REQUIRE(templated_tests(3)); } +#ifdef __clang__ +#pragma clang diagnostic push +#pragma clang diagnostic ignored "-Wunused-function" +#endif +#ifdef __GNUC__ +// Note that because -~GCC~-, this warning cannot be silenced temporarily, by pushing diagnostic stack... +// Luckily it is firing in test files and thus can be silenced for the whole file, without losing much. +#pragma GCC diagnostic ignored "-Wunused-function" +#endif + // Test containing example where original stream insertable check breaks compilation -#if defined (CATCH_CONFIG_CPP11_STREAM_INSERTABLE_CHECK) namespace { struct A {}; std::ostream& operator<< (std::ostream &o, const A &) { return o << 0; } @@ -64,9 +75,13 @@ namespace { B f (); std::ostream g (); } +#ifdef __clang__ +#pragma clang diagnostic pop +#endif TEST_CASE( "#872" ) { + A dummy; + CAPTURE( dummy ); B x; REQUIRE (x == 4); } -#endif diff --git a/projects/SelfTest/ConditionTests.cpp b/projects/SelfTest/ConditionTests.cpp index 2eb99ba3..4bc09817 100644 --- a/projects/SelfTest/ConditionTests.cpp +++ b/projects/SelfTest/ConditionTests.cpp @@ -6,27 +6,22 @@ * file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) */ #ifdef __clang__ +# pragma clang diagnostic push # pragma clang diagnostic ignored "-Wpadded" -# pragma clang diagnostic ignored "-Wc++98-compat" +# pragma clang diagnostic ignored "-Wdouble-promotion" #endif #include "catch.hpp" #include #include +#include struct TestData { - TestData() - : int_seven( 7 ), - str_hello( "hello" ), - float_nine_point_one( 9.1f ), - double_pi( 3.1415926535 ) - {} - - int int_seven; - std::string str_hello; - float float_nine_point_one; - double double_pi; + int int_seven = 7; + std::string str_hello = "hello"; + float float_nine_point_one = 9.1f; + double double_pi = 3.1415926535; }; @@ -37,14 +32,13 @@ struct TestDef { TestDef& operator[]( const std::string& ) { return *this; } - }; // The "failing" tests all use the CHECK macro, which continues if the specific test fails. // This allows us to see all results, even if an earlier check fails // Equality tests -TEST_CASE( "Equality checks that should succeed", "" ) +TEST_CASE( "Equality checks that should succeed" ) { TestDef td; @@ -84,7 +78,7 @@ TEST_CASE( "Equality checks that should fail", "[.][failing][!mayfail]" ) CHECK( x == Approx( 1.301 ) ); } -TEST_CASE( "Inequality checks that should succeed", "" ) +TEST_CASE( "Inequality checks that should succeed" ) { TestData data; @@ -113,7 +107,7 @@ TEST_CASE( "Inequality checks that should fail", "[.][failing][!shouldfail]" ) } // Ordering comparison tests -TEST_CASE( "Ordering comparison checks that should succeed", "" ) +TEST_CASE( "Ordering comparison checks that should succeed" ) { TestData data; @@ -169,8 +163,13 @@ TEST_CASE( "Ordering comparison checks that should fail", "[.][failing]" ) CHECK( data.str_hello <= "a" ); } +#ifdef __clang__ +# pragma clang diagnostic pop +#endif + + // Comparisons with int literals -TEST_CASE( "Comparisons with int literals don't warn when mixing signed/ unsigned", "" ) +TEST_CASE( "Comparisons with int literals don't warn when mixing signed/ unsigned" ) { int i = 1; unsigned int ui = 2; @@ -193,7 +192,7 @@ TEST_CASE( "Comparisons with int literals don't warn when mixing signed/ unsigne REQUIRE( 5 == c ); REQUIRE( 6 == uc ); - REQUIRE( (std::numeric_limits::max)() > ul ); + REQUIRE( (std::numeric_limits::max)() > ul ); } // Disable warnings about sign conversions for the next two tests @@ -208,7 +207,7 @@ TEST_CASE( "Comparisons with int literals don't warn when mixing signed/ unsigne #pragma warning(disable:4389) // '==' : signed/unsigned mismatch #endif -TEST_CASE( "comparisons between int variables", "" ) +TEST_CASE( "comparisons between int variables" ) { long long_var = 1L; unsigned char unsigned_char_var = 1; @@ -222,7 +221,7 @@ TEST_CASE( "comparisons between int variables", "" ) REQUIRE( long_var == unsigned_long_var ); } -TEST_CASE( "comparisons between const int variables", "" ) +TEST_CASE( "comparisons between const int variables" ) { const unsigned char unsigned_char_var = 1; const unsigned short unsigned_short_var = 1; @@ -235,7 +234,7 @@ TEST_CASE( "comparisons between const int variables", "" ) REQUIRE( unsigned_long_var == 1 ); } -TEST_CASE( "Comparisons between unsigned ints and negative signed ints match c++ standard behaviour", "" ) +TEST_CASE( "Comparisons between unsigned ints and negative signed ints match c++ standard behaviour" ) { CHECK( ( -1 > 2u ) ); CHECK( -1 > 2u ); @@ -248,7 +247,7 @@ TEST_CASE( "Comparisons between unsigned ints and negative signed ints match c++ CHECK( minInt > 2u ); } -TEST_CASE( "Comparisons between ints where one side is computed", "" ) +TEST_CASE( "Comparisons between ints where one side is computed" ) { CHECK( 54 == 6*9 ); } @@ -257,32 +256,32 @@ TEST_CASE( "Comparisons between ints where one side is computed", "" ) #pragma GCC diagnostic pop #endif -inline const char* returnsConstNull(){ return CATCH_NULL; } -inline char* returnsNull(){ return CATCH_NULL; } +inline const char* returnsConstNull(){ return nullptr; } +inline char* returnsNull(){ return nullptr; } -TEST_CASE( "Pointers can be compared to null", "" ) +TEST_CASE( "Pointers can be compared to null" ) { - TestData* p = CATCH_NULL; - TestData* pNULL = CATCH_NULL; + TestData* p = nullptr; + TestData* pNULL = nullptr; - REQUIRE( p == CATCH_NULL ); + REQUIRE( p == nullptr ); REQUIRE( p == pNULL ); TestData data; p = &data; - REQUIRE( p != CATCH_NULL ); + REQUIRE( p != nullptr ); const TestData* cp = p; - REQUIRE( cp != CATCH_NULL ); + REQUIRE( cp != nullptr ); const TestData* const cpc = p; - REQUIRE( cpc != CATCH_NULL ); + REQUIRE( cpc != nullptr ); - REQUIRE( returnsNull() == CATCH_NULL ); - REQUIRE( returnsConstNull() == CATCH_NULL ); + REQUIRE( returnsNull() == nullptr ); + REQUIRE( returnsConstNull() == nullptr ); - REQUIRE( CATCH_NULL != p ); + REQUIRE( nullptr != p ); } // Not (!) tests @@ -292,7 +291,7 @@ TEST_CASE( "Pointers can be compared to null", "" ) // is detected and a warning issued. // An alternative form of the macros (CHECK_FALSE and REQUIRE_FALSE) can be used instead to capture // the operand value. -TEST_CASE( "'Not' checks that should succeed", "" ) +TEST_CASE( "'Not' checks that should succeed" ) { bool falseValue = false; diff --git a/projects/SelfTest/DecompositionTests.cpp b/projects/SelfTest/DecompositionTests.cpp index f2e04260..1b44b602 100644 --- a/projects/SelfTest/DecompositionTests.cpp +++ b/projects/SelfTest/DecompositionTests.cpp @@ -7,6 +7,7 @@ */ #include +#include struct truthy { truthy(bool b):m_value(b){} @@ -26,3 +27,9 @@ std::ostream& operator<<(std::ostream& o, truthy) { TEST_CASE( "Reconstruction should be based on stringification: #914" , "[Decomposition][failing][.]") { CHECK(truthy(false)); } + +TEST_CASE("#1005: Comparing pointer to int and long (NULL can be either on various systems)", "[Decomposition]") { + FILE* fptr = nullptr; + REQUIRE(fptr == 0); + REQUIRE(fptr == 0l); +} diff --git a/projects/SelfTest/EnumToString.cpp b/projects/SelfTest/EnumToString.cpp index f591520f..be0da03a 100644 --- a/projects/SelfTest/EnumToString.cpp +++ b/projects/SelfTest/EnumToString.cpp @@ -1,54 +1,46 @@ #include "catch.hpp" -/* - TODO: maybe ought to check that user-provided specialisations of - Catch::toString also do the right thing -*/ // Enum without user-provided stream operator enum Enum1 { Enum1Value0, Enum1Value1 }; TEST_CASE( "toString(enum)", "[toString][enum]" ) { Enum1 e0 = Enum1Value0; - CHECK( Catch::toString(e0) == "0" ); + CHECK( ::Catch::Detail::stringify(e0) == "0" ); Enum1 e1 = Enum1Value1; - CHECK( Catch::toString(e1) == "1" ); + CHECK( ::Catch::Detail::stringify(e1) == "1" ); } // Enum with user-provided stream operator enum Enum2 { Enum2Value0, Enum2Value1 }; -inline std::ostream& operator<<( std::ostream& os, Enum2 v ) { +std::ostream& operator<<( std::ostream& os, Enum2 v ) { return os << "E2{" << static_cast(v) << "}"; } TEST_CASE( "toString(enum w/operator<<)", "[toString][enum]" ) { Enum2 e0 = Enum2Value0; - CHECK( Catch::toString(e0) == "E2{0}" ); + CHECK( ::Catch::Detail::stringify(e0) == "E2{0}" ); Enum2 e1 = Enum2Value1; - CHECK( Catch::toString(e1) == "E2{1}" ); + CHECK( ::Catch::Detail::stringify(e1) == "E2{1}" ); } -#if defined(CATCH_CPP11_OR_GREATER) -#ifdef __clang__ -#pragma clang diagnostic push -#pragma clang diagnostic ignored "-Wc++98-compat" -#endif - // Enum class without user-provided stream operator enum class EnumClass1 { EnumClass1Value0, EnumClass1Value1 }; -TEST_CASE( "toString(enum class)", "[toString][enum][enumClass][c++11][.]" ) { +// This fails, but has been hidden for a while - not sure if it's a regression or if it never worked +// - need to investigate +TEST_CASE( "toString(enum class)", "[toString][enum][enumClass][.]" ) { EnumClass1 e0 = EnumClass1::EnumClass1Value0; - CHECK( Catch::toString(e0) == "0" ); + CHECK( ::Catch::Detail::stringify(e0) == "0" ); EnumClass1 e1 = EnumClass1::EnumClass1Value1; - CHECK( Catch::toString(e1) == "1" ); + CHECK( ::Catch::Detail::stringify(e1) == "1" ); } // Enum class with user-provided stream operator enum class EnumClass2 : short { EnumClass2Value0, EnumClass2Value1 }; -inline std::ostream& operator<<( std::ostream& os, EnumClass2 e2 ) { +std::ostream& operator<<( std::ostream& os, EnumClass2 e2 ) { switch( static_cast( e2 ) ) { case static_cast( EnumClass2::EnumClass2Value0 ): return os << "E2/V0"; @@ -59,18 +51,13 @@ inline std::ostream& operator<<( std::ostream& os, EnumClass2 e2 ) { } } -TEST_CASE( "toString(enum class w/operator<<)", "[toString][enum][enumClass][c++11][.]" ) { +TEST_CASE( "toString(enum class w/operator<<)", "[toString][enum][enumClass]" ) { EnumClass2 e0 = EnumClass2::EnumClass2Value0; - CHECK( Catch::toString(e0) == "E2/V0" ); + CHECK( ::Catch::Detail::stringify(e0) == "E2/V0" ); EnumClass2 e1 = EnumClass2::EnumClass2Value1; - CHECK( Catch::toString(e1) == "E2/V1" ); + CHECK( ::Catch::Detail::stringify(e1) == "E2/V1" ); EnumClass2 e3 = static_cast(10); - CHECK( Catch::toString(e3) == "Unknown enum value 10" ); + CHECK( ::Catch::Detail::stringify(e3) == "Unknown enum value 10" ); } -#ifdef __clang__ -#pragma clang diagnostic pop -#endif -#endif // CATCH_CPP11_OR_GREATER - diff --git a/projects/SelfTest/ExceptionTests.cpp b/projects/SelfTest/ExceptionTests.cpp index 8ba5cb54..4d092f44 100644 --- a/projects/SelfTest/ExceptionTests.cpp +++ b/projects/SelfTest/ExceptionTests.cpp @@ -14,6 +14,10 @@ #ifdef _MSC_VER #pragma warning(disable:4702) // Unreachable code -- MSVC 19 (VS 2015) sees right through the indirection #endif +#ifdef __clang__ +#pragma clang diagnostic push +#pragma clang diagnostic ignored "-Wweak-vtables" +#endif namespace { @@ -59,7 +63,7 @@ TEST_CASE( "An unchecked exception reports the line of the last assertion", "[.] TEST_CASE( "When unchecked exceptions are thrown from sections they are always failures", "[.][failing][!throws]" ) { - SECTION( "section name", "" ) + SECTION( "section name" ) { if( Catch::alwaysTrue() ) throw std::domain_error( "unexpected exception" ); @@ -120,7 +124,7 @@ public: CustomStdException( const std::string& msg ) : m_msg( msg ) {} - ~CustomStdException() CATCH_NOEXCEPT {} + ~CustomStdException() noexcept {} std::string getMessage() const { @@ -144,7 +148,7 @@ CATCH_TRANSLATE_EXCEPTION( CustomStdException& ex ) CATCH_TRANSLATE_EXCEPTION( double& ex ) { - return Catch::toString( ex ); + return Catch::Detail::stringify( ex ); } TEST_CASE("Non-std exceptions can be translated", "[.][failing][!throws]" ) @@ -181,14 +185,7 @@ TEST_CASE( "Unexpected exceptions can be translated", "[.][failing][!throws]" ) throw double( 3.14 ); } -inline int thisFunctionNotImplemented( int ) { - CATCH_NOT_IMPLEMENTED; -} - -TEST_CASE( "NotImplemented exception", "[!throws]" ) -{ - REQUIRE_THROWS( thisFunctionNotImplemented( 7 ) ); -} +#ifndef CATCH_CONFIG_DISABLE_MATCHERS TEST_CASE( "Exception messages can be tested for", "[!throws]" ) { using namespace Catch::Matchers; @@ -204,13 +201,15 @@ TEST_CASE( "Exception messages can be tested for", "[!throws]" ) { } } +#endif + TEST_CASE( "Mismatching exception messages failing the test", "[.][failing][!throws]" ) { REQUIRE_THROWS_WITH( thisThrows(), "expected exception" ); REQUIRE_THROWS_WITH( thisThrows(), "should fail" ); REQUIRE_THROWS_WITH( thisThrows(), "expected exception" ); } -TEST_CASE( "#748 - captures with unexpected exceptions", "[!shouldfail][!throws]" ) { +TEST_CASE( "#748 - captures with unexpected exceptions", "[.][failing][!throws][!shouldfail]" ) { int answer = 42; CAPTURE( answer ); // the message should be printed on the first two sections but not on the third @@ -224,3 +223,7 @@ TEST_CASE( "#748 - captures with unexpected exceptions", "[!shouldfail][!throws] REQUIRE_THROWS( thisThrows() ); } } + +#ifdef __clang__ +#pragma clang diagnostic pop +#endif diff --git a/projects/SelfTest/GeneratorTests.cpp b/projects/SelfTest/GeneratorTests.cpp deleted file mode 100644 index af08b1d3..00000000 --- a/projects/SelfTest/GeneratorTests.cpp +++ /dev/null @@ -1,42 +0,0 @@ -/* - * Created by Phil on 28/01/2011. - * Copyright 2011 Two Blue Cubes Ltd. All rights reserved. - * - * Distributed under the Boost Software License, Version 1.0. (See accompanying - * file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) - */ - -// This define means we have to prefix all the CATCH macros with CATCH_ -// We're using it here to test it out -#define CATCH_CONFIG_PREFIX_ALL -#include "catch.hpp" - -inline int multiply( int a, int b ) -{ - return a*b; -} - -CATCH_TEST_CASE( "Generators over two ranges", "[generators]" ) -{ - using namespace Catch::Generators; - - int i = CATCH_GENERATE( between( 1, 5 ).then( values( 15, 20, 21 ).then( 36 ) ) ); - int j = CATCH_GENERATE( between( 100, 107 ) ); - - CATCH_REQUIRE( multiply( i, 2 ) == i*2 ); - CATCH_REQUIRE( multiply( j, 2 ) == j*2 ); -} - -struct IntPair { int first, second; }; - -CATCH_TEST_CASE( "Generator over a range of pairs", "[generators]" ) -{ - using namespace Catch::Generators; - - IntPair p[] = { { 0, 1 }, { 2, 3 } }; - - IntPair* i = CATCH_GENERATE( between( p, &p[1] ) ); - - CATCH_REQUIRE( i->first == i->second-1 ); - -} diff --git a/projects/SelfTest/MatchersTests.cpp b/projects/SelfTest/MatchersTests.cpp index 7bcd3cf8..2a9040a8 100644 --- a/projects/SelfTest/MatchersTests.cpp +++ b/projects/SelfTest/MatchersTests.cpp @@ -8,6 +8,14 @@ #include "catch.hpp" +#ifdef __clang__ +#pragma clang diagnostic push +#pragma clang diagnostic ignored "-Wweak-vtables" +#pragma clang diagnostic ignored "-Wpadded" +#endif + +#ifndef CATCH_CONFIG_DISABLE_MATCHERS + inline const char* testStringForMatching() { return "this string contains 'abc' as a substring"; @@ -48,16 +56,6 @@ TEST_CASE("Equals string matcher", "[.][failing][matchers]") CHECK_THAT( testStringForMatching(), Equals( "something else" ) ); } -TEST_CASE("AllOf matcher", "[matchers]") -{ - CHECK_THAT( testStringForMatching(), AllOf( Catch::Contains( "string" ), Catch::Contains( "abc" ) ) ); -} -TEST_CASE("AnyOf matcher", "[matchers]") -{ - CHECK_THAT( testStringForMatching(), AnyOf( Catch::Contains( "string" ), Catch::Contains( "not there" ) ) ); - CHECK_THAT( testStringForMatching(), AnyOf( Catch::Contains( "not there" ), Catch::Contains( "string" ) ) ); -} - TEST_CASE("Equals", "[matchers]") { CHECK_THAT( testStringForMatching(), Equals( "this string contains 'abc' as a substring" ) ); @@ -166,3 +164,68 @@ TEST_CASE( "Vector matchers that fail", "[matchers][vector][.][failing]" ) { CHECK_THAT( v, Equals( empty ) ); } } + +#ifdef _MSC_VER +#pragma warning(disable:4702) // Unreachable code -- MSVC 19 (VS 2015) sees right through the indirection +#endif + +#include + +struct SpecialException : std::exception { + SpecialException(int i_):i(i_) {} + int i; +}; + +void doesNotThrow() {} + +[[noreturn]] +void throws(int i) { + throw SpecialException{ i }; +} + +[[noreturn]] +void throwsAsInt(int i) { + throw i; +} + +class ExceptionMatcher : public Catch::MatcherBase { + int m_expected; +public: + ExceptionMatcher(int i):m_expected(i) {} + bool match(SpecialException const& se) const override { + return se.i == m_expected; + } + + std::string describe() const override { + std::ostringstream ss; + ss << "special exception has value of " << m_expected; + return ss.str(); + } +}; + + +TEST_CASE( "Exception matchers that succeed", "[matchers][exceptions][!throws]" ) { + CHECK_THROWS_MATCHES(throws(1), SpecialException, ExceptionMatcher{ 1 }); + REQUIRE_THROWS_MATCHES(throws(2), SpecialException, ExceptionMatcher{ 2 }); +} + +TEST_CASE("Exception matchers that fail", "[matchers][exceptions][!throws][.failing]") { + SECTION("No exception") { + CHECK_THROWS_MATCHES(doesNotThrow(), SpecialException, ExceptionMatcher{ 1 }); + REQUIRE_THROWS_MATCHES(doesNotThrow(), SpecialException, ExceptionMatcher{ 1 }); + } + SECTION("Type mismatch") { + CHECK_THROWS_MATCHES(throwsAsInt(1), SpecialException, ExceptionMatcher{ 1 }); + REQUIRE_THROWS_MATCHES(throwsAsInt(1), SpecialException, ExceptionMatcher{ 1 }); + } + SECTION("Contents are wrong") { + CHECK_THROWS_MATCHES(throws(3), SpecialException, ExceptionMatcher{ 1 }); + REQUIRE_THROWS_MATCHES(throws(4), SpecialException, ExceptionMatcher{ 1 }); + } +} + +#endif // CATCH_CONFIG_DISABLE_MATCHERS + +#ifdef __clang__ +#pragma clang diagnostic pop +#endif diff --git a/projects/SelfTest/MessageTests.cpp b/projects/SelfTest/MessageTests.cpp index 047bead7..6082a4e6 100644 --- a/projects/SelfTest/MessageTests.cpp +++ b/projects/SelfTest/MessageTests.cpp @@ -13,26 +13,23 @@ #pragma clang diagnostic ignored "-Wc++98-compat-pedantic" #endif -TEST_CASE( "INFO and WARN do not abort tests", "[messages][.]" ) -{ +TEST_CASE( "INFO and WARN do not abort tests", "[messages][.]" ) { INFO( "this is a " << "message" ); // This should output the message if a failure occurs WARN( "this is a " << "warning" ); // This should always output the message but then continue } -TEST_CASE( "SUCCEED counts as a test pass", "[messages]" ) -{ + +TEST_CASE( "SUCCEED counts as a test pass", "[messages]" ) { SUCCEED( "this is a " << "success" ); } -TEST_CASE( "INFO gets logged on failure", "[failing][messages][.]" ) -{ +TEST_CASE( "INFO gets logged on failure", "[failing][messages][.]" ) { INFO( "this message should be logged" ); INFO( "so should this" ); int a = 2; REQUIRE( a == 1 ); } -TEST_CASE( "INFO gets logged on failure, even if captured before successful assertions", "[failing][messages][.]" ) -{ +TEST_CASE( "INFO gets logged on failure, even if captured before successful assertions", "[failing][messages][.]" ) { INFO( "this message may be logged later" ); int a = 2; CHECK( a == 2 ); @@ -50,56 +47,45 @@ TEST_CASE( "INFO gets logged on failure, even if captured before successful asse CHECK( a == 2 ); } -TEST_CASE( "FAIL aborts the test", "[failing][messages][.]" ) -{ +TEST_CASE( "FAIL aborts the test", "[failing][messages][.]" ) { FAIL( "This is a " << "failure" ); // This should output the message and abort WARN( "We should never see this"); } -TEST_CASE( "FAIL_CHECK does not abort the test", "[failing][messages][.]" ) -{ +TEST_CASE( "FAIL_CHECK does not abort the test", "[failing][messages][.]" ) { FAIL_CHECK( "This is a " << "failure" ); // This should output the message then continue WARN( "This message appears in the output"); } -#ifdef CATCH_CONFIG_VARIADIC_MACROS -TEST_CASE( "FAIL does not require an argument", "[failing][messages][.]" ) -{ +TEST_CASE( "FAIL does not require an argument", "[failing][messages][.]" ) { FAIL(); } -TEST_CASE( "SUCCESS does not require an argument", "[messages][.]" ) -{ + +TEST_CASE( "SUCCESS does not require an argument", "[messages][.]" ) { SUCCEED(); } -#endif -TEST_CASE( "Output from all sections is reported", "[failing][messages][.]" ) -{ - SECTION( "one", "" ) - { +TEST_CASE( "Output from all sections is reported", "[failing][messages][.]" ) { + SECTION( "one" ) { FAIL( "Message from section one" ); } - SECTION( "two", "" ) - { + SECTION( "two" ) { FAIL( "Message from section two" ); } } -TEST_CASE( "Standard output from all sections is reported", "[messages][.]" ) -{ - SECTION( "one", "" ) - { +TEST_CASE( "Standard output from all sections is reported", "[messages][.]" ) { + SECTION( "one" ) { std::cout << "Message from section one" << std::endl; } - SECTION( "two", "" ) - { + SECTION( "two" ) { std::cout << "Message from section two" << std::endl; } } -TEST_CASE( "Standard error is reported and redirected", "[messages][.]" ) { +TEST_CASE( "Standard error is reported and redirected", "[messages][.][approvals]" ) { SECTION( "std::cerr" ) { std::cerr << "Write to std::cerr" << std::endl; } @@ -112,46 +98,40 @@ TEST_CASE( "Standard error is reported and redirected", "[messages][.]" ) { std::cerr << ' '; std::clog << "writes"; std::cerr << " to error"; - std::clog << " streams\n"; + std::clog << " streams" << std::endl; } } -TEST_CASE( "SCOPED_INFO is reset for each loop", "[messages][failing][.]" ) -{ +TEST_CASE( "INFO is reset for each loop", "[messages][failing][.]" ) { for( int i=0; i<100; i++ ) { - SCOPED_INFO( "current counter " << i ); - SCOPED_CAPTURE( i ); + INFO( "current counter " << i ); + CAPTURE( i ); REQUIRE( i < 10 ); } } -TEST_CASE( "The NO_FAIL macro reports a failure but does not fail the test", "[messages]" ) -{ +TEST_CASE( "The NO_FAIL macro reports a failure but does not fail the test", "[messages]" ) { CHECK_NOFAIL( 1 == 2 ); } -TEST_CASE( "just info", "[info][isolated info][messages]" ) -{ +TEST_CASE( "just info", "[info][isolated info][messages]" ) { INFO( "this should never be seen" ); } -TEST_CASE( "just failure", "[fail][isolated info][.][messages]" ) -{ +TEST_CASE( "just failure", "[fail][isolated info][.][messages]" ) { FAIL( "Previous info should not be seen" ); } -TEST_CASE( "sends information to INFO", "[.][failing]" ) -{ +TEST_CASE( "sends information to INFO", "[.][failing]" ) { INFO( "hi" ); int i = 7; CAPTURE( i ); REQUIRE( false ); } -TEST_CASE( "Pointers can be converted to strings", "[messages][.]" ) -{ +TEST_CASE( "Pointers can be converted to strings", "[messages][.][approvals]" ) { int p; WARN( "actual address of p: " << &p ); - WARN( "toString(p): " << Catch::toString( &p ) ); + WARN( "toString(p): " << ::Catch::Detail::stringify( &p ) ); } diff --git a/projects/SelfTest/MiscTests.cpp b/projects/SelfTest/MiscTests.cpp index 39a0ac23..ff9a0a10 100644 --- a/projects/SelfTest/MiscTests.cpp +++ b/projects/SelfTest/MiscTests.cpp @@ -13,191 +13,157 @@ # pragma clang diagnostic ignored "-Wc++98-compat-pedantic" #endif -#include "../include/internal/catch_xmlwriter.hpp" +#include "internal/catch_xmlwriter.h" #include #include +#include -TEST_CASE( "random SECTION tests", "[.][sections][failing]" ) -{ +TEST_CASE( "random SECTION tests", "[.][sections][failing]" ) { int a = 1; int b = 2; - SECTION( "s1", "doesn't equal" ) - { + SECTION( "s1", "doesn't equal" ) { REQUIRE( a != b ); REQUIRE( b != a ); } - SECTION( "s2", "not equal" ) - { + SECTION( "s2", "not equal" ) { REQUIRE( a != b); } } -TEST_CASE( "nested SECTION tests", "[.][sections][failing]" ) -{ +TEST_CASE( "nested SECTION tests", "[.][sections][failing]" ) { int a = 1; int b = 2; - SECTION( "s1", "doesn't equal" ) - { + SECTION( "s1", "doesn't equal" ) { REQUIRE( a != b ); REQUIRE( b != a ); - SECTION( "s2", "not equal" ) - { + SECTION( "s2", "not equal" ) { REQUIRE( a != b); } } } -TEST_CASE( "more nested SECTION tests", "[sections][failing][.]" ) -{ +TEST_CASE( "more nested SECTION tests", "[sections][failing][.]" ) { int a = 1; int b = 2; - SECTION( "s1", "doesn't equal" ) - { - SECTION( "s2", "equal" ) - { + SECTION( "s1", "doesn't equal" ) { + SECTION( "s2", "equal" ) { REQUIRE( a == b ); } - SECTION( "s3", "not equal" ) - { + SECTION( "s3", "not equal" ) { REQUIRE( a != b ); } - SECTION( "s4", "less than" ) - { + SECTION( "s4", "less than" ) { REQUIRE( a < b ); } } } -TEST_CASE( "even more nested SECTION tests", "[sections]" ) -{ - SECTION( "c", "" ) - { - SECTION( "d (leaf)", "" ) - { +TEST_CASE( "even more nested SECTION tests", "[sections]" ) { + SECTION( "c" ) { + SECTION( "d (leaf)" ) { SUCCEED(""); // avoid failing due to no tests } - SECTION( "e (leaf)", "" ) - { + SECTION( "e (leaf)" ) { SUCCEED(""); // avoid failing due to no tests } } - SECTION( "f (leaf)", "" ) - { + SECTION( "f (leaf)" ) { SUCCEED(""); // avoid failing due to no tests } } -TEST_CASE( "looped SECTION tests", "[.][failing][sections]" ) -{ +TEST_CASE( "looped SECTION tests", "[.][failing][sections]" ) { int a = 1; - for( int b = 0; b < 10; ++b ) - { + for( int b = 0; b < 10; ++b ) { std::ostringstream oss; oss << "b is currently: " << b; - SECTION( "s1", oss.str() ) - { + SECTION( "s1", oss.str() ) { CHECK( b > a ); } } } -TEST_CASE( "looped tests", "[.][failing]" ) -{ +TEST_CASE( "looped tests", "[.][failing]" ) { static const int fib[] = { 1, 1, 2, 3, 5, 8, 13, 21 }; - for( size_t i=0; i < sizeof(fib)/sizeof(int); ++i ) - { + for( std::size_t i=0; i < sizeof(fib)/sizeof(int); ++i ) { INFO( "Testing if fib[" << i << "] (" << fib[i] << ") is even" ); CHECK( ( fib[i] % 2 ) == 0 ); } } -TEST_CASE( "Sends stuff to stdout and stderr", "[.]" ) -{ +TEST_CASE( "Sends stuff to stdout and stderr", "[.]" ) { std::cout << "A string sent directly to stdout" << std::endl; std::cerr << "A string sent directly to stderr" << std::endl; } -inline const char* makeString( bool makeNull ) -{ - return makeNull ? CATCH_NULL : "valid string"; +inline const char* makeString( bool makeNull ) { + return makeNull ? nullptr : "valid string"; } -TEST_CASE( "null strings", "" ) -{ - REQUIRE( makeString( false ) != static_cast(CATCH_NULL)); - REQUIRE( makeString( true ) == static_cast(CATCH_NULL)); +TEST_CASE( "null strings" ) { + REQUIRE( makeString( false ) != static_cast(nullptr)); + REQUIRE( makeString( true ) == static_cast(nullptr)); } -inline bool testCheckedIf( bool flag ) -{ +inline bool testCheckedIf( bool flag ) { CHECKED_IF( flag ) return true; else return false; } -TEST_CASE( "checkedIf", "" ) -{ +TEST_CASE( "checkedIf" ) { REQUIRE( testCheckedIf( true ) ); } -TEST_CASE( "checkedIf, failing", "[failing][.]" ) -{ +TEST_CASE( "checkedIf, failing", "[failing][.]" ) { REQUIRE( testCheckedIf( false ) ); } -inline bool testCheckedElse( bool flag ) -{ +inline bool testCheckedElse( bool flag ) { CHECKED_ELSE( flag ) return false; return true; } -TEST_CASE( "checkedElse", "" ) -{ +TEST_CASE( "checkedElse" ) { REQUIRE( testCheckedElse( true ) ); } -TEST_CASE( "checkedElse, failing", "[failing][.]" ) -{ +TEST_CASE( "checkedElse, failing", "[failing][.]" ) { REQUIRE( testCheckedElse( false ) ); } -TEST_CASE( "xmlentitycheck", "" ) -{ - SECTION( "embedded xml", "it should be possible to embed xml characters, such as <, \" or &, or even whole documents within an attribute" ) - { +TEST_CASE( "xmlentitycheck" ) { + SECTION( "embedded xml", "it should be possible to embed xml characters, such as <, \" or &, or even whole documents within an attribute" ) { SUCCEED(""); // We need this here to stop it failing due to no tests } - SECTION( "encoded chars", "these should all be encoded: &&&\"\"\"<<<&\"<<&\"" ) - { + SECTION( "encoded chars", "these should all be encoded: &&&\"\"\"<<<&\"<<&\"" ) { SUCCEED(""); // We need this here to stop it failing due to no tests } } -TEST_CASE( "send a single char to INFO", "[failing][.]" ) -{ +TEST_CASE( "send a single char to INFO", "[failing][.]" ) { INFO(3); REQUIRE(false); } -TEST_CASE( "atomic if", "[failing][0]") -{ - size_t x = 0; +TEST_CASE( "atomic if", "[failing][0]") { + std::size_t x = 0; if( x ) REQUIRE(x > 0); @@ -205,9 +171,7 @@ TEST_CASE( "atomic if", "[failing][0]") REQUIRE(x == 0); } -inline unsigned int Factorial( unsigned int number ) -{ -// return number <= 1 ? number : Factorial(number-1)*number; +inline unsigned int Factorial( unsigned int number ) { return number > 1 ? Factorial(number-1)*number : 1; } @@ -219,20 +183,14 @@ TEST_CASE( "Factorials are computed", "[factorial]" ) { REQUIRE( Factorial(10) == 3628800 ); } -TEST_CASE( "An empty test with no assertions", "[empty]" ) -{ -} +TEST_CASE( "An empty test with no assertions", "[empty]" ) {} -TEST_CASE( "Nice descriptive name", "[tag1][tag2][tag3][.]" ) -{ +TEST_CASE( "Nice descriptive name", "[tag1][tag2][tag3][.]" ) { WARN( "This one ran" ); } -TEST_CASE( "first tag", "[tag1]" ) -{ -} -TEST_CASE( "second tag", "[tag2]" ) -{ -} +TEST_CASE( "first tag", "[tag1]" ) {} +TEST_CASE( "second tag", "[tag2]" ) {} + // //TEST_CASE( "spawn a new process", "[.]" ) //{ @@ -250,32 +208,32 @@ TEST_CASE( "vectors can be sized and resized", "[vector]" ) { REQUIRE( v.size() == 5 ); REQUIRE( v.capacity() >= 5 ); - SECTION( "resizing bigger changes size and capacity", "" ) { + SECTION( "resizing bigger changes size and capacity" ) { v.resize( 10 ); REQUIRE( v.size() == 10 ); REQUIRE( v.capacity() >= 10 ); } - SECTION( "resizing smaller changes size but not capacity", "" ) { + SECTION( "resizing smaller changes size but not capacity" ) { v.resize( 0 ); REQUIRE( v.size() == 0 ); REQUIRE( v.capacity() >= 5 ); - SECTION( "We can use the 'swap trick' to reset the capacity", "" ) { + SECTION( "We can use the 'swap trick' to reset the capacity" ) { std::vector empty; empty.swap( v ); REQUIRE( v.capacity() == 0 ); } } - SECTION( "reserving bigger changes capacity but not size", "" ) { + SECTION( "reserving bigger changes capacity but not size" ) { v.reserve( 10 ); REQUIRE( v.size() == 5 ); REQUIRE( v.capacity() >= 10 ); } - SECTION( "reserving smaller does not change size or capacity", "" ) { + SECTION( "reserving smaller does not change size or capacity" ) { v.reserve( 0 ); REQUIRE( v.size() == 5 ); @@ -284,8 +242,7 @@ TEST_CASE( "vectors can be sized and resized", "[vector]" ) { } // https://github.com/philsquared/Catch/issues/166 -TEST_CASE("A couple of nested sections followed by a failure", "[failing][.]") -{ +TEST_CASE("A couple of nested sections followed by a failure", "[failing][.]") { SECTION("Outer", "") SECTION("Inner", "") SUCCEED("that's not flying - that's failing in style"); @@ -293,8 +250,7 @@ TEST_CASE("A couple of nested sections followed by a failure", "[failing][.]") FAIL("to infinity and beyond"); } -TEST_CASE("not allowed", "[!throws]") -{ +TEST_CASE("not allowed", "[!throws]") { // This test case should not be included if you run with -e on the command line SUCCEED( "" ); } @@ -314,25 +270,25 @@ TEST_CASE( "Tabs and newlines show in output", "[.][whitespace][failing]" ) { TEST_CASE( "toString on const wchar_t const pointer returns the string contents", "[toString]" ) { const wchar_t * const s = L"wide load"; - std::string result = Catch::toString( s ); + std::string result = ::Catch::Detail::stringify( s ); CHECK( result == "\"wide load\"" ); } TEST_CASE( "toString on const wchar_t pointer returns the string contents", "[toString]" ) { const wchar_t * s = L"wide load"; - std::string result = Catch::toString( s ); + std::string result = ::Catch::Detail::stringify( s ); CHECK( result == "\"wide load\"" ); } TEST_CASE( "toString on wchar_t const pointer returns the string contents", "[toString]" ) { wchar_t * const s = const_cast( L"wide load" ); - std::string result = Catch::toString( s ); + 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" ); - std::string result = Catch::toString( s ); + std::string result = ::Catch::Detail::stringify( s ); CHECK( result == "\"wide load\"" ); } @@ -372,13 +328,11 @@ TEST_CASE( "XmlEncode" ) { } } -#ifdef CATCH_CONFIG_CPP11_LONG_LONG -TEST_CASE( "long long", "[c++11][.]" ) { +TEST_CASE( "long long" ) { long long l = std::numeric_limits::max(); REQUIRE( l == std::numeric_limits::max() ); } -#endif //TEST_CASE( "Divide by Zero signal handler", "[.][sig]" ) { // int i = 0; @@ -386,8 +340,7 @@ TEST_CASE( "long long", "[c++11][.]" ) { // CHECK( x == 0 ); //} -TEST_CASE( "This test 'should' fail but doesn't", "[.][failing][!shouldfail]" ) -{ +TEST_CASE( "This test 'should' fail but doesn't", "[.][failing][!shouldfail]" ) { SUCCEED( "oops!" ); } @@ -400,7 +353,7 @@ static int f() { return 1; } -TEST_CASE( "#835 -- errno should not be touched by Catch", "[!shouldfail]" ) { +TEST_CASE( "#835 -- errno should not be touched by Catch", "[.][failing][!shouldfail]" ) { errno = 1; CHECK(f() == 0); REQUIRE(errno == 1); // Check that f() doesn't touch errno. diff --git a/projects/SelfTest/PartTrackerTests.cpp b/projects/SelfTest/PartTrackerTests.cpp index 45f4cf1c..042c03a7 100644 --- a/projects/SelfTest/PartTrackerTests.cpp +++ b/projects/SelfTest/PartTrackerTests.cpp @@ -6,7 +6,7 @@ * file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) */ #include "internal/catch_suppress_warnings.h" -#include "internal/catch_test_case_tracker.hpp" +#include "internal/catch_test_case_tracker.h" namespace Catch @@ -37,10 +37,10 @@ using namespace Catch; //} Catch::TestCaseTracking::NameAndLocation makeNAL( std::string const& name ) { - return Catch::TestCaseTracking::NameAndLocation( name, Catch::SourceLineInfo() ); + return Catch::TestCaseTracking::NameAndLocation( name, Catch::SourceLineInfo("",0) ); } -TEST_CASE( "Tracker", "" ) { +TEST_CASE( "Tracker" ) { TrackerContext ctx; ctx.startRun(); @@ -53,7 +53,7 @@ TEST_CASE( "Tracker", "" ) { ITracker& s1 = SectionTracker::acquire( ctx, makeNAL( "S1" ) ); REQUIRE( s1.isOpen() ); - SECTION( "successfully close one section", "" ) { + SECTION( "successfully close one section" ) { s1.close(); REQUIRE( s1.isSuccessfullyCompleted() ); REQUIRE( testCase.isComplete() == false ); @@ -63,7 +63,7 @@ TEST_CASE( "Tracker", "" ) { REQUIRE( testCase.isSuccessfullyCompleted() ); } - SECTION( "fail one section", "" ) { + SECTION( "fail one section" ) { s1.fail(); REQUIRE( s1.isComplete() ); REQUIRE( s1.isSuccessfullyCompleted() == false ); @@ -73,7 +73,7 @@ TEST_CASE( "Tracker", "" ) { REQUIRE( ctx.completedCycle() ); REQUIRE( testCase.isSuccessfullyCompleted() == false ); - SECTION( "re-enter after failed section", "" ) { + SECTION( "re-enter after failed section" ) { ctx.startCycle(); ITracker& testCase2 = SectionTracker::acquire( ctx, makeNAL( "Testcase" ) ); REQUIRE( testCase2.isOpen() ); @@ -86,7 +86,7 @@ TEST_CASE( "Tracker", "" ) { REQUIRE( testCase.isComplete() ); REQUIRE( testCase.isSuccessfullyCompleted() ); } - SECTION( "re-enter after failed section and find next section", "" ) { + SECTION( "re-enter after failed section and find next section" ) { ctx.startCycle(); ITracker& testCase2 = SectionTracker::acquire( ctx, makeNAL( "Testcase" ) ); REQUIRE( testCase2.isOpen() ); @@ -106,7 +106,7 @@ TEST_CASE( "Tracker", "" ) { } } - SECTION( "successfully close one section, then find another", "" ) { + SECTION( "successfully close one section, then find another" ) { s1.close(); ITracker& s2 = SectionTracker::acquire( ctx, makeNAL( "S2" ) ); @@ -115,7 +115,7 @@ TEST_CASE( "Tracker", "" ) { testCase.close(); REQUIRE( testCase.isComplete() == false ); - SECTION( "Re-enter - skips S1 and enters S2", "" ) { + SECTION( "Re-enter - skips S1 and enters S2" ) { ctx.startCycle(); ITracker& testCase2 = SectionTracker::acquire( ctx, makeNAL( "Testcase" ) ); REQUIRE( testCase2.isOpen() ); @@ -165,7 +165,7 @@ TEST_CASE( "Tracker", "" ) { } } - SECTION( "open a nested section", "" ) { + SECTION( "open a nested section" ) { ITracker& s2 = SectionTracker::acquire( ctx, makeNAL( "S2" ) ); REQUIRE( s2.isOpen() ); @@ -181,7 +181,7 @@ TEST_CASE( "Tracker", "" ) { REQUIRE( testCase.isComplete() ); } - SECTION( "start a generator", "" ) { + SECTION( "start a generator" ) { IndexTracker& g1 = IndexTracker::acquire( ctx, makeNAL( "G1" ), 2 ); REQUIRE( g1.isOpen() ); REQUIRE( g1.index() == 0 ); @@ -196,7 +196,7 @@ TEST_CASE( "Tracker", "" ) { testCase.close(); REQUIRE( testCase.isSuccessfullyCompleted() == false ); - SECTION( "Re-enter for second generation", "" ) { + SECTION( "Re-enter for second generation" ) { ctx.startCycle(); ITracker& testCase2 = SectionTracker::acquire( ctx, makeNAL( "Testcase" ) ); REQUIRE( testCase2.isOpen() ); @@ -218,7 +218,7 @@ TEST_CASE( "Tracker", "" ) { REQUIRE( testCase2.isComplete() ); } } - SECTION( "Start a new inner section", "" ) { + SECTION( "Start a new inner section" ) { ITracker& s2 = SectionTracker::acquire( ctx, makeNAL( "S2" ) ); REQUIRE( s2.isOpen() ); @@ -231,7 +231,7 @@ TEST_CASE( "Tracker", "" ) { testCase.close(); REQUIRE( testCase.isComplete() == false ); - SECTION( "Re-enter for second generation", "" ) { + SECTION( "Re-enter for second generation" ) { ctx.startCycle(); ITracker& testCase2 = SectionTracker::acquire( ctx, makeNAL( "Testcase" ) ); REQUIRE( testCase2.isOpen() ); @@ -260,7 +260,7 @@ TEST_CASE( "Tracker", "" ) { } } - SECTION( "Fail an inner section", "" ) { + SECTION( "Fail an inner section" ) { ITracker& s2 = SectionTracker::acquire( ctx, makeNAL( "S2" ) ); REQUIRE( s2.isOpen() ); @@ -274,7 +274,7 @@ TEST_CASE( "Tracker", "" ) { testCase.close(); REQUIRE( testCase.isComplete() == false ); - SECTION( "Re-enter for second generation", "" ) { + SECTION( "Re-enter for second generation" ) { ctx.startCycle(); ITracker& testCase2 = SectionTracker::acquire( ctx, makeNAL( "Testcase" ) ); REQUIRE( testCase2.isOpen() ); diff --git a/projects/SelfTest/StringRef.tests.cpp b/projects/SelfTest/StringRef.tests.cpp new file mode 100644 index 00000000..d12c79a3 --- /dev/null +++ b/projects/SelfTest/StringRef.tests.cpp @@ -0,0 +1,167 @@ +#include "../include/internal/catch_stringref.h" + +#include "catch.hpp" + +#include + +namespace Catch { + + // Implementation of test accessors + struct StringRefTestAccess { + static auto isOwned( StringRef const& stringRef ) -> bool { + return stringRef.isOwned(); + } + static auto isSubstring( StringRef const& stringRef ) -> bool { + return stringRef.isSubstring(); + } + static auto data( StringRef const& stringRef ) -> char const* { + return stringRef.data(); + } + }; + + auto isOwned( StringRef const& stringRef ) -> bool { + return StringRefTestAccess::isOwned( stringRef ); + } + auto isSubstring( StringRef const& stringRef ) -> bool { + return StringRefTestAccess::isSubstring( stringRef ); + } + auto data( StringRef const& stringRef ) -> char const* { + return StringRefTestAccess::data( stringRef ); + } +} // namespace Catch2 + +namespace Catch { + inline auto toString( Catch::StringRef const& stringRef ) -> std::string { + return std::string( data( stringRef ), stringRef.size() ); + } +} // namespace Catch + +TEST_CASE( "StringRef", "[Strings]" ) { + + using Catch::StringRef; + + SECTION( "Empty string" ) { + StringRef empty; + REQUIRE( empty.empty() ); + REQUIRE( empty.size() == 0 ); + REQUIRE( std::strcmp( empty.c_str(), "" ) == 0 ); + } + + SECTION( "From string literal" ) { + StringRef s = "hello"; + REQUIRE( s.empty() == false ); + REQUIRE( s.size() == 5 ); + REQUIRE( isSubstring( s ) == false ); + + auto rawChars = data( s ); + REQUIRE( std::strcmp( rawChars, "hello" ) == 0 ); + + SECTION( "c_str() does not cause copy" ) { + REQUIRE( isOwned( s ) == false ); + + REQUIRE( s.c_str() == rawChars ); + + REQUIRE( isOwned( s ) == false ); + } + } + SECTION( "From sub-string" ) { + StringRef original = StringRef( "original string" ).substr(0, 8); + REQUIRE( original == "original" ); + REQUIRE( isSubstring( original ) ); + REQUIRE( isOwned( original ) == false ); + + original.c_str(); // Forces it to take ownership + + REQUIRE( isSubstring( original ) == false ); + REQUIRE( isOwned( original ) ); + + } + + + SECTION( "Substrings" ) { + StringRef s = "hello world!"; + StringRef ss = s.substr(0, 5); + + SECTION( "zero-based substring" ) { + REQUIRE( ss.empty() == false ); + REQUIRE( ss.size() == 5 ); + REQUIRE( std::strcmp( ss.c_str(), "hello" ) == 0 ); + REQUIRE( ss == "hello" ); + } + SECTION( "c_str() causes copy" ) { + REQUIRE( isSubstring( ss ) ); + REQUIRE( isOwned( ss ) == false ); + + auto rawChars = data( ss ); + REQUIRE( rawChars == data( s ) ); // same pointer value + REQUIRE( ss.c_str() != rawChars ); + + REQUIRE( isSubstring( ss ) == false ); + REQUIRE( isOwned( ss ) ); + + REQUIRE( data( ss ) != data( s ) ); // different pointer value + } + + SECTION( "non-zero-based substring") { + ss = s.substr( 6, 6 ); + REQUIRE( ss.size() == 6 ); + REQUIRE( std::strcmp( ss.c_str(), "world!" ) == 0 ); + } + + SECTION( "Pointer values of full refs should match" ) { + StringRef s2 = s; + REQUIRE( s.c_str() == s2.c_str() ); + } + + SECTION( "Pointer values of substring refs should not match" ) { + REQUIRE( s.c_str() != ss.c_str() ); + } + } + + SECTION( "Comparisons" ) { + REQUIRE( StringRef("hello") == StringRef("hello") ); + REQUIRE( StringRef("hello") != StringRef("cello") ); + } + + SECTION( "from std::string" ) { + std::string stdStr = "a standard string"; + + SECTION( "implicitly constructed" ) { + StringRef sr = stdStr; + REQUIRE( sr == "a standard string" ); + REQUIRE( sr.size() == stdStr.size() ); + } + SECTION( "explicitly constructed" ) { + StringRef sr( stdStr ); + REQUIRE( sr == "a standard string" ); + REQUIRE( sr.size() == stdStr.size() ); + } + SECTION( "assigned" ) { + StringRef sr; + sr = stdStr; + REQUIRE( sr == "a standard string" ); + REQUIRE( sr.size() == stdStr.size() ); + } + } + + SECTION( "to std::string" ) { + StringRef sr = "a stringref"; + + SECTION( "implicitly constructed" ) { + std::string stdStr = sr; + REQUIRE( stdStr == "a stringref" ); + REQUIRE( stdStr.size() == sr.size() ); + } + SECTION( "explicitly constructed" ) { + std::string stdStr( sr ); + REQUIRE( stdStr == "a stringref" ); + REQUIRE( stdStr.size() == sr.size() ); + } + SECTION( "assigned" ) { + std::string stdStr; + stdStr = sr; + REQUIRE( stdStr == "a stringref" ); + REQUIRE( stdStr.size() == sr.size() ); + } + } +} diff --git a/projects/SelfTest/SurrogateCpps/catch_common.cpp b/projects/SelfTest/SurrogateCpps/catch_common.cpp deleted file mode 100644 index 65377f30..00000000 --- a/projects/SelfTest/SurrogateCpps/catch_common.cpp +++ /dev/null @@ -1,3 +0,0 @@ -// This file is only here to verify (to the extent possible) the self sufficiency of the header -#include "internal/catch_suppress_warnings.h" -#include "internal/catch_common.h" diff --git a/projects/SelfTest/SurrogateCpps/catch_console_colour.cpp b/projects/SelfTest/SurrogateCpps/catch_console_colour.cpp index c28aa011..a2377500 100644 --- a/projects/SelfTest/SurrogateCpps/catch_console_colour.cpp +++ b/projects/SelfTest/SurrogateCpps/catch_console_colour.cpp @@ -1,3 +1,3 @@ // This file is only here to verify (to the extent possible) the self sufficiency of the header #include "internal/catch_suppress_warnings.h" -#include "internal/catch_console_colour.hpp" +#include "internal/catch_console_colour.h" diff --git a/projects/SelfTest/SurrogateCpps/catch_interfaces_capture.cpp b/projects/SelfTest/SurrogateCpps/catch_interfaces_capture.cpp deleted file mode 100644 index f46dae48..00000000 --- a/projects/SelfTest/SurrogateCpps/catch_interfaces_capture.cpp +++ /dev/null @@ -1,3 +0,0 @@ -// This file is only here to verify (to the extent possible) the self sufficiency of the header -#include "internal/catch_suppress_warnings.h" -#include "internal/catch_interfaces_capture.h" diff --git a/projects/SelfTest/SurrogateCpps/catch_interfaces_config.cpp b/projects/SelfTest/SurrogateCpps/catch_interfaces_config.cpp deleted file mode 100644 index 46f80e86..00000000 --- a/projects/SelfTest/SurrogateCpps/catch_interfaces_config.cpp +++ /dev/null @@ -1,2 +0,0 @@ -#include "internal/catch_suppress_warnings.h" -#include "internal/catch_interfaces_config.h" diff --git a/projects/SelfTest/SurrogateCpps/catch_interfaces_exception.cpp b/projects/SelfTest/SurrogateCpps/catch_interfaces_exception.cpp deleted file mode 100644 index f5ad4870..00000000 --- a/projects/SelfTest/SurrogateCpps/catch_interfaces_exception.cpp +++ /dev/null @@ -1,2 +0,0 @@ -#include "internal/catch_suppress_warnings.h" -#include "internal/catch_interfaces_exception.h" diff --git a/projects/SelfTest/SurrogateCpps/catch_interfaces_generators.cpp b/projects/SelfTest/SurrogateCpps/catch_interfaces_generators.cpp deleted file mode 100644 index 2eda9812..00000000 --- a/projects/SelfTest/SurrogateCpps/catch_interfaces_generators.cpp +++ /dev/null @@ -1 +0,0 @@ -#include "internal/catch_interfaces_generators.h" diff --git a/projects/SelfTest/SurrogateCpps/catch_interfaces_registry_hub.cpp b/projects/SelfTest/SurrogateCpps/catch_interfaces_registry_hub.cpp deleted file mode 100644 index b399d862..00000000 --- a/projects/SelfTest/SurrogateCpps/catch_interfaces_registry_hub.cpp +++ /dev/null @@ -1,3 +0,0 @@ -// This file is only here to verify (to the extent possible) the self sufficiency of the header -#include "internal/catch_suppress_warnings.h" -#include "internal/catch_interfaces_registry_hub.h" diff --git a/projects/SelfTest/SurrogateCpps/catch_interfaces_runner.cpp b/projects/SelfTest/SurrogateCpps/catch_interfaces_runner.cpp deleted file mode 100644 index 800f32ab..00000000 --- a/projects/SelfTest/SurrogateCpps/catch_interfaces_runner.cpp +++ /dev/null @@ -1 +0,0 @@ -#include "internal/catch_interfaces_runner.h" diff --git a/projects/SelfTest/SurrogateCpps/catch_interfaces_testcase.cpp b/projects/SelfTest/SurrogateCpps/catch_interfaces_testcase.cpp deleted file mode 100644 index 0d6903cc..00000000 --- a/projects/SelfTest/SurrogateCpps/catch_interfaces_testcase.cpp +++ /dev/null @@ -1,2 +0,0 @@ -#include "internal/catch_suppress_warnings.h" -#include "internal/catch_interfaces_testcase.h" diff --git a/projects/SelfTest/SurrogateCpps/catch_message.cpp b/projects/SelfTest/SurrogateCpps/catch_message.cpp deleted file mode 100644 index 40f4403c..00000000 --- a/projects/SelfTest/SurrogateCpps/catch_message.cpp +++ /dev/null @@ -1,3 +0,0 @@ -// This file is only here to verify (to the extent possible) the self sufficiency of the header -#include "internal/catch_suppress_warnings.h" -#include "internal/catch_message.h" diff --git a/projects/SelfTest/SurrogateCpps/catch_ptr.cpp b/projects/SelfTest/SurrogateCpps/catch_ptr.cpp deleted file mode 100644 index 90796ca1..00000000 --- a/projects/SelfTest/SurrogateCpps/catch_ptr.cpp +++ /dev/null @@ -1,3 +0,0 @@ -// This file is only here to verify (to the extent possible) the self sufficiency of the header -#include "internal/catch_suppress_warnings.h" -#include "internal/catch_ptr.hpp" diff --git a/projects/SelfTest/SurrogateCpps/catch_streambuf.cpp b/projects/SelfTest/SurrogateCpps/catch_streambuf.cpp deleted file mode 100644 index a9222e4b..00000000 --- a/projects/SelfTest/SurrogateCpps/catch_streambuf.cpp +++ /dev/null @@ -1,3 +0,0 @@ -// This file is only here to verify (to the extent possible) the self sufficiency of the header -#include "internal/catch_suppress_warnings.h" -#include "internal/catch_streambuf.h" diff --git a/projects/SelfTest/SurrogateCpps/catch_test_case_tracker.cpp b/projects/SelfTest/SurrogateCpps/catch_test_case_tracker.cpp index bd67a8a7..0d697b0f 100644 --- a/projects/SelfTest/SurrogateCpps/catch_test_case_tracker.cpp +++ b/projects/SelfTest/SurrogateCpps/catch_test_case_tracker.cpp @@ -1,2 +1,2 @@ // This file is only here to verify (to the extent possible) the self sufficiency of the header -#include "internal/catch_test_case_tracker.hpp" +#include "internal/catch_test_case_tracker.h" diff --git a/projects/SelfTest/SurrogateCpps/catch_test_spec.cpp b/projects/SelfTest/SurrogateCpps/catch_test_spec.cpp index 9993b80a..8be135ec 100644 --- a/projects/SelfTest/SurrogateCpps/catch_test_spec.cpp +++ b/projects/SelfTest/SurrogateCpps/catch_test_spec.cpp @@ -1,3 +1,3 @@ // This file is only here to verify (to the extent possible) the self sufficiency of the header #include "internal/catch_suppress_warnings.h" -#include "internal/catch_test_spec.hpp" +#include "internal/catch_test_spec.h" diff --git a/projects/SelfTest/SurrogateCpps/catch_xmlwriter.cpp b/projects/SelfTest/SurrogateCpps/catch_xmlwriter.cpp index 169d2d70..930e3825 100644 --- a/projects/SelfTest/SurrogateCpps/catch_xmlwriter.cpp +++ b/projects/SelfTest/SurrogateCpps/catch_xmlwriter.cpp @@ -1,4 +1,4 @@ // This file is only here to verify (to the extent possible) the self sufficiency of the header #include "internal/catch_suppress_warnings.h" -#include "internal/catch_xmlwriter.hpp" +#include "internal/catch_xmlwriter.h" #include "internal/catch_reenable_warnings.h" diff --git a/projects/SelfTest/TagAliasTests.cpp b/projects/SelfTest/TagAliasTests.cpp index 002a93c0..b533c454 100644 --- a/projects/SelfTest/TagAliasTests.cpp +++ b/projects/SelfTest/TagAliasTests.cpp @@ -9,30 +9,31 @@ #include "catch.hpp" #include "internal/catch_tag_alias_registry.h" -TEST_CASE( "Tag alias can be registered against tag patterns", "" ) { - - using namespace Catch::Matchers; +TEST_CASE( "Tag alias can be registered against tag patterns" ) { Catch::TagAliasRegistry registry; registry.add( "[@zzz]", "[one][two]", Catch::SourceLineInfo( "file", 2 ) ); - SECTION( "The same tag alias can only be registered once", "" ) { + SECTION( "The same tag alias can only be registered once" ) { try { registry.add( "[@zzz]", "[one][two]", Catch::SourceLineInfo( "file", 10 ) ); FAIL( "expected exception" ); } catch( std::exception& ex ) { +#ifndef CATCH_CONFIG_DISABLE_MATCHERS std::string what = ex.what(); + using namespace Catch::Matchers; CHECK_THAT( what, Contains( "[@zzz]" ) ); CHECK_THAT( what, Contains( "file" ) ); CHECK_THAT( what, Contains( "2" ) ); CHECK_THAT( what, Contains( "10" ) ); +#endif } } - SECTION( "Tag aliases must be of the form [@name]", "" ) { + SECTION( "Tag aliases must be of the form [@name]" ) { CHECK_THROWS( registry.add( "[no ampersat]", "", Catch::SourceLineInfo( "file", 3 ) ) ); CHECK_THROWS( registry.add( "[the @ is not at the start]", "", Catch::SourceLineInfo( "file", 3 ) ) ); CHECK_THROWS( registry.add( "@no square bracket at start]", "", Catch::SourceLineInfo( "file", 3 ) ) ); diff --git a/projects/SelfTest/TestMain.cpp b/projects/SelfTest/TestMain.cpp index e03a9e44..68271b7e 100644 --- a/projects/SelfTest/TestMain.cpp +++ b/projects/SelfTest/TestMain.cpp @@ -24,479 +24,245 @@ CATCH_REGISTER_TAG_ALIAS( "[@tricky]", "[tricky]~[.]" ) # pragma clang diagnostic ignored "-Wc++98-compat" #endif +struct TestListener : Catch::TestEventListenerBase { + using TestEventListenerBase::TestEventListenerBase; // inherit constructor +}; +CATCH_REGISTER_LISTENER( TestListener ); -template -void parseIntoConfig( const char * (&argv)[size], Catch::ConfigData& config ) { - Catch::Clara::CommandLine parser = Catch::makeCommandLineParser(); - parser.parseInto( Catch::Clara::argsToVector( size, argv ), config ); -} - -template -std::string parseIntoConfigAndReturnError( const char * (&argv)[size], Catch::ConfigData& config ) { - try { - parseIntoConfig( argv, config ); - FAIL( "expected exception" ); - } - catch( std::exception& ex ) { - return ex.what(); - } - return ""; -} - -inline Catch::TestCase fakeTestCase( const char* name, const char* desc = "" ){ return Catch::makeTestCase( CATCH_NULL, "", name, desc, CATCH_INTERNAL_LINEINFO ); } +inline Catch::TestCase fakeTestCase( const char* name, const char* desc = "" ){ return Catch::makeTestCase( nullptr, "", name, desc, CATCH_INTERNAL_LINEINFO ); } TEST_CASE( "Process can be configured on command line", "[config][command-line]" ) { +#ifndef CATCH_CONFIG_DISABLE_MATCHERS using namespace Catch::Matchers; +#endif Catch::ConfigData config; + auto cli = Catch::makeCommandLineParser(config); - SECTION( "empty args don't cause a crash" ) { - Catch::Clara::CommandLine parser = Catch::makeCommandLineParser(); - CHECK_NOTHROW( parser.parseInto( std::vector(), config ) ); - - CHECK( config.processName == "" ); + SECTION("empty args don't cause a crash") { + auto result = cli.parse({""}); + CHECK(result); + CHECK(config.processName == ""); } - SECTION( "default - no arguments", "" ) { - const char* argv[] = { "test" }; - CHECK_NOTHROW( parseIntoConfig( argv, config ) ); - CHECK( config.processName == "test" ); - CHECK( config.shouldDebugBreak == false ); - CHECK( config.abortAfter == -1 ); - CHECK( config.noThrow == false ); - CHECK( config.reporterNames.empty() ); + SECTION("default - no arguments") { + auto result = cli.parse({"test"}); + CHECK(result); + CHECK(config.processName == "test"); + CHECK(config.shouldDebugBreak == false); + CHECK(config.abortAfter == -1); + CHECK(config.noThrow == false); + CHECK(config.reporterNames.empty()); } - SECTION( "test lists", "" ) { - SECTION( "1 test", "Specify one test case using" ) { - const char* argv[] = { "test", "test1" }; - CHECK_NOTHROW( parseIntoConfig( argv, config ) ); + SECTION("test lists") { + SECTION("1 test", "Specify one test case using") { + auto result = cli.parse({"test", "test1"}); + CHECK(result); - Catch::Config cfg( config ); - REQUIRE( cfg.testSpec().matches( fakeTestCase( "notIncluded" ) ) == false ); - REQUIRE( cfg.testSpec().matches( fakeTestCase( "test1" ) ) ); + Catch::Config cfg(config); + REQUIRE(cfg.testSpec().matches(fakeTestCase("notIncluded")) == false); + REQUIRE(cfg.testSpec().matches(fakeTestCase("test1"))); } - SECTION( "Specify one test case exclusion using exclude:", "" ) { - const char* argv[] = { "test", "exclude:test1" }; - CHECK_NOTHROW( parseIntoConfig( argv, config ) ); + SECTION("Specify one test case exclusion using exclude:") { + auto result = cli.parse({"test", "exclude:test1"}); + CHECK(result); - Catch::Config cfg( config ); - REQUIRE( cfg.testSpec().matches( fakeTestCase( "test1" ) ) == false ); - REQUIRE( cfg.testSpec().matches( fakeTestCase( "alwaysIncluded" ) ) ); + Catch::Config cfg(config); + REQUIRE(cfg.testSpec().matches(fakeTestCase("test1")) == false); + REQUIRE(cfg.testSpec().matches(fakeTestCase("alwaysIncluded"))); } - SECTION( "Specify one test case exclusion using ~", "" ) { - const char* argv[] = { "test", "~test1" }; - CHECK_NOTHROW( parseIntoConfig( argv, config ) ); + SECTION("Specify one test case exclusion using ~") { + auto result = cli.parse({"test", "~test1"}); + CHECK(result); - Catch::Config cfg( config ); - REQUIRE( cfg.testSpec().matches( fakeTestCase( "test1" ) ) == false ); - REQUIRE( cfg.testSpec().matches( fakeTestCase( "alwaysIncluded" ) ) ); + Catch::Config cfg(config); + REQUIRE(cfg.testSpec().matches(fakeTestCase("test1")) == false); + REQUIRE(cfg.testSpec().matches(fakeTestCase("alwaysIncluded"))); } } - SECTION( "reporter", "" ) { - SECTION( "-r/console", "" ) { - const char* argv[] = { "test", "-r", "console" }; - CHECK_NOTHROW( parseIntoConfig( argv, config ) ); + SECTION("reporter") { + SECTION("-r/console") { + CHECK(cli.parse({"test", "-r", "console"})); - REQUIRE( config.reporterNames[0] == "console" ); + REQUIRE(config.reporterNames[0] == "console"); } - SECTION( "-r/xml", "" ) { - const char* argv[] = { "test", "-r", "xml" }; - CHECK_NOTHROW( parseIntoConfig( argv, config ) ); + SECTION("-r/xml") { + CHECK(cli.parse({"test", "-r", "xml"})); - REQUIRE( config.reporterNames[0] == "xml" ); + REQUIRE(config.reporterNames[0] == "xml"); } - SECTION( "-r xml and junit", "" ) { - const char* argv[] = { "test", "-r", "xml", "-r", "junit" }; - CHECK_NOTHROW( parseIntoConfig( argv, config ) ); + SECTION("-r xml and junit") { + CHECK(cli.parse({"test", "-r", "xml", "-r", "junit"})); - REQUIRE( config.reporterNames.size() == 2 ); - REQUIRE( config.reporterNames[0] == "xml" ); - REQUIRE( config.reporterNames[1] == "junit" ); + REQUIRE(config.reporterNames.size() == 2); + REQUIRE(config.reporterNames[0] == "xml"); + REQUIRE(config.reporterNames[1] == "junit"); } - SECTION( "--reporter/junit", "" ) { - const char* argv[] = { "test", "--reporter", "junit" }; - CHECK_NOTHROW( parseIntoConfig( argv, config ) ); + SECTION("--reporter/junit") { + CHECK(cli.parse({"test", "--reporter", "junit"})); - REQUIRE( config.reporterNames[0] == "junit" ); + REQUIRE(config.reporterNames[0] == "junit"); } } - SECTION( "debugger", "" ) { - SECTION( "-b", "" ) { - const char* argv[] = { "test", "-b" }; - CHECK_NOTHROW( parseIntoConfig( argv, config ) ); - REQUIRE( config.shouldDebugBreak == true ); + SECTION("debugger") { + SECTION("-b") { + CHECK(cli.parse({"test", "-b"})); + + REQUIRE(config.shouldDebugBreak == true); } - SECTION( "--break", "" ) { - const char* argv[] = { "test", "--break" }; - CHECK_NOTHROW( parseIntoConfig( argv, config ) ); + SECTION("--break") { + CHECK(cli.parse({"test", "--break"})); - REQUIRE( config.shouldDebugBreak ); + REQUIRE(config.shouldDebugBreak); } } - SECTION( "abort", "" ) { - SECTION( "-a aborts after first failure", "" ) { - const char* argv[] = { "test", "-a" }; - CHECK_NOTHROW( parseIntoConfig( argv, config ) ); - REQUIRE( config.abortAfter == 1 ); - } - SECTION( "-x 2 aborts after two failures", "" ) { - const char* argv[] = { "test", "-x", "2" }; - CHECK_NOTHROW( parseIntoConfig( argv, config ) ); + SECTION("abort") { + SECTION("-a aborts after first failure") { + CHECK(cli.parse({"test", "-a"})); - REQUIRE( config.abortAfter == 2 ); + REQUIRE(config.abortAfter == 1); } - SECTION( "-x must be greater than zero", "" ) { - const char* argv[] = { "test", "-x", "0" }; - REQUIRE_THAT( parseIntoConfigAndReturnError( argv, config ), Contains( "greater than zero" ) ); + SECTION("-x 2 aborts after two failures") { + CHECK(cli.parse({"test", "-x", "2"})); + + REQUIRE(config.abortAfter == 2); } - SECTION( "-x must be numeric", "" ) { - const char* argv[] = { "test", "-x", "oops" }; - REQUIRE_THAT( parseIntoConfigAndReturnError( argv, config ), Contains( "-x" ) ); + SECTION("-x must be numeric") { + auto result = cli.parse({"test", "-x", "oops"}); + CHECK(!result); + +#ifndef CATCH_CONFIG_DISABLE_MATCHERS + REQUIRE_THAT(result.errorMessage(), Contains("convert") && Contains("oops")); +#endif } } - SECTION( "nothrow", "" ) { - SECTION( "-e", "" ) { - const char* argv[] = { "test", "-e" }; - CHECK_NOTHROW( parseIntoConfig( argv, config ) ); + SECTION("nothrow") { + SECTION("-e") { + CHECK(cli.parse({"test", "-e"})); - REQUIRE( config.noThrow == true ); + REQUIRE(config.noThrow); } - SECTION( "--nothrow", "" ) { - const char* argv[] = { "test", "--nothrow" }; - CHECK_NOTHROW( parseIntoConfig( argv, config ) ); + SECTION("--nothrow") { + CHECK(cli.parse({"test", "--nothrow"})); - REQUIRE( config.noThrow == true ); + REQUIRE(config.noThrow); } } - SECTION( "output filename", "" ) { - SECTION( "-o filename", "" ) { - const char* argv[] = { "test", "-o", "filename.ext" }; - CHECK_NOTHROW( parseIntoConfig( argv, config ) ); + SECTION("output filename") { + SECTION("-o filename") { + CHECK(cli.parse({"test", "-o", "filename.ext"})); - REQUIRE( config.outputFilename == "filename.ext" ); + REQUIRE(config.outputFilename == "filename.ext"); } - SECTION( "--out", "" ) { - const char* argv[] = { "test", "--out", "filename.ext" }; - CHECK_NOTHROW( parseIntoConfig( argv, config ) ); + SECTION("--out") { + CHECK(cli.parse({"test", "--out", "filename.ext"})); - REQUIRE( config.outputFilename == "filename.ext" ); + REQUIRE(config.outputFilename == "filename.ext"); } } - SECTION( "combinations", "" ) { - SECTION( "Single character flags can be combined", "" ) { - const char* argv[] = { "test", "-abe" }; - CHECK_NOTHROW( parseIntoConfig( argv, config ) ); + SECTION("combinations") { + SECTION("Single character flags can be combined") { + CHECK(cli.parse({"test", "-abe"})); - CHECK( config.abortAfter == 1 ); - CHECK( config.shouldDebugBreak ); - CHECK( config.noThrow == true ); + CHECK(config.abortAfter == 1); + CHECK(config.shouldDebugBreak); + CHECK(config.noThrow == true); } } - SECTION( "use-colour", "") { + + SECTION( "use-colour") { using Catch::UseColour; - SECTION( "without option", "" ) { - const char* argv[] = { "test" }; - CHECK_NOTHROW( parseIntoConfig( argv, config ) ); + SECTION( "without option" ) { + CHECK(cli.parse({"test"})); REQUIRE( config.useColour == UseColour::Auto ); } - SECTION( "auto", "" ) { - const char* argv[] = { "test", "--use-colour", "auto" }; - CHECK_NOTHROW( parseIntoConfig( argv, config ) ); + SECTION( "auto" ) { + CHECK(cli.parse({"test", "--use-colour", "auto"})); REQUIRE( config.useColour == UseColour::Auto ); } - SECTION( "yes", "" ) { - const char* argv[] = { "test", "--use-colour", "yes" }; - CHECK_NOTHROW( parseIntoConfig( argv, config ) ); + SECTION( "yes" ) { + CHECK(cli.parse({"test", "--use-colour", "yes"})); REQUIRE( config.useColour == UseColour::Yes ); } - SECTION( "no", "" ) { - const char* argv[] = { "test", "--use-colour", "no" }; - CHECK_NOTHROW( parseIntoConfig( argv, config ) ); + SECTION( "no" ) { + CHECK(cli.parse({"test", "--use-colour", "no"})); REQUIRE( config.useColour == UseColour::No ); } - SECTION( "error", "" ) { - const char* argv[] = { "test", "--use-colour", "wrong" }; - REQUIRE_THROWS_WITH( parseIntoConfig( argv, config ), Contains( "colour mode must be one of" ) ); + SECTION( "error" ) { + auto result = cli.parse({"test", "--use-colour", "wrong"}); + CHECK( !result ); +#ifndef CATCH_CONFIG_DISABLE_MATCHERS + CHECK_THAT( result.errorMessage(), Contains( "colour mode must be one of" ) ); +#endif } } } -TEST_CASE( "Long strings can be wrapped", "[wrap]" ) { - - using namespace Catch; - SECTION( "plain string", "" ) { - // guide: 123456789012345678 - std::string testString = "one two three four"; - - SECTION( "No wrapping", "" ) { - CHECK( Text( testString, TextAttributes().setWidth( 80 ) ).toString() == testString ); - CHECK( Text( testString, TextAttributes().setWidth( 18 ) ).toString() == testString ); - } - SECTION( "Wrapped once", "" ) { - CHECK( Text( testString, TextAttributes().setWidth( 17 ) ).toString() == "one two three\nfour" ); - CHECK( Text( testString, TextAttributes().setWidth( 16 ) ).toString() == "one two three\nfour" ); - CHECK( Text( testString, TextAttributes().setWidth( 14 ) ).toString() == "one two three\nfour" ); - CHECK( Text( testString, TextAttributes().setWidth( 13 ) ).toString() == "one two three\nfour" ); - CHECK( Text( testString, TextAttributes().setWidth( 12 ) ).toString() == "one two\nthree four" ); - } - SECTION( "Wrapped twice", "" ) { - CHECK( Text( testString, TextAttributes().setWidth( 9 ) ).toString() == "one two\nthree\nfour" ); - CHECK( Text( testString, TextAttributes().setWidth( 8 ) ).toString() == "one two\nthree\nfour" ); - CHECK( Text( testString, TextAttributes().setWidth( 7 ) ).toString() == "one two\nthree\nfour" ); - } - SECTION( "Wrapped three times", "" ) { - CHECK( Text( testString, TextAttributes().setWidth( 6 ) ).toString() == "one\ntwo\nthree\nfour" ); - CHECK( Text( testString, TextAttributes().setWidth( 5 ) ).toString() == "one\ntwo\nthree\nfour" ); - } - SECTION( "Short wrap", "" ) { - CHECK( Text( "abcdef", TextAttributes().setWidth( 4 ) ).toString() == "abc-\ndef" ); - CHECK( Text( "abcdefg", TextAttributes().setWidth( 4 ) ).toString() == "abc-\ndefg" ); - CHECK( Text( "abcdefgh", TextAttributes().setWidth( 4 ) ).toString() == "abc-\ndef-\ngh" ); - - CHECK( Text( testString, TextAttributes().setWidth( 4 ) ).toString() == "one\ntwo\nthr-\nee\nfour" ); - CHECK( Text( testString, TextAttributes().setWidth( 3 ) ).toString() == "one\ntwo\nth-\nree\nfo-\nur" ); - } - SECTION( "As container", "" ) { - Text text( testString, TextAttributes().setWidth( 6 ) ); - REQUIRE( text.size() == 4 ); - CHECK( text[0] == "one" ); - CHECK( text[1] == "two" ); - CHECK( text[2] == "three" ); - CHECK( text[3] == "four" ); - } - SECTION( "Indent first line differently", "" ) { - Text text( testString, TextAttributes() - .setWidth( 10 ) - .setIndent( 4 ) - .setInitialIndent( 1 ) ); - CHECK( text.toString() == " one two\n three\n four" ); - } - - } - - SECTION( "With newlines", "" ) { - - // guide: 1234567890123456789 - std::string testString = "one two\nthree four"; - - SECTION( "No wrapping" , "" ) { - CHECK( Text( testString, TextAttributes().setWidth( 80 ) ).toString() == testString ); - CHECK( Text( testString, TextAttributes().setWidth( 18 ) ).toString() == testString ); - CHECK( Text( testString, TextAttributes().setWidth( 10 ) ).toString() == testString ); - } - SECTION( "Trailing newline" , "" ) { - CHECK( Text( "abcdef\n", TextAttributes().setWidth( 10 ) ).toString() == "abcdef" ); - CHECK( Text( "abcdef", TextAttributes().setWidth( 6 ) ).toString() == "abcdef" ); - CHECK( Text( "abcdef\n", TextAttributes().setWidth( 6 ) ).toString() == "abcdef" ); - CHECK( Text( "abcdef\n", TextAttributes().setWidth( 5 ) ).toString() == "abcd-\nef" ); - } - SECTION( "Wrapped once", "" ) { - CHECK( Text( testString, TextAttributes().setWidth( 9 ) ).toString() == "one two\nthree\nfour" ); - CHECK( Text( testString, TextAttributes().setWidth( 8 ) ).toString() == "one two\nthree\nfour" ); - CHECK( Text( testString, TextAttributes().setWidth( 7 ) ).toString() == "one two\nthree\nfour" ); - } - SECTION( "Wrapped twice", "" ) { - CHECK( Text( testString, TextAttributes().setWidth( 6 ) ).toString() == "one\ntwo\nthree\nfour" ); - } - } - - SECTION( "With wrap-before/ after characters", "" ) { - std::string testString = "one,two(three) "; - - SECTION( "No wrapping", "" ) { - CHECK( Text( testString, TextAttributes().setWidth( 80 ) ).toString() == testString ); - CHECK( Text( testString, TextAttributes().setWidth( 24 ) ).toString() == testString ); - } - SECTION( "Wrap before", "" ) { - CHECK( Text( testString, TextAttributes().setWidth( 11 ) ).toString() == "one,two\n(three)\n" ); - } - SECTION( "Wrap after", "" ) { - CHECK( Text( testString, TextAttributes().setWidth( 6 ) ).toString() == "one,\ntwo\n(thre-\ne)\n" ); - CHECK( Text( testString, TextAttributes().setWidth( 5 ) ).toString() == "one,\ntwo\n(thr-\nee)\n" ); - CHECK( Text( testString, TextAttributes().setWidth( 4 ) ).toString() == "one,\ntwo\n(th-\nree)\n" ); - } - } - -} - -using namespace Catch; - -class ColourString { -public: - - struct ColourIndex { - ColourIndex( Colour::Code _colour, std::size_t _fromIndex, std::size_t _toIndex ) - : colour( _colour ), - fromIndex( _fromIndex ), - toIndex( _toIndex ) - {} - - Colour::Code colour; - std::size_t fromIndex; - std::size_t toIndex; - }; - - ColourString( std::string const& _string ) - : string( _string ) - {} - ColourString( std::string const& _string, std::vector const& _colours ) - : string( _string ), colours( _colours ) - {} - - ColourString& addColour( Colour::Code colour, int _index ) { - colours.push_back( ColourIndex( colour, - resolveRelativeIndex( _index ), - resolveRelativeIndex( _index )+1 ) ); - return *this; - } - ColourString& addColour( Colour::Code colour, int _fromIndex, int _toIndex ) { - colours.push_back( ColourIndex( colour, - resolveRelativeIndex(_fromIndex), - resolveLastRelativeIndex( _toIndex ) ) ); - return *this; - } - - void writeToStream( std::ostream& _stream ) const { - std::size_t last = 0; - for( std::size_t i = 0; i < colours.size(); ++i ) { - ColourIndex const& index = colours[i]; - if( index.fromIndex > last ) - _stream << string.substr( last, index.fromIndex-last ); - { - Colour colourGuard( index.colour ); - _stream << string.substr( index.fromIndex, index.toIndex-index.fromIndex ); - } - last = index.toIndex; - } - if( last < string.size() ) - _stream << string.substr( last ); - } - friend std::ostream& operator << ( std::ostream& _stream, ColourString const& _colourString ) { - _colourString.writeToStream( _stream ); - return _stream; - } - -private: - - std::size_t resolveLastRelativeIndex( int _index ) { - std::size_t index = resolveRelativeIndex( _index ); - return index == 0 ? string.size() : index; - } - std::size_t resolveRelativeIndex( int _index ) { - return static_cast( _index >= 0 - ? _index - : static_cast( string.size() )+_index ); - } - std::string string; - std::vector colours; -}; - -TEST_CASE( "replaceInPlace", "" ) { +TEST_CASE( "replaceInPlace" ) { std::string letters = "abcdefcg"; SECTION( "replace single char" ) { - CHECK( replaceInPlace( letters, "b", "z" ) ); + CHECK( Catch::replaceInPlace( letters, "b", "z" ) ); CHECK( letters == "azcdefcg" ); } SECTION( "replace two chars" ) { - CHECK( replaceInPlace( letters, "c", "z" ) ); + CHECK( Catch::replaceInPlace( letters, "c", "z" ) ); CHECK( letters == "abzdefzg" ); } SECTION( "replace first char" ) { - CHECK( replaceInPlace( letters, "a", "z" ) ); + CHECK( Catch::replaceInPlace( letters, "a", "z" ) ); CHECK( letters == "zbcdefcg" ); } SECTION( "replace last char" ) { - CHECK( replaceInPlace( letters, "g", "z" ) ); + CHECK( Catch::replaceInPlace( letters, "g", "z" ) ); CHECK( letters == "abcdefcz" ); } SECTION( "replace all chars" ) { - CHECK( replaceInPlace( letters, letters, "replaced" ) ); + CHECK( Catch::replaceInPlace( letters, letters, "replaced" ) ); CHECK( letters == "replaced" ); } SECTION( "replace no chars" ) { - CHECK_FALSE( replaceInPlace( letters, "x", "z" ) ); + CHECK_FALSE( Catch::replaceInPlace( letters, "x", "z" ) ); CHECK( letters == letters ); } SECTION( "escape '" ) { std::string s = "didn't"; - CHECK( replaceInPlace( s, "'", "|'" ) ); + CHECK( Catch::replaceInPlace( s, "'", "|'" ) ); CHECK( s == "didn|'t" ); } } -// !TBD: This will be folded into Text class -TEST_CASE( "Strings can be rendered with colour", "[.colour]" ) { - - { - ColourString cs( "hello" ); - cs .addColour( Colour::Red, 0 ) - .addColour( Colour::Green, -1 ); - - Catch::cout() << cs << std::endl; - } - - { - ColourString cs( "hello" ); - cs .addColour( Colour::Blue, 1, -2 ); - - Catch::cout() << cs << std::endl; - } - -} - -TEST_CASE( "Text can be formatted using the Text class", "" ) { - - CHECK( Text( "hi there" ).toString() == "hi there" ); - - TextAttributes narrow; - narrow.setWidth( 6 ); - - CHECK( Text( "hi there", narrow ).toString() == "hi\nthere" ); -} - -TEST_CASE( "Long text is truncated", "[Text][Truncated]" ) { - - std::string longLine( 90, '*' ); - - std::ostringstream oss; - for(int i = 0; i < 600; ++i ) - oss << longLine << longLine << "\n"; - Text t( oss.str() ); - CHECK_THAT( t.toString(), EndsWith( "... message truncated due to excessive size" ) ); - -} inline void manuallyRegisteredTestFunction() { SUCCEED( "was called" ); } struct AutoTestReg { AutoTestReg() { - REGISTER_TEST_CASE( manuallyRegisteredTestFunction, "ManuallyRegistered", "" ); + REGISTER_TEST_CASE( manuallyRegisteredTestFunction, "ManuallyRegistered" ); } }; -AutoTestReg autoTestReg; +static AutoTestReg autoTestReg; diff --git a/projects/SelfTest/ToStringChrono.cpp b/projects/SelfTest/ToStringChrono.cpp new file mode 100644 index 00000000..97d0c1cb --- /dev/null +++ b/projects/SelfTest/ToStringChrono.cpp @@ -0,0 +1,41 @@ +#define CATCH_CONFIG_ENABLE_CHRONO_STRINGMAKER +#include "catch.hpp" + +#include +#include + +TEST_CASE("Stringifying std::chrono::duration helpers", "[toString][chrono]") { + // No literals because we still support c++11 + auto hour = std::chrono::hours(1); + auto minute = std::chrono::minutes(1); + auto seconds = std::chrono::seconds(60); + auto micro = std::chrono::microseconds(1); + auto milli = std::chrono::milliseconds(1); + auto nano = std::chrono::nanoseconds(1); + REQUIRE(minute == seconds); + REQUIRE(hour != seconds); + REQUIRE(micro != milli); + REQUIRE(nano != micro); +} + +TEST_CASE("Stringifying std::chrono::duration with weird ratios", "[toString][chrono]") { + std::chrono::duration> half_minute(1); + std::chrono::duration> femto_second(1); + REQUIRE(half_minute != femto_second); +} + +TEST_CASE("Stringifying std::chrono::time_point", "[toString][chrono]") { + auto now = std::chrono::system_clock::now(); + auto later = now + std::chrono::minutes(2); + REQUIRE(now != later); +} + +TEST_CASE("Stringifying std::chrono::time_point", "[toString][chrono][!nonportable]") { + auto now = std::chrono::high_resolution_clock::now(); + auto later = now + std::chrono::minutes(2); + REQUIRE(now != later); + + auto now2 = std::chrono::steady_clock::now(); + auto later2 = now2 + std::chrono::minutes(2); + REQUIRE(now2 != later2); +} diff --git a/projects/SelfTest/ToStringPair.cpp b/projects/SelfTest/ToStringPair.cpp index 8f510700..1445c54f 100644 --- a/projects/SelfTest/ToStringPair.cpp +++ b/projects/SelfTest/ToStringPair.cpp @@ -1,37 +1,20 @@ +#define CATCH_CONFIG_ENABLE_PAIR_STRINGMAKER #include "catch.hpp" -// === Pair === -namespace Catch { - // Note: If we put this in the right place in catch_tostring, then - // we can make it an overload of Catch::toString - template - struct StringMaker > { - static std::string convert( const std::pair& pair ) { - std::ostringstream oss; - oss << "{ " - << toString( pair.first ) - << ", " - << toString( pair.second ) - << " }"; - return oss.str(); - } - }; -} - TEST_CASE( "std::pair -> toString", "[toString][pair]" ) { std::pair value( 34, "xyzzy" ); - REQUIRE( Catch::toString( value ) == "{ 34, \"xyzzy\" }" ); + REQUIRE( ::Catch::Detail::stringify( value ) == "{ 34, \"xyzzy\" }" ); } TEST_CASE( "std::pair -> toString", "[toString][pair]" ) { std::pair value( 34, "xyzzy" ); - REQUIRE( Catch::toString(value) == "{ 34, \"xyzzy\" }" ); + REQUIRE( ::Catch::Detail::stringify(value) == "{ 34, \"xyzzy\" }" ); } TEST_CASE( "std::vector > -> toString", "[toString][pair]" ) { std::vector > pr; pr.push_back( std::make_pair("green", 55 ) ); - REQUIRE( Catch::toString( pr ) == "{ { \"green\", 55 } }" ); + REQUIRE( ::Catch::Detail::stringify( pr ) == "{ { \"green\", 55 } }" ); } // This is pretty contrived - I figure if this works, anything will... @@ -43,5 +26,5 @@ TEST_CASE( "pair > -> toString", "[t right_t right( "Ford", 24 ); std::pair pair( left, right ); - REQUIRE( Catch::toString( pair ) == "{ { 42, \"Arthur\" }, { \"Ford\", 24 } }" ); + REQUIRE( ::Catch::Detail::stringify( pair ) == "{ { 42, \"Arthur\" }, { \"Ford\", 24 } }" ); } diff --git a/projects/SelfTest/ToStringTuple.cpp b/projects/SelfTest/ToStringTuple.cpp index fa61f631..fe19ce01 100644 --- a/projects/SelfTest/ToStringTuple.cpp +++ b/projects/SelfTest/ToStringTuple.cpp @@ -1,59 +1,47 @@ +#define CATCH_CONFIG_ENABLE_TUPLE_STRINGMAKER #include "catch.hpp" -#ifdef CATCH_CPP11_OR_GREATER +#include -#ifdef __clang__ -#pragma clang diagnostic push -#pragma clang diagnostic ignored "-Wc++98-compat" -#endif - -TEST_CASE( "tuple<>", "[toString][tuple][c++11][.]" ) +TEST_CASE( "tuple<>", "[toString][tuple]" ) { typedef std::tuple<> type; - CHECK( "{ }" == Catch::toString(type{}) ); + CHECK( "{ }" == ::Catch::Detail::stringify(type{}) ); type value {}; - CHECK( "{ }" == Catch::toString(value) ); + CHECK( "{ }" == ::Catch::Detail::stringify(value) ); } -TEST_CASE( "tuple", "[toString][tuple][c++11][.]" ) +TEST_CASE( "tuple", "[toString][tuple]" ) { typedef std::tuple type; - CHECK( "{ 0 }" == Catch::toString(type{0}) ); + CHECK( "{ 0 }" == ::Catch::Detail::stringify(type{0}) ); } -TEST_CASE( "tuple", "[toString][tuple][c++11][.]" ) +TEST_CASE( "tuple", "[toString][tuple]" ) { typedef std::tuple type; - CHECK( "1.2f" == Catch::toString(float(1.2)) ); - CHECK( "{ 1.2f, 0 }" == Catch::toString(type{1.2,0}) ); + CHECK( "1.2f" == ::Catch::Detail::stringify(float(1.2)) ); + CHECK( "{ 1.2f, 0 }" == ::Catch::Detail::stringify(type{1.2f,0}) ); } -TEST_CASE( "tuple", "[toString][tuple][c++11][.]" ) +TEST_CASE( "tuple", "[toString][tuple]" ) { typedef std::tuple type; - CHECK( "{ \"hello\", \"world\" }" == Catch::toString(type{"hello","world"}) ); + CHECK( "{ \"hello\", \"world\" }" == ::Catch::Detail::stringify(type{"hello","world"}) ); } -TEST_CASE( "tuple,tuple<>,float>", "[toString][tuple][c++11][.]" ) +TEST_CASE( "tuple,tuple<>,float>", "[toString][tuple]" ) { typedef std::tuple,std::tuple<>,float> type; type value { std::tuple{42}, {}, 1.2f }; - CHECK( "{ { 42 }, { }, 1.2f }" == Catch::toString(value) ); + CHECK( "{ { 42 }, { }, 1.2f }" == ::Catch::Detail::stringify(value) ); } -#ifdef CATCH_CONFIG_CPP11_NULLPTR -TEST_CASE( "tuple", "[toString][tuple][c++11][.]" ) +TEST_CASE( "tuple", "[toString][tuple]" ) { typedef std::tuple type; type value { nullptr, 42, "Catch me" }; - CHECK( "{ nullptr, 42, \"Catch me\" }" == Catch::toString(value) ); + CHECK( "{ nullptr, 42, \"Catch me\" }" == ::Catch::Detail::stringify(value) ); } -#endif - -#ifdef __clang__ -#pragma clang diagnostic pop -#endif - -#endif /* #ifdef CATCH_CPP11_OR_GREATER */ diff --git a/projects/SelfTest/ToStringVector.cpp b/projects/SelfTest/ToStringVector.cpp index cd071a9d..df9c55ab 100644 --- a/projects/SelfTest/ToStringVector.cpp +++ b/projects/SelfTest/ToStringVector.cpp @@ -6,39 +6,35 @@ TEST_CASE( "vector -> toString", "[toString][vector]" ) { std::vector vv; - REQUIRE( Catch::toString(vv) == "{ }" ); + REQUIRE( ::Catch::Detail::stringify(vv) == "{ }" ); vv.push_back( 42 ); - REQUIRE( Catch::toString(vv) == "{ 42 }" ); + REQUIRE( ::Catch::Detail::stringify(vv) == "{ 42 }" ); vv.push_back( 250 ); - REQUIRE( Catch::toString(vv) == "{ 42, 250 }" ); + REQUIRE( ::Catch::Detail::stringify(vv) == "{ 42, 250 }" ); } TEST_CASE( "vector -> toString", "[toString][vector]" ) { std::vector vv; - REQUIRE( Catch::toString(vv) == "{ }" ); + REQUIRE( ::Catch::Detail::stringify(vv) == "{ }" ); vv.push_back( "hello" ); - REQUIRE( Catch::toString(vv) == "{ \"hello\" }" ); + REQUIRE( ::Catch::Detail::stringify(vv) == "{ \"hello\" }" ); vv.push_back( "world" ); - REQUIRE( Catch::toString(vv) == "{ \"hello\", \"world\" }" ); + REQUIRE( ::Catch::Detail::stringify(vv) == "{ \"hello\", \"world\" }" ); } -#if defined(CATCH_CPP11_OR_GREATER) -#ifdef __clang__ -#pragma clang diagnostic push -#pragma clang diagnostic ignored "-Wc++98-compat" -#endif - -/* - Note: These tests *can* be made to work with C++ < 11, but the - allocator is a lot more work... -*/ namespace { /* Minimal Allocator */ template struct minimal_allocator { - typedef T value_type; - typedef std::size_t size_type; + using value_type = T; + using size_type = std::size_t; + + minimal_allocator() = default; + template + minimal_allocator(const minimal_allocator&) {} + + T *allocate( size_type n ) { return static_cast( ::operator new( n * sizeof(T) ) ); } @@ -52,26 +48,21 @@ namespace { }; } -TEST_CASE( "vector -> toString", "[toString][vector,allocator][c++11][.]" ) { +TEST_CASE( "vector -> toString", "[toString][vector,allocator]" ) { std::vector > vv; - REQUIRE( Catch::toString(vv) == "{ }" ); + REQUIRE( ::Catch::Detail::stringify(vv) == "{ }" ); vv.push_back( 42 ); - REQUIRE( Catch::toString(vv) == "{ 42 }" ); + REQUIRE( ::Catch::Detail::stringify(vv) == "{ 42 }" ); vv.push_back( 250 ); - REQUIRE( Catch::toString(vv) == "{ 42, 250 }" ); + REQUIRE( ::Catch::Detail::stringify(vv) == "{ 42, 250 }" ); } -TEST_CASE( "vec> -> toString", "[toString][vector,allocator][c++11][.]" ) { - typedef std::vector > inner; - typedef std::vector vector; +TEST_CASE( "vec> -> toString", "[toString][vector,allocator]" ) { + using inner = std::vector>; + using vector = std::vector; vector v; - REQUIRE( Catch::toString(v) == "{ }" ); + REQUIRE( ::Catch::Detail::stringify(v) == "{ }" ); v.push_back( inner { "hello" } ); v.push_back( inner { "world" } ); - REQUIRE( Catch::toString(v) == "{ { \"hello\" }, { \"world\" } }" ); + REQUIRE( ::Catch::Detail::stringify(v) == "{ { \"hello\" }, { \"world\" } }" ); } - -#ifdef __clang__ -#pragma clang diagnostic pop -#endif -#endif // CATCH_CPP11_OR_GREATER diff --git a/projects/SelfTest/ToStringWhich.cpp b/projects/SelfTest/ToStringWhich.cpp index a1d2a609..fc658a7a 100644 --- a/projects/SelfTest/ToStringWhich.cpp +++ b/projects/SelfTest/ToStringWhich.cpp @@ -5,17 +5,21 @@ */ -struct has_toString { }; +struct has_operator { }; struct has_maker {}; -struct has_maker_and_toString {}; +struct has_maker_and_operator {}; + +std::ostream& operator<<(std::ostream& os, const has_operator&) { + os << "operator<<( has_operator )"; + return os; +} + +std::ostream& operator<<(std::ostream& os, const has_maker_and_operator&) { + os << "operator<<( has_maker_and_operator )"; + return os; +} namespace Catch { - inline std::string toString( const has_toString& ) { - return "toString( has_toString )"; - } - inline std::string toString( const has_maker_and_toString& ) { - return "toString( has_maker_and_toString )"; - } template<> struct StringMaker { static std::string convert( const has_maker& ) { @@ -23,49 +27,47 @@ namespace Catch { } }; template<> - struct StringMaker { - static std::string convert( const has_maker_and_toString& ) { - return "StringMaker"; + struct StringMaker { + static std::string convert( const has_maker_and_operator& ) { + return "StringMaker"; } }; } -// Call the overload -TEST_CASE( "toString( has_toString )", "[toString]" ) { - has_toString item; - REQUIRE( Catch::toString( item ) == "toString( has_toString )" ); +// Call the operator +TEST_CASE( "stringify( has_operator )", "[toString]" ) { + has_operator item; + REQUIRE( ::Catch::Detail::stringify( item ) == "operator<<( has_operator )" ); } -// Call the overload -TEST_CASE( "toString( has_maker )", "toString]" ) { +// Call the stringmaker +TEST_CASE( "stringify( has_maker )", "[toString]" ) { has_maker item; - REQUIRE( Catch::toString( item ) == "StringMaker" ); + REQUIRE( ::Catch::Detail::stringify( item ) == "StringMaker" ); } -// Call the overload -TEST_CASE( "toString( has_maker_and_toString )", "[.][toString]" ) { - has_maker_and_toString item; - REQUIRE( Catch::toString( item ) == "toString( has_maker_and_toString )" ); +// Call the stringmaker +TEST_CASE( "stringify( has_maker_and_toString )", "[.][toString]" ) { + has_maker_and_operator item; + REQUIRE( ::Catch::Detail::stringify( item ) == "StringMaker" ); } // Vectors... // Don't run this in approval tests as it is sensitive to two phase lookup differences TEST_CASE( "toString( vectors v(1); - // This invokes template toString which actually gives us '{ ? }' - REQUIRE( Catch::toString( v ) == "{ {?} }" ); + std::vector v(1); + REQUIRE( ::Catch::Detail::stringify( v ) == "{ operator<<( has_operator ) }" ); } TEST_CASE( "toString( vectors v(1); - REQUIRE( Catch::toString( v ) == "{ StringMaker }" ); + 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 v(1); - // Note: This invokes the template toString -> StringMaker - REQUIRE( Catch::toString( v ) == "{ StringMaker }" ); + std::vector v(1); + REQUIRE( ::Catch::Detail::stringify( v ) == "{ StringMaker }" ); } diff --git a/projects/SelfTest/TrickyTests.cpp b/projects/SelfTest/TrickyTests.cpp index 60f58131..130d5f43 100644 --- a/projects/SelfTest/TrickyTests.cpp +++ b/projects/SelfTest/TrickyTests.cpp @@ -10,24 +10,20 @@ #pragma clang diagnostic ignored "-Wpadded" #endif +#ifdef _MSC_VER +#pragma warning (disable : 4702) // Disable unreachable code warning for the last test + // that is triggered when compiling as Win32|Release +#endif + #include #include "catch.hpp" -#ifdef __clang__ -#pragma clang diagnostic ignored "-Wc++98-compat" -#pragma clang diagnostic ignored "-Wc++98-compat-pedantic" -#endif - -namespace Catch -{ - template<> - std::string toString >( const std::pair& value ) - { +namespace Catch { + std::string toString( const std::pair& value ) { std::ostringstream oss; oss << "std::pair( " << value.first << ", " << value.second << " )"; return oss.str(); - } } @@ -172,14 +168,14 @@ namespace ObjectWithConversions { struct Object { - operator unsigned int() {return 0xc0000000;} + operator unsigned int() const {return 0xc0000000;} }; /////////////////////////////////////////////////////////////////////////////// TEST_CASE ( "Operators at different namespace levels not hijacked by Koenig lookup", - "[Tricky]" + "[Tricky][approvals]" ) { Object o; @@ -187,37 +183,18 @@ namespace ObjectWithConversions } } -namespace ObjectWithNonConstEqualityOperator -{ - struct Test - { - Test( unsigned int v ) - : m_value(v) - {} - - bool operator==( const Test&rhs ) - { - return (m_value == rhs.m_value); - } - bool operator==( const Test&rhs ) const - { - return (m_value != rhs.m_value); - } - unsigned int m_value; - }; - - TEST_CASE("Demonstrate that a non-const == is not used", "[Tricky]" ) - { - Test t( 1 ); - REQUIRE( t == 1u ); - } -} - namespace EnumBitFieldTests { - enum Bits {bit0 = 0x0001, bit1 = 0x0002, bit2 = 0x0004, bit3 = 0x0008, bit1and2 = 0x0006, - bit30 = 0x40000000, bit31 = 0x80000000, - bit30and31 = 0xc0000000}; + enum Bits : uint32_t { + bit0 = 0x0001, + bit1 = 0x0002, + bit2 = 0x0004, + bit3 = 0x0008, + bit1and2 = bit1 | bit2, + bit30 = 0x40000000, + bit31 = 0x80000000, + bit30and31 = bit30 | bit31 + }; TEST_CASE( "Test enum bit values", "[Tricky]" ) { @@ -236,7 +213,7 @@ struct Obj TEST_CASE("boolean member", "[Tricky]") { Obj obj; - REQUIRE( obj.prop != CATCH_NULL ); + REQUIRE( obj.prop != nullptr ); } // Tests for a problem submitted by Ralph McArdell @@ -298,8 +275,8 @@ struct Boolable { explicit Boolable( bool value ) : m_value( value ) {} - operator Catch::SafeBool::type() const { - return Catch::SafeBool::makeSafe( m_value ); + explicit operator bool() const { + return m_value; } bool m_value; @@ -322,15 +299,15 @@ TEST_CASE( "Assertions then sections", "[Tricky]" ) REQUIRE( Catch::alwaysTrue() ); - SECTION( "A section", "" ) + SECTION( "A section" ) { REQUIRE( Catch::alwaysTrue() ); - SECTION( "Another section", "" ) + SECTION( "Another section" ) { REQUIRE( Catch::alwaysTrue() ); } - SECTION( "Another other section", "" ) + SECTION( "Another other section" ) { REQUIRE( Catch::alwaysTrue() ); } @@ -345,7 +322,7 @@ struct Awkward TEST_CASE( "non streamable - with conv. op", "[Tricky]" ) { Awkward awkward; - std::string s = Catch::toString( awkward ); + std::string s = ::Catch::Detail::stringify( awkward ); REQUIRE( s == "7" ); } @@ -369,7 +346,7 @@ struct S }; -TEST_CASE( "Comparing member function pointers", "[Tricky][member function pointer]" ) +TEST_CASE( "Comparing member function pointers", "[Tricky][member function pointer][approvals]" ) { typedef void (S::*MF)(); MF m = &S::f; @@ -385,27 +362,88 @@ TEST_CASE( "pointer to class", "[Tricky]" ) REQUIRE( p == 0 ); } -#ifdef CATCH_CONFIG_CPP11_NULLPTR - #include -TEST_CASE( "null_ptr", "[Tricky][c++11][.]" ) +TEST_CASE( "null_ptr", "[Tricky]" ) { std::unique_ptr ptr; REQUIRE(ptr.get() == nullptr); } -#endif - TEST_CASE( "X/level/0/a", "[Tricky]" ) { SUCCEED(""); } TEST_CASE( "X/level/0/b", "[Tricky][fizz]" ){ SUCCEED(""); } TEST_CASE( "X/level/1/a", "[Tricky]" ) { SUCCEED(""); } TEST_CASE( "X/level/1/b", "[Tricky]" ) { SUCCEED(""); } -TEST_CASE( "has printf", "" ) { +TEST_CASE( "has printf" ) { - // This can cause problems as, currently, stdout itself is not redirect - only the cout (and cerr) buffer - printf( "spanner" ); + // This can cause problems as, currently, stdout itself is not redirected - only the cout (and cerr) buffer + printf( "loose text artifact\n" ); +} + +namespace { + struct constructor_throws { + [[noreturn]] constructor_throws() { + throw 1; + } + }; +} + +TEST_CASE("Commas in various macros are allowed") { + REQUIRE_THROWS( std::vector{constructor_throws{}, constructor_throws{}} ); + CHECK_THROWS( std::vector{constructor_throws{}, constructor_throws{}} ); + REQUIRE_NOTHROW( std::vector{1, 2, 3} == std::vector{1, 2, 3} ); + CHECK_NOTHROW( std::vector{1, 2, 3} == std::vector{1, 2, 3} ); + + REQUIRE(std::vector{1, 2} == std::vector{1, 2}); + CHECK( std::vector{1, 2} == std::vector{1, 2} ); + REQUIRE_FALSE(std::vector{1, 2} == std::vector{1, 2, 3}); + CHECK_FALSE( std::vector{1, 2} == std::vector{1, 2, 3} ); + + CHECK_NOFAIL( std::vector{1, 2} == std::vector{1, 2} ); + CHECKED_IF( std::vector{1, 2} == std::vector{1, 2} ) { + REQUIRE(true); + } CHECKED_ELSE( std::vector{1, 2} == std::vector{1, 2} ) { + CHECK(true); + } +} + +TEST_CASE( "null deref", "[.][failing][!nonportable]" ) { + CHECK( false ); + int *x = NULL; + *x = 1; +} + +TEST_CASE( "non-copyable objects", "[.][failing]" ) { + // Thanks to Agustin Bergé (@k-ballo on the cpplang Slack) for raising this + std::type_info const& ti = typeid(int); + CHECK( ti == typeid(int) ); +} + +// #925 +using signal_t = void (*) (void*); + +struct TestClass { + signal_t testMethod_uponComplete_arg = nullptr; +}; + +namespace utility { + inline static void synchronizing_callback( void * ) { } +} + +TEST_CASE("#925: comparing function pointer to function address failed to compile", "[!nonportable]" ) { + + TestClass test; + REQUIRE(utility::synchronizing_callback != test.testMethod_uponComplete_arg); +} + +TEST_CASE( "Bitfields can be captured (#1027)" ) { + struct Y { + uint32_t v : 1; + }; + Y y{ 0 }; + REQUIRE( y.v == 0 ); + REQUIRE( 0 == y.v ); } TEST_CASE( "null deref", "[.][failing][!nonportable]" ) { diff --git a/projects/SelfTest/VariadicMacrosTests.cpp b/projects/SelfTest/VariadicMacrosTests.cpp index b784076d..fd651c5e 100644 --- a/projects/SelfTest/VariadicMacrosTests.cpp +++ b/projects/SelfTest/VariadicMacrosTests.cpp @@ -8,7 +8,6 @@ #include "catch.hpp" -#ifdef CATCH_CONFIG_VARIADIC_MACROS TEST_CASE() { @@ -28,4 +27,3 @@ TEST_CASE( "Variadic macros", "[variadic][sections]" ) } } -#endif diff --git a/projects/XCode/OCTest/OCTest.xcodeproj/project.pbxproj b/projects/XCode/OCTest/OCTest.xcodeproj/project.pbxproj new file mode 100644 index 00000000..91e2cd9a --- /dev/null +++ b/projects/XCode/OCTest/OCTest.xcodeproj/project.pbxproj @@ -0,0 +1,267 @@ +// !$*UTF8*$! +{ + archiveVersion = 1; + classes = { + }; + objectVersion = 46; + objects = { + +/* Begin PBXBuildFile section */ + 4A5C29AA1F715603007CB94C /* catch_objc_impl.mm in Sources */ = {isa = PBXBuildFile; fileRef = 4A5C29A91F715603007CB94C /* catch_objc_impl.mm */; }; + 4A63D2AC14E3C1A900F615CB /* OCTest.1 in CopyFiles */ = {isa = PBXBuildFile; fileRef = 4A63D2AB14E3C1A900F615CB /* OCTest.1 */; }; + 4A63D2B314E3C1E600F615CB /* Main.mm in Sources */ = {isa = PBXBuildFile; fileRef = 4A63D2B214E3C1E600F615CB /* Main.mm */; }; + 4A63D2C014E4544700F615CB /* Foundation.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 4A63D2BF14E4544700F615CB /* Foundation.framework */; }; + 4A63D2C614E454CC00F615CB /* CatchOCTestCase.mm in Sources */ = {isa = PBXBuildFile; fileRef = 4A63D2C214E454CC00F615CB /* CatchOCTestCase.mm */; }; + 4A63D2C714E454CC00F615CB /* OCTest.mm in Sources */ = {isa = PBXBuildFile; fileRef = 4A63D2C314E454CC00F615CB /* OCTest.mm */; }; + 4A63D2C814E454CC00F615CB /* TestObj.m in Sources */ = {isa = PBXBuildFile; fileRef = 4A63D2C514E454CC00F615CB /* TestObj.m */; }; +/* End PBXBuildFile section */ + +/* Begin PBXCopyFilesBuildPhase section */ + 4A63D2A314E3C1A900F615CB /* CopyFiles */ = { + isa = PBXCopyFilesBuildPhase; + buildActionMask = 2147483647; + dstPath = /usr/share/man/man1/; + dstSubfolderSpec = 0; + files = ( + 4A63D2AC14E3C1A900F615CB /* OCTest.1 in CopyFiles */, + ); + runOnlyForDeploymentPostprocessing = 1; + }; +/* End PBXCopyFilesBuildPhase section */ + +/* Begin PBXFileReference section */ + 4A5C29A91F715603007CB94C /* catch_objc_impl.mm */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.objcpp; path = catch_objc_impl.mm; sourceTree = ""; }; + 4A63D2A514E3C1A900F615CB /* OCTest */ = {isa = PBXFileReference; explicitFileType = "compiled.mach-o.executable"; includeInIndex = 0; path = OCTest; sourceTree = BUILT_PRODUCTS_DIR; }; + 4A63D2AB14E3C1A900F615CB /* OCTest.1 */ = {isa = PBXFileReference; lastKnownFileType = text.man; path = OCTest.1; sourceTree = ""; }; + 4A63D2B214E3C1E600F615CB /* Main.mm */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.objcpp; path = Main.mm; sourceTree = ""; }; + 4A63D2BF14E4544700F615CB /* Foundation.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = Foundation.framework; path = System/Library/Frameworks/Foundation.framework; sourceTree = SDKROOT; }; + 4A63D2C114E454CC00F615CB /* CatchOCTestCase.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = CatchOCTestCase.h; sourceTree = ""; }; + 4A63D2C214E454CC00F615CB /* CatchOCTestCase.mm */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.objcpp; path = CatchOCTestCase.mm; sourceTree = ""; }; + 4A63D2C314E454CC00F615CB /* OCTest.mm */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.objcpp; path = OCTest.mm; sourceTree = ""; }; + 4A63D2C414E454CC00F615CB /* TestObj.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = TestObj.h; sourceTree = ""; }; + 4A63D2C514E454CC00F615CB /* TestObj.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = TestObj.m; sourceTree = ""; }; +/* End PBXFileReference section */ + +/* Begin PBXFrameworksBuildPhase section */ + 4A63D2A214E3C1A900F615CB /* Frameworks */ = { + isa = PBXFrameworksBuildPhase; + buildActionMask = 2147483647; + files = ( + 4A63D2C014E4544700F615CB /* Foundation.framework in Frameworks */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; +/* End PBXFrameworksBuildPhase section */ + +/* Begin PBXGroup section */ + 4A63D29A14E3C1A900F615CB = { + isa = PBXGroup; + children = ( + 4AA0D94F154C0A63004B4193 /* Catch */, + 4A63D2BF14E4544700F615CB /* Foundation.framework */, + 4A63D2A814E3C1A900F615CB /* OCTest */, + 4A63D2A614E3C1A900F615CB /* Products */, + ); + sourceTree = ""; + }; + 4A63D2A614E3C1A900F615CB /* Products */ = { + isa = PBXGroup; + children = ( + 4A63D2A514E3C1A900F615CB /* OCTest */, + ); + name = Products; + sourceTree = ""; + }; + 4A63D2A814E3C1A900F615CB /* OCTest */ = { + isa = PBXGroup; + children = ( + 4A63D2C114E454CC00F615CB /* CatchOCTestCase.h */, + 4A63D2C214E454CC00F615CB /* CatchOCTestCase.mm */, + 4A63D2C314E454CC00F615CB /* OCTest.mm */, + 4A63D2C414E454CC00F615CB /* TestObj.h */, + 4A63D2C514E454CC00F615CB /* TestObj.m */, + 4A63D2B214E3C1E600F615CB /* Main.mm */, + 4A63D2AB14E3C1A900F615CB /* OCTest.1 */, + ); + path = OCTest; + sourceTree = ""; + }; + 4AA0D94F154C0A63004B4193 /* Catch */ = { + isa = PBXGroup; + children = ( + 4A5C29A91F715603007CB94C /* catch_objc_impl.mm */, + ); + name = Catch; + sourceTree = ""; + }; +/* End PBXGroup section */ + +/* Begin PBXNativeTarget section */ + 4A63D2A414E3C1A900F615CB /* OCTest */ = { + isa = PBXNativeTarget; + buildConfigurationList = 4A63D2AF14E3C1A900F615CB /* Build configuration list for PBXNativeTarget "OCTest" */; + buildPhases = ( + 4A63D2A114E3C1A900F615CB /* Sources */, + 4A63D2A214E3C1A900F615CB /* Frameworks */, + 4A63D2A314E3C1A900F615CB /* CopyFiles */, + ); + buildRules = ( + ); + dependencies = ( + ); + name = OCTest; + productName = OCTest; + productReference = 4A63D2A514E3C1A900F615CB /* OCTest */; + productType = "com.apple.product-type.tool"; + }; +/* End PBXNativeTarget section */ + +/* Begin PBXProject section */ + 4A63D29C14E3C1A900F615CB /* Project object */ = { + isa = PBXProject; + attributes = { + LastUpgradeCheck = 0500; + }; + buildConfigurationList = 4A63D29F14E3C1A900F615CB /* Build configuration list for PBXProject "OCTest" */; + compatibilityVersion = "Xcode 3.2"; + developmentRegion = English; + hasScannedForEncodings = 0; + knownRegions = ( + en, + ); + mainGroup = 4A63D29A14E3C1A900F615CB; + productRefGroup = 4A63D2A614E3C1A900F615CB /* Products */; + projectDirPath = ""; + projectRoot = ""; + targets = ( + 4A63D2A414E3C1A900F615CB /* OCTest */, + ); + }; +/* End PBXProject section */ + +/* Begin PBXSourcesBuildPhase section */ + 4A63D2A114E3C1A900F615CB /* Sources */ = { + isa = PBXSourcesBuildPhase; + buildActionMask = 2147483647; + files = ( + 4A63D2B314E3C1E600F615CB /* Main.mm in Sources */, + 4A5C29AA1F715603007CB94C /* catch_objc_impl.mm in Sources */, + 4A63D2C614E454CC00F615CB /* CatchOCTestCase.mm in Sources */, + 4A63D2C714E454CC00F615CB /* OCTest.mm in Sources */, + 4A63D2C814E454CC00F615CB /* TestObj.m in Sources */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; +/* End PBXSourcesBuildPhase section */ + +/* Begin XCBuildConfiguration section */ + 4A63D2AD14E3C1A900F615CB /* Debug */ = { + isa = XCBuildConfiguration; + buildSettings = { + ALWAYS_SEARCH_USER_PATHS = NO; + CLANG_WARN_BOOL_CONVERSION = YES; + CLANG_WARN_CONSTANT_CONVERSION = YES; + CLANG_WARN_EMPTY_BODY = YES; + CLANG_WARN_ENUM_CONVERSION = YES; + CLANG_WARN_INT_CONVERSION = YES; + CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; + COPY_PHASE_STRIP = NO; + GCC_C_LANGUAGE_STANDARD = gnu99; + GCC_DYNAMIC_NO_PIC = NO; + GCC_ENABLE_OBJC_EXCEPTIONS = YES; + GCC_OPTIMIZATION_LEVEL = 0; + GCC_PREPROCESSOR_DEFINITIONS = ( + "DEBUG=1", + "$(inherited)", + ); + GCC_SYMBOLS_PRIVATE_EXTERN = NO; + GCC_VERSION = com.apple.compilers.llvm.clang.1_0; + GCC_WARN_64_TO_32_BIT_CONVERSION = YES; + GCC_WARN_ABOUT_MISSING_PROTOTYPES = YES; + GCC_WARN_ABOUT_RETURN_TYPE = YES; + GCC_WARN_UNDECLARED_SELECTOR = YES; + GCC_WARN_UNINITIALIZED_AUTOS = YES; + GCC_WARN_UNUSED_FUNCTION = YES; + GCC_WARN_UNUSED_VARIABLE = YES; + HEADER_SEARCH_PATHS = ../../../include; + MACOSX_DEPLOYMENT_TARGET = 10.7; + ONLY_ACTIVE_ARCH = YES; + SDKROOT = macosx; + }; + name = Debug; + }; + 4A63D2AE14E3C1A900F615CB /* Release */ = { + isa = XCBuildConfiguration; + buildSettings = { + ALWAYS_SEARCH_USER_PATHS = NO; + CLANG_WARN_BOOL_CONVERSION = YES; + CLANG_WARN_CONSTANT_CONVERSION = YES; + CLANG_WARN_EMPTY_BODY = YES; + CLANG_WARN_ENUM_CONVERSION = YES; + CLANG_WARN_INT_CONVERSION = YES; + CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; + COPY_PHASE_STRIP = YES; + DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym"; + GCC_C_LANGUAGE_STANDARD = gnu99; + GCC_ENABLE_OBJC_EXCEPTIONS = YES; + GCC_VERSION = com.apple.compilers.llvm.clang.1_0; + GCC_WARN_64_TO_32_BIT_CONVERSION = YES; + GCC_WARN_ABOUT_MISSING_PROTOTYPES = YES; + GCC_WARN_ABOUT_RETURN_TYPE = YES; + GCC_WARN_UNDECLARED_SELECTOR = YES; + GCC_WARN_UNINITIALIZED_AUTOS = YES; + GCC_WARN_UNUSED_FUNCTION = YES; + GCC_WARN_UNUSED_VARIABLE = YES; + HEADER_SEARCH_PATHS = ../../../include; + MACOSX_DEPLOYMENT_TARGET = 10.7; + SDKROOT = macosx; + }; + name = Release; + }; + 4A63D2B014E3C1A900F615CB /* Debug */ = { + isa = XCBuildConfiguration; + buildSettings = { + CLANG_CXX_LANGUAGE_STANDARD = "c++0x"; + CLANG_CXX_LIBRARY = "libc++"; + CLANG_ENABLE_OBJC_ARC = YES; + HEADER_SEARCH_PATHS = ../../../include; + PRODUCT_NAME = "$(TARGET_NAME)"; + }; + name = Debug; + }; + 4A63D2B114E3C1A900F615CB /* Release */ = { + isa = XCBuildConfiguration; + buildSettings = { + CLANG_CXX_LANGUAGE_STANDARD = "c++0x"; + CLANG_CXX_LIBRARY = "libc++"; + CLANG_ENABLE_OBJC_ARC = YES; + HEADER_SEARCH_PATHS = ../../../include; + PRODUCT_NAME = "$(TARGET_NAME)"; + }; + name = Release; + }; +/* End XCBuildConfiguration section */ + +/* Begin XCConfigurationList section */ + 4A63D29F14E3C1A900F615CB /* Build configuration list for PBXProject "OCTest" */ = { + isa = XCConfigurationList; + buildConfigurations = ( + 4A63D2AD14E3C1A900F615CB /* Debug */, + 4A63D2AE14E3C1A900F615CB /* Release */, + ); + defaultConfigurationIsVisible = 0; + defaultConfigurationName = Release; + }; + 4A63D2AF14E3C1A900F615CB /* Build configuration list for PBXNativeTarget "OCTest" */ = { + isa = XCConfigurationList; + buildConfigurations = ( + 4A63D2B014E3C1A900F615CB /* Debug */, + 4A63D2B114E3C1A900F615CB /* Release */, + ); + defaultConfigurationIsVisible = 0; + defaultConfigurationName = Release; + }; +/* End XCConfigurationList section */ + }; + rootObject = 4A63D29C14E3C1A900F615CB /* Project object */; +} diff --git a/projects/XCode/OCTest/OCTest.xcodeproj/project.xcworkspace/contents.xcworkspacedata b/projects/XCode/OCTest/OCTest.xcodeproj/project.xcworkspace/contents.xcworkspacedata new file mode 100644 index 00000000..119e61c5 --- /dev/null +++ b/projects/XCode/OCTest/OCTest.xcodeproj/project.xcworkspace/contents.xcworkspacedata @@ -0,0 +1,7 @@ + + + + + diff --git a/projects/XCode/OCTest/OCTest/CatchOCTestCase.h b/projects/XCode/OCTest/OCTest/CatchOCTestCase.h new file mode 100644 index 00000000..bd26239a --- /dev/null +++ b/projects/XCode/OCTest/OCTest/CatchOCTestCase.h @@ -0,0 +1,25 @@ +// +// CatchOCTestCase.h +// OCTest +// +// Created by Phil on 13/11/2010. +// Copyright 2010 Two Blue Cubes Ltd. All rights reserved. +// +// Distributed under the Boost Software License, Version 1.0. (See accompanying +// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) +#ifndef TWOBLUECUBES_CATCHOCTESTCASE_H_INCLUDED +#define TWOBLUECUBES_CATCHOCTESTCASE_H_INCLUDED + +#include "catch.hpp" + +#import +#import "TestObj.h" + +@interface TestFixture : NSObject +{ + TestObj* obj; +} + +@end + +#endif // TWOBLUECUBES_CATCHOCTESTCASE_H_INCLUDED diff --git a/projects/XCode/OCTest/OCTest/CatchOCTestCase.mm b/projects/XCode/OCTest/OCTest/CatchOCTestCase.mm new file mode 100644 index 00000000..9bd1fa09 --- /dev/null +++ b/projects/XCode/OCTest/OCTest/CatchOCTestCase.mm @@ -0,0 +1,87 @@ +// +// CatchOCTestCase.mm +// OCTest +// +// Created by Phil Nash on 13/11/2010. +// Copyright 2010 Two Blue Cubes Ltd. All rights reserved. +// +// Distributed under the Boost Software License, Version 1.0. (See accompanying +// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) + +#import "CatchOCTestCase.h" + + +@implementation TestFixture + + +-(void) setUp +{ + obj = [[TestObj alloc] init]; +} + +-(void) tearDown +{ + arcSafeRelease( obj ); +} + +OC_TEST_CASE( "This is a test case", "" ) +{ + REQUIRE( obj.int_val == 0 ); + + obj.int_val = 1; + + REQUIRE( obj.int_val == 1 ); +} + +OC_TEST_CASE( "This is another test case", "" ) +{ + REQUIRE( obj.int_val == 0 ); + + obj.int_val = 2; + + REQUIRE( obj.int_val == 2 ); +} + +OC_TEST_CASE( "tests a boolean value", "[!shouldfail]" ) +{ + CHECK( [obj isTrue] == NO ); + CHECK( [obj isFalse] == YES ); +} + +OC_TEST_CASE( "throws an Objective-C exception", "[!throws][!shouldfail]" ) +{ + @throw [[NSException alloc] initWithName: NSGenericException + reason: @"Objective-C exception" + userInfo: nil]; +} +OC_TEST_CASE( "throws a std c++ exception", "[!throws][!shouldfail]" ) +{ + throw std::domain_error( "std C++ exception" ); +} + +/////////////////////////////////////////////////////////////////////////// +template +void useObject( const T& object ){} + +template +void useObject( const T* object ){} + +OC_TEST_CASE( "Matches work with OC types (NSString so far)", "[!shouldfail]" ) +{ + using namespace Catch::Matchers; + + REQUIRE_THAT( @"This is a string", Equals( @"This isnt a string" ) ); + REQUIRE_THAT( @"This is a string", Contains( @"is a" ) ); + REQUIRE_THAT( @"This is a string", StartsWith( @"This" ) ); + REQUIRE_THAT( @"This is a string", EndsWith( @"string" ) ); +} + +OC_TEST_CASE( "nil NSString should not crash the test", "[!shouldfail]" ) +{ + using namespace Catch::Matchers; + + CHECK_THAT( (NSString*)nil, Equals( @"This should fail, but not crash" ) ); + CHECK_THAT( (NSString*)nil, StartsWith( @"anything" ) ); +} + +@end diff --git a/projects/XCode/OCTest/OCTest/Main.mm b/projects/XCode/OCTest/OCTest/Main.mm new file mode 100644 index 00000000..569dc4d9 --- /dev/null +++ b/projects/XCode/OCTest/OCTest/Main.mm @@ -0,0 +1,2 @@ +#define CATCH_CONFIG_MAIN +#import "catch.hpp" diff --git a/projects/XCode/OCTest/OCTest/OCTest.1 b/projects/XCode/OCTest/OCTest/OCTest.1 new file mode 100644 index 00000000..38afeb5f --- /dev/null +++ b/projects/XCode/OCTest/OCTest/OCTest.1 @@ -0,0 +1,79 @@ +.\"Modified from man(1) of FreeBSD, the NetBSD mdoc.template, and mdoc.samples. +.\"See Also: +.\"man mdoc.samples for a complete listing of options +.\"man mdoc for the short list of editing options +.\"/usr/share/misc/mdoc.template +.Dd 09/02/2012 \" DATE +.Dt OCTest 1 \" Program name and manual section number +.Os Darwin +.Sh NAME \" Section Header - required - don't modify +.Nm OCTest, +.\" The following lines are read in generating the apropos(man -k) database. Use only key +.\" words here as the database is built based on the words here and in the .ND line. +.Nm Other_name_for_same_program(), +.Nm Yet another name for the same program. +.\" Use .Nm macro to designate other names for the documented program. +.Nd This line parsed for whatis database. +.Sh SYNOPSIS \" Section Header - required - don't modify +.Nm +.Op Fl abcd \" [-abcd] +.Op Fl a Ar path \" [-a path] +.Op Ar file \" [file] +.Op Ar \" [file ...] +.Ar arg0 \" Underlined argument - use .Ar anywhere to underline +arg2 ... \" Arguments +.Sh DESCRIPTION \" Section Header - required - don't modify +Use the .Nm macro to refer to your program throughout the man page like such: +.Nm +Underlining is accomplished with the .Ar macro like this: +.Ar underlined text . +.Pp \" Inserts a space +A list of items with descriptions: +.Bl -tag -width -indent \" Begins a tagged list +.It item a \" Each item preceded by .It macro +Description of item a +.It item b +Description of item b +.El \" Ends the list +.Pp +A list of flags and their descriptions: +.Bl -tag -width -indent \" Differs from above in tag removed +.It Fl a \"-a flag as a list item +Description of -a flag +.It Fl b +Description of -b flag +.El \" Ends the list +.Pp +.\" .Sh ENVIRONMENT \" May not be needed +.\" .Bl -tag -width "ENV_VAR_1" -indent \" ENV_VAR_1 is width of the string ENV_VAR_1 +.\" .It Ev ENV_VAR_1 +.\" Description of ENV_VAR_1 +.\" .It Ev ENV_VAR_2 +.\" Description of ENV_VAR_2 +.\" .El +.Sh FILES \" File used or created by the topic of the man page +.Bl -tag -width "/Users/joeuser/Library/really_long_file_name" -compact +.It Pa /usr/share/file_name +FILE_1 description +.It Pa /Users/joeuser/Library/really_long_file_name +FILE_2 description +.El \" Ends the list +.\" .Sh DIAGNOSTICS \" May not be needed +.\" .Bl -diag +.\" .It Diagnostic Tag +.\" Diagnostic informtion here. +.\" .It Diagnostic Tag +.\" Diagnostic informtion here. +.\" .El +.Sh SEE ALSO +.\" List links in ascending order by section, alphabetically within a section. +.\" Please do not reference files that do not exist without filing a bug report +.Xr a 1 , +.Xr b 1 , +.Xr c 1 , +.Xr a 2 , +.Xr b 2 , +.Xr a 3 , +.Xr b 3 +.\" .Sh BUGS \" Document known, unremedied bugs +.\" .Sh HISTORY \" Document history if command behaves in a unique manner \ No newline at end of file diff --git a/projects/XCode/OCTest/OCTest/OCTest.mm b/projects/XCode/OCTest/OCTest/OCTest.mm new file mode 100644 index 00000000..fa3ffea6 --- /dev/null +++ b/projects/XCode/OCTest/OCTest/OCTest.mm @@ -0,0 +1,28 @@ +/* + * OCTest.mm + * OCTest + * + * Created by Phil on 13/11/2010. + * Copyright 2010 Two Blue Cubes Ltd. All rights reserved. + * + * Distributed under the Boost Software License, Version 1.0. (See accompanying + * file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) + * + */ + +#import "catch.hpp" + +#import "TestObj.h" + +TEST_CASE( "OCTest/TestObj", "tests TestObj" ) +{ + TestObj* obj = [[TestObj alloc] init]; + + REQUIRE( obj.int_val == 0 ); + + obj.int_val = 1; + + REQUIRE( obj.int_val == 1 ); + + arcSafeRelease( obj ); +} diff --git a/projects/XCode/OCTest/OCTest/TestObj.h b/projects/XCode/OCTest/OCTest/TestObj.h new file mode 100644 index 00000000..8443921f --- /dev/null +++ b/projects/XCode/OCTest/OCTest/TestObj.h @@ -0,0 +1,28 @@ +// +// TestObj.h +// OCTest +// +// Created by Phil on 13/11/2010. +// Copyright 2010 Two Blue Cubes Ltd. All rights reserved. +// +// Distributed under the Boost Software License, Version 1.0. (See accompanying +// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) +#ifndef TWOBLUECUBES_TESTOBJ_H_INCLUDED +#define TWOBLUECUBES_TESTOBJ_H_INCLUDED + +#import + + +@interface TestObj : NSObject { + + int int_val; +} + +-(BOOL) isTrue; +-(BOOL) isFalse; + +@property (nonatomic, assign ) int int_val; + +@end + +#endif // TWOBLUECUBES_TESTOBJ_H_INCLUDED diff --git a/projects/XCode/OCTest/OCTest/TestObj.m b/projects/XCode/OCTest/OCTest/TestObj.m new file mode 100644 index 00000000..2c7dc99b --- /dev/null +++ b/projects/XCode/OCTest/OCTest/TestObj.m @@ -0,0 +1,25 @@ +// +// TestObj.m +// OCTest +// +// Created by Phil on 13/11/2010. +// Copyright 2010 Two Blue Cubes Ltd. All rights reserved. +// +// Distributed under the Boost Software License, Version 1.0. (See accompanying +// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) + +#import "TestObj.h" + + +@implementation TestObj + +@synthesize int_val; + +-(BOOL) isTrue { + return YES; +} +-(BOOL) isFalse { + return NO; +} + +@end diff --git a/projects/XCode/OCTest/catch_objc_impl.mm b/projects/XCode/OCTest/catch_objc_impl.mm new file mode 100644 index 00000000..5b081ad7 --- /dev/null +++ b/projects/XCode/OCTest/catch_objc_impl.mm @@ -0,0 +1,68 @@ +// This file #includes all the .cpp files into a single .mm +// - so they get compiled as ObjectiveC++ + +#include "../../../include/internal/catch_approx.cpp" +#include "../../../include/internal/catch_assertionhandler.cpp" +#include "../../../include/internal/catch_assertionresult.cpp" +#include "../../../include/internal/catch_benchmark.cpp" +#include "../../../include/internal/catch_capture_matchers.cpp" +#include "../../../include/internal/catch_commandline.cpp" +#include "../../../include/internal/catch_common.cpp" +#include "../../../include/internal/catch_config.cpp" +#include "../../../include/internal/catch_console_colour.cpp" +#include "../../../include/internal/catch_context.cpp" +#include "../../../include/internal/catch_debug_console.cpp" +#include "../../../include/internal/catch_debugger.cpp" +#include "../../../include/internal/catch_decomposer.cpp" +#include "../../../include/internal/catch_errno_guard.cpp" +#include "../../../include/internal/catch_exception_translator_registry.cpp" +#include "../../../include/internal/catch_fatal_condition.cpp" +#include "../../../include/internal/catch_interfaces_capture.cpp" +#include "../../../include/internal/catch_interfaces_config.cpp" +#include "../../../include/internal/catch_interfaces_exception.cpp" +#include "../../../include/internal/catch_interfaces_registry_hub.cpp" +#include "../../../include/internal/catch_interfaces_reporter.cpp" +#include "../../../include/internal/catch_interfaces_runner.cpp" +#include "../../../include/internal/catch_interfaces_testcase.cpp" +#include "../../../include/internal/catch_leak_detector.cpp" +#include "../../../include/internal/catch_list.cpp" +#include "../../../include/internal/catch_matchers.cpp" +#include "../../../include/internal/catch_matchers_string.cpp" +#include "../../../include/internal/catch_message.cpp" +#include "../../../include/internal/catch_random_number_generator.cpp" +#include "../../../include/internal/catch_registry_hub.cpp" +#include "../../../include/internal/catch_reporter_registry.cpp" +#include "../../../include/internal/catch_result_type.cpp" +#include "../../../include/internal/catch_run_context.cpp" +#include "../../../include/internal/catch_section.cpp" +#include "../../../include/internal/catch_section_info.cpp" +#include "../../../include/internal/catch_session.cpp" +#include "../../../include/internal/catch_startup_exception_registry.cpp" +#include "../../../include/internal/catch_stream.cpp" +#include "../../../include/internal/catch_streambuf.cpp" +#include "../../../include/internal/catch_string_manip.cpp" +#include "../../../include/internal/catch_stringref.cpp" +#include "../../../include/internal/catch_tag_alias.cpp" +#include "../../../include/internal/catch_tag_alias_autoregistrar.cpp" +#include "../../../include/internal/catch_tag_alias_registry.cpp" +#include "../../../include/internal/catch_test_case_info.cpp" +#include "../../../include/internal/catch_test_case_registry_impl.cpp" +#include "../../../include/internal/catch_test_case_tracker.cpp" +#include "../../../include/internal/catch_test_registry.cpp" +#include "../../../include/internal/catch_test_spec.cpp" +#include "../../../include/internal/catch_test_spec_parser.cpp" +#include "../../../include/internal/catch_timer.cpp" +#include "../../../include/internal/catch_tostring.cpp" +#include "../../../include/internal/catch_totals.cpp" +#include "../../../include/internal/catch_version.cpp" +#include "../../../include/internal/catch_wildcard_pattern.cpp" +#include "../../../include/internal/catch_xmlwriter.cpp" + + +// Reporters +#include "../../../include/reporters/catch_reporter_bases.cpp" +#include "../../../include/reporters/catch_reporter_compact.cpp" +#include "../../../include/reporters/catch_reporter_console.cpp" +#include "../../../include/reporters/catch_reporter_junit.cpp" +#include "../../../include/reporters/catch_reporter_multi.cpp" +#include "../../../include/reporters/catch_reporter_xml.cpp" diff --git a/scripts/approvalTests.py b/scripts/approvalTests.py index 3b71be3e..8194c422 100755 --- a/scripts/approvalTests.py +++ b/scripts/approvalTests.py @@ -24,7 +24,7 @@ filelocParser = re.compile(r''' lineNumberParser = re.compile(r' line="[0-9]*"') hexParser = re.compile(r'\b(0[xX][0-9a-fA-F]+)\b') durationsParser = re.compile(r' time="[0-9]*\.[0-9]*"') -timestampsParser = re.compile(r' timestamp="\d{4}-\d{2}-\d{2}T\d{2}\:\d{2}\:\d{2}Z"') +timestampsParser = re.compile(r'\d{4}-\d{2}-\d{2}T\d{2}\:\d{2}\:\d{2}Z') versionParser = re.compile(r'Catch v[0-9]+\.[0-9]+\.[0-9]+(-develop\.[0-9]+)?') nullParser = re.compile(r'\b(__null|nullptr)\b') exeNameParser = re.compile(r''' @@ -41,6 +41,16 @@ errnoParser = re.compile(r''' \(\*__errno_location\ \(\)\) | \(\*__error\(\)\) + | + \(\*_errno\(\)\) +''', re.VERBOSE) +sinceEpochParser = re.compile(r'\d+ .+ since epoch') +infParser = re.compile(r''' + \(\(float\)\(1e\+300\ \*\ 1e\+300\)\) # MSVC INFINITY macro + | + \(__builtin_inff\(\)\) # Linux (ubuntu) INFINITY macro + | + __builtin_huge_valf\(\) # OSX macro ''', re.VERBOSE) if len(sys.argv) == 2: @@ -95,9 +105,11 @@ def filterLine(line): # strip durations and timestamps line = durationsParser.sub(' time="{duration}"', line) - line = timestampsParser.sub(' timestamp="{iso8601-timestamp}"', line) + line = timestampsParser.sub('{iso8601-timestamp}', line) line = specialCaseParser.sub('file:\g<1>', line) line = errnoParser.sub('errno', line) + line = sinceEpochParser.sub('{since-epoch-report}', line) + line = infParser.sub('INFINITY', line) return line @@ -145,15 +157,15 @@ print("Running approvals against executable:") print(" " + cmdPath) # Standard console reporter -approve("console.std", ["~[c++11]~[!nonportable]", "--order", "lex"]) +approve("console.std", ["~[!nonportable]~[!benchmark]~[approvals]", "--order", "lex"]) # console reporter, include passes, warn about No Assertions -approve("console.sw", ["~[c++11]~[!nonportable]", "-s", "-w", "NoAssertions", "--order", "lex"]) +approve("console.sw", ["~[!nonportable]~[!benchmark]~[approvals]", "-s", "-w", "NoAssertions", "--order", "lex"]) # console reporter, include passes, warn about No Assertions, limit failures to first 4 -approve("console.swa4", ["~[c++11]~[!nonportable]", "-s", "-w", "NoAssertions", "-x", "4", "--order", "lex"]) +approve("console.swa4", ["~[!nonportable]~[!benchmark]~[approvals]", "-s", "-w", "NoAssertions", "-x", "4", "--order", "lex"]) # junit reporter, include passes, warn about No Assertions -approve("junit.sw", ["~[c++11]~[!nonportable]", "-s", "-w", "NoAssertions", "-r", "junit", "--order", "lex"]) +approve("junit.sw", ["~[!nonportable]~[!benchmark]~[approvals]", "-s", "-w", "NoAssertions", "-r", "junit", "--order", "lex"]) # xml reporter, include passes, warn about No Assertions -approve("xml.sw", ["~[c++11]~[!nonportable]", "-s", "-w", "NoAssertions", "-r", "xml", "--order", "lex"]) +approve("xml.sw", ["~[!nonportable]~[!benchmark]~[approvals]", "-s", "-w", "NoAssertions", "-r", "xml", "--order", "lex"]) if overallResult != 0: print("If these differences are expected, run approve.py to approve new baselines.") diff --git a/scripts/benchmarkCompile.py b/scripts/benchmarkCompile.py index 685d1257..586c26ac 100755 --- a/scripts/benchmarkCompile.py +++ b/scripts/benchmarkCompile.py @@ -59,7 +59,7 @@ def compile_main(): return end_t - start_t def compile_files(): - cpp_files = glob.glob('*.cpp') + cpp_files = glob.glob('tests*.cpp') start_t = time.time() subprocess.check_call([compiler_path, '-c'] + flags + cpp_files) end_t = time.time() @@ -114,7 +114,7 @@ parser.add_argument('benchmark_kind', nargs='?', default='all', choices=options, parser.add_argument('-I', '--catch-header', default='catch.hpp', help = 'Path to catch.hpp, default: catch.hpp') parser.add_argument('-c', '--compiler', default='g++', help = 'Compiler to use, default: g++') -parser.add_argument('-f', '--flags', nargs='*', help = 'Flags to be passed to the compiler') +parser.add_argument('-f', '--flags', help = 'Flags to be passed to the compiler. Pass as "," separated list') # Allow creating files only, without running the whole thing parser.add_argument('-g', '--generate-files', action='store_true', help='Generate test files and quit') @@ -137,7 +137,7 @@ if args.generate_files: os.chdir(dir_name) if args.flags: - flags = args.flags + flags = args.flags.split(',') print('Time needed for ...') if args.benchmark_kind in ('all', 'main'): diff --git a/scripts/developBuild.py b/scripts/developBuild.py index d751f108..a8115fe2 100755 --- a/scripts/developBuild.py +++ b/scripts/developBuild.py @@ -1,13 +1,10 @@ #!/usr/bin/env python from __future__ import print_function -from releaseCommon import Version +import releaseCommon -v = Version() +v = releaseCommon.Version() v.incrementBuildNumber() -v.updateVersionFile() -v.updateReadmeFile() -v.updateConanFile() -v.updateConanTestFile() +releaseCommon.performUpdates(v) print( "Updated Version.hpp, README and Conan to v{0}".format( v.getVersionString() ) ) diff --git a/scripts/embed.py b/scripts/embed.py new file mode 100644 index 00000000..23b157c8 --- /dev/null +++ b/scripts/embed.py @@ -0,0 +1,63 @@ +import re + +preprocessorRe = re.compile( r'\s*#.*' ) + +fdefineRe = re.compile( r'\s*#\s*define\s*(\S*)\s*\(' ) # #defines that take arguments +defineRe = re.compile( r'\s*#\s*define\s*(\S*)(\s+)(.*)' ) # all #defines +undefRe = re.compile( r'\s*#\s*undef\s*(\S*)' ) # all #undefs + +ifdefCommonRe = re.compile( r'\s*#\s*if' ) # all #ifdefs +ifdefRe = re.compile( r'\s*#\s*ifdef\s*(\S*)' ) +ifndefRe = re.compile( r'\s*#\s*ifndef\s*(\S*)' ) +endifRe = re.compile( r'\s*#\s*endif\s*//\s*(.*)' ) +elseRe = re.compile( r'\s*#\s*else' ) +ifRe = re.compile( r'\s*#\s*if\s+(.*)' ) + +nsRe = re.compile( r'(.*?\s*\s*namespace\s+)(\w+)(\s*{?)(.*)' ) +nsCloseRe = re.compile( r'(.*\s*})(\s*\/\/\s*namespace\s+)(\w+)(\s*)(.*)' ) + + +class LineMapper: + def __init__( self, idMap, outerNamespace ): + self.idMap = idMap + self.outerNamespace = outerNamespace + + # TBD: + # #if, #ifdef, comments after #else + def mapLine( self, lineNo, line ): + for idFrom, idTo in self.idMap.iteritems(): + r = re.compile("(.*)" + idFrom + "(.*)") + + m = r.match( line ) + if m: + line = m.group(1) + idTo + m.group(2) + "\n" + + m = nsCloseRe.match( line ) + if m: + originalNs = m.group(3) + # print("[{0}] originalNs: '{1}' - closing".format(lineNo, originalNs)) + # print( " " + line ) + # print( " 1:[{0}]\n 2:[{1}]\n 3:[{2}]\n 4:[{3}]\n 5:[{4}]".format( m.group(1), m.group(2), m.group(3), m.group(4), m.group(5) ) ) + if self.outerNamespace.has_key(originalNs): + outerNs, innerNs = self.outerNamespace[originalNs] + return "{0}}}{1}{2}::{3}{4}{5}\n".format( m.group(1), m.group(2), outerNs, innerNs, m.group(4), m.group(5)) + m = nsRe.match( line ) + if m: + originalNs = m.group(2) + # print("[{0}] originalNs: '{1}'".format(lineNo, originalNs)) + # print( " " + line ) + # print( " 1:[{0}]\n 2:[{1}]\n 3:[{2}]\n 4:[{3}]".format( m.group(1), m.group(2), m.group(3), m.group(4) ) ) + if self.outerNamespace.has_key(originalNs): + outerNs, innerNs = self.outerNamespace[originalNs] + return "{0}{1} {{ namespace {2}{3}{4}\n".format( m.group(1), outerNs, innerNs, m.group(3), m.group(4) ) + + return line + + def mapFile(self, filenameIn, filenameOut ): + print( "Embedding:\n {0}\nas:\n {1}".format( filenameIn, filenameOut ) ) + with open( filenameIn, 'r' ) as f, open( filenameOut, 'w' ) as outf: + lineNo = 1 + for line in f: + outf.write( self.mapLine( lineNo, line ) ) + lineNo = lineNo + 1 + print( "Written {0} lines".format( lineNo ) ) \ No newline at end of file diff --git a/scripts/embedClara.py b/scripts/embedClara.py new file mode 100644 index 00000000..e7bb9335 --- /dev/null +++ b/scripts/embedClara.py @@ -0,0 +1,25 @@ +# Execute this script any time you import a new copy of Clara into the third_party area +import os +import sys +import embed + +rootPath = os.path.dirname(os.path.realpath( os.path.dirname(sys.argv[0]))) + +filename = os.path.join( rootPath, "third_party", "clara.hpp" ) +outfilename = os.path.join( rootPath, "include", "external", "clara.hpp" ) + + +# Mapping of pre-processor identifiers +idMap = { + "CLARA_HPP_INCLUDED": "CATCH_CLARA_HPP_INCLUDED", + "CLARA_CONFIG_CONSOLE_WIDTH": "CATCH_CLARA_CONFIG_CONSOLE_WIDTH", + "CLARA_TEXTFLOW_HPP_INCLUDED": "CATCH_CLARA_TEXTFLOW_HPP_INCLUDED", + "CLARA_TEXTFLOW_CONFIG_CONSOLE_WIDTH": "CATCH_CLARA_TEXTFLOW_CONFIG_CONSOLE_WIDTH", + "CLARA_PLATFORM_WINDOWS": "CATCH_PLATFORM_WINDOWS" + } + +# outer namespace to add +outerNamespace = { "clara": ("Catch", "clara") } + +mapper = embed.LineMapper( idMap, outerNamespace ) +mapper.mapFile( filename, outfilename ) \ No newline at end of file diff --git a/scripts/generateSingleHeader.py b/scripts/generateSingleHeader.py index d5d51a7a..b1d18673 100755 --- a/scripts/generateSingleHeader.py +++ b/scripts/generateSingleHeader.py @@ -7,29 +7,29 @@ import sys import re import datetime import string +from glob import glob from scriptCommon import catchPath - def generate(v): includesParser = re.compile( r'\s*#\s*include\s*"(.*)"' ) - guardParser = re.compile( r'\s*#.*TWOBLUECUBES_CATCH_.*_INCLUDED') - defineParser = re.compile( r'\s*#define') - ifParser = re.compile( r'\s*#ifndef TWOBLUECUBES_CATCH_.*_INCLUDED') - endIfParser = re.compile( r'\s*#endif // TWOBLUECUBES_CATCH_.*_INCLUDED') + guardParser = re.compile( r'\s*#.*(TWOBLUECUBES_)?CATCH_.*_INCLUDED') + defineParser = re.compile( r'\s*#define\s+(TWOBLUECUBES_)?CATCH_.*_INCLUDED') + ifParser = re.compile( r'\s*#ifndef (TWOBLUECUBES_)?CATCH_.*_INCLUDED') + endIfParser = re.compile( r'\s*#endif // (TWOBLUECUBES_)?CATCH_.*_INCLUDED') ifImplParser = re.compile( r'\s*#ifdef CATCH_CONFIG_RUNNER' ) commentParser1 = re.compile( r'^\s*/\*') commentParser2 = re.compile( r'^ \*') blankParser = re.compile( r'^\s*$') + seenHeaders = set([]) rootPath = os.path.join( catchPath, 'include/' ) outputPath = os.path.join( catchPath, 'single_include/catch.hpp' ) - globals = { - 'ifdefs' : 0, - 'implIfDefs' : -1, - 'includeImpl': True + 'includeImpl' : True, + 'ifdefs' : 0, + 'implIfDefs' : -1 } for arg in sys.argv[1:]: @@ -41,17 +41,37 @@ def generate(v): print( "\n** Unrecognised argument: " + arg + " **\n" ) exit(1) + + # ensure that the output directory exists (hopefully no races) + outDir = os.path.dirname(outputPath) + if not os.path.exists(outDir): + os.makedirs(outDir) out = open( outputPath, 'w' ) def write( line ): if globals['includeImpl'] or globals['implIfDefs'] == -1: out.write( line ) + def insertCpps(): + dirs = [os.path.join( rootPath, s) for s in ['', 'internal', 'reporters']] + cppFiles = [] + for dir in dirs: + cppFiles += glob(os.path.join(dir, '*.cpp')) + # To minimize random diffs, sort the files before processing them + for fname in sorted(cppFiles): + dir, name = fname.rsplit(os.path.sep, 1) + dir += os.path.sep + parseFile(dir, name) + def parseFile( path, filename ): - f = open( path + filename, 'r' ) + f = open( os.path.join(path, filename), 'r' ) blanks = 0 + write( "// start {0}\n".format( filename ) ) for line in f: - if ifParser.match( line ): + if '// ~*~* CATCH_CPP_STITCH_PLACE *~*~' in line: + insertCpps() + continue + elif ifParser.match( line ): globals['ifdefs'] += 1 elif endIfParser.match( line ): globals['ifdefs'] -= 1 @@ -64,7 +84,6 @@ def generate(v): if not headerFile in seenHeaders: if headerFile != "tbc_text_format.h" and headerFile != "clara.h": seenHeaders.add( headerFile ) - write( "// #included from: {0}\n".format( header ) ) if headerPath == "internal" and path.endswith("internal/"): headerPath = "" sep = "" @@ -80,8 +99,9 @@ def generate(v): blanks = blanks + 1 else: blanks = 0 - if blanks < 2: + if blanks < 2 and not defineParser.match(line): write( line.rstrip() + "\n" ) + write( '// end {}\n'.format(filename) ) out.write( "/*\n" ) @@ -89,7 +109,7 @@ def generate(v): out.write( " * Generated: {0}\n".format( datetime.datetime.now() ) ) out.write( " * ----------------------------------------------------------\n" ) out.write( " * This file has been merged from multiple headers. Please don't edit it directly\n" ) - out.write( " * Copyright (c) 2012 Two Blue Cubes Ltd. All rights reserved.\n" ) + out.write( " * Copyright (c) {} Two Blue Cubes Ltd. All rights reserved.\n".format( datetime.date.today().year ) ) out.write( " *\n" ) out.write( " * Distributed under the Boost Software License, Version 1.0. (See accompanying\n" ) out.write( " * file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)\n" ) @@ -103,6 +123,7 @@ def generate(v): out.close() print ("Generated single include for Catch v{0}\n".format( v.getVersionString() ) ) + if __name__ == '__main__': from releaseCommon import Version generate(Version()) diff --git a/scripts/releaseCommon.py b/scripts/releaseCommon.py index a5fdd7d0..2acd4869 100644 --- a/scripts/releaseCommon.py +++ b/scripts/releaseCommon.py @@ -11,10 +11,11 @@ import updateWandbox versionParser = re.compile( r'(\s*static\sVersion\sversion)\s*\(\s*(.*)\s*,\s*(.*)\s*,\s*(.*)\s*,\s*\"(.*)\"\s*,\s*(.*)\s*\).*' ) rootPath = os.path.join( catchPath, 'include/' ) -versionPath = os.path.join( rootPath, "internal/catch_version.hpp" ) +versionPath = os.path.join( rootPath, "internal/catch_version.cpp" ) readmePath = os.path.join( catchPath, "README.md" ) conanPath = os.path.join(catchPath, 'conanfile.py') conanTestPath = os.path.join(catchPath, 'test_package', 'conanfile.py') +cmakePath = os.path.join(catchPath, 'CMakeLists.txt') class Version: def __init__(self): @@ -126,13 +127,23 @@ def updateConanTestFile(version): for line in lines: f.write( line + "\n" ) +def updateCmakeFile(version): + cmakeParser = re.compile(r'set(CATCH_VERSION_NUMBER \d+\.\d+\.\d+)') + with open(cmakePath, 'r') as file: + lines = file.readlines() + with open(cmakePath, 'w') as file: + for line in lines: + if 'set(CATCH_VERSION_NUMBER ' in line: + file.write('set(CATCH_VERSION_NUMBER {0})\n'.format(version.getVersionString())) + else: + file.write(line) + def performUpdates(version): # First update version file, so we can regenerate single header and # have it ready for upload to wandbox, when updating readme version.updateVersionFile() - # ToDo: Regenerate single header generateSingleHeader.generate(version) updateReadmeFile(version) - updateConanFile(version) updateConanTestFile(version) + updateCmakeFile(version) diff --git a/scripts/updateDocumentToC.py b/scripts/updateDocumentToC.py new file mode 100644 index 00000000..948e5e1d --- /dev/null +++ b/scripts/updateDocumentToC.py @@ -0,0 +1,446 @@ +#!/usr/bin/env python + +# +# updateDocumentToC.py +# +# Insert table of contents at top of Catch markdown documents. +# +# This script is distributed under the GNU General Public License v3.0 +# +# It is based on markdown-toclify version 1.7.1 by Sebastian Raschka, +# https://github.com/rasbt/markdown-toclify +# + +from __future__ import print_function +from scriptCommon import catchPath + +import argparse +import glob +import os +import re +import sys + +# Configuration: + +minTocEntries = 4 + +headingExcludeDefault = [1,3,4,5] # use level 2 headers for at default +headingExcludeRelease = [2,3,4,5] # use level 1 headers for release-notes.md + +documentsDefault = os.path.join(os.path.relpath(catchPath), 'docs/*.md') +releaseNotesName = 'release-notes.md' + +contentTitle = '**Contents**' +contentLineNo = 4 +contentLineNdx = contentLineNo - 1 + +# End configuration + +VALIDS = '0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ_-&' + +def readLines(in_file): + """Returns a list of lines from a input markdown file.""" + + with open(in_file, 'r') as inf: + in_contents = inf.read().split('\n') + return in_contents + +def removeLines(lines, remove=('[[back to top]', ' tags.""" + + if not remove: + return lines[:] + + out = [] + for l in lines: + if l.startswith(remove): + continue + out.append(l) + return out + +def removeToC(lines): + """Removes existing table of contents starting at index contentLineNdx.""" + if not lines[contentLineNdx ].startswith(contentTitle): + return lines[:] + + result_top = lines[:contentLineNdx] + + pos = contentLineNdx + 1 + while lines[pos].startswith('['): + pos = pos + 1 + + result_bottom = lines[pos + 1:] + + return result_top + result_bottom + +def dashifyHeadline(line): + """ + Takes a header line from a Markdown document and + returns a tuple of the + '#'-stripped version of the head line, + a string version for anchor tags, + and the level of the headline as integer. + E.g., + >>> dashifyHeadline('### some header lvl3') + ('Some header lvl3', 'some-header-lvl3', 3) + + """ + stripped_right = line.rstrip('#') + stripped_both = stripped_right.lstrip('#') + level = len(stripped_right) - len(stripped_both) + stripped_wspace = stripped_both.strip() + + # character replacements + replaced_colon = stripped_wspace.replace('.', '') + replaced_slash = replaced_colon.replace('/', '') + rem_nonvalids = ''.join([c if c in VALIDS + else '-' for c in replaced_slash]) + + lowered = rem_nonvalids.lower() + dashified = re.sub(r'(-)\1+', r'\1', lowered) # remove duplicate dashes + dashified = dashified.strip('-') # strip dashes from start and end + + # exception '&' (double-dash in github) + dashified = dashified.replace('-&-', '--') + + return [stripped_wspace, dashified, level] + +def tagAndCollect(lines, id_tag=True, back_links=False, exclude_h=None): + """ + Gets headlines from the markdown document and creates anchor tags. + + Keyword arguments: + lines: a list of sublists where every sublist + represents a line from a Markdown document. + id_tag: if true, creates inserts a the tags (not req. by GitHub) + back_links: if true, adds "back to top" links below each headline + exclude_h: header levels to exclude. E.g., [2, 3] + excludes level 2 and 3 headings. + + Returns a tuple of 2 lists: + 1st list: + A modified version of the input list where + anchor tags where inserted + above the header lines (if github is False). + + 2nd list: + A list of 3-value sublists, where the first value + represents the heading, the second value the string + that was inserted assigned to the IDs in the anchor tags, + and the third value is an integer that reprents the headline level. + E.g., + [['some header lvl3', 'some-header-lvl3', 3], ...] + + """ + out_contents = [] + headlines = [] + for l in lines: + saw_headline = False + + orig_len = len(l) + l_stripped = l.lstrip() + + if l_stripped.startswith(('# ', '## ', '### ', '#### ', '##### ', '###### ')): + + # comply with new markdown standards + + # not a headline if '#' not followed by whitespace '##no-header': + if not l.lstrip('#').startswith(' '): + continue + # not a headline if more than 6 '#': + if len(l) - len(l.lstrip('#')) > 6: + continue + # headers can be indented by at most 3 spaces: + if orig_len - len(l_stripped) > 3: + continue + + # ignore empty headers + if not set(l) - {'#', ' '}: + continue + + saw_headline = True + dashified = dashifyHeadline(l) + + if not exclude_h or not dashified[-1] in exclude_h: + if id_tag: + id_tag = ''\ + % (dashified[1]) + out_contents.append(id_tag) + headlines.append(dashified) + + out_contents.append(l) + if back_links and saw_headline: + out_contents.append('[[back to top](#table-of-contents)]') + return out_contents, headlines + +def positioningHeadlines(headlines): + """ + Strips unnecessary whitespaces/tabs if first header is not left-aligned + """ + left_just = False + for row in headlines: + if row[-1] == 1: + left_just = True + break + if not left_just: + for row in headlines: + row[-1] -= 1 + return headlines + +def createToc(headlines, hyperlink=True, top_link=False, no_toc_header=False): + """ + Creates the table of contents from the headline list + that was returned by the tagAndCollect function. + + Keyword Arguments: + headlines: list of lists + e.g., ['Some header lvl3', 'some-header-lvl3', 3] + hyperlink: Creates hyperlinks in Markdown format if True, + e.g., '- [Some header lvl1](#some-header-lvl1)' + top_link: if True, add a id tag for linking the table + of contents itself (for the back-to-top-links) + no_toc_header: suppresses TOC header if True. + + Returns a list of headlines for a table of contents + in Markdown format, + e.g., [' - [Some header lvl3](#some-header-lvl3)', ...] + + """ + processed = [] + if not no_toc_header: + if top_link: + processed.append('\n') + processed.append(contentTitle + '
') + + for line in headlines: + if hyperlink: + item = '[%s](#%s)' % (line[0], line[1]) + else: + item = '%s- %s' % ((line[2]-1)*' ', line[0]) + processed.append(item + '
') + processed.append('\n') + return processed + +def buildMarkdown(toc_headlines, body, spacer=0, placeholder=None): + """ + Returns a string with the Markdown output contents incl. + the table of contents. + + Keyword arguments: + toc_headlines: lines for the table of contents + as created by the createToc function. + body: contents of the Markdown file including + ID-anchor tags as returned by the + tagAndCollect function. + spacer: Adds vertical space after the table + of contents. Height in pixels. + placeholder: If a placeholder string is provided, the placeholder + will be replaced by the TOC instead of inserting the TOC at + the top of the document + + """ + if spacer: + spacer_line = ['\n
\n' % (spacer)] + toc_markdown = "\n".join(toc_headlines + spacer_line) + else: + toc_markdown = "\n".join(toc_headlines) + + if placeholder: + body_markdown = "\n".join(body) + markdown = body_markdown.replace(placeholder, toc_markdown) + else: + body_markdown_p1 = "\n".join(body[:contentLineNdx ]) + '\n' + body_markdown_p2 = "\n".join(body[ contentLineNdx:]) + markdown = body_markdown_p1 + toc_markdown + body_markdown_p2 + + return markdown + +def outputMarkdown(markdown_cont, output_file): + """ + Writes to an output file if `outfile` is a valid path. + + """ + if output_file: + with open(output_file, 'w') as out: + out.write(markdown_cont) + +def markdownToclify( + input_file, + output_file=None, + min_toc_len=2, + github=False, + back_to_top=False, + nolink=False, + no_toc_header=False, + spacer=0, + placeholder=None, + exclude_h=None): + """ Function to add table of contents to markdown files. + + Parameters + ----------- + input_file: str + Path to the markdown input file. + + output_file: str (defaul: None) + Path to the markdown output file. + + min_toc_len: int (default: 2) + Miniumum number of entries to create a table of contents for. + + github: bool (default: False) + Uses GitHub TOC syntax if True. + + back_to_top: bool (default: False) + Inserts back-to-top links below headings if True. + + nolink: bool (default: False) + Creates the table of contents without internal links if True. + + no_toc_header: bool (default: False) + Suppresses the Table of Contents header if True + + spacer: int (default: 0) + Inserts horizontal space (in pixels) after the table of contents. + + placeholder: str (default: None) + Inserts the TOC at the placeholder string instead + of inserting the TOC at the top of the document. + + exclude_h: list (default None) + Excludes header levels, e.g., if [2, 3], ignores header + levels 2 and 3 in the TOC. + + Returns + ----------- + changed: Boolean + True if the file has been updated, False otherwise. + + """ + cleaned_contents = removeLines( + removeToC(readLines(input_file)), + remove=('[[back to top]', '= (3, 3): + os.replace(output_file, input_file) + else: + os.remove(input_file) + os.rename(output_file, input_file) + + return 1 + +def updateDocumentToC(paths, min_toc_len, verbose): + """Add or update table of contents to specified paths. Return number of changed files""" + n = 0 + for g in paths: + for f in glob.glob(g): + if os.path.isfile(f): + n = n + updateSingleDocumentToC(input_file=f, min_toc_len=min_toc_len, verbose=verbose) + return n + +def updateDocumentToCMain(): + """Add or update table of contents to specified paths.""" + + parser = argparse.ArgumentParser( + description='Add or update table of contents in markdown documents.', + epilog="""""", + formatter_class=argparse.RawTextHelpFormatter) + + parser.add_argument( + 'Input', + metavar='file', + type=str, + nargs=argparse.REMAINDER, + help='files to process, at default: docs/*.md') + + parser.add_argument( + '-v', '--verbose', + action='store_true', + help='report the name of the file being processed') + + parser.add_argument( + '--min-toc-entries', + dest='minTocEntries', + default=minTocEntries, + type=int, + metavar='N', + help='the minimum number of entries to create a table of contents for [{deflt}]'.format(deflt=minTocEntries)) + + parser.add_argument( + '--remove-toc', + action='store_const', + dest='minTocEntries', + const=99, + help='remove all tables of contents') + + args = parser.parse_args() + + paths = args.Input if len(args.Input) > 0 else [documentsDefault] + + changedFiles = updateDocumentToC(paths=paths, min_toc_len=args.minTocEntries, verbose=args.verbose) + + if changedFiles > 0: + print( "Processed table of contents in " + str(changedFiles) + " file(s)" ) + else: + print( "No table of contents added or updated" ) + +if __name__ == '__main__': + updateDocumentToCMain() + +# end of file diff --git a/single_include/catch.hpp b/single_include/catch.hpp index 849c53c1..362f8693 100644 --- a/single_include/catch.hpp +++ b/single_include/catch.hpp @@ -1,17 +1,17 @@ /* - * Catch v1.11.0 - * Generated: 2017-10-31 13:42:42.914833 + * Catch v2.0.1 + * Generated: 2017-11-03 11:53:39.642003 * ---------------------------------------------------------- * This file has been merged from multiple headers. Please don't edit it directly - * Copyright (c) 2012 Two Blue Cubes Ltd. All rights reserved. + * Copyright (c) 2017 Two Blue Cubes Ltd. All rights reserved. * * Distributed under the Boost Software License, Version 1.0. (See accompanying * file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) */ #ifndef TWOBLUECUBES_SINGLE_INCLUDE_CATCH_HPP_INCLUDED #define TWOBLUECUBES_SINGLE_INCLUDE_CATCH_HPP_INCLUDED +// start catch.hpp -#define TWOBLUECUBES_CATCH_HPP_INCLUDED #ifdef __clang__ # pragma clang system_header @@ -19,7 +19,7 @@ # pragma GCC system_header #endif -// #included from: internal/catch_suppress_warnings.h +// start catch_suppress_warnings.h #ifdef __clang__ # ifdef __ICC // icpc defines the __clang__ macro @@ -32,8 +32,6 @@ # pragma clang diagnostic ignored "-Wunused-variable" # pragma clang diagnostic push # pragma clang diagnostic ignored "-Wpadded" -# pragma clang diagnostic ignored "-Wc++98-compat" -# pragma clang diagnostic ignored "-Wc++98-compat-pedantic" # pragma clang diagnostic ignored "-Wswitch-enum" # pragma clang diagnostic ignored "-Wcovered-switch-default" # endif @@ -45,10 +43,33 @@ # pragma GCC diagnostic push # pragma GCC diagnostic ignored "-Wpadded" #endif +// end catch_suppress_warnings.h #if defined(CATCH_CONFIG_MAIN) || defined(CATCH_CONFIG_RUNNER) # define CATCH_IMPL +# define CATCH_CONFIG_EXTERNAL_INTERFACES +# if defined(CATCH_CONFIG_DISABLE_MATCHERS) +# undef CATCH_CONFIG_DISABLE_MATCHERS +# endif #endif +// start catch_platform.h + +#ifdef __APPLE__ +# include +# if TARGET_OS_MAC == 1 +# define CATCH_PLATFORM_MAC +# elif TARGET_OS_IPHONE == 1 +# define CATCH_PLATFORM_IPHONE +# endif + +#elif defined(linux) || defined(__linux) || defined(__linux__) +# define CATCH_PLATFORM_LINUX + +#elif defined(WIN32) || defined(__WIN32__) || defined(_WIN32) || defined(_MSC_VER) +# define CATCH_PLATFORM_WINDOWS +#endif + +// end catch_platform.h #ifdef CATCH_IMPL # ifndef CLARA_CONFIG_MAIN # define CLARA_CONFIG_MAIN_NOT_DEFINED @@ -56,32 +77,15 @@ # endif #endif -// #included from: internal/catch_notimplemented_exception.h -#define TWOBLUECUBES_CATCH_NOTIMPLEMENTED_EXCEPTION_H_INCLUDED +// start catch_tag_alias_autoregistrar.h -// #included from: catch_common.h -#define TWOBLUECUBES_CATCH_COMMON_H_INCLUDED +// start catch_common.h -// #included from: catch_compiler_capabilities.h -#define TWOBLUECUBES_CATCH_COMPILER_CAPABILITIES_HPP_INCLUDED +// start catch_compiler_capabilities.h -// Detect a number of compiler features - mostly C++11/14 conformance - by compiler +// Detect a number of compiler features - by compiler // The following features are defined: // -// CATCH_CONFIG_CPP11_NULLPTR : is nullptr supported? -// CATCH_CONFIG_CPP11_NOEXCEPT : is noexcept supported? -// CATCH_CONFIG_CPP11_GENERATED_METHODS : The delete and default keywords for compiler generated methods -// CATCH_CONFIG_CPP11_IS_ENUM : std::is_enum is supported? -// CATCH_CONFIG_CPP11_TUPLE : std::tuple is supported -// CATCH_CONFIG_CPP11_LONG_LONG : is long long supported? -// CATCH_CONFIG_CPP11_OVERRIDE : is override supported? -// CATCH_CONFIG_CPP11_UNIQUE_PTR : is unique_ptr supported (otherwise use auto_ptr) -// CATCH_CONFIG_CPP11_SHUFFLE : is std::shuffle supported? -// CATCH_CONFIG_CPP11_TYPE_TRAITS : are type_traits and enable_if supported? - -// CATCH_CONFIG_CPP11_OR_GREATER : Is C++11 supported? - -// CATCH_CONFIG_VARIADIC_MACROS : are variadic macros supported? // CATCH_CONFIG_COUNTER : is the __COUNTER__ macro supported? // CATCH_CONFIG_WINDOWS_SEH : is Windows SEH supported? // CATCH_CONFIG_POSIX_SIGNALS : are POSIX signals supported? @@ -91,18 +95,12 @@ // **************** // In general each macro has a _NO_ form -// (e.g. CATCH_CONFIG_CPP11_NO_NULLPTR) which disables the feature. +// (e.g. CATCH_CONFIG_NO_POSIX_SIGNALS) which disables the feature. // Many features, at point of detection, define an _INTERNAL_ macro, so they // can be combined, en-mass, with the _NO_ forms later. -// All the C++11 features can be disabled with CATCH_CONFIG_NO_CPP11 - #ifdef __cplusplus -# if __cplusplus >= 201103L -# define CATCH_CPP11_OR_GREATER -# endif - # if __cplusplus >= 201402L # define CATCH_CPP14_OR_GREATER # endif @@ -111,19 +109,11 @@ #ifdef __clang__ -# if __has_feature(cxx_nullptr) -# define CATCH_INTERNAL_CONFIG_CPP11_NULLPTR -# endif - -# if __has_feature(cxx_noexcept) -# define CATCH_INTERNAL_CONFIG_CPP11_NOEXCEPT -# endif - -# if defined(CATCH_CPP11_OR_GREATER) -# define CATCH_INTERNAL_SUPPRESS_ETD_WARNINGS \ +# define CATCH_INTERNAL_SUPPRESS_GLOBALS_WARNINGS \ _Pragma( "clang diagnostic push" ) \ - _Pragma( "clang diagnostic ignored \"-Wexit-time-destructors\"" ) -# define CATCH_INTERNAL_UNSUPPRESS_ETD_WARNINGS \ + _Pragma( "clang diagnostic ignored \"-Wexit-time-destructors\"" ) \ + _Pragma( "clang diagnostic ignored \"-Wglobal-constructors\"") +# define CATCH_INTERNAL_UNSUPPRESS_GLOBALS_WARNINGS \ _Pragma( "clang diagnostic pop" ) # define CATCH_INTERNAL_SUPPRESS_PARENTHESES_WARNINGS \ @@ -131,7 +121,6 @@ _Pragma( "clang diagnostic ignored \"-Wparentheses\"" ) # define CATCH_INTERNAL_UNSUPPRESS_PARENTHESES_WARNINGS \ _Pragma( "clang diagnostic pop" ) -# endif #endif // __clang__ @@ -160,169 +149,34 @@ #endif // __CYGWIN__ -//////////////////////////////////////////////////////////////////////////////// -// Borland -#ifdef __BORLANDC__ - -#endif // __BORLANDC__ - -//////////////////////////////////////////////////////////////////////////////// -// EDG -#ifdef __EDG_VERSION__ - -#endif // __EDG_VERSION__ - -//////////////////////////////////////////////////////////////////////////////// -// Digital Mars -#ifdef __DMC__ - -#endif // __DMC__ - -//////////////////////////////////////////////////////////////////////////////// -// GCC -#ifdef __GNUC__ - -# if __GNUC__ == 4 && __GNUC_MINOR__ >= 6 && defined(__GXX_EXPERIMENTAL_CXX0X__) -# define CATCH_INTERNAL_CONFIG_CPP11_NULLPTR -# endif - -// - otherwise more recent versions define __cplusplus >= 201103L -// and will get picked up below - -#endif // __GNUC__ - //////////////////////////////////////////////////////////////////////////////// // Visual C++ #ifdef _MSC_VER -#define CATCH_INTERNAL_CONFIG_WINDOWS_SEH - -#if (_MSC_VER >= 1600) -# define CATCH_INTERNAL_CONFIG_CPP11_NULLPTR -# define CATCH_INTERNAL_CONFIG_CPP11_UNIQUE_PTR -#endif - -#if (_MSC_VER >= 1900 ) // (VC++ 13 (VS2015)) -#define CATCH_INTERNAL_CONFIG_CPP11_NOEXCEPT -#define CATCH_INTERNAL_CONFIG_CPP11_GENERATED_METHODS -#define CATCH_INTERNAL_CONFIG_CPP11_SHUFFLE -#define CATCH_INTERNAL_CONFIG_CPP11_TYPE_TRAITS -#endif +// Universal Windows platform does not support SEH +// Or console colours (or console at all...) +# if defined(WINAPI_FAMILY) && (WINAPI_FAMILY == WINAPI_FAMILY_APP) +# define CATCH_CONFIG_COLOUR_NONE +# else +# define CATCH_INTERNAL_CONFIG_WINDOWS_SEH +# endif #endif // _MSC_VER //////////////////////////////////////////////////////////////////////////////// -// Use variadic macros if the compiler supports them -#if ( defined _MSC_VER && _MSC_VER > 1400 && !defined __EDGE__) || \ - ( defined __WAVE__ && __WAVE_HAS_VARIADICS ) || \ - ( defined __GNUC__ && __GNUC__ >= 3 ) || \ - ( !defined __cplusplus && __STDC_VERSION__ >= 199901L || __cplusplus >= 201103L ) - -#define CATCH_INTERNAL_CONFIG_VARIADIC_MACROS - -#endif - -// Use __COUNTER__ if the compiler supports it -#if ( defined _MSC_VER && _MSC_VER >= 1300 ) || \ - ( defined __GNUC__ && ( __GNUC__ > 4 || (__GNUC__ == 4 && __GNUC_MINOR__ >= 3 )) ) || \ - ( defined __clang__ && __clang_major__ >= 3 ) - -// Use of __COUNTER__ is suppressed during code analysis in CLion/AppCode 2017.2.x and former, -// because __COUNTER__ is not properly handled by it. -// This does not affect compilation -#if ( !defined __JETBRAINS_IDE__ || __JETBRAINS_IDE__ >= 20170300L ) +// Use of __COUNTER__ is suppressed during code analysis in +// CLion/AppCode 2017.2.x and former, because __COUNTER__ is not properly +// handled by it. +// Otherwise all supported compilers support COUNTER macro, +// but user still might want to turn it off +#if ( !defined(__JETBRAINS_IDE__) || __JETBRAINS_IDE__ >= 20170300L ) #define CATCH_INTERNAL_CONFIG_COUNTER #endif -#endif - -//////////////////////////////////////////////////////////////////////////////// -// C++ language feature support - -// catch all support for C++11 -#if defined(CATCH_CPP11_OR_GREATER) - -# if !defined(CATCH_INTERNAL_CONFIG_CPP11_NULLPTR) -# define CATCH_INTERNAL_CONFIG_CPP11_NULLPTR -# endif - -# ifndef CATCH_INTERNAL_CONFIG_CPP11_NOEXCEPT -# define CATCH_INTERNAL_CONFIG_CPP11_NOEXCEPT -# endif - -# ifndef CATCH_INTERNAL_CONFIG_CPP11_GENERATED_METHODS -# define CATCH_INTERNAL_CONFIG_CPP11_GENERATED_METHODS -# endif - -# ifndef CATCH_INTERNAL_CONFIG_CPP11_IS_ENUM -# define CATCH_INTERNAL_CONFIG_CPP11_IS_ENUM -# endif - -# ifndef CATCH_INTERNAL_CONFIG_CPP11_TUPLE -# define CATCH_INTERNAL_CONFIG_CPP11_TUPLE -# endif - -# ifndef CATCH_INTERNAL_CONFIG_VARIADIC_MACROS -# define CATCH_INTERNAL_CONFIG_VARIADIC_MACROS -# endif - -# if !defined(CATCH_INTERNAL_CONFIG_CPP11_LONG_LONG) -# define CATCH_INTERNAL_CONFIG_CPP11_LONG_LONG -# endif - -# if !defined(CATCH_INTERNAL_CONFIG_CPP11_OVERRIDE) -# define CATCH_INTERNAL_CONFIG_CPP11_OVERRIDE -# endif -# if !defined(CATCH_INTERNAL_CONFIG_CPP11_UNIQUE_PTR) -# define CATCH_INTERNAL_CONFIG_CPP11_UNIQUE_PTR -# endif -# if !defined(CATCH_INTERNAL_CONFIG_CPP11_SHUFFLE) -# define CATCH_INTERNAL_CONFIG_CPP11_SHUFFLE -# endif -# if !defined(CATCH_INTERNAL_CONFIG_CPP11_TYPE_TRAITS) -# define CATCH_INTERNAL_CONFIG_CPP11_TYPE_TRAITS -# endif - -#endif // __cplusplus >= 201103L - -// Now set the actual defines based on the above + anything the user has configured -#if defined(CATCH_INTERNAL_CONFIG_CPP11_NULLPTR) && !defined(CATCH_CONFIG_CPP11_NO_NULLPTR) && !defined(CATCH_CONFIG_CPP11_NULLPTR) && !defined(CATCH_CONFIG_NO_CPP11) -# define CATCH_CONFIG_CPP11_NULLPTR -#endif -#if defined(CATCH_INTERNAL_CONFIG_CPP11_NOEXCEPT) && !defined(CATCH_CONFIG_CPP11_NO_NOEXCEPT) && !defined(CATCH_CONFIG_CPP11_NOEXCEPT) && !defined(CATCH_CONFIG_NO_CPP11) -# define CATCH_CONFIG_CPP11_NOEXCEPT -#endif -#if defined(CATCH_INTERNAL_CONFIG_CPP11_GENERATED_METHODS) && !defined(CATCH_CONFIG_CPP11_NO_GENERATED_METHODS) && !defined(CATCH_CONFIG_CPP11_GENERATED_METHODS) && !defined(CATCH_CONFIG_NO_CPP11) -# define CATCH_CONFIG_CPP11_GENERATED_METHODS -#endif -#if defined(CATCH_INTERNAL_CONFIG_CPP11_IS_ENUM) && !defined(CATCH_CONFIG_CPP11_NO_IS_ENUM) && !defined(CATCH_CONFIG_CPP11_IS_ENUM) && !defined(CATCH_CONFIG_NO_CPP11) -# define CATCH_CONFIG_CPP11_IS_ENUM -#endif -#if defined(CATCH_INTERNAL_CONFIG_CPP11_TUPLE) && !defined(CATCH_CONFIG_CPP11_NO_TUPLE) && !defined(CATCH_CONFIG_CPP11_TUPLE) && !defined(CATCH_CONFIG_NO_CPP11) -# define CATCH_CONFIG_CPP11_TUPLE -#endif -#if defined(CATCH_INTERNAL_CONFIG_VARIADIC_MACROS) && !defined(CATCH_CONFIG_NO_VARIADIC_MACROS) && !defined(CATCH_CONFIG_VARIADIC_MACROS) -# define CATCH_CONFIG_VARIADIC_MACROS -#endif -#if defined(CATCH_INTERNAL_CONFIG_CPP11_LONG_LONG) && !defined(CATCH_CONFIG_CPP11_NO_LONG_LONG) && !defined(CATCH_CONFIG_CPP11_LONG_LONG) && !defined(CATCH_CONFIG_NO_CPP11) -# define CATCH_CONFIG_CPP11_LONG_LONG -#endif -#if defined(CATCH_INTERNAL_CONFIG_CPP11_OVERRIDE) && !defined(CATCH_CONFIG_CPP11_NO_OVERRIDE) && !defined(CATCH_CONFIG_CPP11_OVERRIDE) && !defined(CATCH_CONFIG_NO_CPP11) -# define CATCH_CONFIG_CPP11_OVERRIDE -#endif -#if defined(CATCH_INTERNAL_CONFIG_CPP11_UNIQUE_PTR) && !defined(CATCH_CONFIG_CPP11_NO_UNIQUE_PTR) && !defined(CATCH_CONFIG_CPP11_UNIQUE_PTR) && !defined(CATCH_CONFIG_NO_CPP11) -# define CATCH_CONFIG_CPP11_UNIQUE_PTR -#endif #if defined(CATCH_INTERNAL_CONFIG_COUNTER) && !defined(CATCH_CONFIG_NO_COUNTER) && !defined(CATCH_CONFIG_COUNTER) # define CATCH_CONFIG_COUNTER #endif -#if defined(CATCH_INTERNAL_CONFIG_CPP11_SHUFFLE) && !defined(CATCH_CONFIG_CPP11_NO_SHUFFLE) && !defined(CATCH_CONFIG_CPP11_SHUFFLE) && !defined(CATCH_CONFIG_NO_CPP11) -# define CATCH_CONFIG_CPP11_SHUFFLE -#endif -# if defined(CATCH_INTERNAL_CONFIG_CPP11_TYPE_TRAITS) && !defined(CATCH_CONFIG_CPP11_NO_TYPE_TRAITS) && !defined(CATCH_CONFIG_CPP11_TYPE_TRAITS) && !defined(CATCH_CONFIG_NO_CPP11) -# define CATCH_CONFIG_CPP11_TYPE_TRAITS -# endif #if defined(CATCH_INTERNAL_CONFIG_WINDOWS_SEH) && !defined(CATCH_CONFIG_NO_WINDOWS_SEH) && !defined(CATCH_CONFIG_WINDOWS_SEH) # define CATCH_CONFIG_WINDOWS_SEH #endif @@ -335,41 +189,12 @@ # define CATCH_INTERNAL_SUPPRESS_PARENTHESES_WARNINGS # define CATCH_INTERNAL_UNSUPPRESS_PARENTHESES_WARNINGS #endif -#if !defined(CATCH_INTERNAL_SUPPRESS_ETD_WARNINGS) -# define CATCH_INTERNAL_SUPPRESS_ETD_WARNINGS -# define CATCH_INTERNAL_UNSUPPRESS_ETD_WARNINGS -#endif - -// noexcept support: -#if defined(CATCH_CONFIG_CPP11_NOEXCEPT) && !defined(CATCH_NOEXCEPT) -# define CATCH_NOEXCEPT noexcept -# define CATCH_NOEXCEPT_IS(x) noexcept(x) -#else -# define CATCH_NOEXCEPT throw() -# define CATCH_NOEXCEPT_IS(x) -#endif - -// nullptr support -#ifdef CATCH_CONFIG_CPP11_NULLPTR -# define CATCH_NULL nullptr -#else -# define CATCH_NULL NULL -#endif - -// override support -#ifdef CATCH_CONFIG_CPP11_OVERRIDE -# define CATCH_OVERRIDE override -#else -# define CATCH_OVERRIDE -#endif - -// unique_ptr support -#ifdef CATCH_CONFIG_CPP11_UNIQUE_PTR -# define CATCH_AUTO_PTR( T ) std::unique_ptr -#else -# define CATCH_AUTO_PTR( T ) std::auto_ptr +#if !defined(CATCH_INTERNAL_SUPPRESS_GLOBALS_WARNINGS) +# define CATCH_INTERNAL_SUPPRESS_GLOBALS_WARNINGS +# define CATCH_INTERNAL_UNSUPPRESS_GLOBALS_WARNINGS #endif +// end catch_compiler_capabilities.h #define INTERNAL_CATCH_UNIQUE_NAME_LINE2( name, line ) name##line #define INTERNAL_CATCH_UNIQUE_NAME_LINE( name, line ) INTERNAL_CATCH_UNIQUE_NAME_LINE2( name, line ) #ifdef CATCH_CONFIG_COUNTER @@ -378,95 +203,41 @@ # define INTERNAL_CATCH_UNIQUE_NAME( name ) INTERNAL_CATCH_UNIQUE_NAME_LINE( name, __LINE__ ) #endif -#define INTERNAL_CATCH_STRINGIFY2( expr ) #expr -#define INTERNAL_CATCH_STRINGIFY( expr ) INTERNAL_CATCH_STRINGIFY2( expr ) - -#include -#include +#include +#include +#include namespace Catch { - struct IConfig; - struct CaseSensitive { enum Choice { Yes, No }; }; class NonCopyable { -#ifdef CATCH_CONFIG_CPP11_GENERATED_METHODS NonCopyable( NonCopyable const& ) = delete; NonCopyable( NonCopyable && ) = delete; NonCopyable& operator = ( NonCopyable const& ) = delete; NonCopyable& operator = ( NonCopyable && ) = delete; -#else - NonCopyable( NonCopyable const& info ); - NonCopyable& operator = ( NonCopyable const& ); -#endif protected: - NonCopyable() {} + NonCopyable(); virtual ~NonCopyable(); }; - class SafeBool { - public: - typedef void (SafeBool::*type)() const; - - static type makeSafe( bool value ) { - return value ? &SafeBool::trueValue : 0; - } - private: - void trueValue() const {} - }; - - template - void deleteAll( ContainerT& container ) { - typename ContainerT::const_iterator it = container.begin(); - typename ContainerT::const_iterator itEnd = container.end(); - for(; it != itEnd; ++it ) - delete *it; - } - template - void deleteAllValues( AssociativeContainerT& container ) { - typename AssociativeContainerT::const_iterator it = container.begin(); - typename AssociativeContainerT::const_iterator itEnd = container.end(); - for(; it != itEnd; ++it ) - delete it->second; - } - - bool startsWith( std::string const& s, std::string const& prefix ); - bool startsWith( std::string const& s, char prefix ); - bool endsWith( std::string const& s, std::string const& suffix ); - bool endsWith( std::string const& s, char suffix ); - bool contains( std::string const& s, std::string const& infix ); - void toLowerInPlace( std::string& s ); - std::string toLower( std::string const& s ); - std::string trim( std::string const& str ); - bool replaceInPlace( std::string& str, std::string const& replaceThis, std::string const& withThis ); - - struct pluralise { - pluralise( std::size_t count, std::string const& label ); - - friend std::ostream& operator << ( std::ostream& os, pluralise const& pluraliser ); - - std::size_t m_count; - std::string m_label; - }; - struct SourceLineInfo { - SourceLineInfo(); - SourceLineInfo( char const* _file, std::size_t _line ); -# ifdef CATCH_CONFIG_CPP11_GENERATED_METHODS - SourceLineInfo(SourceLineInfo const& other) = default; + SourceLineInfo() = delete; + SourceLineInfo( char const* _file, std::size_t _line ) noexcept; + + SourceLineInfo( SourceLineInfo const& other ) = default; SourceLineInfo( SourceLineInfo && ) = default; SourceLineInfo& operator = ( SourceLineInfo const& ) = default; SourceLineInfo& operator = ( SourceLineInfo && ) = default; -# endif - bool empty() const; - bool operator == ( SourceLineInfo const& other ) const; - bool operator < ( SourceLineInfo const& other ) const; + + bool empty() const noexcept; + bool operator == ( SourceLineInfo const& other ) const noexcept; + bool operator < ( SourceLineInfo const& other ) const noexcept; char const* file; std::size_t line; @@ -475,23 +246,16 @@ 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; } - inline bool alwaysTrue() { return true; } - inline bool alwaysFalse() { return false; } - - void throwLogicError( std::string const& message, SourceLineInfo const& locationInfo ); - - void seedRng( IConfig const& config ); - unsigned int rngSeed(); + bool isTrue( bool value ); + bool alwaysTrue(); + bool alwaysFalse(); // Use this in variadic streaming macros to allow // >> +StreamEndStop // as well as // >> stuff +StreamEndStop struct StreamEndStop { - std::string operator+() { - return std::string(); - } + std::string operator+() const; }; template T const& operator + ( T const& value, StreamEndStop ) { @@ -499,193 +263,39 @@ namespace Catch { } } -#define CATCH_INTERNAL_LINEINFO ::Catch::SourceLineInfo( __FILE__, static_cast( __LINE__ ) ) -#define CATCH_INTERNAL_ERROR( msg ) ::Catch::throwLogicError( msg, CATCH_INTERNAL_LINEINFO ); +#define CATCH_INTERNAL_LINEINFO \ + ::Catch::SourceLineInfo( __FILE__, static_cast( __LINE__ ) ) +// end catch_common.h namespace Catch { - class NotImplementedException : public std::exception - { - public: - NotImplementedException( SourceLineInfo const& lineInfo ); - - virtual ~NotImplementedException() CATCH_NOEXCEPT {} - - virtual const char* what() const CATCH_NOEXCEPT; - - private: - std::string m_what; - SourceLineInfo m_lineInfo; + struct RegistrarForTagAliases { + RegistrarForTagAliases( char const* alias, char const* tag, SourceLineInfo const& lineInfo ); }; } // end namespace Catch -/////////////////////////////////////////////////////////////////////////////// -#define CATCH_NOT_IMPLEMENTED throw Catch::NotImplementedException( CATCH_INTERNAL_LINEINFO ) +#define CATCH_REGISTER_TAG_ALIAS( alias, spec ) namespace{ Catch::RegistrarForTagAliases INTERNAL_CATCH_UNIQUE_NAME( AutoRegisterTagAlias )( alias, spec, CATCH_INTERNAL_LINEINFO ); } -// #included from: internal/catch_context.h -#define TWOBLUECUBES_CATCH_CONTEXT_H_INCLUDED +// end catch_tag_alias_autoregistrar.h +// start catch_test_registry.h -// #included from: catch_interfaces_generators.h -#define TWOBLUECUBES_CATCH_INTERFACES_GENERATORS_H_INCLUDED - -#include - -namespace Catch { - - struct IGeneratorInfo { - virtual ~IGeneratorInfo(); - virtual bool moveNext() = 0; - virtual std::size_t getCurrentIndex() const = 0; - }; - - struct IGeneratorsForTest { - virtual ~IGeneratorsForTest(); - - virtual IGeneratorInfo& getGeneratorInfo( std::string const& fileInfo, std::size_t size ) = 0; - virtual bool moveNext() = 0; - }; - - IGeneratorsForTest* createGeneratorsForTest(); - -} // end namespace Catch - -// #included from: catch_ptr.hpp -#define TWOBLUECUBES_CATCH_PTR_HPP_INCLUDED - -#ifdef __clang__ -#pragma clang diagnostic push -#pragma clang diagnostic ignored "-Wpadded" -#endif - -namespace Catch { - - // An intrusive reference counting smart pointer. - // T must implement addRef() and release() methods - // typically implementing the IShared interface - template - class Ptr { - public: - Ptr() : m_p( CATCH_NULL ){} - Ptr( T* p ) : m_p( p ){ - if( m_p ) - m_p->addRef(); - } - Ptr( Ptr const& other ) : m_p( other.m_p ){ - if( m_p ) - m_p->addRef(); - } - ~Ptr(){ - if( m_p ) - m_p->release(); - } - void reset() { - if( m_p ) - m_p->release(); - m_p = CATCH_NULL; - } - Ptr& operator = ( T* p ){ - Ptr temp( p ); - swap( temp ); - return *this; - } - Ptr& operator = ( Ptr const& other ){ - Ptr temp( other ); - swap( temp ); - return *this; - } - void swap( Ptr& other ) { std::swap( m_p, other.m_p ); } - T* get() const{ return m_p; } - T& operator*() const { return *m_p; } - T* operator->() const { return m_p; } - bool operator !() const { return m_p == CATCH_NULL; } - operator SafeBool::type() const { return SafeBool::makeSafe( m_p != CATCH_NULL ); } - - private: - T* m_p; - }; - - struct IShared : NonCopyable { - virtual ~IShared(); - virtual void addRef() const = 0; - virtual void release() const = 0; - }; - - template - struct SharedImpl : T { - - SharedImpl() : m_rc( 0 ){} - - virtual void addRef() const { - ++m_rc; - } - virtual void release() const { - if( --m_rc == 0 ) - delete this; - } - - mutable unsigned int m_rc; - }; - -} // end namespace Catch - -#ifdef __clang__ -#pragma clang diagnostic pop -#endif - -namespace Catch { - - class TestCase; - class Stream; - struct IResultCapture; - struct IRunner; - struct IGeneratorsForTest; - struct IConfig; - - struct IContext - { - virtual ~IContext(); - - virtual IResultCapture* getResultCapture() = 0; - virtual IRunner* getRunner() = 0; - virtual size_t getGeneratorIndex( std::string const& fileInfo, size_t totalSize ) = 0; - virtual bool advanceGeneratorsForCurrentTest() = 0; - virtual Ptr getConfig() const = 0; - }; - - struct IMutableContext : IContext - { - virtual ~IMutableContext(); - virtual void setResultCapture( IResultCapture* resultCapture ) = 0; - virtual void setRunner( IRunner* runner ) = 0; - virtual void setConfig( Ptr const& config ) = 0; - }; - - IContext& getCurrentContext(); - IMutableContext& getCurrentMutableContext(); - void cleanUpContext(); - Stream createStream( std::string const& streamName ); - -} - -// #included from: internal/catch_test_registry.hpp -#define TWOBLUECUBES_CATCH_TEST_REGISTRY_HPP_INCLUDED - -// #included from: catch_interfaces_testcase.h -#define TWOBLUECUBES_CATCH_INTERFACES_TESTCASE_H_INCLUDED +// start catch_interfaces_testcase.h #include +#include namespace Catch { class TestSpec; - struct ITestCase : IShared { + struct ITestInvoker { virtual void invoke () const = 0; - protected: - virtual ~ITestCase(); + virtual ~ITestInvoker(); }; + using ITestCasePtr = std::shared_ptr; + class TestCase; struct IConfig; @@ -701,742 +311,180 @@ namespace Catch { } +// end catch_interfaces_testcase.h +// start catch_stringref.h + +#include +#include +#include + +namespace Catch { + + class StringData; + + /// A non-owning string class (similar to the forthcoming std::string_view) + /// Note that, because a StringRef may be a substring of another string, + /// it may not be null terminated. c_str() must return a null terminated + /// string, however, and so the StringRef will internally take ownership + /// (taking a copy), if necessary. In theory this ownership is not externally + /// visible - but it does mean (substring) StringRefs should not be shared between + /// threads. + class StringRef { + friend struct StringRefTestAccess; + + using size_type = std::size_t; + + char const* m_start; + size_type m_size; + + char* m_data = nullptr; + + void takeOwnership(); + + public: // construction/ assignment + StringRef() noexcept; + StringRef( StringRef const& other ) noexcept; + StringRef( StringRef&& other ) noexcept; + StringRef( char const* rawChars ) noexcept; + StringRef( char const* rawChars, size_type size ) noexcept; + StringRef( std::string const& stdString ) noexcept; + ~StringRef() noexcept; + + auto operator = ( StringRef other ) noexcept -> StringRef&; + operator std::string() const; + + void swap( StringRef& other ) noexcept; + + public: // operators + auto operator == ( StringRef const& other ) const noexcept -> bool; + auto operator != ( StringRef const& other ) const noexcept -> bool; + + auto operator[] ( size_type index ) const noexcept -> char; + + public: // named queries + auto empty() const noexcept -> bool; + auto size() const noexcept -> size_type; + auto numberOfCharacters() const noexcept -> size_type; + auto c_str() const -> char const*; + + public: // substrings and searches + auto substr( size_type start, size_type size ) const noexcept -> StringRef; + + private: // ownership queries - may not be consistent between calls + auto isOwned() const noexcept -> bool; + auto isSubstring() const noexcept -> bool; + auto data() const noexcept -> char const*; + }; + + auto operator + ( StringRef const& lhs, StringRef const& rhs ) -> std::string; + auto operator + ( StringRef const& lhs, char const* rhs ) -> std::string; + auto operator + ( char const* lhs, StringRef const& rhs ) -> std::string; + + auto operator << ( std::ostream& os, StringRef const& sr ) -> std::ostream&; + +} // namespace Catch + +// end catch_stringref.h namespace Catch { template -class MethodTestCase : public SharedImpl { - +class TestInvokerAsMethod : public ITestInvoker { + void (C::*m_testAsMethod)(); public: - MethodTestCase( void (C::*method)() ) : m_method( method ) {} + TestInvokerAsMethod( void (C::*testAsMethod)() ) noexcept : m_testAsMethod( testAsMethod ) {} - virtual void invoke() const { + void invoke() const override { C obj; - (obj.*m_method)(); + (obj.*m_testAsMethod)(); } - -private: - virtual ~MethodTestCase() {} - - void (C::*m_method)(); }; -typedef void(*TestFunction)(); +auto makeTestInvoker( void(*testAsFunction)() ) noexcept -> ITestInvoker*; -struct NameAndDesc { - NameAndDesc( const char* _name = "", const char* _description= "" ) - : name( _name ), description( _description ) - {} +template +auto makeTestInvoker( void (C::*testAsMethod)() ) noexcept -> ITestInvoker* { + return new(std::nothrow) TestInvokerAsMethod( testAsMethod ); +} - const char* name; - const char* description; +struct NameAndTags { + NameAndTags( StringRef name_ = "", StringRef tags_ = "" ) noexcept; + StringRef name; + StringRef tags; }; -void registerTestCase - ( ITestCase* testCase, - char const* className, - NameAndDesc const& nameAndDesc, - SourceLineInfo const& lineInfo ); - -struct AutoReg { - - AutoReg - ( TestFunction function, - SourceLineInfo const& lineInfo, - NameAndDesc const& nameAndDesc ); - - template - AutoReg - ( void (C::*method)(), - char const* className, - NameAndDesc const& nameAndDesc, - SourceLineInfo const& lineInfo ) { - - registerTestCase - ( new MethodTestCase( method ), - className, - nameAndDesc, - lineInfo ); - } - +struct AutoReg : NonCopyable { + AutoReg( ITestInvoker* invoker, SourceLineInfo const& lineInfo, StringRef classOrMethod, NameAndTags const& nameAndTags ) noexcept; ~AutoReg(); - -private: - AutoReg( AutoReg const& ); - void operator= ( AutoReg const& ); }; -void registerTestCaseFunction - ( TestFunction function, - SourceLineInfo const& lineInfo, - NameAndDesc const& nameAndDesc ); - } // end namespace Catch -#ifdef CATCH_CONFIG_VARIADIC_MACROS +#if defined(CATCH_CONFIG_DISABLE) + #define INTERNAL_CATCH_TESTCASE_NO_REGISTRATION( TestName, ... ) \ + static void TestName() + #define INTERNAL_CATCH_TESTCASE_METHOD_NO_REGISTRATION( TestName, ClassName, ... ) \ + namespace{ \ + struct TestName : ClassName { \ + void test(); \ + }; \ + } \ + void TestName::test() + +#endif + /////////////////////////////////////////////////////////////////////////////// #define INTERNAL_CATCH_TESTCASE2( TestName, ... ) \ static void TestName(); \ - CATCH_INTERNAL_SUPPRESS_ETD_WARNINGS \ - namespace{ Catch::AutoReg INTERNAL_CATCH_UNIQUE_NAME( autoRegistrar )( &TestName, CATCH_INTERNAL_LINEINFO, Catch::NameAndDesc( __VA_ARGS__ ) ); } /* NOLINT */ \ - CATCH_INTERNAL_UNSUPPRESS_ETD_WARNINGS \ + CATCH_INTERNAL_SUPPRESS_GLOBALS_WARNINGS \ + namespace{ Catch::AutoReg INTERNAL_CATCH_UNIQUE_NAME( autoRegistrar )( Catch::makeTestInvoker( &TestName ), CATCH_INTERNAL_LINEINFO, "", Catch::NameAndTags{ __VA_ARGS__ } ); } /* NOLINT */ \ + CATCH_INTERNAL_UNSUPPRESS_GLOBALS_WARNINGS \ static void TestName() #define INTERNAL_CATCH_TESTCASE( ... ) \ INTERNAL_CATCH_TESTCASE2( INTERNAL_CATCH_UNIQUE_NAME( ____C_A_T_C_H____T_E_S_T____ ), __VA_ARGS__ ) /////////////////////////////////////////////////////////////////////////////// #define INTERNAL_CATCH_METHOD_AS_TEST_CASE( QualifiedMethod, ... ) \ - CATCH_INTERNAL_SUPPRESS_ETD_WARNINGS \ - namespace{ Catch::AutoReg INTERNAL_CATCH_UNIQUE_NAME( autoRegistrar )( &QualifiedMethod, "&" #QualifiedMethod, Catch::NameAndDesc( __VA_ARGS__ ), CATCH_INTERNAL_LINEINFO ); } /* NOLINT */ \ - CATCH_INTERNAL_UNSUPPRESS_ETD_WARNINGS + CATCH_INTERNAL_SUPPRESS_GLOBALS_WARNINGS \ + namespace{ Catch::AutoReg INTERNAL_CATCH_UNIQUE_NAME( autoRegistrar )( Catch::makeTestInvoker( &QualifiedMethod ), CATCH_INTERNAL_LINEINFO, "&" #QualifiedMethod, Catch::NameAndTags{ __VA_ARGS__ } ); } /* NOLINT */ \ + CATCH_INTERNAL_UNSUPPRESS_GLOBALS_WARNINGS /////////////////////////////////////////////////////////////////////////////// #define INTERNAL_CATCH_TEST_CASE_METHOD2( TestName, ClassName, ... )\ - CATCH_INTERNAL_SUPPRESS_ETD_WARNINGS \ + CATCH_INTERNAL_SUPPRESS_GLOBALS_WARNINGS \ namespace{ \ struct TestName : ClassName{ \ void test(); \ }; \ - Catch::AutoReg INTERNAL_CATCH_UNIQUE_NAME( autoRegistrar ) ( &TestName::test, #ClassName, Catch::NameAndDesc( __VA_ARGS__ ), CATCH_INTERNAL_LINEINFO ); /* NOLINT */ \ + Catch::AutoReg INTERNAL_CATCH_UNIQUE_NAME( autoRegistrar ) ( Catch::makeTestInvoker( &TestName::test ), CATCH_INTERNAL_LINEINFO, #ClassName, Catch::NameAndTags{ __VA_ARGS__ } ); /* NOLINT */ \ } \ - CATCH_INTERNAL_UNSUPPRESS_ETD_WARNINGS \ + CATCH_INTERNAL_UNSUPPRESS_GLOBALS_WARNINGS \ void TestName::test() #define INTERNAL_CATCH_TEST_CASE_METHOD( ClassName, ... ) \ INTERNAL_CATCH_TEST_CASE_METHOD2( INTERNAL_CATCH_UNIQUE_NAME( ____C_A_T_C_H____T_E_S_T____ ), ClassName, __VA_ARGS__ ) /////////////////////////////////////////////////////////////////////////////// #define INTERNAL_CATCH_REGISTER_TESTCASE( Function, ... ) \ - CATCH_INTERNAL_SUPPRESS_ETD_WARNINGS \ - Catch::AutoReg( Function, CATCH_INTERNAL_LINEINFO, Catch::NameAndDesc( __VA_ARGS__ ) ); /* NOLINT */ \ - CATCH_INTERNAL_UNSUPPRESS_ETD_WARNINGS + CATCH_INTERNAL_SUPPRESS_GLOBALS_WARNINGS \ + Catch::AutoReg INTERNAL_CATCH_UNIQUE_NAME( autoRegistrar )( Catch::makeTestInvoker( Function ), CATCH_INTERNAL_LINEINFO, "", Catch::NameAndTags{ __VA_ARGS__ } ); /* NOLINT */ \ + CATCH_INTERNAL_UNSUPPRESS_GLOBALS_WARNINGS -#else - /////////////////////////////////////////////////////////////////////////////// - #define INTERNAL_CATCH_TESTCASE2( TestName, Name, Desc ) \ - static void TestName(); \ - CATCH_INTERNAL_SUPPRESS_ETD_WARNINGS \ - namespace{ Catch::AutoReg INTERNAL_CATCH_UNIQUE_NAME( autoRegistrar )( &TestName, CATCH_INTERNAL_LINEINFO, Catch::NameAndDesc( Name, Desc ) ); } /* NOLINT */ \ - CATCH_INTERNAL_UNSUPPRESS_ETD_WARNINGS \ - static void TestName() - #define INTERNAL_CATCH_TESTCASE( Name, Desc ) \ - INTERNAL_CATCH_TESTCASE2( INTERNAL_CATCH_UNIQUE_NAME( ____C_A_T_C_H____T_E_S_T____ ), Name, Desc ) +// end catch_test_registry.h +// start catch_capture.hpp - /////////////////////////////////////////////////////////////////////////////// - #define INTERNAL_CATCH_METHOD_AS_TEST_CASE( QualifiedMethod, Name, Desc ) \ - CATCH_INTERNAL_SUPPRESS_ETD_WARNINGS \ - namespace{ Catch::AutoReg INTERNAL_CATCH_UNIQUE_NAME( autoRegistrar )( &QualifiedMethod, "&" #QualifiedMethod, Catch::NameAndDesc( Name, Desc ), CATCH_INTERNAL_LINEINFO ); } /* NOLINT */ \ - CATCH_INTERNAL_UNSUPPRESS_ETD_WARNINGS +// start catch_assertionhandler.h - /////////////////////////////////////////////////////////////////////////////// - #define INTERNAL_CATCH_TEST_CASE_METHOD2( TestCaseName, ClassName, TestName, Desc )\ - CATCH_INTERNAL_SUPPRESS_ETD_WARNINGS \ - namespace{ \ - struct TestCaseName : ClassName{ \ - void test(); \ - }; \ - Catch::AutoReg INTERNAL_CATCH_UNIQUE_NAME( autoRegistrar ) ( &TestCaseName::test, #ClassName, Catch::NameAndDesc( TestName, Desc ), CATCH_INTERNAL_LINEINFO ); /* NOLINT */ \ - } \ - CATCH_INTERNAL_UNSUPPRESS_ETD_WARNINGS \ - void TestCaseName::test() - #define INTERNAL_CATCH_TEST_CASE_METHOD( ClassName, TestName, Desc )\ - INTERNAL_CATCH_TEST_CASE_METHOD2( INTERNAL_CATCH_UNIQUE_NAME( ____C_A_T_C_H____T_E_S_T____ ), ClassName, TestName, Desc ) +// start catch_decomposer.h - /////////////////////////////////////////////////////////////////////////////// - #define INTERNAL_CATCH_REGISTER_TESTCASE( Function, Name, Desc ) \ - CATCH_INTERNAL_SUPPRESS_ETD_WARNINGS \ - Catch::AutoReg( Function, CATCH_INTERNAL_LINEINFO, Catch::NameAndDesc( Name, Desc ) ); /* NOLINT */ \ - CATCH_INTERNAL_UNSUPPRESS_ETD_WARNINGS - -#endif - -// #included from: internal/catch_capture.hpp -#define TWOBLUECUBES_CATCH_CAPTURE_HPP_INCLUDED - -// #included from: catch_result_builder.h -#define TWOBLUECUBES_CATCH_RESULT_BUILDER_H_INCLUDED - -// #included from: catch_result_type.h -#define TWOBLUECUBES_CATCH_RESULT_TYPE_H_INCLUDED - -namespace Catch { - - // ResultWas::OfType enum - struct ResultWas { enum OfType { - Unknown = -1, - Ok = 0, - Info = 1, - Warning = 2, - - FailureBit = 0x10, - - ExpressionFailed = FailureBit | 1, - ExplicitFailure = FailureBit | 2, - - Exception = 0x100 | FailureBit, - - ThrewException = Exception | 1, - DidntThrowException = Exception | 2, - - FatalErrorCondition = 0x200 | FailureBit - - }; }; - - inline bool isOk( ResultWas::OfType resultType ) { - return ( resultType & ResultWas::FailureBit ) == 0; - } - inline bool isJustInfo( int flags ) { - return flags == ResultWas::Info; - } - - // ResultDisposition::Flags enum - struct ResultDisposition { enum Flags { - Normal = 0x01, - - ContinueOnFailure = 0x02, // Failures fail test, but execution continues - FalseTest = 0x04, // Prefix expression with ! - SuppressFail = 0x08 // Failures are reported but do not fail the test - }; }; - - inline ResultDisposition::Flags operator | ( ResultDisposition::Flags lhs, ResultDisposition::Flags rhs ) { - return static_cast( static_cast( lhs ) | static_cast( rhs ) ); - } - - inline bool shouldContinueOnFailure( int flags ) { return ( flags & ResultDisposition::ContinueOnFailure ) != 0; } - inline bool isFalseTest( int flags ) { return ( flags & ResultDisposition::FalseTest ) != 0; } - inline bool shouldSuppressFailure( int flags ) { return ( flags & ResultDisposition::SuppressFail ) != 0; } - -} // end namespace Catch - -// #included from: catch_assertionresult.h -#define TWOBLUECUBES_CATCH_ASSERTIONRESULT_H_INCLUDED - -#include - -namespace Catch { - - struct STATIC_ASSERT_Expression_Too_Complex_Please_Rewrite_As_Binary_Comparison; - - struct DecomposedExpression - { - virtual ~DecomposedExpression() {} - virtual bool isBinaryExpression() const { - return false; - } - virtual void reconstructExpression( std::string& dest ) const = 0; - - // Only simple binary comparisons can be decomposed. - // If more complex check is required then wrap sub-expressions in parentheses. - template STATIC_ASSERT_Expression_Too_Complex_Please_Rewrite_As_Binary_Comparison& operator + ( T const& ); - template STATIC_ASSERT_Expression_Too_Complex_Please_Rewrite_As_Binary_Comparison& operator - ( T const& ); - template STATIC_ASSERT_Expression_Too_Complex_Please_Rewrite_As_Binary_Comparison& operator * ( T const& ); - template STATIC_ASSERT_Expression_Too_Complex_Please_Rewrite_As_Binary_Comparison& operator / ( T const& ); - template STATIC_ASSERT_Expression_Too_Complex_Please_Rewrite_As_Binary_Comparison& operator % ( T const& ); - template STATIC_ASSERT_Expression_Too_Complex_Please_Rewrite_As_Binary_Comparison& operator && ( T const& ); - template STATIC_ASSERT_Expression_Too_Complex_Please_Rewrite_As_Binary_Comparison& operator || ( T const& ); - - private: - DecomposedExpression& operator = (DecomposedExpression const&); - }; - - struct AssertionInfo - { - AssertionInfo(); - AssertionInfo( char const * _macroName, - SourceLineInfo const& _lineInfo, - char const * _capturedExpression, - ResultDisposition::Flags _resultDisposition, - char const * _secondArg = ""); - - char const * macroName; - SourceLineInfo lineInfo; - char const * capturedExpression; - ResultDisposition::Flags resultDisposition; - char const * secondArg; - }; - - struct AssertionResultData - { - AssertionResultData() : decomposedExpression( CATCH_NULL ) - , resultType( ResultWas::Unknown ) - , negated( false ) - , parenthesized( false ) {} - - void negate( bool parenthesize ) { - negated = !negated; - parenthesized = parenthesize; - if( resultType == ResultWas::Ok ) - resultType = ResultWas::ExpressionFailed; - else if( resultType == ResultWas::ExpressionFailed ) - resultType = ResultWas::Ok; - } - - std::string const& reconstructExpression() const { - if( decomposedExpression != CATCH_NULL ) { - decomposedExpression->reconstructExpression( reconstructedExpression ); - if( parenthesized ) { - reconstructedExpression.insert( 0, 1, '(' ); - reconstructedExpression.append( 1, ')' ); - } - if( negated ) { - reconstructedExpression.insert( 0, 1, '!' ); - } - decomposedExpression = CATCH_NULL; - } - return reconstructedExpression; - } - - mutable DecomposedExpression const* decomposedExpression; - mutable std::string reconstructedExpression; - std::string message; - ResultWas::OfType resultType; - bool negated; - bool parenthesized; - }; - - class AssertionResult { - public: - AssertionResult(); - AssertionResult( AssertionInfo const& info, AssertionResultData const& data ); - ~AssertionResult(); -# ifdef CATCH_CONFIG_CPP11_GENERATED_METHODS - AssertionResult( AssertionResult const& ) = default; - AssertionResult( AssertionResult && ) = default; - AssertionResult& operator = ( AssertionResult const& ) = default; - AssertionResult& operator = ( AssertionResult && ) = default; -# endif - - bool isOk() const; - bool succeeded() const; - ResultWas::OfType getResultType() const; - bool hasExpression() const; - bool hasMessage() const; - std::string getExpression() const; - std::string getExpressionInMacro() const; - bool hasExpandedExpression() const; - std::string getExpandedExpression() const; - std::string getMessage() const; - SourceLineInfo getSourceInfo() const; - std::string getTestMacroName() const; - void discardDecomposedExpression() const; - void expandDecomposedExpression() const; - - protected: - AssertionInfo m_info; - AssertionResultData m_resultData; - }; - -} // end namespace Catch - -// #included from: catch_matchers.hpp -#define TWOBLUECUBES_CATCH_MATCHERS_HPP_INCLUDED - -namespace Catch { -namespace Matchers { - namespace Impl { - - template struct MatchAllOf; - template struct MatchAnyOf; - template struct MatchNotOf; - - class MatcherUntypedBase { - public: - std::string toString() const { - if( m_cachedToString.empty() ) - m_cachedToString = describe(); - return m_cachedToString; - } - - protected: - virtual ~MatcherUntypedBase(); - virtual std::string describe() const = 0; - mutable std::string m_cachedToString; - private: - MatcherUntypedBase& operator = ( MatcherUntypedBase const& ); - }; - - template - struct MatcherMethod { - virtual bool match( ObjectT const& arg ) const = 0; - }; - template - struct MatcherMethod { - virtual bool match( PtrT* arg ) const = 0; - }; - - template - struct MatcherBase : MatcherUntypedBase, MatcherMethod { - - MatchAllOf operator && ( MatcherBase const& other ) const; - MatchAnyOf operator || ( MatcherBase const& other ) const; - MatchNotOf operator ! () const; - }; - - template - struct MatchAllOf : MatcherBase { - virtual bool match( ArgT const& arg ) const CATCH_OVERRIDE { - for( std::size_t i = 0; i < m_matchers.size(); ++i ) { - if (!m_matchers[i]->match(arg)) - return false; - } - return true; - } - virtual std::string describe() const CATCH_OVERRIDE { - std::string description; - description.reserve( 4 + m_matchers.size()*32 ); - description += "( "; - for( std::size_t i = 0; i < m_matchers.size(); ++i ) { - if( i != 0 ) - description += " and "; - description += m_matchers[i]->toString(); - } - description += " )"; - return description; - } - - MatchAllOf& operator && ( MatcherBase const& other ) { - m_matchers.push_back( &other ); - return *this; - } - - std::vector const*> m_matchers; - }; - template - struct MatchAnyOf : MatcherBase { - - virtual bool match( ArgT const& arg ) const CATCH_OVERRIDE { - for( std::size_t i = 0; i < m_matchers.size(); ++i ) { - if (m_matchers[i]->match(arg)) - return true; - } - return false; - } - virtual std::string describe() const CATCH_OVERRIDE { - std::string description; - description.reserve( 4 + m_matchers.size()*32 ); - description += "( "; - for( std::size_t i = 0; i < m_matchers.size(); ++i ) { - if( i != 0 ) - description += " or "; - description += m_matchers[i]->toString(); - } - description += " )"; - return description; - } - - MatchAnyOf& operator || ( MatcherBase const& other ) { - m_matchers.push_back( &other ); - return *this; - } - - std::vector const*> m_matchers; - }; - - template - struct MatchNotOf : MatcherBase { - - MatchNotOf( MatcherBase const& underlyingMatcher ) : m_underlyingMatcher( underlyingMatcher ) {} - - virtual bool match( ArgT const& arg ) const CATCH_OVERRIDE { - return !m_underlyingMatcher.match( arg ); - } - - virtual std::string describe() const CATCH_OVERRIDE { - return "not " + m_underlyingMatcher.toString(); - } - MatcherBase const& m_underlyingMatcher; - }; - - template - MatchAllOf MatcherBase::operator && ( MatcherBase const& other ) const { - return MatchAllOf() && *this && other; - } - template - MatchAnyOf MatcherBase::operator || ( MatcherBase const& other ) const { - return MatchAnyOf() || *this || other; - } - template - MatchNotOf MatcherBase::operator ! () const { - return MatchNotOf( *this ); - } - - } // namespace Impl - - // The following functions create the actual matcher objects. - // This allows the types to be inferred - // - deprecated: prefer ||, && and ! - template - Impl::MatchNotOf Not( Impl::MatcherBase const& underlyingMatcher ) { - return Impl::MatchNotOf( underlyingMatcher ); - } - template - Impl::MatchAllOf AllOf( Impl::MatcherBase const& m1, Impl::MatcherBase const& m2 ) { - return Impl::MatchAllOf() && m1 && m2; - } - template - Impl::MatchAllOf AllOf( Impl::MatcherBase const& m1, Impl::MatcherBase const& m2, Impl::MatcherBase const& m3 ) { - return Impl::MatchAllOf() && m1 && m2 && m3; - } - template - Impl::MatchAnyOf AnyOf( Impl::MatcherBase const& m1, Impl::MatcherBase const& m2 ) { - return Impl::MatchAnyOf() || m1 || m2; - } - template - Impl::MatchAnyOf AnyOf( Impl::MatcherBase const& m1, Impl::MatcherBase const& m2, Impl::MatcherBase const& m3 ) { - return Impl::MatchAnyOf() || m1 || m2 || m3; - } - -} // namespace Matchers - -using namespace Matchers; -using Matchers::Impl::MatcherBase; - -} // namespace Catch - -namespace Catch { - - struct TestFailureException{}; - - template class ExpressionLhs; - - struct CopyableStream { - CopyableStream() {} - CopyableStream( CopyableStream const& other ) { - oss << other.oss.str(); - } - CopyableStream& operator=( CopyableStream const& other ) { - oss.str(std::string()); - oss << other.oss.str(); - return *this; - } - std::ostringstream oss; - }; - - class ResultBuilder : public DecomposedExpression { - public: - ResultBuilder( char const* macroName, - SourceLineInfo const& lineInfo, - char const* capturedExpression, - ResultDisposition::Flags resultDisposition, - char const* secondArg = "" ); - ~ResultBuilder(); - - template - ExpressionLhs operator <= ( T const& operand ); - ExpressionLhs operator <= ( bool value ); - - template - ResultBuilder& operator << ( T const& value ) { - stream().oss << value; - return *this; - } - - ResultBuilder& setResultType( ResultWas::OfType result ); - ResultBuilder& setResultType( bool result ); - - void endExpression( DecomposedExpression const& expr ); - - virtual void reconstructExpression( std::string& dest ) const CATCH_OVERRIDE; - - AssertionResult build() const; - AssertionResult build( DecomposedExpression const& expr ) const; - - void useActiveException( ResultDisposition::Flags resultDisposition = ResultDisposition::Normal ); - void captureResult( ResultWas::OfType resultType ); - void captureExpression(); - void captureExpectedException( std::string const& expectedMessage ); - void captureExpectedException( Matchers::Impl::MatcherBase const& matcher ); - void handleResult( AssertionResult const& result ); - void react(); - bool shouldDebugBreak() const; - bool allowThrows() const; - - template - void captureMatch( ArgT const& arg, MatcherT const& matcher, char const* matcherString ); - - void setExceptionGuard(); - void unsetExceptionGuard(); - - private: - AssertionInfo m_assertionInfo; - AssertionResultData m_data; - - CopyableStream &stream() - { - if(!m_usedStream) - { - m_usedStream = true; - m_stream().oss.str(""); - } - return m_stream(); - } - - static CopyableStream &m_stream() - { - static CopyableStream s; - return s; - } - - bool m_shouldDebugBreak; - bool m_shouldThrow; - bool m_guardException; - bool m_usedStream; - }; - -} // namespace Catch - -// Include after due to circular dependency: -// #included from: catch_expression_lhs.hpp -#define TWOBLUECUBES_CATCH_EXPRESSION_LHS_HPP_INCLUDED - -// #included from: catch_evaluate.hpp -#define TWOBLUECUBES_CATCH_EVALUATE_HPP_INCLUDED - -#ifdef _MSC_VER -#pragma warning(push) -#pragma warning(disable:4389) // '==' : signed/unsigned mismatch -#pragma warning(disable:4018) // more "signed/unsigned mismatch" -#pragma warning(disable:4312) // Converting int to T* using reinterpret_cast (issue on x64 platform) -#endif - -#include - -namespace Catch { -namespace Internal { - - enum Operator { - IsEqualTo, - IsNotEqualTo, - IsLessThan, - IsGreaterThan, - IsLessThanOrEqualTo, - IsGreaterThanOrEqualTo - }; - - template struct OperatorTraits { static const char* getName(){ return "*error*"; } }; - template<> struct OperatorTraits { static const char* getName(){ return "=="; } }; - template<> struct OperatorTraits { static const char* getName(){ return "!="; } }; - template<> struct OperatorTraits { static const char* getName(){ return "<"; } }; - template<> struct OperatorTraits { static const char* getName(){ return ">"; } }; - template<> struct OperatorTraits { static const char* getName(){ return "<="; } }; - template<> struct OperatorTraits{ static const char* getName(){ return ">="; } }; - - template - T& removeConst(T const &t) { return const_cast(t); } -#ifdef CATCH_CONFIG_CPP11_NULLPTR - inline std::nullptr_t removeConst(std::nullptr_t) { return nullptr; } -#endif - - // So the compare overloads can be operator agnostic we convey the operator as a template - // enum, which is used to specialise an Evaluator for doing the comparison. - template - struct Evaluator{}; - - template - struct Evaluator { - static bool evaluate( T1 const& lhs, T2 const& rhs) { - return bool(removeConst(lhs) == removeConst(rhs) ); - } - }; - template - struct Evaluator { - static bool evaluate( T1 const& lhs, T2 const& rhs ) { - return bool(removeConst(lhs) != removeConst(rhs) ); - } - }; - template - struct Evaluator { - static bool evaluate( T1 const& lhs, T2 const& rhs ) { - return bool(removeConst(lhs) < removeConst(rhs) ); - } - }; - template - struct Evaluator { - static bool evaluate( T1 const& lhs, T2 const& rhs ) { - return bool(removeConst(lhs) > removeConst(rhs) ); - } - }; - template - struct Evaluator { - static bool evaluate( T1 const& lhs, T2 const& rhs ) { - return bool(removeConst(lhs) >= removeConst(rhs) ); - } - }; - template - struct Evaluator { - static bool evaluate( T1 const& lhs, T2 const& rhs ) { - return bool(removeConst(lhs) <= removeConst(rhs) ); - } - }; - - // Special case for comparing a pointer to an int (deduced for p==0) - template - struct Evaluator { - static bool evaluate( int lhs, T* rhs) { - return reinterpret_cast( lhs ) == rhs; - } - }; - template - struct Evaluator { - static bool evaluate( T* lhs, int rhs) { - return lhs == reinterpret_cast( rhs ); - } - }; - template - struct Evaluator { - static bool evaluate( int lhs, T* rhs) { - return reinterpret_cast( lhs ) != rhs; - } - }; - template - struct Evaluator { - static bool evaluate( T* lhs, int rhs) { - return lhs != reinterpret_cast( rhs ); - } - }; - - template - struct Evaluator { - static bool evaluate( long lhs, T* rhs) { - return reinterpret_cast( lhs ) == rhs; - } - }; - template - struct Evaluator { - static bool evaluate( T* lhs, long rhs) { - return lhs == reinterpret_cast( rhs ); - } - }; - template - struct Evaluator { - static bool evaluate( long lhs, T* rhs) { - return reinterpret_cast( lhs ) != rhs; - } - }; - template - struct Evaluator { - static bool evaluate( T* lhs, long rhs) { - return lhs != reinterpret_cast( rhs ); - } - }; - -} // end of namespace Internal -} // end of namespace Catch - -#ifdef _MSC_VER -#pragma warning(pop) -#endif - -// #included from: catch_tostring.h -#define TWOBLUECUBES_CATCH_TOSTRING_H_INCLUDED +// start catch_tostring.h #include -#include -#include #include #include +#include +#include #ifdef __OBJC__ -// #included from: catch_objc_arc.hpp -#define TWOBLUECUBES_CATCH_OBJC_ARC_HPP_INCLUDED +// start catch_objc_arc.hpp #import @@ -1478,451 +526,735 @@ inline id performOptionalSelector( id obj, SEL sel ) { #define CATCH_ARC_STRONG __strong #endif +// end catch_objc_arc.hpp #endif -#ifdef CATCH_CONFIG_CPP11_TUPLE -#include +#ifdef _MSC_VER +#pragma warning(push) +#pragma warning(disable:4180) // We attempt to stream a function (address) by const&, which MSVC complains about but is harmless #endif -#ifdef CATCH_CONFIG_CPP11_IS_ENUM -#include -#endif +// We need a dummy global operator<< so we can bring it into Catch namespace later +struct Catch_global_namespace_dummy; +std::ostream& operator<<(std::ostream&, Catch_global_namespace_dummy); namespace Catch { + // Bring in operator<< from global namespace into Catch namespace + using ::operator<<; -// Why we're here. -template -std::string toString( T const& value ); + namespace Detail { -// Built in overloads + extern const std::string unprintableString; -std::string toString( std::string const& value ); -std::string toString( std::wstring const& value ); -std::string toString( const char* const value ); -std::string toString( char* const value ); -std::string toString( const wchar_t* const value ); -std::string toString( wchar_t* const value ); -std::string toString( int value ); -std::string toString( unsigned long value ); -std::string toString( unsigned int value ); -std::string toString( const double value ); -std::string toString( const float value ); -std::string toString( bool value ); -std::string toString( char value ); -std::string toString( signed char value ); -std::string toString( unsigned char value ); + std::string rawMemoryToString( const void *object, std::size_t size ); -#ifdef CATCH_CONFIG_CPP11_LONG_LONG -std::string toString( long long value ); -std::string toString( unsigned long long value ); -#endif + template + std::string rawMemoryToString( const T& object ) { + return rawMemoryToString( &object, sizeof(object) ); + } -#ifdef CATCH_CONFIG_CPP11_NULLPTR -std::string toString( std::nullptr_t ); -#endif + template + class IsStreamInsertable { + template + static auto test(int) + -> decltype(std::declval() << std::declval(), std::true_type()); -#ifdef __OBJC__ - std::string toString( NSString const * const& nsstring ); - std::string toString( NSString * CATCH_ARC_STRONG & nsstring ); - std::string toString( NSObject* const& nsObject ); -#endif + template + static auto test(...)->std::false_type; -namespace Detail { + public: + static const bool value = decltype(test(0))::value; + }; - extern const std::string unprintableString; + } // namespace Detail - #if !defined(CATCH_CONFIG_CPP11_STREAM_INSERTABLE_CHECK) - struct BorgType { - template BorgType( T const& ); - }; + // If we decide for C++14, change these to enable_if_ts + template + struct StringMaker { + template + static + typename std::enable_if<::Catch::Detail::IsStreamInsertable::value, std::string>::type + convert(const Fake& t) { + std::ostringstream sstr; + sstr << t; + return sstr.str(); + } - struct TrueType { char sizer[1]; }; - struct FalseType { char sizer[2]; }; - - TrueType& testStreamable( std::ostream& ); - FalseType testStreamable( FalseType ); - - FalseType operator<<( std::ostream const&, BorgType const& ); - - template - struct IsStreamInsertable { - static std::ostream &s; - static T const&t; - enum { value = sizeof( testStreamable(s << t) ) == sizeof( TrueType ) }; - }; -#else - template - class IsStreamInsertable { - template - static auto test(int) - -> decltype( std::declval() << std::declval(), std::true_type() ); - - template - static auto test(...) -> std::false_type; - - public: - static const bool value = decltype(test(0))::value; - }; -#endif - -#if defined(CATCH_CONFIG_CPP11_IS_ENUM) - template::value - > - struct EnumStringMaker - { - static std::string convert( T const& ) { return unprintableString; } - }; - - template - struct EnumStringMaker - { - static std::string convert( T const& v ) - { - return ::Catch::toString( - static_cast::type>(v) - ); + template + static + typename std::enable_if::value, std::string>::type + convert(const Fake&) { + return Detail::unprintableString; } }; -#endif - template - struct StringMakerBase { -#if defined(CATCH_CONFIG_CPP11_IS_ENUM) - template - static std::string convert( T const& v ) - { - return EnumStringMaker::convert( v ); + + namespace Detail { + + // This function dispatches all stringification requests inside of Catch. + // Should be preferably called fully qualified, like ::Catch::Detail::stringify + template + std::string stringify(const T& e) { + return ::Catch::StringMaker::type>::type>::convert(e); } -#else - template - static std::string convert( T const& ) { return unprintableString; } -#endif + + } // namespace Detail + + // Some predefined specializations + + template<> + struct StringMaker { + static std::string convert(const std::string& str); + }; + template<> + struct StringMaker { + static std::string convert(const std::wstring& wstr); }; template<> - struct StringMakerBase { - template - static std::string convert( T const& _value ) { + struct StringMaker { + static std::string convert(char const * str); + }; + template<> + struct StringMaker { + static std::string convert(char * str); + }; + template<> + struct StringMaker { + static std::string convert(wchar_t const * str); + }; + template<> + struct StringMaker { + static std::string convert(wchar_t * str); + }; + + template + struct StringMaker { + static std::string convert(const char* str) { + return ::Catch::Detail::stringify(std::string{ str }); + } + }; + template + struct StringMaker { + static std::string convert(const char* str) { + return ::Catch::Detail::stringify(std::string{ str }); + } + }; + template + struct StringMaker { + static std::string convert(const char* str) { + return ::Catch::Detail::stringify(std::string{ str }); + } + }; + + template<> + struct StringMaker { + static std::string convert(int value); + }; + template<> + struct StringMaker { + static std::string convert(long value); + }; + template<> + struct StringMaker { + static std::string convert(long long value); + }; + template<> + struct StringMaker { + static std::string convert(unsigned int value); + }; + template<> + struct StringMaker { + static std::string convert(unsigned long value); + }; + template<> + struct StringMaker { + static std::string convert(unsigned long long value); + }; + + template<> + struct StringMaker { + static std::string convert(bool b); + }; + + template<> + struct StringMaker { + static std::string convert(char c); + }; + template<> + struct StringMaker { + static std::string convert(signed char c); + }; + template<> + struct StringMaker { + static std::string convert(unsigned char c); + }; + + template<> + struct StringMaker { + static std::string convert(std::nullptr_t); + }; + + template<> + struct StringMaker { + static std::string convert(float value); + }; + template<> + struct StringMaker { + static std::string convert(double value); + }; + + template + struct StringMaker { + template + static std::string convert(U* p) { + if (p) { + return ::Catch::Detail::rawMemoryToString(p); + } else { + return "nullptr"; + } + } + }; + + template + struct StringMaker { + static std::string convert(R C::* p) { + if (p) { + return ::Catch::Detail::rawMemoryToString(p); + } else { + return "nullptr"; + } + } + }; + + namespace Detail { + template + std::string rangeToString(InputIterator first, InputIterator last) { std::ostringstream oss; - oss << _value; + oss << "{ "; + if (first != last) { + oss << ::Catch::Detail::stringify(*first); + for (++first; first != last; ++first) + oss << ", " << ::Catch::Detail::stringify(*first); + } + oss << " }"; + return oss.str(); + } + } + + template + struct StringMaker > { + static std::string convert( std::vector const& v ) { + return ::Catch::Detail::rangeToString( v.begin(), v.end() ); + } + }; + + template + struct EnumStringMaker { + static std::string convert(const T& t) { + return ::Catch::Detail::stringify(static_cast::type>(t)); + } + }; + +#ifdef __OBJC__ + template<> + struct StringMaker { + static std::string convert(NSString * nsstring) { + if (!nsstring) + return "nil"; + return std::string("@") + [nsstring UTF8String]; + } + }; + template<> + struct StringMaker { + static std::string convert(NSObject* nsObject) { + return ::Catch::Detail::stringify([nsObject description]); + } + + }; + namespace Detail { + inline std::string stringify( NSString* nsstring ) { + return StringMaker::convert( nsstring ); + } + + } // namespace Detail +#endif // __OBJC__ + +} // namespace Catch + +////////////////////////////////////////////////////// +// Separate std-lib types stringification, so it can be selectively enabled +// This means that we do not bring in + +#if defined(CATCH_CONFIG_ENABLE_ALL_STRINGMAKERS) +# define CATCH_CONFIG_ENABLE_PAIR_STRINGMAKER +# define CATCH_CONFIG_ENABLE_TUPLE_STRINGMAKER +# define CATCH_CONFIG_ENABLE_CHRONO_STRINGMAKER +#endif + +// Separate std::pair specialization +#if defined(CATCH_CONFIG_ENABLE_PAIR_STRINGMAKER) +#include +namespace Catch { + template + struct StringMaker > { + static std::string convert(const std::pair& pair) { + std::ostringstream oss; + oss << "{ " + << ::Catch::Detail::stringify(pair.first) + << ", " + << ::Catch::Detail::stringify(pair.second) + << " }"; + return oss.str(); + } + }; +} +#endif // CATCH_CONFIG_ENABLE_PAIR_STRINGMAKER + +// Separate std::tuple specialization +#if defined(CATCH_CONFIG_ENABLE_TUPLE_STRINGMAKER) +#include +namespace Catch { + namespace Detail { + template< + typename Tuple, + std::size_t N = 0, + bool = (N < std::tuple_size::value) + > + struct TupleElementPrinter { + static void print(const Tuple& tuple, std::ostream& os) { + os << (N ? ", " : " ") + << ::Catch::Detail::stringify(std::get(tuple)); + TupleElementPrinter::print(tuple, os); + } + }; + + template< + typename Tuple, + std::size_t N + > + struct TupleElementPrinter { + static void print(const Tuple&, std::ostream&) {} + }; + + } + + template + struct StringMaker> { + static std::string convert(const std::tuple& tuple) { + std::ostringstream os; + os << '{'; + Detail::TupleElementPrinter>::print(tuple, os); + os << " }"; + return os.str(); + } + }; +} +#endif // CATCH_CONFIG_ENABLE_TUPLE_STRINGMAKER + +// Separate std::chrono::duration specialization +#if defined(CATCH_CONFIG_ENABLE_CHRONO_STRINGMAKER) +#include +#include +#include + +template +struct ratio_string { + static std::string symbol(); +}; + +template +std::string ratio_string::symbol() { + std::ostringstream oss; + oss << '[' << Ratio::num << '/' + << Ratio::den << ']'; + return oss.str(); +} +template <> +struct ratio_string { + static std::string symbol() { return "a"; } +}; +template <> +struct ratio_string { + static std::string symbol() { return "f"; } +}; +template <> +struct ratio_string { + static std::string symbol() { return "p"; } +}; +template <> +struct ratio_string { + static std::string symbol() { return "n"; } +}; +template <> +struct ratio_string { + static std::string symbol() { return "u"; } +}; +template <> +struct ratio_string { + static std::string symbol() { return "m"; } +}; + +namespace Catch { + //////////// + // std::chrono::duration specializations + template + struct StringMaker> { + static std::string convert(std::chrono::duration const& duration) { + std::ostringstream oss; + oss << duration.count() << ' ' << ratio_string::symbol() << 's'; + return oss.str(); + } + }; + template + struct StringMaker>> { + static std::string convert(std::chrono::duration> const& duration) { + std::ostringstream oss; + oss << duration.count() << " s"; + return oss.str(); + } + }; + template + struct StringMaker>> { + static std::string convert(std::chrono::duration> const& duration) { + std::ostringstream oss; + oss << duration.count() << " m"; + return oss.str(); + } + }; + template + struct StringMaker>> { + static std::string convert(std::chrono::duration> const& duration) { + std::ostringstream oss; + oss << duration.count() << " h"; return oss.str(); } }; - std::string rawMemoryToString( const void *object, std::size_t size ); - - template - std::string rawMemoryToString( const T& object ) { - return rawMemoryToString( &object, sizeof(object) ); - } - -} // end namespace Detail - -template -struct StringMaker : - Detail::StringMakerBase::value> {}; - -template -struct StringMaker { - template - static std::string convert( U* p ) { - if( !p ) - return "NULL"; - else - return Detail::rawMemoryToString( p ); - } -}; - -template -struct StringMaker { - static std::string convert( R C::* p ) { - if( !p ) - return "NULL"; - else - return Detail::rawMemoryToString( p ); - } -}; - -namespace Detail { - template - std::string rangeToString( InputIterator first, InputIterator last ); -} - -//template -//struct StringMaker > { -// static std::string convert( std::vector const& v ) { -// return Detail::rangeToString( v.begin(), v.end() ); -// } -//}; - -template -std::string toString( std::vector const& v ) { - return Detail::rangeToString( v.begin(), v.end() ); -} - -#ifdef CATCH_CONFIG_CPP11_TUPLE - -// toString for tuples -namespace TupleDetail { - template< - typename Tuple, - std::size_t N = 0, - bool = (N < std::tuple_size::value) - > - struct ElementPrinter { - static void print( const Tuple& tuple, std::ostream& os ) - { - os << ( N ? ", " : " " ) - << Catch::toString(std::get(tuple)); - ElementPrinter::print(tuple,os); - } - }; - - template< - typename Tuple, - std::size_t N - > - struct ElementPrinter { - static void print( const Tuple&, std::ostream& ) {} - }; - -} - -template -struct StringMaker> { - - static std::string convert( const std::tuple& tuple ) - { - std::ostringstream os; - os << '{'; - TupleDetail::ElementPrinter>::print( tuple, os ); - os << " }"; - return os.str(); - } -}; -#endif // CATCH_CONFIG_CPP11_TUPLE - -namespace Detail { - template - std::string makeString( T const& value ) { - return StringMaker::convert( value ); - } -} // end namespace Detail - -/// \brief converts any type to a string -/// -/// The default template forwards on to ostringstream - except when an -/// ostringstream overload does not exist - in which case it attempts to detect -/// that and writes {?}. -/// Overload (not specialise) this template for custom typs that you don't want -/// to provide an ostream overload for. -template -std::string toString( T const& value ) { - return StringMaker::convert( value ); -} - - namespace Detail { - template - std::string rangeToString( InputIterator first, InputIterator last ) { - std::ostringstream oss; - oss << "{ "; - if( first != last ) { - oss << Catch::toString( *first ); - for( ++first ; first != last ; ++first ) - oss << ", " << Catch::toString( *first ); + //////////// + // std::chrono::time_point specialization + // Generic time_point cannot be specialized, only std::chrono::time_point + template + struct StringMaker> { + static std::string convert(std::chrono::time_point const& time_point) { + return ::Catch::Detail::stringify(time_point.time_since_epoch()) + " since epoch"; } - oss << " }"; - return oss.str(); - } + }; + // std::chrono::time_point specialization + template + struct StringMaker> { + static std::string convert(std::chrono::time_point const& time_point) { + auto converted = std::chrono::system_clock::to_time_t(time_point); + +#ifdef _MSC_VER + std::tm timeInfo = {}; + gmtime_s(&timeInfo, &converted); +#else + std::tm* timeInfo = std::gmtime(&converted); +#endif + + auto const timeStampSize = sizeof("2017-01-16T17:06:45Z"); + char timeStamp[timeStampSize]; + const char * const fmt = "%Y-%m-%dT%H:%M:%SZ"; + +#ifdef _MSC_VER + std::strftime(timeStamp, timeStampSize, fmt, &timeInfo); +#else + std::strftime(timeStamp, timeStampSize, fmt, timeInfo); +#endif + return std::string(timeStamp); + } + }; } +#endif // CATCH_CONFIG_ENABLE_CHRONO_STRINGMAKER -} // end namespace Catch +#ifdef _MSC_VER +#pragma warning(pop) +#endif + +// end catch_tostring.h +#include + +#ifdef _MSC_VER +#pragma warning(push) +#pragma warning(disable:4389) // '==' : signed/unsigned mismatch +#pragma warning(disable:4018) // more "signed/unsigned mismatch" +#pragma warning(disable:4312) // Converting int to T* using reinterpret_cast (issue on x64 platform) +#pragma warning(disable:4180) // qualifier applied to function type has no meaning +#endif namespace Catch { -template -class BinaryExpression; + struct ITransientExpression { + virtual auto isBinaryExpression() const -> bool = 0; + virtual auto getResult() const -> bool = 0; + virtual void streamReconstructedExpression( std::ostream &os ) const = 0; -template -class MatchExpression; + // We don't actually need a virtual destructore, but many static analysers + // complain if it's not here :-( + virtual ~ITransientExpression(); + }; -// Wraps the LHS of an expression and overloads comparison operators -// for also capturing those and RHS (if any) -template -class ExpressionLhs : public DecomposedExpression { -public: - ExpressionLhs( ResultBuilder& rb, T lhs ) : m_rb( rb ), m_lhs( lhs ), m_truthy(false) {} + void formatReconstructedExpression( std::ostream &os, std::string const& lhs, StringRef op, std::string const& rhs ); - ExpressionLhs& operator = ( const ExpressionLhs& ); + template + class BinaryExpr : public ITransientExpression { + bool m_result; + LhsT m_lhs; + StringRef m_op; + RhsT m_rhs; - template - BinaryExpression - operator == ( RhsT const& rhs ) { - return captureExpression( rhs ); - } + auto isBinaryExpression() const -> bool override { return true; } + auto getResult() const -> bool override { return m_result; } - template - BinaryExpression - operator != ( RhsT const& rhs ) { - return captureExpression( rhs ); - } + void streamReconstructedExpression( std::ostream &os ) const override { + formatReconstructedExpression + ( os, Catch::Detail::stringify( m_lhs ), m_op, Catch::Detail::stringify( m_rhs ) ); + } - template - BinaryExpression - operator < ( RhsT const& rhs ) { - return captureExpression( rhs ); - } + public: + BinaryExpr( bool comparisonResult, LhsT lhs, StringRef op, RhsT rhs ) + : m_result( comparisonResult ), + m_lhs( lhs ), + m_op( op ), + m_rhs( rhs ) + {} + }; - template - BinaryExpression - operator > ( RhsT const& rhs ) { - return captureExpression( rhs ); - } + template + class UnaryExpr : public ITransientExpression { + LhsT m_lhs; - template - BinaryExpression - operator <= ( RhsT const& rhs ) { - return captureExpression( rhs ); - } + auto isBinaryExpression() const -> bool override { return false; } + auto getResult() const -> bool override { return m_lhs ? true : false; } - template - BinaryExpression - operator >= ( RhsT const& rhs ) { - return captureExpression( rhs ); - } + void streamReconstructedExpression( std::ostream &os ) const override { + os << Catch::Detail::stringify( m_lhs ); + } - BinaryExpression operator == ( bool rhs ) { - return captureExpression( rhs ); - } + public: + UnaryExpr( LhsT lhs ) : m_lhs( lhs ) {} + }; - BinaryExpression operator != ( bool rhs ) { - return captureExpression( rhs ); - } + // Specialised comparison functions to handle equality comparisons between ints and pointers (NULL deduces as an int) + template + auto compareEqual( LhsT const& lhs, RhsT const& rhs ) -> bool { return lhs == rhs; }; + template + auto compareEqual( T* const& lhs, int rhs ) -> bool { return lhs == reinterpret_cast( rhs ); } + template + auto compareEqual( T* const& lhs, long rhs ) -> bool { return lhs == reinterpret_cast( rhs ); } + template + auto compareEqual( int lhs, T* const& rhs ) -> bool { return reinterpret_cast( lhs ) == rhs; } + template + auto compareEqual( long lhs, T* const& rhs ) -> bool { return reinterpret_cast( lhs ) == rhs; } - void endExpression() { - m_truthy = m_lhs ? true : false; - m_rb - .setResultType( m_truthy ) - .endExpression( *this ); - } + template + auto compareNotEqual( LhsT const& lhs, RhsT&& rhs ) -> bool { return lhs != rhs; }; + template + auto compareNotEqual( T* const& lhs, int rhs ) -> bool { return lhs != reinterpret_cast( rhs ); } + template + auto compareNotEqual( T* const& lhs, long rhs ) -> bool { return lhs != reinterpret_cast( rhs ); } + template + auto compareNotEqual( int lhs, T* const& rhs ) -> bool { return reinterpret_cast( lhs ) != rhs; } + template + auto compareNotEqual( long lhs, T* const& rhs ) -> bool { return reinterpret_cast( lhs ) != rhs; } - virtual void reconstructExpression( std::string& dest ) const CATCH_OVERRIDE { - dest = Catch::toString( m_lhs ); - } + template + class ExprLhs { + LhsT m_lhs; + public: + ExprLhs( LhsT lhs ) : m_lhs( lhs ) {} -private: - template - BinaryExpression captureExpression( RhsT& rhs ) const { - return BinaryExpression( m_rb, m_lhs, rhs ); - } + template + auto operator == ( RhsT const& rhs ) -> BinaryExpr const { + return BinaryExpr( compareEqual( m_lhs, rhs ), m_lhs, "==", rhs ); + } + auto operator == ( bool rhs ) -> BinaryExpr const { + return BinaryExpr( m_lhs == rhs, m_lhs, "==", rhs ); + } - template - BinaryExpression captureExpression( bool rhs ) const { - return BinaryExpression( m_rb, m_lhs, rhs ); - } + template + auto operator != ( RhsT const& rhs ) -> BinaryExpr const { + return BinaryExpr( compareNotEqual( m_lhs, rhs ), m_lhs, "!=", rhs ); + } + auto operator != ( bool rhs ) -> BinaryExpr const { + return BinaryExpr( m_lhs != rhs, m_lhs, "!=", rhs ); + } -private: - ResultBuilder& m_rb; - T m_lhs; - bool m_truthy; -}; + template + auto operator > ( RhsT const& rhs ) -> BinaryExpr const { + return BinaryExpr( m_lhs > rhs, m_lhs, ">", rhs ); + } + template + auto operator < ( RhsT const& rhs ) -> BinaryExpr const { + return BinaryExpr( m_lhs < rhs, m_lhs, "<", rhs ); + } + template + auto operator >= ( RhsT const& rhs ) -> BinaryExpr const { + return BinaryExpr( m_lhs >= rhs, m_lhs, ">=", rhs ); + } + template + auto operator <= ( RhsT const& rhs ) -> BinaryExpr const { + return BinaryExpr( m_lhs <= rhs, m_lhs, "<=", rhs ); + } -template -class BinaryExpression : public DecomposedExpression { -public: - BinaryExpression( ResultBuilder& rb, LhsT lhs, RhsT rhs ) - : m_rb( rb ), m_lhs( lhs ), m_rhs( rhs ) {} + auto makeUnaryExpr() const -> UnaryExpr { + return UnaryExpr( m_lhs ); + } + }; - BinaryExpression& operator = ( BinaryExpression& ); - - void endExpression() const { - m_rb - .setResultType( Internal::Evaluator::evaluate( m_lhs, m_rhs ) ) - .endExpression( *this ); - } - - virtual bool isBinaryExpression() const CATCH_OVERRIDE { - return true; - } - - virtual void reconstructExpression( std::string& dest ) const CATCH_OVERRIDE { - std::string lhs = Catch::toString( m_lhs ); - std::string rhs = Catch::toString( m_rhs ); - char delim = lhs.size() + rhs.size() < 40 && - lhs.find('\n') == std::string::npos && - rhs.find('\n') == std::string::npos ? ' ' : '\n'; - dest.reserve( 7 + lhs.size() + rhs.size() ); - // 2 for spaces around operator - // 2 for operator - // 2 for parentheses (conditionally added later) - // 1 for negation (conditionally added later) - dest = lhs; - dest += delim; - dest += Internal::OperatorTraits::getName(); - dest += delim; - dest += rhs; - } - -private: - ResultBuilder& m_rb; - LhsT m_lhs; - RhsT m_rhs; -}; - -template -class MatchExpression : public DecomposedExpression { -public: - MatchExpression( ArgT arg, MatcherT matcher, char const* matcherString ) - : m_arg( arg ), m_matcher( matcher ), m_matcherString( matcherString ) {} - - virtual bool isBinaryExpression() const CATCH_OVERRIDE { - return true; - } - - virtual void reconstructExpression( std::string& dest ) const CATCH_OVERRIDE { - std::string matcherAsString = m_matcher.toString(); - dest = Catch::toString( m_arg ); - dest += ' '; - if( matcherAsString == Detail::unprintableString ) - dest += m_matcherString; - else - dest += matcherAsString; - } - -private: - ArgT m_arg; - MatcherT m_matcher; - char const* m_matcherString; -}; - -} // end namespace Catch - - -namespace Catch { + void handleExpression( ITransientExpression const& expr ); template - ExpressionLhs ResultBuilder::operator <= ( T const& operand ) { - return ExpressionLhs( *this, operand ); + void handleExpression( ExprLhs const& expr ) { + handleExpression( expr.makeUnaryExpr() ); } - inline ExpressionLhs ResultBuilder::operator <= ( bool value ) { - return ExpressionLhs( *this, value ); - } + struct Decomposer { + template + auto operator <= ( T const& lhs ) -> ExprLhs { + return ExprLhs( lhs ); + } + auto operator <=( bool value ) -> ExprLhs { + return ExprLhs( value ); + } + }; - template - void ResultBuilder::captureMatch( ArgT const& arg, MatcherT const& matcher, - char const* matcherString ) { - MatchExpression expr( arg, matcher, matcherString ); - setResultType( matcher.match( arg ) ); - endExpression( expr ); - } +} // end namespace Catch + +#ifdef _MSC_VER +#pragma warning(pop) +#endif + +// end catch_decomposer.h +// start catch_assertioninfo.h + +// start catch_result_type.h + +namespace Catch { + + // ResultWas::OfType enum + struct ResultWas { enum OfType { + Unknown = -1, + Ok = 0, + Info = 1, + Warning = 2, + + FailureBit = 0x10, + + ExpressionFailed = FailureBit | 1, + ExplicitFailure = FailureBit | 2, + + Exception = 0x100 | FailureBit, + + ThrewException = Exception | 1, + DidntThrowException = Exception | 2, + + FatalErrorCondition = 0x200 | FailureBit + + }; }; + + bool isOk( ResultWas::OfType resultType ); + bool isJustInfo( int flags ); + + // ResultDisposition::Flags enum + struct ResultDisposition { enum Flags { + Normal = 0x01, + + ContinueOnFailure = 0x02, // Failures fail test, but execution continues + FalseTest = 0x04, // Prefix expression with ! + SuppressFail = 0x08 // Failures are reported but do not fail the test + }; }; + + ResultDisposition::Flags operator | ( ResultDisposition::Flags lhs, ResultDisposition::Flags rhs ); + + bool shouldContinueOnFailure( int flags ); + bool isFalseTest( int flags ); + bool shouldSuppressFailure( int flags ); + +} // end namespace Catch + +// end catch_result_type.h +namespace Catch { + + struct AssertionInfo + { + StringRef macroName; + SourceLineInfo lineInfo; + StringRef capturedExpression; + ResultDisposition::Flags resultDisposition; + + // We want to delete this constructor but a compiler bug in 4.8 means + // the struct is then treated as non-aggregate + //AssertionInfo() = delete; + }; + +} // end namespace Catch + +// end catch_assertioninfo.h +namespace Catch { + + struct TestFailureException{}; + struct AssertionResultData; + + class LazyExpression { + friend class AssertionHandler; + friend struct AssertionStats; + + ITransientExpression const* m_transientExpression = nullptr; + bool m_isNegated; + public: + LazyExpression( bool isNegated ); + LazyExpression( LazyExpression const& other ); + LazyExpression& operator = ( LazyExpression const& ) = delete; + + explicit operator bool() const; + + friend auto operator << ( std::ostream& os, LazyExpression const& lazyExpr ) -> std::ostream&; + }; + + class AssertionHandler { + AssertionInfo m_assertionInfo; + bool m_shouldDebugBreak = false; + bool m_shouldThrow = false; + bool m_inExceptionGuard = false; + + public: + AssertionHandler + ( StringRef macroName, + SourceLineInfo const& lineInfo, + StringRef capturedExpression, + ResultDisposition::Flags resultDisposition ); + ~AssertionHandler(); + + void handle( ITransientExpression const& expr ); + + template + void handle( ExprLhs const& expr ) { + handle( 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 ); + + auto shouldDebugBreak() const -> bool; + auto allowThrows() const -> bool; + void reactWithDebugBreak() const; + void reactWithoutDebugBreak() const; + void useActiveException(); + void setExceptionGuard(); + void unsetExceptionGuard(); + }; + + void handleExceptionMatchExpr( AssertionHandler& handler, std::string const& str, StringRef matcherString ); } // namespace Catch -// #included from: catch_message.h -#define TWOBLUECUBES_CATCH_MESSAGE_H_INCLUDED +// end catch_assertionhandler.h +// start catch_message.h #include +#include namespace Catch { @@ -1932,27 +1264,33 @@ namespace Catch { ResultWas::OfType _type ); std::string macroName; + std::string message; SourceLineInfo lineInfo; ResultWas::OfType type; - std::string message; unsigned int sequence; - bool operator == ( MessageInfo const& other ) const { - return sequence == other.sequence; - } - bool operator < ( MessageInfo const& other ) const { - return sequence < other.sequence; - } + bool operator == ( MessageInfo const& other ) const; + bool operator < ( MessageInfo const& other ) const; private: static unsigned int globalCount; }; - struct MessageBuilder { + struct MessageStream { + + template + MessageStream& operator << ( T const& value ) { + m_stream << value; + return *this; + } + + // !TBD reuse a global/ thread-local stream + std::ostringstream m_stream; + }; + + struct MessageBuilder : MessageStream { MessageBuilder( std::string const& macroName, SourceLineInfo const& lineInfo, - ResultWas::OfType type ) - : m_info( macroName, lineInfo, type ) - {} + ResultWas::OfType type ); template MessageBuilder& operator << ( T const& value ) { @@ -1961,13 +1299,11 @@ namespace Catch { } MessageInfo m_info; - std::ostringstream m_stream; }; class ScopedMessage { public: ScopedMessage( MessageBuilder const& builder ); - ScopedMessage( ScopedMessage const& other ); ~ScopedMessage(); MessageInfo m_info; @@ -1975,31 +1311,36 @@ namespace Catch { } // end namespace Catch -// #included from: catch_interfaces_capture.h -#define TWOBLUECUBES_CATCH_INTERFACES_CAPTURE_H_INCLUDED +// end catch_message.h +// start catch_interfaces_capture.h #include namespace Catch { - class TestCase; class AssertionResult; struct AssertionInfo; struct SectionInfo; struct SectionEndInfo; struct MessageInfo; - class ScopedMessageBuilder; struct Counts; + struct BenchmarkInfo; + struct BenchmarkStats; 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; virtual void sectionEndedEarly( SectionEndInfo const& endInfo ) = 0; + + virtual void benchmarkStarting( BenchmarkInfo const& info ) = 0; + virtual void benchmarkEnded( BenchmarkStats const& stats ) = 0; + virtual void pushScopedMessage( MessageInfo const& message ) = 0; virtual void popScopedMessage( MessageInfo const& message ) = 0; @@ -2008,7 +1349,7 @@ namespace Catch { virtual void exceptionEarlyReported() = 0; - virtual void handleFatalErrorCondition( std::string const& message ) = 0; + virtual void handleFatalErrorCondition( StringRef message ) = 0; virtual bool lastAssertionPassed() = 0; virtual void assertionPassed() = 0; @@ -2018,47 +1359,16 @@ namespace Catch { IResultCapture& getResultCapture(); } -// #included from: catch_debugger.h -#define TWOBLUECUBES_CATCH_DEBUGGER_H_INCLUDED - -// #included from: catch_platform.h -#define TWOBLUECUBES_CATCH_PLATFORM_H_INCLUDED - -#if defined(__MAC_OS_X_VERSION_MIN_REQUIRED) -# define CATCH_PLATFORM_MAC -#elif defined(__IPHONE_OS_VERSION_MIN_REQUIRED) -# define CATCH_PLATFORM_IPHONE -#elif defined(linux) || defined(__linux) || defined(__linux__) -# define CATCH_PLATFORM_LINUX -#elif defined(WIN32) || defined(__WIN32__) || defined(_WIN32) || defined(_MSC_VER) -# define CATCH_PLATFORM_WINDOWS -# if !defined(NOMINMAX) && !defined(CATCH_CONFIG_NO_NOMINMAX) -# define CATCH_DEFINES_NOMINMAX -# endif -# if !defined(WIN32_LEAN_AND_MEAN) && !defined(CATCH_CONFIG_NO_WIN32_LEAN_AND_MEAN) -# define CATCH_DEFINES_WIN32_LEAN_AND_MEAN -# endif -#endif - -#include - -namespace Catch{ +// end catch_interfaces_capture.h +// start catch_debugger.h +namespace Catch { bool isDebuggerActive(); - void writeToDebugConsole( std::string const& text ); } #ifdef CATCH_PLATFORM_MAC - // The following code snippet based on: - // http://cocoawithlove.com/2008/03/break-into-debugger.html - #if defined(__ppc64__) || defined(__ppc__) - #define CATCH_TRAP() \ - __asm__("li r0, 20\nsc\nnop\nli r0, 37\nli r4, 2\nsc\nnop\n" \ - : : : "memory","r0","r3","r4" ) /* NOLINT */ - #else - #define CATCH_TRAP() __asm__("int $3\n" : : /* NOLINT */ ) - #endif + #define CATCH_TRAP() __asm__("int $3\n" : : ) /* NOLINT */ #elif defined(CATCH_PLATFORM_LINUX) // If we can use inline assembler, do it because this allows us to break @@ -2084,22 +1394,13 @@ namespace Catch{ #define CATCH_BREAK_INTO_DEBUGGER() Catch::alwaysTrue(); #endif -// #included from: catch_interfaces_runner.h -#define TWOBLUECUBES_CATCH_INTERFACES_RUNNER_H_INCLUDED - -namespace Catch { - class TestCase; - - struct IRunner { - virtual ~IRunner(); - virtual bool aborting() const = 0; - }; -} +// end catch_debugger.h +#if !defined(CATCH_CONFIG_DISABLE) #if !defined(CATCH_CONFIG_DISABLE_STRINGIFICATION) -# define CATCH_INTERNAL_STRINGIFY(expr) #expr + #define CATCH_INTERNAL_STRINGIFY(...) #__VA_ARGS__ #else -# define CATCH_INTERNAL_STRINGIFY(expr) "Disabled by CATCH_CONFIG_DISABLE_STRINGIFICATION" + #define CATCH_INTERNAL_STRINGIFY(...) "Disabled by CATCH_CONFIG_DISABLE_STRINGIFICATION" #endif #if defined(CATCH_CONFIG_FAST_COMPILE) @@ -2107,234 +1408,176 @@ namespace Catch { // 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( resultBuilder ) \ - resultBuilder.react(); +#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_TEST_NO_TRY( macroName, resultDisposition, expr ) \ - do { \ - Catch::ResultBuilder __catchResult( macroName, CATCH_INTERNAL_LINEINFO, CATCH_INTERNAL_STRINGIFY(expr), resultDisposition ); \ - __catchResult.setExceptionGuard(); \ - CATCH_INTERNAL_SUPPRESS_PARENTHESES_WARNINGS \ - ( __catchResult <= expr ).endExpression(); \ - CATCH_INTERNAL_UNSUPPRESS_PARENTHESES_WARNINGS \ - __catchResult.unsetExceptionGuard(); \ - INTERNAL_CATCH_REACT( __catchResult ) \ - } while( Catch::isTrue( false && static_cast( !!(expr) ) ) ) // expr 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 &&. +#define INTERNAL_CATCH_TRY( capturer ) capturer.setExceptionGuard(); +#define INTERNAL_CATCH_CATCH( capturer ) capturer.unsetExceptionGuard(); -#define INTERNAL_CHECK_THAT_NO_TRY( macroName, matcher, resultDisposition, arg ) \ - do { \ - Catch::ResultBuilder __catchResult( macroName, CATCH_INTERNAL_LINEINFO, CATCH_INTERNAL_STRINGIFY(arg) ", " CATCH_INTERNAL_STRINGIFY(matcher), resultDisposition ); \ - __catchResult.setExceptionGuard(); \ - __catchResult.captureMatch( arg, matcher, CATCH_INTERNAL_STRINGIFY(matcher) ); \ - __catchResult.unsetExceptionGuard(); \ - INTERNAL_CATCH_REACT( __catchResult ) \ - } while( Catch::alwaysFalse() ) +#else // CATCH_CONFIG_FAST_COMPILE -#else /////////////////////////////////////////////////////////////////////////////// // 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( resultBuilder ) \ - if( resultBuilder.shouldDebugBreak() ) CATCH_BREAK_INTO_DEBUGGER(); \ - resultBuilder.react(); +#define INTERNAL_CATCH_REACT( handler ) \ + if( handler.shouldDebugBreak() ) CATCH_BREAK_INTO_DEBUGGER(); \ + handler.reactWithoutDebugBreak(); + +#define INTERNAL_CATCH_TRY( capturer ) try +#define INTERNAL_CATCH_CATCH( capturer ) catch(...) { capturer.useActiveException(); } + #endif /////////////////////////////////////////////////////////////////////////////// -#define INTERNAL_CATCH_TEST( macroName, resultDisposition, expr ) \ +#define INTERNAL_CATCH_TEST( macroName, resultDisposition, ... ) \ do { \ - Catch::ResultBuilder __catchResult( macroName, CATCH_INTERNAL_LINEINFO, CATCH_INTERNAL_STRINGIFY(expr), resultDisposition ); \ - try { \ + Catch::AssertionHandler catchAssertionHandler( macroName, CATCH_INTERNAL_LINEINFO, CATCH_INTERNAL_STRINGIFY(__VA_ARGS__), resultDisposition ); \ + INTERNAL_CATCH_TRY( catchAssertionHandler ) { \ CATCH_INTERNAL_SUPPRESS_PARENTHESES_WARNINGS \ - ( __catchResult <= expr ).endExpression(); \ + catchAssertionHandler.handle( Catch::Decomposer() <= __VA_ARGS__ ); \ CATCH_INTERNAL_UNSUPPRESS_PARENTHESES_WARNINGS \ - } \ - catch( ... ) { \ - __catchResult.useActiveException( resultDisposition ); \ - } \ - INTERNAL_CATCH_REACT( __catchResult ) \ - } while( Catch::isTrue( false && static_cast( !!(expr) ) ) ) // expr here is never evaluated at runtime but it forces the compiler to give it a look + } 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 // The double negation silences MSVC's C4800 warning, the static_cast forces short-circuit evaluation if the type has overloaded &&. /////////////////////////////////////////////////////////////////////////////// -#define INTERNAL_CATCH_IF( macroName, resultDisposition, expr ) \ - INTERNAL_CATCH_TEST( macroName, resultDisposition, expr ); \ +#define INTERNAL_CATCH_IF( macroName, resultDisposition, ... ) \ + INTERNAL_CATCH_TEST( macroName, resultDisposition, __VA_ARGS__ ); \ if( Catch::getResultCapture().lastAssertionPassed() ) /////////////////////////////////////////////////////////////////////////////// -#define INTERNAL_CATCH_ELSE( macroName, resultDisposition, expr ) \ - INTERNAL_CATCH_TEST( macroName, resultDisposition, expr ); \ +#define INTERNAL_CATCH_ELSE( macroName, resultDisposition, ... ) \ + INTERNAL_CATCH_TEST( macroName, resultDisposition, __VA_ARGS__ ); \ if( !Catch::getResultCapture().lastAssertionPassed() ) /////////////////////////////////////////////////////////////////////////////// -#define INTERNAL_CATCH_NO_THROW( macroName, resultDisposition, expr ) \ +#define INTERNAL_CATCH_NO_THROW( macroName, resultDisposition, ... ) \ do { \ - Catch::ResultBuilder __catchResult( macroName, CATCH_INTERNAL_LINEINFO, CATCH_INTERNAL_STRINGIFY(expr), resultDisposition ); \ + Catch::AssertionHandler catchAssertionHandler( macroName, CATCH_INTERNAL_LINEINFO, CATCH_INTERNAL_STRINGIFY(__VA_ARGS__), resultDisposition ); \ try { \ - static_cast(expr); \ - __catchResult.captureResult( Catch::ResultWas::Ok ); \ + static_cast(__VA_ARGS__); \ + catchAssertionHandler.handle( Catch::ResultWas::Ok ); \ } \ catch( ... ) { \ - __catchResult.useActiveException( resultDisposition ); \ + catchAssertionHandler.useActiveException(); \ } \ - INTERNAL_CATCH_REACT( __catchResult ) \ + INTERNAL_CATCH_REACT( catchAssertionHandler ) \ } while( Catch::alwaysFalse() ) /////////////////////////////////////////////////////////////////////////////// -#define INTERNAL_CATCH_THROWS( macroName, resultDisposition, matcher, expr ) \ +#define INTERNAL_CATCH_THROWS( macroName, resultDisposition, ... ) \ do { \ - Catch::ResultBuilder __catchResult( macroName, CATCH_INTERNAL_LINEINFO, CATCH_INTERNAL_STRINGIFY(expr), resultDisposition, CATCH_INTERNAL_STRINGIFY(matcher) ); \ - if( __catchResult.allowThrows() ) \ + Catch::AssertionHandler catchAssertionHandler( macroName, CATCH_INTERNAL_LINEINFO, CATCH_INTERNAL_STRINGIFY(__VA_ARGS__), resultDisposition); \ + if( catchAssertionHandler.allowThrows() ) \ try { \ - static_cast(expr); \ - __catchResult.captureResult( Catch::ResultWas::DidntThrowException ); \ + static_cast(__VA_ARGS__); \ + catchAssertionHandler.handle( Catch::ResultWas::DidntThrowException ); \ } \ catch( ... ) { \ - __catchResult.captureExpectedException( matcher ); \ + catchAssertionHandler.handle( Catch::ResultWas::Ok ); \ } \ else \ - __catchResult.captureResult( Catch::ResultWas::Ok ); \ - INTERNAL_CATCH_REACT( __catchResult ) \ + catchAssertionHandler.handle( Catch::ResultWas::Ok ); \ + INTERNAL_CATCH_REACT( catchAssertionHandler ) \ } while( Catch::alwaysFalse() ) /////////////////////////////////////////////////////////////////////////////// #define INTERNAL_CATCH_THROWS_AS( macroName, exceptionType, resultDisposition, expr ) \ do { \ - Catch::ResultBuilder __catchResult( macroName, CATCH_INTERNAL_LINEINFO, CATCH_INTERNAL_STRINGIFY(expr) ", " CATCH_INTERNAL_STRINGIFY(exceptionType), resultDisposition ); \ - if( __catchResult.allowThrows() ) \ + Catch::AssertionHandler catchAssertionHandler( macroName, CATCH_INTERNAL_LINEINFO, CATCH_INTERNAL_STRINGIFY(expr) ", " CATCH_INTERNAL_STRINGIFY(exceptionType), resultDisposition ); \ + if( catchAssertionHandler.allowThrows() ) \ try { \ static_cast(expr); \ - __catchResult.captureResult( Catch::ResultWas::DidntThrowException ); \ + catchAssertionHandler.handle( Catch::ResultWas::DidntThrowException ); \ } \ - catch( exceptionType ) { \ - __catchResult.captureResult( Catch::ResultWas::Ok ); \ + catch( exceptionType const& ) { \ + catchAssertionHandler.handle( Catch::ResultWas::Ok ); \ } \ catch( ... ) { \ - __catchResult.useActiveException( resultDisposition ); \ + catchAssertionHandler.useActiveException(); \ } \ else \ - __catchResult.captureResult( Catch::ResultWas::Ok ); \ - INTERNAL_CATCH_REACT( __catchResult ) \ + catchAssertionHandler.handle( Catch::ResultWas::Ok ); \ + INTERNAL_CATCH_REACT( catchAssertionHandler ) \ } while( Catch::alwaysFalse() ) /////////////////////////////////////////////////////////////////////////////// -#ifdef CATCH_CONFIG_VARIADIC_MACROS - #define INTERNAL_CATCH_MSG( macroName, messageType, resultDisposition, ... ) \ - do { \ - Catch::ResultBuilder __catchResult( macroName, CATCH_INTERNAL_LINEINFO, "", resultDisposition ); \ - __catchResult << __VA_ARGS__ + ::Catch::StreamEndStop(); \ - __catchResult.captureResult( messageType ); \ - INTERNAL_CATCH_REACT( __catchResult ) \ - } while( Catch::alwaysFalse() ) -#else - #define INTERNAL_CATCH_MSG( macroName, messageType, resultDisposition, log ) \ - do { \ - Catch::ResultBuilder __catchResult( macroName, CATCH_INTERNAL_LINEINFO, "", resultDisposition ); \ - __catchResult << log + ::Catch::StreamEndStop(); \ - __catchResult.captureResult( messageType ); \ - INTERNAL_CATCH_REACT( __catchResult ) \ - } while( Catch::alwaysFalse() ) -#endif +#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() ); \ + INTERNAL_CATCH_REACT( catchAssertionHandler ) \ + } while( Catch::alwaysFalse() ) /////////////////////////////////////////////////////////////////////////////// #define INTERNAL_CATCH_INFO( macroName, log ) \ Catch::ScopedMessage INTERNAL_CATCH_UNIQUE_NAME( scopedMessage ) = Catch::MessageBuilder( macroName, CATCH_INTERNAL_LINEINFO, Catch::ResultWas::Info ) << log; /////////////////////////////////////////////////////////////////////////////// -#define INTERNAL_CHECK_THAT( macroName, matcher, resultDisposition, arg ) \ +// Although this is matcher-based, it can be used with just a string +#define INTERNAL_CATCH_THROWS_STR_MATCHES( macroName, resultDisposition, matcher, ... ) \ do { \ - Catch::ResultBuilder __catchResult( macroName, CATCH_INTERNAL_LINEINFO, CATCH_INTERNAL_STRINGIFY(arg) ", " CATCH_INTERNAL_STRINGIFY(matcher), resultDisposition ); \ - try { \ - __catchResult.captureMatch( arg, matcher, CATCH_INTERNAL_STRINGIFY(matcher) ); \ - } catch( ... ) { \ - __catchResult.useActiveException( resultDisposition | Catch::ResultDisposition::ContinueOnFailure ); \ - } \ - INTERNAL_CATCH_REACT( __catchResult ) \ + Catch::AssertionHandler catchAssertionHandler( macroName, CATCH_INTERNAL_LINEINFO, CATCH_INTERNAL_STRINGIFY(__VA_ARGS__) ", " CATCH_INTERNAL_STRINGIFY(matcher), resultDisposition ); \ + if( catchAssertionHandler.allowThrows() ) \ + try { \ + static_cast(__VA_ARGS__); \ + catchAssertionHandler.handle( Catch::ResultWas::DidntThrowException ); \ + } \ + catch( ... ) { \ + handleExceptionMatchExpr( catchAssertionHandler, matcher, #matcher ); \ + } \ + else \ + catchAssertionHandler.handle( Catch::ResultWas::Ok ); \ + INTERNAL_CATCH_REACT( catchAssertionHandler ) \ } while( Catch::alwaysFalse() ) -// #included from: internal/catch_section.h -#define TWOBLUECUBES_CATCH_SECTION_H_INCLUDED +#endif // CATCH_CONFIG_DISABLE -// #included from: catch_section_info.h -#define TWOBLUECUBES_CATCH_SECTION_INFO_H_INCLUDED +// end catch_capture.hpp +// start catch_section.h -// #included from: catch_totals.hpp -#define TWOBLUECUBES_CATCH_TOTALS_HPP_INCLUDED +// start catch_section_info.h + +// start catch_totals.h #include namespace Catch { struct Counts { - Counts() : passed( 0 ), failed( 0 ), failedButOk( 0 ) {} + Counts operator - ( Counts const& other ) const; + Counts& operator += ( Counts const& other ); - Counts operator - ( Counts const& other ) const { - Counts diff; - diff.passed = passed - other.passed; - diff.failed = failed - other.failed; - diff.failedButOk = failedButOk - other.failedButOk; - return diff; - } - Counts& operator += ( Counts const& other ) { - passed += other.passed; - failed += other.failed; - failedButOk += other.failedButOk; - return *this; - } + std::size_t total() const; + bool allPassed() const; + bool allOk() const; - std::size_t total() const { - return passed + failed + failedButOk; - } - bool allPassed() const { - return failed == 0 && failedButOk == 0; - } - bool allOk() const { - return failed == 0; - } - - std::size_t passed; - std::size_t failed; - std::size_t failedButOk; + std::size_t passed = 0; + std::size_t failed = 0; + std::size_t failedButOk = 0; }; struct Totals { - Totals operator - ( Totals const& other ) const { - Totals diff; - diff.assertions = assertions - other.assertions; - diff.testCases = testCases - other.testCases; - return diff; - } + Totals operator - ( Totals const& other ) const; + Totals& operator += ( Totals const& other ); - Totals delta( Totals const& prevTotals ) const { - Totals diff = *this - prevTotals; - if( diff.assertions.failed > 0 ) - ++diff.testCases.failed; - else if( diff.assertions.failedButOk > 0 ) - ++diff.testCases.failedButOk; - else - ++diff.testCases.passed; - return diff; - } - - Totals& operator += ( Totals const& other ) { - assertions += other.assertions; - testCases += other.testCases; - return *this; - } + Totals delta( Totals const& prevTotals ) const; Counts assertions; Counts testCases; }; } +// end catch_totals.h #include namespace Catch { @@ -2351,9 +1594,7 @@ namespace Catch { }; struct SectionEndInfo { - SectionEndInfo( SectionInfo const& _sectionInfo, Counts const& _prevAssertions, double _durationInSeconds ) - : sectionInfo( _sectionInfo ), prevAssertions( _prevAssertions ), durationInSeconds( _durationInSeconds ) - {} + SectionEndInfo( SectionInfo const& _sectionInfo, Counts const& _prevAssertions, double _durationInSeconds ); SectionInfo sectionInfo; Counts prevAssertions; @@ -2362,36 +1603,29 @@ namespace Catch { } // end namespace Catch -// #included from: catch_timer.h -#define TWOBLUECUBES_CATCH_TIMER_H_INCLUDED +// end catch_section_info.h +// start catch_timer.h -#ifdef _MSC_VER +#include namespace Catch { - typedef unsigned long long UInt64; -} -#else -#include -namespace Catch { - typedef uint64_t UInt64; -} -#endif -namespace Catch { + auto getCurrentNanosecondsSinceEpoch() -> uint64_t; + auto getEstimatedClockResolution() -> uint64_t; + class Timer { + uint64_t m_nanoseconds = 0; public: - Timer() : m_ticks( 0 ) {} void start(); - unsigned int getElapsedMicroseconds() const; - unsigned int getElapsedMilliseconds() const; - double getElapsedSeconds() const; - - private: - UInt64 m_ticks; + auto getElapsedNanoseconds() const -> unsigned int; + auto getElapsedMicroseconds() const -> unsigned int; + auto getElapsedMilliseconds() const -> unsigned int; + auto getElapsedSeconds() const -> double; }; } // namespace Catch +// end catch_timer.h #include namespace Catch { @@ -2402,7 +1636,7 @@ namespace Catch { ~Section(); // This indicates whether the section should be executed or not - operator bool() const; + explicit operator bool() const; private: SectionInfo m_info; @@ -2415,203 +1649,62 @@ namespace Catch { } // end namespace Catch -#ifdef CATCH_CONFIG_VARIADIC_MACROS #define INTERNAL_CATCH_SECTION( ... ) \ if( Catch::Section const& INTERNAL_CATCH_UNIQUE_NAME( catch_internal_Section ) = Catch::SectionInfo( CATCH_INTERNAL_LINEINFO, __VA_ARGS__ ) ) -#else - #define INTERNAL_CATCH_SECTION( name, desc ) \ - if( Catch::Section const& INTERNAL_CATCH_UNIQUE_NAME( catch_internal_Section ) = Catch::SectionInfo( CATCH_INTERNAL_LINEINFO, name, desc ) ) -#endif -// #included from: internal/catch_generators.hpp -#define TWOBLUECUBES_CATCH_GENERATORS_HPP_INCLUDED +// end catch_section.h +// start catch_benchmark.h -#include +#include #include -#include namespace Catch { -template -struct IGenerator { - virtual ~IGenerator() {} - virtual T getValue( std::size_t index ) const = 0; - virtual std::size_t size () const = 0; -}; + class BenchmarkLooper { -template -class BetweenGenerator : public IGenerator { -public: - BetweenGenerator( T from, T to ) : m_from( from ), m_to( to ){} + std::string m_name; + std::size_t m_count = 0; + std::size_t m_iterationsToRun = 1; + uint64_t m_resolution; + Timer m_timer; - virtual T getValue( std::size_t index ) const { - return m_from+static_cast( index ); - } - - virtual std::size_t size() const { - return static_cast( 1+m_to-m_from ); - } - -private: - - T m_from; - T m_to; -}; - -template -class ValuesGenerator : public IGenerator { -public: - ValuesGenerator(){} - - void add( T value ) { - m_values.push_back( value ); - } - - virtual T getValue( std::size_t index ) const { - return m_values[index]; - } - - virtual std::size_t size() const { - return m_values.size(); - } - -private: - std::vector m_values; -}; - -template -class CompositeGenerator { -public: - CompositeGenerator() : m_totalSize( 0 ) {} - - // *** Move semantics, similar to auto_ptr *** - CompositeGenerator( CompositeGenerator& other ) - : m_fileInfo( other.m_fileInfo ), - m_totalSize( 0 ) - { - move( other ); - } - - CompositeGenerator& setFileInfo( const char* fileInfo ) { - m_fileInfo = fileInfo; - return *this; - } - - ~CompositeGenerator() { - deleteAll( m_composed ); - } - - operator T () const { - size_t overallIndex = getCurrentContext().getGeneratorIndex( m_fileInfo, m_totalSize ); - - typename std::vector*>::const_iterator it = m_composed.begin(); - typename std::vector*>::const_iterator itEnd = m_composed.end(); - for( size_t index = 0; it != itEnd; ++it ) + static auto getResolution() -> uint64_t; + public: + // Keep most of this inline as it's on the code path that is being timed + BenchmarkLooper( StringRef name ) + : m_name( name ), + m_resolution( getResolution() ) { - const IGenerator* generator = *it; - if( overallIndex >= index && overallIndex < index + generator->size() ) - { - return generator->getValue( overallIndex-index ); - } - index += generator->size(); + reportStart(); + m_timer.start(); } - CATCH_INTERNAL_ERROR( "Indexed past end of generated range" ); - return T(); // Suppress spurious "not all control paths return a value" warning in Visual Studio - if you know how to fix this please do so - } - void add( const IGenerator* generator ) { - m_totalSize += generator->size(); - m_composed.push_back( generator ); - } + explicit operator bool() { + if( m_count < m_iterationsToRun ) + return true; + return needsMoreIterations(); + } - CompositeGenerator& then( CompositeGenerator& other ) { - move( other ); - return *this; - } + void increment() { + ++m_count; + } - CompositeGenerator& then( T value ) { - ValuesGenerator* valuesGen = new ValuesGenerator(); - valuesGen->add( value ); - add( valuesGen ); - return *this; - } - -private: - - void move( CompositeGenerator& other ) { - m_composed.insert( m_composed.end(), other.m_composed.begin(), other.m_composed.end() ); - m_totalSize += other.m_totalSize; - other.m_composed.clear(); - } - - std::vector*> m_composed; - std::string m_fileInfo; - size_t m_totalSize; -}; - -namespace Generators -{ - template - CompositeGenerator between( T from, T to ) { - CompositeGenerator generators; - generators.add( new BetweenGenerator( from, to ) ); - return generators; - } - - template - CompositeGenerator values( T val1, T val2 ) { - CompositeGenerator generators; - ValuesGenerator* valuesGen = new ValuesGenerator(); - valuesGen->add( val1 ); - valuesGen->add( val2 ); - generators.add( valuesGen ); - return generators; - } - - template - CompositeGenerator values( T val1, T val2, T val3 ){ - CompositeGenerator generators; - ValuesGenerator* valuesGen = new ValuesGenerator(); - valuesGen->add( val1 ); - valuesGen->add( val2 ); - valuesGen->add( val3 ); - generators.add( valuesGen ); - return generators; - } - - template - CompositeGenerator values( T val1, T val2, T val3, T val4 ) { - CompositeGenerator generators; - ValuesGenerator* valuesGen = new ValuesGenerator(); - valuesGen->add( val1 ); - valuesGen->add( val2 ); - valuesGen->add( val3 ); - valuesGen->add( val4 ); - generators.add( valuesGen ); - return generators; - } - -} // end namespace Generators - -using namespace Generators; + void reportStart(); + auto needsMoreIterations() -> bool; + }; } // end namespace Catch -#define INTERNAL_CATCH_LINESTR2( line ) #line -#define INTERNAL_CATCH_LINESTR( line ) INTERNAL_CATCH_LINESTR2( line ) +#define BENCHMARK( name ) \ + for( Catch::BenchmarkLooper looper( name ); looper; looper.increment() ) -#define INTERNAL_CATCH_GENERATE( expr ) expr.setFileInfo( __FILE__ "(" INTERNAL_CATCH_LINESTR( __LINE__ ) ")" ) +// end catch_benchmark.h +// start catch_interfaces_exception.h -// #included from: internal/catch_interfaces_exception.h -#define TWOBLUECUBES_CATCH_INTERFACES_EXCEPTION_H_INCLUDED - -#include -#include - -// #included from: catch_interfaces_registry_hub.h -#define TWOBLUECUBES_CATCH_INTERFACES_REGISTRY_HUB_H_INCLUDED +// start catch_interfaces_registry_hub.h #include +#include namespace Catch { @@ -2622,6 +1715,9 @@ namespace Catch { struct IReporterRegistry; struct IReporterFactory; struct ITagAliasRegistry; + class StartupExceptionRegistry; + + using IReporterFactoryPtr = std::shared_ptr; struct IRegistryHub { virtual ~IRegistryHub(); @@ -2631,15 +1727,18 @@ namespace Catch { virtual ITagAliasRegistry const& getTagAliasRegistry() const = 0; virtual IExceptionTranslatorRegistry& getExceptionTranslatorRegistry() = 0; + + virtual StartupExceptionRegistry const& getStartupExceptionRegistry() const = 0; }; struct IMutableRegistryHub { virtual ~IMutableRegistryHub(); - virtual void registerReporter( std::string const& name, Ptr const& factory ) = 0; - virtual void registerListener( Ptr const& factory ) = 0; + virtual void registerReporter( std::string const& name, IReporterFactoryPtr const& factory ) = 0; + virtual void registerListener( IReporterFactoryPtr const& factory ) = 0; virtual void registerTest( TestCase const& testInfo ) = 0; virtual void registerTranslator( const IExceptionTranslator* translator ) = 0; virtual void registerTagAlias( std::string const& alias, std::string const& tag, SourceLineInfo const& lineInfo ) = 0; + virtual void registerStartupException() noexcept = 0; }; IRegistryHub& getRegistryHub(); @@ -2649,12 +1748,21 @@ namespace Catch { } -namespace Catch { +// end catch_interfaces_registry_hub.h +#if defined(CATCH_CONFIG_DISABLE) + #define INTERNAL_CATCH_TRANSLATE_EXCEPTION_NO_REG( translatorName, signature) \ + static std::string translatorName( signature ) +#endif - typedef std::string(*exceptionTranslateFunction)(); +#include +#include +#include + +namespace Catch { + using exceptionTranslateFunction = std::string(*)(); struct IExceptionTranslator; - typedef std::vector ExceptionTranslators; + using ExceptionTranslators = std::vector>; struct IExceptionTranslator { virtual ~IExceptionTranslator(); @@ -2676,10 +1784,10 @@ namespace Catch { : m_translateFunction( translateFunction ) {} - virtual std::string translate( ExceptionTranslators::const_iterator it, ExceptionTranslators::const_iterator itEnd ) const CATCH_OVERRIDE { + std::string translate( ExceptionTranslators::const_iterator it, ExceptionTranslators::const_iterator itEnd ) const override { try { if( it == itEnd ) - throw; + std::rethrow_exception(std::current_exception()); else return (*it)->translate( it+1, itEnd ); } @@ -2709,36 +1817,40 @@ namespace Catch { #define INTERNAL_CATCH_TRANSLATE_EXCEPTION( signature ) INTERNAL_CATCH_TRANSLATE_EXCEPTION2( INTERNAL_CATCH_UNIQUE_NAME( catch_internal_ExceptionTranslator ), signature ) -// #included from: internal/catch_approx.hpp -#define TWOBLUECUBES_CATCH_APPROX_HPP_INCLUDED +// end catch_interfaces_exception.h +// start catch_approx.h -#include -#include +// start catch_enforce.h -#if defined(CATCH_CONFIG_CPP11_TYPE_TRAITS) +#include +#include + +#define CATCH_PREPARE_EXCEPTION( type, msg ) \ + type( static_cast( std::ostringstream() << msg ).str() ) +#define CATCH_INTERNAL_ERROR( msg ) \ + throw CATCH_PREPARE_EXCEPTION( std::logic_error, CATCH_INTERNAL_LINEINFO << ": Internal Catch error: " << msg); +#define CATCH_ERROR( msg ) \ + throw CATCH_PREPARE_EXCEPTION( std::domain_error, msg ) +#define CATCH_ENFORCE( condition, msg ) \ + do{ if( !(condition) ) CATCH_ERROR( msg ); } while(false) + +// end catch_enforce.h #include -#endif namespace Catch { namespace Detail { class Approx { + private: + bool equalityComparisonImpl(double other) const; + public: - explicit Approx ( double value ) - : m_epsilon( std::numeric_limits::epsilon()*100 ), - m_margin( 0.0 ), - m_scale( 1.0 ), - m_value( value ) - {} + explicit Approx ( double value ); - static Approx custom() { - return Approx( 0 ); - } - -#if defined(CATCH_CONFIG_CPP11_TYPE_TRAITS) + static Approx custom(); template ::value>::type> - Approx operator()( T value ) { + Approx operator()( T const& value ) { Approx approx( static_cast(value) ); approx.epsilon( m_epsilon ); approx.margin( m_margin ); @@ -2747,19 +1859,13 @@ namespace Detail { } template ::value>::type> - explicit Approx( T value ): Approx(static_cast(value)) + explicit Approx( T const& value ): Approx(static_cast(value)) {} template ::value>::type> friend bool operator == ( const T& lhs, Approx const& rhs ) { - // Thanks to Richard Harris for his help refining this formula - auto lhs_v = double(lhs); - bool relativeOK = std::fabs(lhs_v - rhs.m_value) < rhs.m_epsilon * (rhs.m_scale + (std::max)(std::fabs(lhs_v), std::fabs(rhs.m_value))); - if (relativeOK) { - return true; - } - - return std::fabs(lhs_v - rhs.m_value) <= rhs.m_margin; + auto lhs_v = static_cast(lhs); + return rhs.equalityComparisonImpl(lhs_v); } template ::value>::type> @@ -2768,121 +1874,62 @@ namespace Detail { } template ::value>::type> - friend bool operator != ( T lhs, Approx const& rhs ) { + friend bool operator != ( T const& lhs, Approx const& rhs ) { return !operator==( lhs, rhs ); } template ::value>::type> - friend bool operator != ( Approx const& lhs, T rhs ) { + friend bool operator != ( Approx const& lhs, T const& rhs ) { return !operator==( rhs, lhs ); } template ::value>::type> - friend bool operator <= ( T lhs, Approx const& rhs ) { - return double(lhs) < rhs.m_value || lhs == rhs; + friend bool operator <= ( T const& lhs, Approx const& rhs ) { + return static_cast(lhs) < rhs.m_value || lhs == rhs; } template ::value>::type> - friend bool operator <= ( Approx const& lhs, T rhs ) { - return lhs.m_value < double(rhs) || lhs == rhs; + friend bool operator <= ( Approx const& lhs, T const& rhs ) { + return lhs.m_value < static_cast(rhs) || lhs == rhs; } template ::value>::type> - friend bool operator >= ( T lhs, Approx const& rhs ) { - return double(lhs) > rhs.m_value || lhs == rhs; + friend bool operator >= ( T const& lhs, Approx const& rhs ) { + return static_cast(lhs) > rhs.m_value || lhs == rhs; } template ::value>::type> - friend bool operator >= ( Approx const& lhs, T rhs ) { - return lhs.m_value > double(rhs) || lhs == rhs; + friend bool operator >= ( Approx const& lhs, T const& rhs ) { + return lhs.m_value > static_cast(rhs) || lhs == rhs; } template ::value>::type> - Approx& epsilon( T newEpsilon ) { - m_epsilon = double(newEpsilon); + Approx& epsilon( T const& newEpsilon ) { + double epsilonAsDouble = static_cast(newEpsilon); + CATCH_ENFORCE(epsilonAsDouble >= 0 && epsilonAsDouble <= 1.0, + "Invalid Approx::epsilon: " << epsilonAsDouble + << ", Approx::epsilon has to be between 0 and 1"); + m_epsilon = epsilonAsDouble; return *this; } template ::value>::type> - Approx& margin( T newMargin ) { - m_margin = double(newMargin); + Approx& margin( T const& newMargin ) { + double marginAsDouble = static_cast(newMargin); + CATCH_ENFORCE(marginAsDouble >= 0, + "Invalid Approx::margin: " << marginAsDouble + << ", Approx::Margin has to be non-negative."); + m_margin = marginAsDouble; return *this; } template ::value>::type> - Approx& scale( T newScale ) { - m_scale = double(newScale); + Approx& scale( T const& newScale ) { + m_scale = static_cast(newScale); return *this; } -#else - - Approx operator()( double value ) { - Approx approx( value ); - approx.epsilon( m_epsilon ); - approx.margin( m_margin ); - approx.scale( m_scale ); - return approx; - } - - friend bool operator == ( double lhs, Approx const& rhs ) { - // Thanks to Richard Harris for his help refining this formula - bool relativeOK = std::fabs( lhs - rhs.m_value ) < rhs.m_epsilon * (rhs.m_scale + (std::max)( std::fabs(lhs), std::fabs(rhs.m_value) ) ); - if (relativeOK) { - return true; - } - return std::fabs(lhs - rhs.m_value) <= rhs.m_margin; - } - - friend bool operator == ( Approx const& lhs, double rhs ) { - return operator==( rhs, lhs ); - } - - friend bool operator != ( double lhs, Approx const& rhs ) { - return !operator==( lhs, rhs ); - } - - friend bool operator != ( Approx const& lhs, double rhs ) { - return !operator==( rhs, lhs ); - } - - friend bool operator <= ( double lhs, Approx const& rhs ) { - return lhs < rhs.m_value || lhs == rhs; - } - - friend bool operator <= ( Approx const& lhs, double rhs ) { - return lhs.m_value < rhs || lhs == rhs; - } - - friend bool operator >= ( double lhs, Approx const& rhs ) { - return lhs > rhs.m_value || lhs == rhs; - } - - friend bool operator >= ( Approx const& lhs, double rhs ) { - return lhs.m_value > rhs || lhs == rhs; - } - - Approx& epsilon( double newEpsilon ) { - m_epsilon = newEpsilon; - return *this; - } - - Approx& margin( double newMargin ) { - m_margin = newMargin; - return *this; - } - - Approx& scale( double newScale ) { - m_scale = newScale; - return *this; - } -#endif - - std::string toString() const { - std::ostringstream oss; - oss << "Approx( " << Catch::toString( m_value ) << " )"; - return oss.str(); - } + std::string toString() const; private: double m_epsilon; @@ -2893,14 +1940,194 @@ namespace Detail { } template<> -inline std::string toString( Detail::Approx const& value ) { - return value.toString(); -} +struct StringMaker { + static std::string convert(Catch::Detail::Approx const& value); +}; } // end namespace Catch -// #included from: internal/catch_matchers_string.h -#define TWOBLUECUBES_CATCH_MATCHERS_STRING_H_INCLUDED +// end catch_approx.h +// start catch_string_manip.h + +#include +#include + +namespace Catch { + + bool startsWith( std::string const& s, std::string const& prefix ); + bool startsWith( std::string const& s, char prefix ); + bool endsWith( std::string const& s, std::string const& suffix ); + bool endsWith( std::string const& s, char suffix ); + bool contains( std::string const& s, std::string const& infix ); + void toLowerInPlace( std::string& s ); + std::string toLower( std::string const& s ); + std::string trim( std::string const& str ); + bool replaceInPlace( std::string& str, std::string const& replaceThis, std::string const& withThis ); + + struct pluralise { + pluralise( std::size_t count, std::string const& label ); + + friend std::ostream& operator << ( std::ostream& os, pluralise const& pluraliser ); + + std::size_t m_count; + std::string m_label; + }; +} + +// end catch_string_manip.h +#ifndef CATCH_CONFIG_DISABLE_MATCHERS +// start catch_capture_matchers.h + +// start catch_matchers.h + +#include +#include + +namespace Catch { +namespace Matchers { + namespace Impl { + + template struct MatchAllOf; + template struct MatchAnyOf; + template struct MatchNotOf; + + class MatcherUntypedBase { + public: + MatcherUntypedBase() = default; + MatcherUntypedBase ( MatcherUntypedBase const& ) = default; + MatcherUntypedBase& operator = ( MatcherUntypedBase const& ) = delete; + std::string toString() const; + + protected: + virtual ~MatcherUntypedBase(); + virtual std::string describe() const = 0; + mutable std::string m_cachedToString; + }; + + template + struct MatcherMethod { + virtual bool match( ObjectT const& arg ) const = 0; + }; + template + struct MatcherMethod { + virtual bool match( PtrT* arg ) const = 0; + }; + + template + struct MatcherBase : MatcherUntypedBase, MatcherMethod { + + MatchAllOf operator && ( MatcherBase const& other ) const; + MatchAnyOf operator || ( MatcherBase const& other ) const; + MatchNotOf operator ! () const; + }; + + template + struct MatchAllOf : MatcherBase { + bool match( ArgT const& arg ) const override { + for( auto matcher : m_matchers ) { + if (!matcher->match(arg)) + return false; + } + return true; + } + std::string describe() const override { + std::string description; + description.reserve( 4 + m_matchers.size()*32 ); + description += "( "; + bool first = true; + for( auto matcher : m_matchers ) { + if( first ) + first = false; + else + description += " and "; + description += matcher->toString(); + } + description += " )"; + return description; + } + + MatchAllOf& operator && ( MatcherBase const& other ) { + m_matchers.push_back( &other ); + return *this; + } + + std::vector const*> m_matchers; + }; + template + struct MatchAnyOf : MatcherBase { + + bool match( ArgT const& arg ) const override { + for( auto matcher : m_matchers ) { + if (matcher->match(arg)) + return true; + } + return false; + } + std::string describe() const override { + std::string description; + description.reserve( 4 + m_matchers.size()*32 ); + description += "( "; + bool first = true; + for( auto matcher : m_matchers ) { + if( first ) + first = false; + else + description += " or "; + description += matcher->toString(); + } + description += " )"; + return description; + } + + MatchAnyOf& operator || ( MatcherBase const& other ) { + m_matchers.push_back( &other ); + return *this; + } + + std::vector const*> m_matchers; + }; + + template + struct MatchNotOf : MatcherBase { + + MatchNotOf( MatcherBase const& underlyingMatcher ) : m_underlyingMatcher( underlyingMatcher ) {} + + bool match( ArgT const& arg ) const override { + return !m_underlyingMatcher.match( arg ); + } + + std::string describe() const override { + return "not " + m_underlyingMatcher.toString(); + } + MatcherBase const& m_underlyingMatcher; + }; + + template + MatchAllOf MatcherBase::operator && ( MatcherBase const& other ) const { + return MatchAllOf() && *this && other; + } + template + MatchAnyOf MatcherBase::operator || ( MatcherBase const& other ) const { + return MatchAnyOf() || *this || other; + } + template + MatchNotOf MatcherBase::operator ! () const { + return MatchNotOf( *this ); + } + + } // namespace Impl + +} // namespace Matchers + +using namespace Matchers; +using Matchers::Impl::MatcherBase; + +} // namespace Catch + +// end catch_matchers.h +// start catch_matchers_string.h + +#include namespace Catch { namespace Matchers { @@ -2919,7 +2146,7 @@ namespace Matchers { struct StringMatcherBase : MatcherBase { StringMatcherBase( std::string const& operation, CasedString const& comparator ); - virtual std::string describe() const CATCH_OVERRIDE; + std::string describe() const override; CasedString m_comparator; std::string m_operation; @@ -2927,19 +2154,19 @@ namespace Matchers { struct EqualsMatcher : StringMatcherBase { EqualsMatcher( CasedString const& comparator ); - virtual bool match( std::string const& source ) const CATCH_OVERRIDE; + bool match( std::string const& source ) const override; }; struct ContainsMatcher : StringMatcherBase { ContainsMatcher( CasedString const& comparator ); - virtual bool match( std::string const& source ) const CATCH_OVERRIDE; + bool match( std::string const& source ) const override; }; struct StartsWithMatcher : StringMatcherBase { StartsWithMatcher( CasedString const& comparator ); - virtual bool match( std::string const& source ) const CATCH_OVERRIDE; + bool match( std::string const& source ) const override; }; struct EndsWithMatcher : StringMatcherBase { EndsWithMatcher( CasedString const& comparator ); - virtual bool match( std::string const& source ) const CATCH_OVERRIDE; + bool match( std::string const& source ) const override; }; } // namespace StdString @@ -2955,8 +2182,8 @@ namespace Matchers { } // namespace Matchers } // namespace Catch -// #included from: internal/catch_matchers_vector.h -#define TWOBLUECUBES_CATCH_MATCHERS_VECTOR_H_INCLUDED +// end catch_matchers_string.h +// start catch_matchers_vector.h namespace Catch { namespace Matchers { @@ -2968,12 +2195,17 @@ namespace Matchers { ContainsElementMatcher(T const &comparator) : m_comparator( comparator) {} - bool match(std::vector const &v) const CATCH_OVERRIDE { - return std::find(v.begin(), v.end(), m_comparator) != v.end(); + bool match(std::vector const &v) const override { + for (auto const& el : v) { + if (el == m_comparator) { + return true; + } + } + return false; } - virtual std::string describe() const CATCH_OVERRIDE { - return "Contains: " + Catch::toString( m_comparator ); + std::string describe() const override { + return "Contains: " + ::Catch::Detail::stringify( m_comparator ); } T const& m_comparator; @@ -2984,17 +2216,26 @@ namespace Matchers { ContainsMatcher(std::vector const &comparator) : m_comparator( comparator ) {} - bool match(std::vector const &v) const CATCH_OVERRIDE { + bool match(std::vector const &v) const override { // !TBD: see note in EqualsMatcher if (m_comparator.size() > v.size()) return false; - for (size_t i = 0; i < m_comparator.size(); ++i) - if (std::find(v.begin(), v.end(), m_comparator[i]) == v.end()) + for (auto const& comparator : m_comparator) { + auto present = false; + for (const auto& el : v) { + if (el == comparator) { + present = true; + break; + } + } + if (!present) { return false; + } + } return true; } - virtual std::string describe() const CATCH_OVERRIDE { - return "Contains: " + Catch::toString( m_comparator ); + std::string describe() const override { + return "Contains: " + ::Catch::Detail::stringify( m_comparator ); } std::vector const& m_comparator; @@ -3005,20 +2246,20 @@ namespace Matchers { EqualsMatcher(std::vector const &comparator) : m_comparator( comparator ) {} - bool match(std::vector const &v) const CATCH_OVERRIDE { + bool match(std::vector const &v) const override { // !TBD: This currently works if all elements can be compared using != // - a more general approach would be via a compare template that defaults // to using !=. but could be specialised for, e.g. std::vector etc // - then just call that directly if (m_comparator.size() != v.size()) return false; - for (size_t i = 0; i < v.size(); ++i) + for (std::size_t i = 0; i < v.size(); ++i) if (m_comparator[i] != v[i]) return false; return true; } - virtual std::string describe() const CATCH_OVERRIDE { - return "Equals: " + Catch::toString( m_comparator ); + std::string describe() const override { + return "Equals: " + ::Catch::Detail::stringify( m_comparator ); } std::vector const& m_comparator; }; @@ -3046,124 +2287,87 @@ namespace Matchers { } // namespace Matchers } // namespace Catch -// #included from: internal/catch_interfaces_tag_alias_registry.h -#define TWOBLUECUBES_CATCH_INTERFACES_TAG_ALIAS_REGISTRY_H_INCLUDED - -// #included from: catch_tag_alias.h -#define TWOBLUECUBES_CATCH_TAG_ALIAS_H_INCLUDED - -#include - +// end catch_matchers_vector.h namespace Catch { - struct TagAlias { - TagAlias( std::string const& _tag, SourceLineInfo _lineInfo ) : tag( _tag ), lineInfo( _lineInfo ) {} - - std::string tag; - SourceLineInfo lineInfo; - }; - - struct RegistrarForTagAliases { - RegistrarForTagAliases( char const* alias, char const* tag, SourceLineInfo const& lineInfo ); - }; - -} // end namespace Catch - -#define CATCH_REGISTER_TAG_ALIAS( alias, spec ) namespace{ Catch::RegistrarForTagAliases INTERNAL_CATCH_UNIQUE_NAME( AutoRegisterTagAlias )( alias, spec, CATCH_INTERNAL_LINEINFO ); } -// #included from: catch_option.hpp -#define TWOBLUECUBES_CATCH_OPTION_HPP_INCLUDED - -namespace Catch { - - // An optional type - template - class Option { + template + class MatchExpr : public ITransientExpression { + ArgT const& m_arg; + MatcherT m_matcher; + StringRef m_matcherString; + bool m_result; public: - Option() : nullableValue( CATCH_NULL ) {} - Option( T const& _value ) - : nullableValue( new( storage ) T( _value ) ) - {} - Option( Option const& _other ) - : nullableValue( _other ? new( storage ) T( *_other ) : CATCH_NULL ) + MatchExpr( ArgT const& arg, MatcherT const& matcher, StringRef matcherString ) + : m_arg( arg ), + m_matcher( matcher ), + m_matcherString( matcherString ), + m_result( matcher.match( arg ) ) {} - ~Option() { - reset(); + 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 ) << ' '; + if( matcherAsString == Detail::unprintableString ) + os << m_matcherString; + else + os << matcherAsString; } + }; - Option& operator= ( Option const& _other ) { - if( &_other != this ) { - reset(); - if( _other ) - nullableValue = new( storage ) T( *_other ); - } - return *this; - } - Option& operator = ( T const& _value ) { - reset(); - nullableValue = new( storage ) T( _value ); - return *this; - } + using StringMatcher = Matchers::Impl::MatcherBase; - void reset() { - if( nullableValue ) - nullableValue->~T(); - nullableValue = CATCH_NULL; - } + void handleExceptionMatchExpr( AssertionHandler& handler, StringMatcher const& matcher, StringRef matcherString ); - T& operator*() { return *nullableValue; } - T const& operator*() const { return *nullableValue; } - T* operator->() { return nullableValue; } - const T* operator->() const { return nullableValue; } + template + auto makeMatchExpr( ArgT const& arg, MatcherT const& matcher, StringRef matcherString ) -> MatchExpr { + return MatchExpr( arg, matcher, matcherString ); + } - T valueOr( T const& defaultValue ) const { - return nullableValue ? *nullableValue : defaultValue; - } +} // namespace Catch - bool some() const { return nullableValue != CATCH_NULL; } - bool none() const { return nullableValue == CATCH_NULL; } +/////////////////////////////////////////////////////////////////////////////// +#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_CATCH( catchAssertionHandler ) \ + INTERNAL_CATCH_REACT( catchAssertionHandler ) \ + } while( Catch::alwaysFalse() ) - bool operator !() const { return nullableValue == CATCH_NULL; } - operator SafeBool::type() const { - return SafeBool::makeSafe( some() ); - } +/////////////////////////////////////////////////////////////////////////////// +#define INTERNAL_CATCH_THROWS_MATCHES( macroName, exceptionType, resultDisposition, matcher, ... ) \ + do { \ + Catch::AssertionHandler catchAssertionHandler( macroName, CATCH_INTERNAL_LINEINFO, CATCH_INTERNAL_STRINGIFY(__VA_ARGS__) ", " CATCH_INTERNAL_STRINGIFY(exceptionType) ", " CATCH_INTERNAL_STRINGIFY(matcher), resultDisposition ); \ + if( catchAssertionHandler.allowThrows() ) \ + try { \ + static_cast(__VA_ARGS__ ); \ + catchAssertionHandler.handle( Catch::ResultWas::DidntThrowException ); \ + } \ + catch( exceptionType const& ex ) { \ + catchAssertionHandler.handle( Catch::makeMatchExpr( ex, matcher, #matcher ) ); \ + } \ + catch( ... ) { \ + catchAssertionHandler.useActiveException(); \ + } \ + else \ + catchAssertionHandler.handle( Catch::ResultWas::Ok ); \ + INTERNAL_CATCH_REACT( catchAssertionHandler ) \ + } while( Catch::alwaysFalse() ) - private: - T *nullableValue; - union { - char storage[sizeof(T)]; - - // These are here to force alignment for the storage - long double dummy1; - void (*dummy2)(); - long double dummy3; -#ifdef CATCH_CONFIG_CPP11_LONG_LONG - long long dummy4; +// end catch_capture_matchers.h #endif - }; - }; - -} // end namespace Catch - -namespace Catch { - - struct ITagAliasRegistry { - virtual ~ITagAliasRegistry(); - virtual Option find( std::string const& alias ) const = 0; - virtual std::string expandAliases( std::string const& unexpandedTestSpec ) const = 0; - - static ITagAliasRegistry const& get(); - }; - -} // end namespace Catch // These files are included here so the single_include script doesn't put them // in the conditionally compiled sections -// #included from: internal/catch_test_case_info.h -#define TWOBLUECUBES_CATCH_TEST_CASE_INFO_H_INCLUDED +// start catch_test_case_info.h #include -#include +#include +#include #ifdef __clang__ #pragma clang diagnostic push @@ -3172,7 +2376,7 @@ namespace Catch { namespace Catch { - struct ITestCase; + struct ITestInvoker; struct TestCaseInfo { enum SpecialProperties{ @@ -3181,30 +2385,30 @@ namespace Catch { ShouldFail = 1 << 2, MayFail = 1 << 3, Throws = 1 << 4, - NonPortable = 1 << 5 + NonPortable = 1 << 5, + Benchmark = 1 << 6 }; TestCaseInfo( std::string const& _name, std::string const& _className, std::string const& _description, - std::set const& _tags, + std::vector const& _tags, SourceLineInfo const& _lineInfo ); - TestCaseInfo( TestCaseInfo const& other ); - - friend void setTags( TestCaseInfo& testCaseInfo, std::set const& tags ); + friend void setTags( TestCaseInfo& testCaseInfo, std::vector tags ); bool isHidden() const; bool throws() const; bool okToFail() const; bool expectedToFail() const; + std::string tagsAsString() const; + std::string name; std::string className; std::string description; - std::set tags; - std::set lcaseTags; - std::string tagsAsString; + std::vector tags; + std::vector lcaseTags; SourceLineInfo lineInfo; SpecialProperties properties; }; @@ -3212,8 +2416,7 @@ namespace Catch { class TestCase : public TestCaseInfo { public: - TestCase( ITestCase* testCase, TestCaseInfo const& info ); - TestCase( TestCase const& other ); + TestCase( ITestInvoker* testCase, TestCaseInfo const& info ); TestCase withName( std::string const& _newName ) const; @@ -3221,16 +2424,14 @@ namespace Catch { TestCaseInfo const& getTestCaseInfo() const; - void swap( TestCase& other ); bool operator == ( TestCase const& other ) const; bool operator < ( TestCase const& other ) const; - TestCase& operator = ( TestCase const& other ); private: - Ptr test; + std::shared_ptr test; }; - TestCase makeTestCase( ITestCase* testCase, + TestCase makeTestCase( ITestInvoker* testCase, std::string const& className, std::string const& name, std::string const& description, @@ -3241,10 +2442,21 @@ namespace Catch { #pragma clang diagnostic pop #endif +// end catch_test_case_info.h +// start catch_interfaces_runner.h + +namespace Catch { + + struct IRunner { + virtual ~IRunner(); + virtual bool aborting() const = 0; + }; +} + +// end catch_interfaces_runner.h #ifdef __OBJC__ -// #included from: internal/catch_objc.hpp -#define TWOBLUECUBES_CATCH_OBJC_HPP_INCLUDED +// start catch_objc.hpp #import @@ -3268,7 +2480,7 @@ namespace Catch { namespace Catch { - class OcMethod : public SharedImpl { + class OcMethod : public ITestInvoker { public: OcMethod( Class cls, SEL sel ) : m_cls( cls ), m_sel( sel ) {} @@ -3304,9 +2516,9 @@ namespace Catch { } } - inline size_t registerTestMethods() { - size_t noTestMethods = 0; - int noClasses = objc_getClassList( CATCH_NULL, 0 ); + inline std::size_t registerTestMethods() { + std::size_t noTestMethods = 0; + int noClasses = objc_getClassList( nullptr, 0 ); Class* classes = (CATCH_UNSAFE_UNRETAINED Class *)malloc( sizeof(Class) * noClasses); objc_getClassList( classes, noClasses ); @@ -3325,7 +2537,7 @@ namespace Catch { std::string desc = Detail::getAnnotation( cls, "Description", testCaseName ); const char* className = class_getName( cls ); - getMutableRegistryHub().registerTest( makeTestCase( new OcMethod( cls, selector ), className, name.c_str(), desc.c_str(), SourceLineInfo() ) ); + getMutableRegistryHub().registerTest( makeTestCase( new OcMethod( cls, selector ), className, name.c_str(), desc.c_str(), SourceLineInfo("",0) ) ); noTestMethods++; } } @@ -3335,6 +2547,8 @@ namespace Catch { return noTestMethods; } +#if !defined(CATCH_CONFIG_DISABLE_MATCHERS) + namespace Matchers { namespace Impl { namespace NSStringMatchers { @@ -3346,61 +2560,61 @@ namespace Catch { arcSafeRelease( m_substr ); } - virtual bool match( NSString* arg ) const CATCH_OVERRIDE { + bool match( NSString* arg ) const override { return false; } - NSString* m_substr; + NSString* CATCH_ARC_STRONG m_substr; }; struct Equals : StringHolder { Equals( NSString* substr ) : StringHolder( substr ){} - virtual bool match( NSString* str ) const CATCH_OVERRIDE { + bool match( NSString* str ) const override { return (str != nil || m_substr == nil ) && [str isEqualToString:m_substr]; } - virtual std::string describe() const CATCH_OVERRIDE { - return "equals string: " + Catch::toString( m_substr ); + std::string describe() const override { + return "equals string: " + Catch::Detail::stringify( m_substr ); } }; struct Contains : StringHolder { Contains( NSString* substr ) : StringHolder( substr ){} - virtual bool match( NSString* str ) const { + bool match( NSString* str ) const { return (str != nil || m_substr == nil ) && [str rangeOfString:m_substr].location != NSNotFound; } - virtual std::string describe() const CATCH_OVERRIDE { - return "contains string: " + Catch::toString( m_substr ); + std::string describe() const override { + return "contains string: " + Catch::Detail::stringify( m_substr ); } }; struct StartsWith : StringHolder { StartsWith( NSString* substr ) : StringHolder( substr ){} - virtual bool match( NSString* str ) const { + bool match( NSString* str ) const override { return (str != nil || m_substr == nil ) && [str rangeOfString:m_substr].location == 0; } - virtual std::string describe() const CATCH_OVERRIDE { - return "starts with: " + Catch::toString( m_substr ); + std::string describe() const override { + return "starts with: " + Catch::Detail::stringify( m_substr ); } }; struct EndsWith : StringHolder { EndsWith( NSString* substr ) : StringHolder( substr ){} - virtual bool match( NSString* str ) const { + bool match( NSString* str ) const override { return (str != nil || m_substr == nil ) && [str rangeOfString:m_substr].location == [str length] - [m_substr length]; } - virtual std::string describe() const CATCH_OVERRIDE { - return "ends with: " + Catch::toString( m_substr ); + std::string describe() const override { + return "ends with: " + Catch::Detail::stringify( m_substr ); } }; @@ -3423,86 +2637,52 @@ namespace Catch { using namespace Matchers; +#endif // CATCH_CONFIG_DISABLE_MATCHERS + } // namespace Catch /////////////////////////////////////////////////////////////////////////////// -#define OC_TEST_CASE( name, desc )\ -+(NSString*) INTERNAL_CATCH_UNIQUE_NAME( Catch_Name_test ) \ -{\ +#define OC_MAKE_UNIQUE_NAME( root, uniqueSuffix ) root##uniqueSuffix +#define OC_TEST_CASE2( name, desc, uniqueSuffix ) \ ++(NSString*) OC_MAKE_UNIQUE_NAME( Catch_Name_test_, uniqueSuffix ) \ +{ \ return @ name; \ -}\ -+(NSString*) INTERNAL_CATCH_UNIQUE_NAME( Catch_Description_test ) \ +} \ ++(NSString*) OC_MAKE_UNIQUE_NAME( Catch_Description_test_, uniqueSuffix ) \ { \ return @ desc; \ } \ --(void) INTERNAL_CATCH_UNIQUE_NAME( Catch_TestCase_test ) +-(void) OC_MAKE_UNIQUE_NAME( Catch_TestCase_test_, uniqueSuffix ) +#define OC_TEST_CASE( name, desc ) OC_TEST_CASE2( name, desc, __LINE__ ) + +// end catch_objc.hpp #endif -#ifdef CATCH_IMPL +#ifdef CATCH_CONFIG_EXTERNAL_INTERFACES +// start catch_external_interfaces.h -// !TBD: Move the leak detector code into a separate header -#ifdef CATCH_CONFIG_WINDOWS_CRTDBG -#include -class LeakDetector { -public: - LeakDetector() { - int flag = _CrtSetDbgFlag(_CRTDBG_REPORT_FLAG); - flag |= _CRTDBG_LEAK_CHECK_DF; - flag |= _CRTDBG_ALLOC_MEM_DF; - _CrtSetDbgFlag(flag); - _CrtSetReportMode(_CRT_WARN, _CRTDBG_MODE_FILE | _CRTDBG_MODE_DEBUG); - _CrtSetReportFile(_CRT_WARN, _CRTDBG_FILE_STDERR); - // Change this to leaking allocation's number to break there - _CrtSetBreakAlloc(-1); - } -}; -#else -class LeakDetector {}; -#endif +// start catch_reporter_bases.hpp -LeakDetector leakDetector; +// start catch_interfaces_reporter.h -// #included from: internal/catch_impl.hpp -#define TWOBLUECUBES_CATCH_IMPL_HPP_INCLUDED +// start catch_config.hpp -// Collect all the implementation files together here -// These are the equivalent of what would usually be cpp files - -#ifdef __clang__ -#pragma clang diagnostic push -#pragma clang diagnostic ignored "-Wweak-vtables" -#endif - -// #included from: ../catch_session.hpp -#define TWOBLUECUBES_CATCH_RUNNER_HPP_INCLUDED - -// #included from: internal/catch_commandline.hpp -#define TWOBLUECUBES_CATCH_COMMANDLINE_HPP_INCLUDED - -// #included from: catch_config.hpp -#define TWOBLUECUBES_CATCH_CONFIG_HPP_INCLUDED - -// #included from: catch_test_spec_parser.hpp -#define TWOBLUECUBES_CATCH_TEST_SPEC_PARSER_HPP_INCLUDED +// start catch_test_spec_parser.h #ifdef __clang__ #pragma clang diagnostic push #pragma clang diagnostic ignored "-Wpadded" #endif -// #included from: catch_test_spec.hpp -#define TWOBLUECUBES_CATCH_TEST_SPEC_HPP_INCLUDED +// start catch_test_spec.h #ifdef __clang__ #pragma clang diagnostic push #pragma clang diagnostic ignored "-Wpadded" #endif -// #included from: catch_wildcard_pattern.hpp -#define TWOBLUECUBES_CATCH_WILDCARD_PATTERN_HPP_INCLUDED - -#include +// start catch_wildcard_pattern.h namespace Catch { @@ -3516,119 +2696,68 @@ namespace Catch public: - WildcardPattern( std::string const& pattern, CaseSensitive::Choice caseSensitivity ) - : m_caseSensitivity( caseSensitivity ), - m_wildcard( NoWildcard ), - m_pattern( adjustCase( pattern ) ) - { - if( startsWith( m_pattern, '*' ) ) { - m_pattern = m_pattern.substr( 1 ); - m_wildcard = WildcardAtStart; - } - if( endsWith( m_pattern, '*' ) ) { - m_pattern = m_pattern.substr( 0, m_pattern.size()-1 ); - m_wildcard = static_cast( m_wildcard | WildcardAtEnd ); - } - } - virtual ~WildcardPattern(); - virtual bool matches( std::string const& str ) const { - switch( m_wildcard ) { - case NoWildcard: - return m_pattern == adjustCase( str ); - case WildcardAtStart: - return endsWith( adjustCase( str ), m_pattern ); - case WildcardAtEnd: - return startsWith( adjustCase( str ), m_pattern ); - case WildcardAtBothEnds: - return contains( adjustCase( str ), m_pattern ); - } + WildcardPattern( std::string const& pattern, CaseSensitive::Choice caseSensitivity ); + virtual ~WildcardPattern() = default; + virtual bool matches( std::string const& str ) const; -#ifdef __clang__ -#pragma clang diagnostic push -#pragma clang diagnostic ignored "-Wunreachable-code" -#endif - throw std::logic_error( "Unknown enum" ); -#ifdef __clang__ -#pragma clang diagnostic pop -#endif - } private: - std::string adjustCase( std::string const& str ) const { - return m_caseSensitivity == CaseSensitive::No ? toLower( str ) : str; - } + std::string adjustCase( std::string const& str ) const; CaseSensitive::Choice m_caseSensitivity; - WildcardPosition m_wildcard; + WildcardPosition m_wildcard = NoWildcard; std::string m_pattern; }; } +// end catch_wildcard_pattern.h #include #include +#include namespace Catch { class TestSpec { - struct Pattern : SharedImpl<> { + struct Pattern { virtual ~Pattern(); virtual bool matches( TestCaseInfo const& testCase ) const = 0; }; + using PatternPtr = std::shared_ptr; + class NamePattern : public Pattern { public: - NamePattern( std::string const& name ) - : m_wildcardPattern( toLower( name ), CaseSensitive::No ) - {} + NamePattern( std::string const& name ); virtual ~NamePattern(); - virtual bool matches( TestCaseInfo const& testCase ) const { - return m_wildcardPattern.matches( toLower( testCase.name ) ); - } + virtual bool matches( TestCaseInfo const& testCase ) const override; private: WildcardPattern m_wildcardPattern; }; class TagPattern : public Pattern { public: - TagPattern( std::string const& tag ) : m_tag( toLower( tag ) ) {} + TagPattern( std::string const& tag ); virtual ~TagPattern(); - virtual bool matches( TestCaseInfo const& testCase ) const { - return testCase.lcaseTags.find( m_tag ) != testCase.lcaseTags.end(); - } + virtual bool matches( TestCaseInfo const& testCase ) const override; private: std::string m_tag; }; class ExcludedPattern : public Pattern { public: - ExcludedPattern( Ptr const& underlyingPattern ) : m_underlyingPattern( underlyingPattern ) {} + ExcludedPattern( PatternPtr const& underlyingPattern ); virtual ~ExcludedPattern(); - virtual bool matches( TestCaseInfo const& testCase ) const { return !m_underlyingPattern->matches( testCase ); } + virtual bool matches( TestCaseInfo const& testCase ) const override; private: - Ptr m_underlyingPattern; + PatternPtr m_underlyingPattern; }; struct Filter { - std::vector > m_patterns; + std::vector m_patterns; - bool matches( TestCaseInfo const& testCase ) const { - // All patterns in a filter must match for the filter to be a match - for( std::vector >::const_iterator it = m_patterns.begin(), itEnd = m_patterns.end(); it != itEnd; ++it ) { - if( !(*it)->matches( testCase ) ) - return false; - } - return true; - } + bool matches( TestCaseInfo const& testCase ) const; }; public: - bool hasFilters() const { - return !m_filters.empty(); - } - bool matches( TestCaseInfo const& testCase ) const { - // A TestSpec matches if any filter matches - for( std::vector::const_iterator it = m_filters.begin(), itEnd = m_filters.end(); it != itEnd; ++it ) - if( it->matches( testCase ) ) - return true; - return false; - } + bool hasFilters() const; + bool matches( TestCaseInfo const& testCase ) const; private: std::vector m_filters; @@ -3641,87 +2770,56 @@ namespace Catch { #pragma clang diagnostic pop #endif +// end catch_test_spec.h +// start catch_interfaces_tag_alias_registry.h + +#include + +namespace Catch { + + struct TagAlias; + + struct ITagAliasRegistry { + virtual ~ITagAliasRegistry(); + // Nullptr if not present + virtual TagAlias const* find( std::string const& alias ) const = 0; + virtual std::string expandAliases( std::string const& unexpandedTestSpec ) const = 0; + + static ITagAliasRegistry const& get(); + }; + +} // end namespace Catch + +// end catch_interfaces_tag_alias_registry.h namespace Catch { class TestSpecParser { enum Mode{ None, Name, QuotedName, Tag, EscapedName }; - Mode m_mode; - bool m_exclusion; - std::size_t m_start, m_pos; + Mode m_mode = None; + bool m_exclusion = false; + std::size_t m_start = std::string::npos, m_pos = 0; std::string m_arg; std::vector m_escapeChars; TestSpec::Filter m_currentFilter; TestSpec m_testSpec; - ITagAliasRegistry const* m_tagAliases; + ITagAliasRegistry const* m_tagAliases = nullptr; public: - TestSpecParser( ITagAliasRegistry const& tagAliases ) :m_mode(None), m_exclusion(false), m_start(0), m_pos(0), m_tagAliases( &tagAliases ) {} + TestSpecParser( ITagAliasRegistry const& tagAliases ); + + TestSpecParser& parse( std::string const& arg ); + TestSpec testSpec(); - TestSpecParser& parse( std::string const& arg ) { - m_mode = None; - m_exclusion = false; - m_start = std::string::npos; - m_arg = m_tagAliases->expandAliases( arg ); - m_escapeChars.clear(); - for( m_pos = 0; m_pos < m_arg.size(); ++m_pos ) - visitChar( m_arg[m_pos] ); - if( m_mode == Name ) - addPattern(); - return *this; - } - TestSpec testSpec() { - addFilter(); - return m_testSpec; - } private: - void visitChar( char c ) { - if( m_mode == None ) { - switch( c ) { - case ' ': return; - case '~': m_exclusion = true; return; - case '[': return startNewMode( Tag, ++m_pos ); - case '"': return startNewMode( QuotedName, ++m_pos ); - case '\\': return escape(); - default: startNewMode( Name, m_pos ); break; - } - } - if( m_mode == Name ) { - if( c == ',' ) { - addPattern(); - addFilter(); - } - else if( c == '[' ) { - if( subString() == "exclude:" ) - m_exclusion = true; - else - addPattern(); - startNewMode( Tag, ++m_pos ); - } - else if( c == '\\' ) - escape(); - } - else if( m_mode == EscapedName ) - m_mode = Name; - else if( m_mode == QuotedName && c == '"' ) - addPattern(); - else if( m_mode == Tag && c == ']' ) - addPattern(); - } - void startNewMode( Mode mode, std::size_t start ) { - m_mode = mode; - m_start = start; - } - void escape() { - if( m_mode == None ) - m_start = m_pos; - m_mode = EscapedName; - m_escapeChars.push_back( m_pos ); - } - std::string subString() const { return m_arg.substr( m_start, m_pos - m_start ); } + void visitChar( char c ); + void startNewMode( Mode mode, std::size_t start ); + void escape(); + std::string subString() const; + template void addPattern() { std::string token = subString(); - for( size_t i = 0; i < m_escapeChars.size(); ++i ) + for( std::size_t i = 0; i < m_escapeChars.size(); ++i ) token = token.substr( 0, m_escapeChars[i]-m_start-i ) + token.substr( m_escapeChars[i]-m_start-i+1 ); m_escapeChars.clear(); if( startsWith( token, "exclude:" ) ) { @@ -3729,24 +2827,18 @@ namespace Catch { token = token.substr( 8 ); } if( !token.empty() ) { - Ptr pattern = new T( token ); + TestSpec::PatternPtr pattern = std::make_shared( token ); if( m_exclusion ) - pattern = new TestSpec::ExcludedPattern( pattern ); + pattern = std::make_shared( pattern ); m_currentFilter.m_patterns.push_back( pattern ); } m_exclusion = false; m_mode = None; } - void addFilter() { - if( !m_currentFilter.m_patterns.empty() ) { - m_testSpec.m_filters.push_back( m_currentFilter ); - m_currentFilter = TestSpec::Filter(); - } - } + + void addFilter(); }; - inline TestSpec parseTestSpec( std::string const& arg ) { - return TestSpecParser( ITagAliasRegistry::get() ).parse( arg ).testSpec(); - } + TestSpec parseTestSpec( std::string const& arg ); } // namespace Catch @@ -3754,20 +2846,21 @@ namespace Catch { #pragma clang diagnostic pop #endif -// #included from: catch_interfaces_config.h -#define TWOBLUECUBES_CATCH_INTERFACES_CONFIG_H_INCLUDED +// end catch_test_spec_parser.h +// start catch_interfaces_config.h #include #include #include +#include namespace Catch { - struct Verbosity { enum Level { - NoOutput = 0, - Quiet, - Normal - }; }; + enum class Verbosity { + Quiet = 0, + Normal, + High + }; struct WarnAbout { enum What { Nothing = 0x00, @@ -3798,7 +2891,7 @@ namespace Catch { class TestSpec; - struct IConfig : IShared { + struct IConfig : NonCopyable { virtual ~IConfig(); @@ -3814,17 +2907,20 @@ namespace Catch { virtual TestSpec const& testSpec() const = 0; virtual RunTests::InWhatOrder runOrder() const = 0; virtual unsigned int rngSeed() const = 0; + virtual int benchmarkResolutionMultiple() const = 0; virtual UseColour::YesOrNo useColour() const = 0; virtual std::vector const& getSectionsToRun() const = 0; - + virtual Verbosity verbosity() const = 0; }; + + using IConfigPtr = std::shared_ptr; } -// #included from: catch_stream.h -#define TWOBLUECUBES_CATCH_STREAM_H_INCLUDED +// end catch_interfaces_config.h +// Libstdc++ doesn't like incomplete classes for unique_ptr +// start catch_stream.h -// #included from: catch_streambuf.h -#define TWOBLUECUBES_CATCH_STREAMBUF_H_INCLUDED +// start catch_streambuf.h #include @@ -3832,10 +2928,11 @@ namespace Catch { class StreamBufBase : public std::streambuf { public: - virtual ~StreamBufBase() CATCH_NOEXCEPT; + virtual ~StreamBufBase(); }; } +// end catch_streambuf.h #include #include #include @@ -3848,7 +2945,7 @@ namespace Catch { std::ostream& clog(); struct IStream { - virtual ~IStream() CATCH_NOEXCEPT; + virtual ~IStream(); virtual std::ostream& stream() const = 0; }; @@ -3856,37 +2953,38 @@ namespace Catch { mutable std::ofstream m_ofs; public: FileStream( std::string const& filename ); - virtual ~FileStream() CATCH_NOEXCEPT; + ~FileStream() override = default; public: // IStream - virtual std::ostream& stream() const CATCH_OVERRIDE; + std::ostream& stream() const override; }; class CoutStream : public IStream { mutable std::ostream m_os; public: CoutStream(); - virtual ~CoutStream() CATCH_NOEXCEPT; + ~CoutStream() override = default; public: // IStream - virtual std::ostream& stream() const CATCH_OVERRIDE; + std::ostream& stream() const override; }; class DebugOutStream : public IStream { - CATCH_AUTO_PTR( StreamBufBase ) m_streamBuf; + std::unique_ptr m_streamBuf; mutable std::ostream m_os; public: DebugOutStream(); - virtual ~DebugOutStream() CATCH_NOEXCEPT; + ~DebugOutStream() override = default; public: // IStream - virtual std::ostream& stream() const CATCH_OVERRIDE; + std::ostream& stream() const override; }; } +// end catch_stream.h + #include #include #include -#include #ifndef CATCH_CONFIG_CONSOLE_WIDTH #define CATCH_CONFIG_CONSOLE_WIDTH 80 @@ -3894,54 +2992,32 @@ namespace Catch { namespace Catch { + struct IStream; + struct ConfigData { + bool listTests = false; + bool listTags = false; + bool listReporters = false; + bool listTestNamesOnly = false; - ConfigData() - : listTests( false ), - listTags( false ), - listReporters( false ), - listTestNamesOnly( false ), - listExtraInfo( false ), - showSuccessfulTests( false ), - shouldDebugBreak( false ), - noThrow( false ), - showHelp( false ), - showInvisibles( false ), - filenamesAsTags( false ), - libIdentify( false ), - abortAfter( -1 ), - rngSeed( 0 ), - verbosity( Verbosity::Normal ), - warnings( WarnAbout::Nothing ), - showDurations( ShowDurations::DefaultForReporter ), - runOrder( RunTests::InDeclarationOrder ), - useColour( UseColour::Auto ), - waitForKeypress( WaitForKeypress::Never ) - {} + bool showSuccessfulTests = false; + bool shouldDebugBreak = false; + bool noThrow = false; + bool showHelp = false; + bool showInvisibles = false; + bool filenamesAsTags = false; + bool libIdentify = false; - bool listTests; - bool listTags; - bool listReporters; - bool listTestNamesOnly; - bool listExtraInfo; + int abortAfter = -1; + unsigned int rngSeed = 0; + int benchmarkResolutionMultiple = 100; - bool showSuccessfulTests; - bool shouldDebugBreak; - bool noThrow; - bool showHelp; - bool showInvisibles; - bool filenamesAsTags; - bool libIdentify; - - int abortAfter; - unsigned int rngSeed; - - Verbosity::Level verbosity; - WarnAbout::What warnings; - ShowDurations::OrNot showDurations; - RunTests::InWhatOrder runOrder; - UseColour::YesOrNo useColour; - WaitForKeypress::When waitForKeypress; + Verbosity verbosity = Verbosity::Normal; + WarnAbout::What warnings = WarnAbout::Nothing; + ShowDurations::OrNot showDurations = ShowDurations::DefaultForReporter; + RunTests::InWhatOrder runOrder = RunTests::InDeclarationOrder; + UseColour::YesOrNo useColour = UseColour::Auto; + WaitForKeypress::When waitForKeypress = WaitForKeypress::Never; std::string outputFilename; std::string name; @@ -3952,1525 +3028,638 @@ namespace Catch { std::vector sectionsToRun; }; - class Config : public SharedImpl { - private: - Config( Config const& other ); - Config& operator = ( Config const& other ); - virtual void dummy(); + class Config : public IConfig { public: - Config() - {} + Config() = default; + Config( ConfigData const& data ); + virtual ~Config() = default; - Config( ConfigData const& data ) - : m_data( data ), - m_stream( openStream() ) - { - if( !data.testsOrTags.empty() ) { - TestSpecParser parser( ITagAliasRegistry::get() ); - for( std::size_t i = 0; i < data.testsOrTags.size(); ++i ) - parser.parse( data.testsOrTags[i] ); - m_testSpec = parser.testSpec(); - } - } + std::string const& getFilename() const; - virtual ~Config() {} + bool listTests() const; + bool listTestNamesOnly() const; + bool listTags() const; + bool listReporters() const; - std::string const& getFilename() const { - return m_data.outputFilename ; - } + std::string getProcessName() const; - bool listTests() const { return m_data.listTests; } - bool listTestNamesOnly() const { return m_data.listTestNamesOnly; } - bool listTags() const { return m_data.listTags; } - bool listReporters() const { return m_data.listReporters; } - bool listExtraInfo() const { return m_data.listExtraInfo; } + std::vector const& getReporterNames() const; + std::vector const& getSectionsToRun() const override; - std::string getProcessName() const { return m_data.processName; } + virtual TestSpec const& testSpec() const override; - std::vector const& getReporterNames() const { return m_data.reporterNames; } - std::vector const& getSectionsToRun() const CATCH_OVERRIDE { return m_data.sectionsToRun; } - - virtual TestSpec const& testSpec() const CATCH_OVERRIDE { return m_testSpec; } - - bool showHelp() const { return m_data.showHelp; } + bool showHelp() const; // IConfig interface - virtual bool allowThrows() const CATCH_OVERRIDE { return !m_data.noThrow; } - virtual std::ostream& stream() const CATCH_OVERRIDE { return m_stream->stream(); } - virtual std::string name() const CATCH_OVERRIDE { return m_data.name.empty() ? m_data.processName : m_data.name; } - virtual bool includeSuccessfulResults() const CATCH_OVERRIDE { return m_data.showSuccessfulTests; } - virtual bool warnAboutMissingAssertions() const CATCH_OVERRIDE { return m_data.warnings & WarnAbout::NoAssertions; } - virtual ShowDurations::OrNot showDurations() const CATCH_OVERRIDE { return m_data.showDurations; } - virtual RunTests::InWhatOrder runOrder() const CATCH_OVERRIDE { return m_data.runOrder; } - virtual unsigned int rngSeed() const CATCH_OVERRIDE { return m_data.rngSeed; } - virtual UseColour::YesOrNo useColour() const CATCH_OVERRIDE { return m_data.useColour; } - virtual bool shouldDebugBreak() const CATCH_OVERRIDE { return m_data.shouldDebugBreak; } - virtual int abortAfter() const CATCH_OVERRIDE { return m_data.abortAfter; } - virtual bool showInvisibles() const CATCH_OVERRIDE { return m_data.showInvisibles; } + bool allowThrows() const override; + std::ostream& stream() const override; + std::string name() const override; + bool includeSuccessfulResults() const override; + bool warnAboutMissingAssertions() const override; + ShowDurations::OrNot showDurations() const override; + RunTests::InWhatOrder runOrder() const override; + unsigned int rngSeed() const override; + int benchmarkResolutionMultiple() const override; + UseColour::YesOrNo useColour() const override; + bool shouldDebugBreak() const override; + int abortAfter() const override; + bool showInvisibles() const override; + Verbosity verbosity() const override; private: - IStream const* openStream() { - if( m_data.outputFilename.empty() ) - return new CoutStream(); - else if( m_data.outputFilename[0] == '%' ) { - if( m_data.outputFilename == "%debug" ) - return new DebugOutStream(); - else - throw std::domain_error( "Unrecognised stream: " + m_data.outputFilename ); - } - else - return new FileStream( m_data.outputFilename ); - } + IStream const* openStream(); ConfigData m_data; - CATCH_AUTO_PTR( IStream const ) m_stream; + std::unique_ptr m_stream; TestSpec m_testSpec; }; } // end namespace Catch -// #included from: catch_clara.h -#define TWOBLUECUBES_CATCH_CLARA_H_INCLUDED - -// Use Catch's value for console width (store Clara's off to the side, if present) -#ifdef CLARA_CONFIG_CONSOLE_WIDTH -#define CATCH_TEMP_CLARA_CONFIG_CONSOLE_WIDTH CLARA_CONFIG_CONSOLE_WIDTH -#undef CLARA_CONFIG_CONSOLE_WIDTH -#endif -#define CLARA_CONFIG_CONSOLE_WIDTH CATCH_CONFIG_CONSOLE_WIDTH - -// Declare Clara inside the Catch namespace -#define STITCH_CLARA_OPEN_NAMESPACE namespace Catch { -// #included from: ../external/clara.h - -// Version 0.0.2.4 - -// Only use header guard if we are not using an outer namespace -#if !defined(TWOBLUECUBES_CLARA_H_INCLUDED) || defined(STITCH_CLARA_OPEN_NAMESPACE) - -#ifndef STITCH_CLARA_OPEN_NAMESPACE -#define TWOBLUECUBES_CLARA_H_INCLUDED -#define STITCH_CLARA_OPEN_NAMESPACE -#define STITCH_CLARA_CLOSE_NAMESPACE -#else -#define STITCH_CLARA_CLOSE_NAMESPACE } -#endif - -#define STITCH_TBC_TEXT_FORMAT_OPEN_NAMESPACE STITCH_CLARA_OPEN_NAMESPACE - -// ----------- #included from tbc_text_format.h ----------- - -// Only use header guard if we are not using an outer namespace -#if !defined(TBC_TEXT_FORMAT_H_INCLUDED) || defined(STITCH_TBC_TEXT_FORMAT_OUTER_NAMESPACE) -#ifndef STITCH_TBC_TEXT_FORMAT_OUTER_NAMESPACE -#define TBC_TEXT_FORMAT_H_INCLUDED -#endif +// end catch_config.hpp +// start catch_assertionresult.h #include -#include -#include -#include -#include - -// Use optional outer namespace -#ifdef STITCH_TBC_TEXT_FORMAT_OUTER_NAMESPACE -namespace STITCH_TBC_TEXT_FORMAT_OUTER_NAMESPACE { -#endif - -namespace Tbc { - -#ifdef TBC_TEXT_FORMAT_CONSOLE_WIDTH - const unsigned int consoleWidth = TBC_TEXT_FORMAT_CONSOLE_WIDTH; -#else - const unsigned int consoleWidth = 80; -#endif - - struct TextAttributes { - TextAttributes() - : initialIndent( std::string::npos ), - indent( 0 ), - width( consoleWidth-1 ), - tabChar( '\t' ) - {} - - TextAttributes& setInitialIndent( std::size_t _value ) { initialIndent = _value; return *this; } - TextAttributes& setIndent( std::size_t _value ) { indent = _value; return *this; } - TextAttributes& setWidth( std::size_t _value ) { width = _value; return *this; } - TextAttributes& setTabChar( char _value ) { tabChar = _value; return *this; } - - std::size_t initialIndent; // indent of first line, or npos - std::size_t indent; // indent of subsequent lines, or all if initialIndent is npos - std::size_t width; // maximum width of text, including indent. Longer text will wrap - char tabChar; // If this char is seen the indent is changed to current pos - }; - - class Text { - public: - Text( std::string const& _str, TextAttributes const& _attr = TextAttributes() ) - : attr( _attr ) - { - std::string wrappableChars = " [({.,/|\\-"; - std::size_t indent = _attr.initialIndent != std::string::npos - ? _attr.initialIndent - : _attr.indent; - std::string remainder = _str; - - while( !remainder.empty() ) { - if( lines.size() >= 1000 ) { - lines.push_back( "... message truncated due to excessive size" ); - return; - } - std::size_t tabPos = std::string::npos; - std::size_t width = (std::min)( remainder.size(), _attr.width - indent ); - std::size_t pos = remainder.find_first_of( '\n' ); - if( pos <= width ) { - width = pos; - } - pos = remainder.find_last_of( _attr.tabChar, width ); - if( pos != std::string::npos ) { - tabPos = pos; - if( remainder[width] == '\n' ) - width--; - remainder = remainder.substr( 0, tabPos ) + remainder.substr( tabPos+1 ); - } - - if( width == remainder.size() ) { - spliceLine( indent, remainder, width ); - } - else if( remainder[width] == '\n' ) { - spliceLine( indent, remainder, width ); - if( width <= 1 || remainder.size() != 1 ) - remainder = remainder.substr( 1 ); - indent = _attr.indent; - } - else { - pos = remainder.find_last_of( wrappableChars, width ); - if( pos != std::string::npos && pos > 0 ) { - spliceLine( indent, remainder, pos ); - if( remainder[0] == ' ' ) - remainder = remainder.substr( 1 ); - } - else { - spliceLine( indent, remainder, width-1 ); - lines.back() += "-"; - } - if( lines.size() == 1 ) - indent = _attr.indent; - if( tabPos != std::string::npos ) - indent += tabPos; - } - } - } - - void spliceLine( std::size_t _indent, std::string& _remainder, std::size_t _pos ) { - lines.push_back( std::string( _indent, ' ' ) + _remainder.substr( 0, _pos ) ); - _remainder = _remainder.substr( _pos ); - } - - typedef std::vector::const_iterator const_iterator; - - const_iterator begin() const { return lines.begin(); } - const_iterator end() const { return lines.end(); } - std::string const& last() const { return lines.back(); } - std::size_t size() const { return lines.size(); } - std::string const& operator[]( std::size_t _index ) const { return lines[_index]; } - std::string toString() const { - std::ostringstream oss; - oss << *this; - return oss.str(); - } - - friend std::ostream& operator << ( std::ostream& _stream, Text const& _text ) { - for( Text::const_iterator it = _text.begin(), itEnd = _text.end(); - it != itEnd; ++it ) { - if( it != _text.begin() ) - _stream << "\n"; - _stream << *it; - } - return _stream; - } - - private: - std::string str; - TextAttributes attr; - std::vector lines; - }; - -} // end namespace Tbc - -#ifdef STITCH_TBC_TEXT_FORMAT_OUTER_NAMESPACE -} // end outer namespace -#endif - -#endif // TBC_TEXT_FORMAT_H_INCLUDED - -// ----------- end of #include from tbc_text_format.h ----------- -// ........... back in clara.h - -#undef STITCH_TBC_TEXT_FORMAT_OPEN_NAMESPACE - -// ----------- #included from clara_compilers.h ----------- - -#ifndef TWOBLUECUBES_CLARA_COMPILERS_H_INCLUDED -#define TWOBLUECUBES_CLARA_COMPILERS_H_INCLUDED - -// Detect a number of compiler features - mostly C++11/14 conformance - by compiler -// The following features are defined: -// -// CLARA_CONFIG_CPP11_NULLPTR : is nullptr supported? -// CLARA_CONFIG_CPP11_NOEXCEPT : is noexcept supported? -// CLARA_CONFIG_CPP11_GENERATED_METHODS : The delete and default keywords for compiler generated methods -// CLARA_CONFIG_CPP11_OVERRIDE : is override supported? -// CLARA_CONFIG_CPP11_UNIQUE_PTR : is unique_ptr supported (otherwise use auto_ptr) - -// CLARA_CONFIG_CPP11_OR_GREATER : Is C++11 supported? - -// CLARA_CONFIG_VARIADIC_MACROS : are variadic macros supported? - -// In general each macro has a _NO_ form -// (e.g. CLARA_CONFIG_CPP11_NO_NULLPTR) which disables the feature. -// Many features, at point of detection, define an _INTERNAL_ macro, so they -// can be combined, en-mass, with the _NO_ forms later. - -// All the C++11 features can be disabled with CLARA_CONFIG_NO_CPP11 - -#ifdef __clang__ - -#if __has_feature(cxx_nullptr) -#define CLARA_INTERNAL_CONFIG_CPP11_NULLPTR -#endif - -#if __has_feature(cxx_noexcept) -#define CLARA_INTERNAL_CONFIG_CPP11_NOEXCEPT -#endif - -#endif // __clang__ - -//////////////////////////////////////////////////////////////////////////////// -// GCC -#ifdef __GNUC__ - -#if __GNUC__ == 4 && __GNUC_MINOR__ >= 6 && defined(__GXX_EXPERIMENTAL_CXX0X__) -#define CLARA_INTERNAL_CONFIG_CPP11_NULLPTR -#endif - -// - otherwise more recent versions define __cplusplus >= 201103L -// and will get picked up below - -#endif // __GNUC__ - -//////////////////////////////////////////////////////////////////////////////// -// Visual C++ -#ifdef _MSC_VER - -#if (_MSC_VER >= 1600) -#define CLARA_INTERNAL_CONFIG_CPP11_NULLPTR -#define CLARA_INTERNAL_CONFIG_CPP11_UNIQUE_PTR -#endif - -#if (_MSC_VER >= 1900 ) // (VC++ 13 (VS2015)) -#define CLARA_INTERNAL_CONFIG_CPP11_NOEXCEPT -#define CLARA_INTERNAL_CONFIG_CPP11_GENERATED_METHODS -#endif - -#endif // _MSC_VER - -//////////////////////////////////////////////////////////////////////////////// -// C++ language feature support - -// catch all support for C++11 -#if defined(__cplusplus) && __cplusplus >= 201103L - -#define CLARA_CPP11_OR_GREATER - -#if !defined(CLARA_INTERNAL_CONFIG_CPP11_NULLPTR) -#define CLARA_INTERNAL_CONFIG_CPP11_NULLPTR -#endif - -#ifndef CLARA_INTERNAL_CONFIG_CPP11_NOEXCEPT -#define CLARA_INTERNAL_CONFIG_CPP11_NOEXCEPT -#endif - -#ifndef CLARA_INTERNAL_CONFIG_CPP11_GENERATED_METHODS -#define CLARA_INTERNAL_CONFIG_CPP11_GENERATED_METHODS -#endif - -#if !defined(CLARA_INTERNAL_CONFIG_CPP11_OVERRIDE) -#define CLARA_INTERNAL_CONFIG_CPP11_OVERRIDE -#endif -#if !defined(CLARA_INTERNAL_CONFIG_CPP11_UNIQUE_PTR) -#define CLARA_INTERNAL_CONFIG_CPP11_UNIQUE_PTR -#endif - -#endif // __cplusplus >= 201103L - -// Now set the actual defines based on the above + anything the user has configured -#if defined(CLARA_INTERNAL_CONFIG_CPP11_NULLPTR) && !defined(CLARA_CONFIG_CPP11_NO_NULLPTR) && !defined(CLARA_CONFIG_CPP11_NULLPTR) && !defined(CLARA_CONFIG_NO_CPP11) -#define CLARA_CONFIG_CPP11_NULLPTR -#endif -#if defined(CLARA_INTERNAL_CONFIG_CPP11_NOEXCEPT) && !defined(CLARA_CONFIG_CPP11_NO_NOEXCEPT) && !defined(CLARA_CONFIG_CPP11_NOEXCEPT) && !defined(CLARA_CONFIG_NO_CPP11) -#define CLARA_CONFIG_CPP11_NOEXCEPT -#endif -#if defined(CLARA_INTERNAL_CONFIG_CPP11_GENERATED_METHODS) && !defined(CLARA_CONFIG_CPP11_NO_GENERATED_METHODS) && !defined(CLARA_CONFIG_CPP11_GENERATED_METHODS) && !defined(CLARA_CONFIG_NO_CPP11) -#define CLARA_CONFIG_CPP11_GENERATED_METHODS -#endif -#if defined(CLARA_INTERNAL_CONFIG_CPP11_OVERRIDE) && !defined(CLARA_CONFIG_NO_OVERRIDE) && !defined(CLARA_CONFIG_CPP11_OVERRIDE) && !defined(CLARA_CONFIG_NO_CPP11) -#define CLARA_CONFIG_CPP11_OVERRIDE -#endif -#if defined(CLARA_INTERNAL_CONFIG_CPP11_UNIQUE_PTR) && !defined(CLARA_CONFIG_NO_UNIQUE_PTR) && !defined(CLARA_CONFIG_CPP11_UNIQUE_PTR) && !defined(CLARA_CONFIG_NO_CPP11) -#define CLARA_CONFIG_CPP11_UNIQUE_PTR -#endif - -// noexcept support: -#if defined(CLARA_CONFIG_CPP11_NOEXCEPT) && !defined(CLARA_NOEXCEPT) -#define CLARA_NOEXCEPT noexcept -# define CLARA_NOEXCEPT_IS(x) noexcept(x) -#else -#define CLARA_NOEXCEPT throw() -# define CLARA_NOEXCEPT_IS(x) -#endif - -// nullptr support -#ifdef CLARA_CONFIG_CPP11_NULLPTR -#define CLARA_NULL nullptr -#else -#define CLARA_NULL NULL -#endif - -// override support -#ifdef CLARA_CONFIG_CPP11_OVERRIDE -#define CLARA_OVERRIDE override -#else -#define CLARA_OVERRIDE -#endif - -// unique_ptr support -#ifdef CLARA_CONFIG_CPP11_UNIQUE_PTR -# define CLARA_AUTO_PTR( T ) std::unique_ptr -#else -# define CLARA_AUTO_PTR( T ) std::auto_ptr -#endif - -#endif // TWOBLUECUBES_CLARA_COMPILERS_H_INCLUDED - -// ----------- end of #include from clara_compilers.h ----------- -// ........... back in clara.h - -#include -#include -#include - -#if defined(WIN32) || defined(__WIN32__) || defined(_WIN32) || defined(_MSC_VER) -#define CLARA_PLATFORM_WINDOWS -#endif - -// Use optional outer namespace -#ifdef STITCH_CLARA_OPEN_NAMESPACE -STITCH_CLARA_OPEN_NAMESPACE -#endif - -namespace Clara { - - struct UnpositionalTag {}; - - extern UnpositionalTag _; - -#ifdef CLARA_CONFIG_MAIN - UnpositionalTag _; -#endif - - namespace Detail { - -#ifdef CLARA_CONSOLE_WIDTH - const unsigned int consoleWidth = CLARA_CONFIG_CONSOLE_WIDTH; -#else - const unsigned int consoleWidth = 80; -#endif - - using namespace Tbc; - - inline bool startsWith( std::string const& str, std::string const& prefix ) { - return str.size() >= prefix.size() && str.substr( 0, prefix.size() ) == prefix; - } - - template struct RemoveConstRef{ typedef T type; }; - template struct RemoveConstRef{ typedef T type; }; - template struct RemoveConstRef{ typedef T type; }; - template struct RemoveConstRef{ typedef T type; }; - - template struct IsBool { static const bool value = false; }; - template<> struct IsBool { static const bool value = true; }; - - template - void convertInto( std::string const& _source, T& _dest ) { - std::stringstream ss; - ss << _source; - ss >> _dest; - if( ss.fail() ) - throw std::runtime_error( "Unable to convert " + _source + " to destination type" ); - } - inline void convertInto( std::string const& _source, std::string& _dest ) { - _dest = _source; - } - char toLowerCh(char c) { - return static_cast( std::tolower( c ) ); - } - inline void convertInto( std::string const& _source, bool& _dest ) { - std::string sourceLC = _source; - std::transform( sourceLC.begin(), sourceLC.end(), sourceLC.begin(), toLowerCh ); - if( sourceLC == "y" || sourceLC == "1" || sourceLC == "true" || sourceLC == "yes" || sourceLC == "on" ) - _dest = true; - else if( sourceLC == "n" || sourceLC == "0" || sourceLC == "false" || sourceLC == "no" || sourceLC == "off" ) - _dest = false; - else - throw std::runtime_error( "Expected a boolean value but did not recognise:\n '" + _source + "'" ); - } - - template - struct IArgFunction { - virtual ~IArgFunction() {} -#ifdef CLARA_CONFIG_CPP11_GENERATED_METHODS - IArgFunction() = default; - IArgFunction( IArgFunction const& ) = default; -#endif - virtual void set( ConfigT& config, std::string const& value ) const = 0; - virtual bool takesArg() const = 0; - virtual IArgFunction* clone() const = 0; - }; - - template - class BoundArgFunction { - public: - BoundArgFunction() : functionObj( CLARA_NULL ) {} - BoundArgFunction( IArgFunction* _functionObj ) : functionObj( _functionObj ) {} - BoundArgFunction( BoundArgFunction const& other ) : functionObj( other.functionObj ? other.functionObj->clone() : CLARA_NULL ) {} - BoundArgFunction& operator = ( BoundArgFunction const& other ) { - IArgFunction* newFunctionObj = other.functionObj ? other.functionObj->clone() : CLARA_NULL; - delete functionObj; - functionObj = newFunctionObj; - return *this; - } - ~BoundArgFunction() { delete functionObj; } - - void set( ConfigT& config, std::string const& value ) const { - functionObj->set( config, value ); - } - bool takesArg() const { return functionObj->takesArg(); } - - bool isSet() const { - return functionObj != CLARA_NULL; - } - private: - IArgFunction* functionObj; - }; - - template - struct NullBinder : IArgFunction{ - virtual void set( C&, std::string const& ) const {} - virtual bool takesArg() const { return true; } - virtual IArgFunction* clone() const { return new NullBinder( *this ); } - }; - - template - struct BoundDataMember : IArgFunction{ - BoundDataMember( M C::* _member ) : member( _member ) {} - virtual void set( C& p, std::string const& stringValue ) const { - convertInto( stringValue, p.*member ); - } - virtual bool takesArg() const { return !IsBool::value; } - virtual IArgFunction* clone() const { return new BoundDataMember( *this ); } - M C::* member; - }; - template - struct BoundUnaryMethod : IArgFunction{ - BoundUnaryMethod( void (C::*_member)( M ) ) : member( _member ) {} - virtual void set( C& p, std::string const& stringValue ) const { - typename RemoveConstRef::type value; - convertInto( stringValue, value ); - (p.*member)( value ); - } - virtual bool takesArg() const { return !IsBool::value; } - virtual IArgFunction* clone() const { return new BoundUnaryMethod( *this ); } - void (C::*member)( M ); - }; - template - struct BoundNullaryMethod : IArgFunction{ - BoundNullaryMethod( void (C::*_member)() ) : member( _member ) {} - virtual void set( C& p, std::string const& stringValue ) const { - bool value; - convertInto( stringValue, value ); - if( value ) - (p.*member)(); - } - virtual bool takesArg() const { return false; } - virtual IArgFunction* clone() const { return new BoundNullaryMethod( *this ); } - void (C::*member)(); - }; - - template - struct BoundUnaryFunction : IArgFunction{ - BoundUnaryFunction( void (*_function)( C& ) ) : function( _function ) {} - virtual void set( C& obj, std::string const& stringValue ) const { - bool value; - convertInto( stringValue, value ); - if( value ) - function( obj ); - } - virtual bool takesArg() const { return false; } - virtual IArgFunction* clone() const { return new BoundUnaryFunction( *this ); } - void (*function)( C& ); - }; - - template - struct BoundBinaryFunction : IArgFunction{ - BoundBinaryFunction( void (*_function)( C&, T ) ) : function( _function ) {} - virtual void set( C& obj, std::string const& stringValue ) const { - typename RemoveConstRef::type value; - convertInto( stringValue, value ); - function( obj, value ); - } - virtual bool takesArg() const { return !IsBool::value; } - virtual IArgFunction* clone() const { return new BoundBinaryFunction( *this ); } - void (*function)( C&, T ); - }; - - } // namespace Detail - - inline std::vector argsToVector( int argc, char const* const* const argv ) { - std::vector args( static_cast( argc ) ); - for( std::size_t i = 0; i < static_cast( argc ); ++i ) - args[i] = argv[i]; - - return args; - } - - class Parser { - enum Mode { None, MaybeShortOpt, SlashOpt, ShortOpt, LongOpt, Positional }; - Mode mode; - std::size_t from; - bool inQuotes; - public: - - struct Token { - enum Type { Positional, ShortOpt, LongOpt }; - Token( Type _type, std::string const& _data ) : type( _type ), data( _data ) {} - Type type; - std::string data; - }; - - Parser() : mode( None ), from( 0 ), inQuotes( false ){} - - void parseIntoTokens( std::vector const& args, std::vector& tokens ) { - const std::string doubleDash = "--"; - for( std::size_t i = 1; i < args.size() && args[i] != doubleDash; ++i ) - parseIntoTokens( args[i], tokens); - } - - void parseIntoTokens( std::string const& arg, std::vector& tokens ) { - for( std::size_t i = 0; i < arg.size(); ++i ) { - char c = arg[i]; - if( c == '"' ) - inQuotes = !inQuotes; - mode = handleMode( i, c, arg, tokens ); - } - mode = handleMode( arg.size(), '\0', arg, tokens ); - } - Mode handleMode( std::size_t i, char c, std::string const& arg, std::vector& tokens ) { - switch( mode ) { - case None: return handleNone( i, c ); - case MaybeShortOpt: return handleMaybeShortOpt( i, c ); - case ShortOpt: - case LongOpt: - case SlashOpt: return handleOpt( i, c, arg, tokens ); - case Positional: return handlePositional( i, c, arg, tokens ); - default: throw std::logic_error( "Unknown mode" ); - } - } - - Mode handleNone( std::size_t i, char c ) { - if( inQuotes ) { - from = i; - return Positional; - } - switch( c ) { - case '-': return MaybeShortOpt; -#ifdef CLARA_PLATFORM_WINDOWS - case '/': from = i+1; return SlashOpt; -#endif - default: from = i; return Positional; - } - } - Mode handleMaybeShortOpt( std::size_t i, char c ) { - switch( c ) { - case '-': from = i+1; return LongOpt; - default: from = i; return ShortOpt; - } - } - - Mode handleOpt( std::size_t i, char c, std::string const& arg, std::vector& tokens ) { - if( std::string( ":=\0", 3 ).find( c ) == std::string::npos ) - return mode; - - std::string optName = arg.substr( from, i-from ); - if( mode == ShortOpt ) - for( std::size_t j = 0; j < optName.size(); ++j ) - tokens.push_back( Token( Token::ShortOpt, optName.substr( j, 1 ) ) ); - else if( mode == SlashOpt && optName.size() == 1 ) - tokens.push_back( Token( Token::ShortOpt, optName ) ); - else - tokens.push_back( Token( Token::LongOpt, optName ) ); - return None; - } - Mode handlePositional( std::size_t i, char c, std::string const& arg, std::vector& tokens ) { - if( inQuotes || std::string( "\0", 1 ).find( c ) == std::string::npos ) - return mode; - - std::string data = arg.substr( from, i-from ); - tokens.push_back( Token( Token::Positional, data ) ); - return None; - } - }; - - template - struct CommonArgProperties { - CommonArgProperties() {} - CommonArgProperties( Detail::BoundArgFunction const& _boundField ) : boundField( _boundField ) {} - - Detail::BoundArgFunction boundField; - std::string description; - std::string detail; - std::string placeholder; // Only value if boundField takes an arg - - bool takesArg() const { - return !placeholder.empty(); - } - void validate() const { - if( !boundField.isSet() ) - throw std::logic_error( "option not bound" ); - } - }; - struct OptionArgProperties { - std::vector shortNames; - std::string longName; - - bool hasShortName( std::string const& shortName ) const { - return std::find( shortNames.begin(), shortNames.end(), shortName ) != shortNames.end(); - } - bool hasLongName( std::string const& _longName ) const { - return _longName == longName; - } - }; - struct PositionalArgProperties { - PositionalArgProperties() : position( -1 ) {} - int position; // -1 means non-positional (floating) - - bool isFixedPositional() const { - return position != -1; - } - }; - - template - class CommandLine { - - struct Arg : CommonArgProperties, OptionArgProperties, PositionalArgProperties { - Arg() {} - Arg( Detail::BoundArgFunction const& _boundField ) : CommonArgProperties( _boundField ) {} - - using CommonArgProperties::placeholder; // !TBD - - std::string dbgName() const { - if( !longName.empty() ) - return "--" + longName; - if( !shortNames.empty() ) - return "-" + shortNames[0]; - return "positional args"; - } - std::string commands() const { - std::ostringstream oss; - bool first = true; - std::vector::const_iterator it = shortNames.begin(), itEnd = shortNames.end(); - for(; it != itEnd; ++it ) { - if( first ) - first = false; - else - oss << ", "; - oss << "-" << *it; - } - if( !longName.empty() ) { - if( !first ) - oss << ", "; - oss << "--" << longName; - } - if( !placeholder.empty() ) - oss << " <" << placeholder << ">"; - return oss.str(); - } - }; - - typedef CLARA_AUTO_PTR( Arg ) ArgAutoPtr; - - friend void addOptName( Arg& arg, std::string const& optName ) - { - if( optName.empty() ) - return; - if( Detail::startsWith( optName, "--" ) ) { - if( !arg.longName.empty() ) - throw std::logic_error( "Only one long opt may be specified. '" - + arg.longName - + "' already specified, now attempting to add '" - + optName + "'" ); - arg.longName = optName.substr( 2 ); - } - else if( Detail::startsWith( optName, "-" ) ) - arg.shortNames.push_back( optName.substr( 1 ) ); - else - throw std::logic_error( "option must begin with - or --. Option was: '" + optName + "'" ); - } - friend void setPositionalArg( Arg& arg, int position ) - { - arg.position = position; - } - - class ArgBuilder { - public: - ArgBuilder( Arg* arg ) : m_arg( arg ) {} - - // Bind a non-boolean data member (requires placeholder string) - template - void bind( M C::* field, std::string const& placeholder ) { - m_arg->boundField = new Detail::BoundDataMember( field ); - m_arg->placeholder = placeholder; - } - // Bind a boolean data member (no placeholder required) - template - void bind( bool C::* field ) { - m_arg->boundField = new Detail::BoundDataMember( field ); - } - - // Bind a method taking a single, non-boolean argument (requires a placeholder string) - template - void bind( void (C::* unaryMethod)( M ), std::string const& placeholder ) { - m_arg->boundField = new Detail::BoundUnaryMethod( unaryMethod ); - m_arg->placeholder = placeholder; - } - - // Bind a method taking a single, boolean argument (no placeholder string required) - template - void bind( void (C::* unaryMethod)( bool ) ) { - m_arg->boundField = new Detail::BoundUnaryMethod( unaryMethod ); - } - - // Bind a method that takes no arguments (will be called if opt is present) - template - void bind( void (C::* nullaryMethod)() ) { - m_arg->boundField = new Detail::BoundNullaryMethod( nullaryMethod ); - } - - // Bind a free function taking a single argument - the object to operate on (no placeholder string required) - template - void bind( void (* unaryFunction)( C& ) ) { - m_arg->boundField = new Detail::BoundUnaryFunction( unaryFunction ); - } - - // Bind a free function taking a single argument - the object to operate on (requires a placeholder string) - template - void bind( void (* binaryFunction)( C&, T ), std::string const& placeholder ) { - m_arg->boundField = new Detail::BoundBinaryFunction( binaryFunction ); - m_arg->placeholder = placeholder; - } - - ArgBuilder& describe( std::string const& description ) { - m_arg->description = description; - return *this; - } - ArgBuilder& detail( std::string const& detail ) { - m_arg->detail = detail; - return *this; - } - - protected: - Arg* m_arg; - }; - - class OptBuilder : public ArgBuilder { - public: - OptBuilder( Arg* arg ) : ArgBuilder( arg ) {} - OptBuilder( OptBuilder& other ) : ArgBuilder( other ) {} - - OptBuilder& operator[]( std::string const& optName ) { - addOptName( *ArgBuilder::m_arg, optName ); - return *this; - } - }; - - public: - - CommandLine() - : m_boundProcessName( new Detail::NullBinder() ), - m_highestSpecifiedArgPosition( 0 ), - m_throwOnUnrecognisedTokens( false ) - {} - CommandLine( CommandLine const& other ) - : m_boundProcessName( other.m_boundProcessName ), - m_options ( other.m_options ), - m_positionalArgs( other.m_positionalArgs ), - m_highestSpecifiedArgPosition( other.m_highestSpecifiedArgPosition ), - m_throwOnUnrecognisedTokens( other.m_throwOnUnrecognisedTokens ) - { - if( other.m_floatingArg.get() ) - m_floatingArg.reset( new Arg( *other.m_floatingArg ) ); - } - - CommandLine& setThrowOnUnrecognisedTokens( bool shouldThrow = true ) { - m_throwOnUnrecognisedTokens = shouldThrow; - return *this; - } - - OptBuilder operator[]( std::string const& optName ) { - m_options.push_back( Arg() ); - addOptName( m_options.back(), optName ); - OptBuilder builder( &m_options.back() ); - return builder; - } - - ArgBuilder operator[]( int position ) { - m_positionalArgs.insert( std::make_pair( position, Arg() ) ); - if( position > m_highestSpecifiedArgPosition ) - m_highestSpecifiedArgPosition = position; - setPositionalArg( m_positionalArgs[position], position ); - ArgBuilder builder( &m_positionalArgs[position] ); - return builder; - } - - // Invoke this with the _ instance - ArgBuilder operator[]( UnpositionalTag ) { - if( m_floatingArg.get() ) - throw std::logic_error( "Only one unpositional argument can be added" ); - m_floatingArg.reset( new Arg() ); - ArgBuilder builder( m_floatingArg.get() ); - return builder; - } - - template - void bindProcessName( M C::* field ) { - m_boundProcessName = new Detail::BoundDataMember( field ); - } - template - void bindProcessName( void (C::*_unaryMethod)( M ) ) { - m_boundProcessName = new Detail::BoundUnaryMethod( _unaryMethod ); - } - - void optUsage( std::ostream& os, std::size_t indent = 0, std::size_t width = Detail::consoleWidth ) const { - typename std::vector::const_iterator itBegin = m_options.begin(), itEnd = m_options.end(), it; - std::size_t maxWidth = 0; - for( it = itBegin; it != itEnd; ++it ) - maxWidth = (std::max)( maxWidth, it->commands().size() ); - - for( it = itBegin; it != itEnd; ++it ) { - Detail::Text usage( it->commands(), Detail::TextAttributes() - .setWidth( maxWidth+indent ) - .setIndent( indent ) ); - Detail::Text desc( it->description, Detail::TextAttributes() - .setWidth( width - maxWidth - 3 ) ); - - for( std::size_t i = 0; i < (std::max)( usage.size(), desc.size() ); ++i ) { - std::string usageCol = i < usage.size() ? usage[i] : ""; - os << usageCol; - - if( i < desc.size() && !desc[i].empty() ) - os << std::string( indent + 2 + maxWidth - usageCol.size(), ' ' ) - << desc[i]; - os << "\n"; - } - } - } - std::string optUsage() const { - std::ostringstream oss; - optUsage( oss ); - return oss.str(); - } - - void argSynopsis( std::ostream& os ) const { - for( int i = 1; i <= m_highestSpecifiedArgPosition; ++i ) { - if( i > 1 ) - os << " "; - typename std::map::const_iterator it = m_positionalArgs.find( i ); - if( it != m_positionalArgs.end() ) - os << "<" << it->second.placeholder << ">"; - else if( m_floatingArg.get() ) - os << "<" << m_floatingArg->placeholder << ">"; - else - throw std::logic_error( "non consecutive positional arguments with no floating args" ); - } - // !TBD No indication of mandatory args - if( m_floatingArg.get() ) { - if( m_highestSpecifiedArgPosition > 1 ) - os << " "; - os << "[<" << m_floatingArg->placeholder << "> ...]"; - } - } - std::string argSynopsis() const { - std::ostringstream oss; - argSynopsis( oss ); - return oss.str(); - } - - void usage( std::ostream& os, std::string const& procName ) const { - validate(); - os << "usage:\n " << procName << " "; - argSynopsis( os ); - if( !m_options.empty() ) { - os << " [options]\n\nwhere options are: \n"; - optUsage( os, 2 ); - } - os << "\n"; - } - std::string usage( std::string const& procName ) const { - std::ostringstream oss; - usage( oss, procName ); - return oss.str(); - } - - ConfigT parse( std::vector const& args ) const { - ConfigT config; - parseInto( args, config ); - return config; - } - - std::vector parseInto( std::vector const& args, ConfigT& config ) const { - std::string processName = args.empty() ? std::string() : args[0]; - std::size_t lastSlash = processName.find_last_of( "/\\" ); - if( lastSlash != std::string::npos ) - processName = processName.substr( lastSlash+1 ); - m_boundProcessName.set( config, processName ); - std::vector tokens; - Parser parser; - parser.parseIntoTokens( args, tokens ); - return populate( tokens, config ); - } - - std::vector populate( std::vector const& tokens, ConfigT& config ) const { - validate(); - std::vector unusedTokens = populateOptions( tokens, config ); - unusedTokens = populateFixedArgs( unusedTokens, config ); - unusedTokens = populateFloatingArgs( unusedTokens, config ); - return unusedTokens; - } - - std::vector populateOptions( std::vector const& tokens, ConfigT& config ) const { - std::vector unusedTokens; - std::vector errors; - for( std::size_t i = 0; i < tokens.size(); ++i ) { - Parser::Token const& token = tokens[i]; - typename std::vector::const_iterator it = m_options.begin(), itEnd = m_options.end(); - for(; it != itEnd; ++it ) { - Arg const& arg = *it; - - try { - if( ( token.type == Parser::Token::ShortOpt && arg.hasShortName( token.data ) ) || - ( token.type == Parser::Token::LongOpt && arg.hasLongName( token.data ) ) ) { - if( arg.takesArg() ) { - if( i == tokens.size()-1 || tokens[i+1].type != Parser::Token::Positional ) - errors.push_back( "Expected argument to option: " + token.data ); - else - arg.boundField.set( config, tokens[++i].data ); - } - else { - arg.boundField.set( config, "true" ); - } - break; - } - } - catch( std::exception& ex ) { - errors.push_back( std::string( ex.what() ) + "\n- while parsing: (" + arg.commands() + ")" ); - } - } - if( it == itEnd ) { - if( token.type == Parser::Token::Positional || !m_throwOnUnrecognisedTokens ) - unusedTokens.push_back( token ); - else if( errors.empty() && m_throwOnUnrecognisedTokens ) - errors.push_back( "unrecognised option: " + token.data ); - } - } - if( !errors.empty() ) { - std::ostringstream oss; - for( std::vector::const_iterator it = errors.begin(), itEnd = errors.end(); - it != itEnd; - ++it ) { - if( it != errors.begin() ) - oss << "\n"; - oss << *it; - } - throw std::runtime_error( oss.str() ); - } - return unusedTokens; - } - std::vector populateFixedArgs( std::vector const& tokens, ConfigT& config ) const { - std::vector unusedTokens; - int position = 1; - for( std::size_t i = 0; i < tokens.size(); ++i ) { - Parser::Token const& token = tokens[i]; - typename std::map::const_iterator it = m_positionalArgs.find( position ); - if( it != m_positionalArgs.end() ) - it->second.boundField.set( config, token.data ); - else - unusedTokens.push_back( token ); - if( token.type == Parser::Token::Positional ) - position++; - } - return unusedTokens; - } - std::vector populateFloatingArgs( std::vector const& tokens, ConfigT& config ) const { - if( !m_floatingArg.get() ) - return tokens; - std::vector unusedTokens; - for( std::size_t i = 0; i < tokens.size(); ++i ) { - Parser::Token const& token = tokens[i]; - if( token.type == Parser::Token::Positional ) - m_floatingArg->boundField.set( config, token.data ); - else - unusedTokens.push_back( token ); - } - return unusedTokens; - } - - void validate() const - { - if( m_options.empty() && m_positionalArgs.empty() && !m_floatingArg.get() ) - throw std::logic_error( "No options or arguments specified" ); - - for( typename std::vector::const_iterator it = m_options.begin(), - itEnd = m_options.end(); - it != itEnd; ++it ) - it->validate(); - } - - private: - Detail::BoundArgFunction m_boundProcessName; - std::vector m_options; - std::map m_positionalArgs; - ArgAutoPtr m_floatingArg; - int m_highestSpecifiedArgPosition; - bool m_throwOnUnrecognisedTokens; - }; - -} // end namespace Clara - -STITCH_CLARA_CLOSE_NAMESPACE -#undef STITCH_CLARA_OPEN_NAMESPACE -#undef STITCH_CLARA_CLOSE_NAMESPACE - -#endif // TWOBLUECUBES_CLARA_H_INCLUDED -#undef STITCH_CLARA_OPEN_NAMESPACE - -// Restore Clara's value for console width, if present -#ifdef CATCH_TEMP_CLARA_CONFIG_CONSOLE_WIDTH -#define CLARA_CONFIG_CONSOLE_WIDTH CATCH_TEMP_CLARA_CONFIG_CONSOLE_WIDTH -#undef CATCH_TEMP_CLARA_CONFIG_CONSOLE_WIDTH -#endif - -#include -#include namespace Catch { - inline void abortAfterFirst( ConfigData& config ) { config.abortAfter = 1; } - inline void abortAfterX( ConfigData& config, int x ) { - if( x < 1 ) - throw std::runtime_error( "Value after -x or --abortAfter must be greater than zero" ); - config.abortAfter = x; - } - inline void addTestOrTags( ConfigData& config, std::string const& _testSpec ) { config.testsOrTags.push_back( _testSpec ); } - inline void addSectionToRun( ConfigData& config, std::string const& sectionName ) { config.sectionsToRun.push_back( sectionName ); } - inline void addReporterName( ConfigData& config, std::string const& _reporterName ) { config.reporterNames.push_back( _reporterName ); } + struct AssertionResultData + { + AssertionResultData() = delete; - inline void addWarning( ConfigData& config, std::string const& _warning ) { - if( _warning == "NoAssertions" ) - config.warnings = static_cast( config.warnings | WarnAbout::NoAssertions ); - else - throw std::runtime_error( "Unrecognised warning: '" + _warning + '\'' ); - } - inline void setOrder( ConfigData& config, std::string const& order ) { - if( startsWith( "declared", order ) ) - config.runOrder = RunTests::InDeclarationOrder; - else if( startsWith( "lexical", order ) ) - config.runOrder = RunTests::InLexicographicalOrder; - else if( startsWith( "random", order ) ) - config.runOrder = RunTests::InRandomOrder; - else - throw std::runtime_error( "Unrecognised ordering: '" + order + '\'' ); - } - inline void setRngSeed( ConfigData& config, std::string const& seed ) { - if( seed == "time" ) { - config.rngSeed = static_cast( std::time(0) ); - } - else { - std::stringstream ss; - ss << seed; - ss >> config.rngSeed; - if( ss.fail() ) - throw std::runtime_error( "Argument to --rng-seed should be the word 'time' or a number" ); - } - } - inline void setVerbosity( ConfigData& config, int level ) { - // !TBD: accept strings? - config.verbosity = static_cast( level ); - } - inline void setShowDurations( ConfigData& config, bool _showDurations ) { - config.showDurations = _showDurations - ? ShowDurations::Always - : ShowDurations::Never; - } - inline void setUseColour( ConfigData& config, std::string const& value ) { - std::string mode = toLower( value ); + AssertionResultData( ResultWas::OfType _resultType, LazyExpression const& _lazyExpression ); - if( mode == "yes" ) - config.useColour = UseColour::Yes; - else if( mode == "no" ) - config.useColour = UseColour::No; - else if( mode == "auto" ) - config.useColour = UseColour::Auto; - else - throw std::runtime_error( "colour mode must be one of: auto, yes or no" ); - } - inline void setWaitForKeypress( ConfigData& config, std::string const& keypress ) { - std::string keypressLc = toLower( keypress ); - if( keypressLc == "start" ) - config.waitForKeypress = WaitForKeypress::BeforeStart; - else if( keypressLc == "exit" ) - config.waitForKeypress = WaitForKeypress::BeforeExit; - else if( keypressLc == "both" ) - config.waitForKeypress = WaitForKeypress::BeforeStartAndExit; - else - throw std::runtime_error( "keypress argument must be one of: start, exit or both. '" + keypress + "' not recognised" ); + std::string message; + mutable std::string reconstructedExpression; + LazyExpression lazyExpression; + ResultWas::OfType resultType; + + std::string reconstructExpression() const; }; - inline void forceColour( ConfigData& config ) { - config.useColour = UseColour::Yes; - } - inline void loadTestNamesFromFile( ConfigData& config, std::string const& _filename ) { - std::ifstream f( _filename.c_str() ); - if( !f.is_open() ) - throw std::domain_error( "Unable to load input file: " + _filename ); + class AssertionResult { + public: + AssertionResult() = delete; + AssertionResult( AssertionInfo const& info, AssertionResultData const& data ); - std::string line; - while( std::getline( f, line ) ) { - line = trim(line); - if( !line.empty() && !startsWith( line, '#' ) ) { - if( !startsWith( line, '"' ) ) - line = '"' + line + '"'; - addTestOrTags( config, line + ',' ); - } - } - } + bool isOk() const; + bool succeeded() const; + ResultWas::OfType getResultType() const; + bool hasExpression() const; + bool hasMessage() const; + std::string getExpression() const; + std::string getExpressionInMacro() const; + bool hasExpandedExpression() const; + std::string getExpandedExpression() const; + std::string getMessage() const; + SourceLineInfo getSourceInfo() const; + std::string getTestMacroName() const; - inline Clara::CommandLine makeCommandLineParser() { - - using namespace Clara; - CommandLine cli; - - cli.bindProcessName( &ConfigData::processName ); - - cli["-?"]["-h"]["--help"] - .describe( "display usage information" ) - .bind( &ConfigData::showHelp ); - - cli["-l"]["--list-tests"] - .describe( "list all/matching test cases" ) - .bind( &ConfigData::listTests ); - - cli["-t"]["--list-tags"] - .describe( "list all/matching tags" ) - .bind( &ConfigData::listTags ); - - cli["-s"]["--success"] - .describe( "include successful tests in output" ) - .bind( &ConfigData::showSuccessfulTests ); - - cli["-b"]["--break"] - .describe( "break into debugger on failure" ) - .bind( &ConfigData::shouldDebugBreak ); - - cli["-e"]["--nothrow"] - .describe( "skip exception tests" ) - .bind( &ConfigData::noThrow ); - - cli["-i"]["--invisibles"] - .describe( "show invisibles (tabs, newlines)" ) - .bind( &ConfigData::showInvisibles ); - - cli["-o"]["--out"] - .describe( "output filename" ) - .bind( &ConfigData::outputFilename, "filename" ); - - cli["-r"]["--reporter"] -// .placeholder( "name[:filename]" ) - .describe( "reporter to use (defaults to console)" ) - .bind( &addReporterName, "name" ); - - cli["-n"]["--name"] - .describe( "suite name" ) - .bind( &ConfigData::name, "name" ); - - cli["-a"]["--abort"] - .describe( "abort at first failure" ) - .bind( &abortAfterFirst ); - - cli["-x"]["--abortx"] - .describe( "abort after x failures" ) - .bind( &abortAfterX, "no. failures" ); - - cli["-w"]["--warn"] - .describe( "enable warnings" ) - .bind( &addWarning, "warning name" ); - -// - needs updating if reinstated -// cli.into( &setVerbosity ) -// .describe( "level of verbosity (0=no output)" ) -// .shortOpt( "v") -// .longOpt( "verbosity" ) -// .placeholder( "level" ); - - cli[_] - .describe( "which test or tests to use" ) - .bind( &addTestOrTags, "test name, pattern or tags" ); - - cli["-d"]["--durations"] - .describe( "show test durations" ) - .bind( &setShowDurations, "yes|no" ); - - cli["-f"]["--input-file"] - .describe( "load test names to run from a file" ) - .bind( &loadTestNamesFromFile, "filename" ); - - cli["-#"]["--filenames-as-tags"] - .describe( "adds a tag for the filename" ) - .bind( &ConfigData::filenamesAsTags ); - - cli["-c"]["--section"] - .describe( "specify section to run" ) - .bind( &addSectionToRun, "section name" ); - - // Less common commands which don't have a short form - cli["--list-test-names-only"] - .describe( "list all/matching test cases names only" ) - .bind( &ConfigData::listTestNamesOnly ); - - cli["--list-extra-info"] - .describe( "list all/matching test cases with more info" ) - .bind( &ConfigData::listExtraInfo ); - - cli["--list-reporters"] - .describe( "list all reporters" ) - .bind( &ConfigData::listReporters ); - - cli["--order"] - .describe( "test case order (defaults to decl)" ) - .bind( &setOrder, "decl|lex|rand" ); - - cli["--rng-seed"] - .describe( "set a specific seed for random numbers" ) - .bind( &setRngSeed, "'time'|number" ); - - cli["--force-colour"] - .describe( "force colourised output (deprecated)" ) - .bind( &forceColour ); - - cli["--use-colour"] - .describe( "should output be colourised" ) - .bind( &setUseColour, "yes|no" ); - - cli["--libidentify"] - .describe( "report name and version according to libidentify standard" ) - .bind( &ConfigData::libIdentify ); - - cli["--wait-for-keypress"] - .describe( "waits for a keypress before exiting" ) - .bind( &setWaitForKeypress, "start|exit|both" ); - - return cli; - } + //protected: + AssertionInfo m_info; + AssertionResultData m_resultData; + }; } // end namespace Catch -// #included from: internal/catch_list.hpp -#define TWOBLUECUBES_CATCH_LIST_HPP_INCLUDED +// end catch_assertionresult.h +// start catch_option.hpp -// #included from: catch_text.h -#define TWOBLUECUBES_CATCH_TEXT_H_INCLUDED +namespace Catch { -#define TBC_TEXT_FORMAT_CONSOLE_WIDTH CATCH_CONFIG_CONSOLE_WIDTH - -#define CLICHE_TBC_TEXT_FORMAT_OUTER_NAMESPACE Catch -// #included from: ../external/tbc_text_format.h -// Only use header guard if we are not using an outer namespace -#ifndef CLICHE_TBC_TEXT_FORMAT_OUTER_NAMESPACE -# ifdef TWOBLUECUBES_TEXT_FORMAT_H_INCLUDED -# ifndef TWOBLUECUBES_TEXT_FORMAT_H_ALREADY_INCLUDED -# define TWOBLUECUBES_TEXT_FORMAT_H_ALREADY_INCLUDED -# endif -# else -# define TWOBLUECUBES_TEXT_FORMAT_H_INCLUDED -# endif -#endif -#ifndef TWOBLUECUBES_TEXT_FORMAT_H_ALREADY_INCLUDED -#include -#include -#include - -// Use optional outer namespace -#ifdef CLICHE_TBC_TEXT_FORMAT_OUTER_NAMESPACE -namespace CLICHE_TBC_TEXT_FORMAT_OUTER_NAMESPACE { -#endif - -namespace Tbc { - -#ifdef TBC_TEXT_FORMAT_CONSOLE_WIDTH - const unsigned int consoleWidth = TBC_TEXT_FORMAT_CONSOLE_WIDTH; -#else - const unsigned int consoleWidth = 80; -#endif - - struct TextAttributes { - TextAttributes() - : initialIndent( std::string::npos ), - indent( 0 ), - width( consoleWidth-1 ) + // An optional type + template + class Option { + public: + Option() : nullableValue( nullptr ) {} + Option( T const& _value ) + : nullableValue( new( storage ) T( _value ) ) + {} + Option( Option const& _other ) + : nullableValue( _other ? new( storage ) T( *_other ) : nullptr ) {} - TextAttributes& setInitialIndent( std::size_t _value ) { initialIndent = _value; return *this; } - TextAttributes& setIndent( std::size_t _value ) { indent = _value; return *this; } - TextAttributes& setWidth( std::size_t _value ) { width = _value; return *this; } - - std::size_t initialIndent; // indent of first line, or npos - std::size_t indent; // indent of subsequent lines, or all if initialIndent is npos - std::size_t width; // maximum width of text, including indent. Longer text will wrap - }; - - class Text { - public: - Text( std::string const& _str, TextAttributes const& _attr = TextAttributes() ) - : attr( _attr ) - { - const std::string wrappableBeforeChars = "[({<\t"; - const std::string wrappableAfterChars = "])}>-,./|\\"; - const std::string wrappableInsteadOfChars = " \n\r"; - std::string indent = _attr.initialIndent != std::string::npos - ? std::string( _attr.initialIndent, ' ' ) - : std::string( _attr.indent, ' ' ); - - typedef std::string::const_iterator iterator; - iterator it = _str.begin(); - const iterator strEnd = _str.end(); - - while( it != strEnd ) { - - if( lines.size() >= 1000 ) { - lines.push_back( "... message truncated due to excessive size" ); - return; - } - - std::string suffix; - std::size_t width = (std::min)( static_cast( strEnd-it ), _attr.width-static_cast( indent.size() ) ); - iterator itEnd = it+width; - iterator itNext = _str.end(); - - iterator itNewLine = std::find( it, itEnd, '\n' ); - if( itNewLine != itEnd ) - itEnd = itNewLine; - - if( itEnd != strEnd ) { - bool foundWrapPoint = false; - iterator findIt = itEnd; - do { - if( wrappableAfterChars.find( *findIt ) != std::string::npos && findIt != itEnd ) { - itEnd = findIt+1; - itNext = findIt+1; - foundWrapPoint = true; - } - else if( findIt > it && wrappableBeforeChars.find( *findIt ) != std::string::npos ) { - itEnd = findIt; - itNext = findIt; - foundWrapPoint = true; - } - else if( wrappableInsteadOfChars.find( *findIt ) != std::string::npos ) { - itNext = findIt+1; - itEnd = findIt; - foundWrapPoint = true; - } - if( findIt == it ) - break; - else - --findIt; - } - while( !foundWrapPoint ); - - if( !foundWrapPoint ) { - // No good wrap char, so we'll break mid word and add a hyphen - --itEnd; - itNext = itEnd; - suffix = "-"; - } - else { - while( itEnd > it && wrappableInsteadOfChars.find( *(itEnd-1) ) != std::string::npos ) - --itEnd; - } - } - lines.push_back( indent + std::string( it, itEnd ) + suffix ); - - if( indent.size() != _attr.indent ) - indent = std::string( _attr.indent, ' ' ); - it = itNext; - } + ~Option() { + reset(); } - typedef std::vector::const_iterator const_iterator; - - const_iterator begin() const { return lines.begin(); } - const_iterator end() const { return lines.end(); } - std::string const& last() const { return lines.back(); } - std::size_t size() const { return lines.size(); } - std::string const& operator[]( std::size_t _index ) const { return lines[_index]; } - std::string toString() const { - std::ostringstream oss; - oss << *this; - return oss.str(); + Option& operator= ( Option const& _other ) { + if( &_other != this ) { + reset(); + if( _other ) + nullableValue = new( storage ) T( *_other ); + } + return *this; + } + Option& operator = ( T const& _value ) { + reset(); + nullableValue = new( storage ) T( _value ); + return *this; } - inline friend std::ostream& operator << ( std::ostream& _stream, Text const& _text ) { - for( Text::const_iterator it = _text.begin(), itEnd = _text.end(); - it != itEnd; ++it ) { - if( it != _text.begin() ) - _stream << "\n"; - _stream << *it; - } - return _stream; + void reset() { + if( nullableValue ) + nullableValue->~T(); + nullableValue = nullptr; + } + + T& operator*() { return *nullableValue; } + T const& operator*() const { return *nullableValue; } + T* operator->() { return nullableValue; } + const T* operator->() const { return nullableValue; } + + T valueOr( T const& defaultValue ) const { + return nullableValue ? *nullableValue : defaultValue; + } + + bool some() const { return nullableValue != nullptr; } + bool none() const { return nullableValue == nullptr; } + + bool operator !() const { return nullableValue == nullptr; } + explicit operator bool() const { + return some(); } private: - std::string str; - TextAttributes attr; - std::vector lines; + T *nullableValue; + alignas(alignof(T)) char storage[sizeof(T)]; }; -} // end namespace Tbc +} // end namespace Catch -#ifdef CLICHE_TBC_TEXT_FORMAT_OUTER_NAMESPACE -} // end outer namespace -#endif - -#endif // TWOBLUECUBES_TEXT_FORMAT_H_ALREADY_INCLUDED -#undef CLICHE_TBC_TEXT_FORMAT_OUTER_NAMESPACE +// end catch_option.hpp +#include +#include +#include +#include +#include namespace Catch { - using Tbc::Text; - using Tbc::TextAttributes; -} -// #included from: catch_console_colour.hpp -#define TWOBLUECUBES_CATCH_CONSOLE_COLOUR_HPP_INCLUDED + struct ReporterConfig { + explicit ReporterConfig( IConfigPtr const& _fullConfig ); + + ReporterConfig( IConfigPtr const& _fullConfig, std::ostream& _stream ); + + std::ostream& stream() const; + IConfigPtr fullConfig() const; + + private: + std::ostream* m_stream; + IConfigPtr m_fullConfig; + }; + + struct ReporterPreferences { + bool shouldRedirectStdOut = false; + }; + + template + struct LazyStat : Option { + LazyStat& operator=( T const& _value ) { + Option::operator=( _value ); + used = false; + return *this; + } + void reset() { + Option::reset(); + used = false; + } + bool used = false; + }; + + struct TestRunInfo { + TestRunInfo( std::string const& _name ); + std::string name; + }; + struct GroupInfo { + GroupInfo( std::string const& _name, + std::size_t _groupIndex, + std::size_t _groupsCount ); + + std::string name; + std::size_t groupIndex; + std::size_t groupsCounts; + }; + + struct AssertionStats { + AssertionStats( AssertionResult const& _assertionResult, + std::vector const& _infoMessages, + Totals const& _totals ); + + AssertionStats( AssertionStats const& ) = default; + AssertionStats( AssertionStats && ) = default; + AssertionStats& operator = ( AssertionStats const& ) = default; + AssertionStats& operator = ( AssertionStats && ) = default; + virtual ~AssertionStats(); + + AssertionResult assertionResult; + std::vector infoMessages; + Totals totals; + }; + + struct SectionStats { + SectionStats( SectionInfo const& _sectionInfo, + Counts const& _assertions, + double _durationInSeconds, + bool _missingAssertions ); + SectionStats( SectionStats const& ) = default; + SectionStats( SectionStats && ) = default; + SectionStats& operator = ( SectionStats const& ) = default; + SectionStats& operator = ( SectionStats && ) = default; + virtual ~SectionStats(); + + SectionInfo sectionInfo; + Counts assertions; + double durationInSeconds; + bool missingAssertions; + }; + + struct TestCaseStats { + TestCaseStats( TestCaseInfo const& _testInfo, + Totals const& _totals, + std::string const& _stdOut, + std::string const& _stdErr, + bool _aborting ); + + TestCaseStats( TestCaseStats const& ) = default; + TestCaseStats( TestCaseStats && ) = default; + TestCaseStats& operator = ( TestCaseStats const& ) = default; + TestCaseStats& operator = ( TestCaseStats && ) = default; + virtual ~TestCaseStats(); + + TestCaseInfo testInfo; + Totals totals; + std::string stdOut; + std::string stdErr; + bool aborting; + }; + + struct TestGroupStats { + TestGroupStats( GroupInfo const& _groupInfo, + Totals const& _totals, + bool _aborting ); + TestGroupStats( GroupInfo const& _groupInfo ); + + TestGroupStats( TestGroupStats const& ) = default; + TestGroupStats( TestGroupStats && ) = default; + TestGroupStats& operator = ( TestGroupStats const& ) = default; + TestGroupStats& operator = ( TestGroupStats && ) = default; + virtual ~TestGroupStats(); + + GroupInfo groupInfo; + Totals totals; + bool aborting; + }; + + struct TestRunStats { + TestRunStats( TestRunInfo const& _runInfo, + Totals const& _totals, + bool _aborting ); + + TestRunStats( TestRunStats const& ) = default; + TestRunStats( TestRunStats && ) = default; + TestRunStats& operator = ( TestRunStats const& ) = default; + TestRunStats& operator = ( TestRunStats && ) = default; + virtual ~TestRunStats(); + + TestRunInfo runInfo; + Totals totals; + bool aborting; + }; + + struct BenchmarkInfo { + std::string name; + }; + struct BenchmarkStats { + BenchmarkInfo info; + std::size_t iterations; + uint64_t elapsedTimeInNanoseconds; + }; + + struct IStreamingReporter { + virtual ~IStreamingReporter() = default; + + // Implementing class must also provide the following static methods: + // static std::string getDescription(); + // static std::set getSupportedVerbosities() + + virtual ReporterPreferences getPreferences() const = 0; + + virtual void noMatchingTestCases( std::string const& spec ) = 0; + + virtual void testRunStarting( TestRunInfo const& testRunInfo ) = 0; + virtual void testGroupStarting( GroupInfo const& groupInfo ) = 0; + + virtual void testCaseStarting( TestCaseInfo const& testInfo ) = 0; + virtual void sectionStarting( SectionInfo const& sectionInfo ) = 0; + + // *** experimental *** + virtual void benchmarkStarting( BenchmarkInfo const& ) {} + + virtual void assertionStarting( AssertionInfo const& assertionInfo ) = 0; + + // The return value indicates if the messages buffer should be cleared: + virtual bool assertionEnded( AssertionStats const& assertionStats ) = 0; + + // *** experimental *** + virtual void benchmarkEnded( BenchmarkStats const& ) {} + + virtual void sectionEnded( SectionStats const& sectionStats ) = 0; + virtual void testCaseEnded( TestCaseStats const& testCaseStats ) = 0; + virtual void testGroupEnded( TestGroupStats const& testGroupStats ) = 0; + virtual void testRunEnded( TestRunStats const& testRunStats ) = 0; + + virtual void skipTest( TestCaseInfo const& testInfo ) = 0; + + // Default empty implementation provided + virtual void fatalErrorEncountered( StringRef name ); + + virtual bool isMulti() const; + }; + using IStreamingReporterPtr = std::unique_ptr; + + struct IReporterFactory { + virtual ~IReporterFactory(); + virtual IStreamingReporterPtr create( ReporterConfig const& config ) const = 0; + virtual std::string getDescription() const = 0; + }; + using IReporterFactoryPtr = std::shared_ptr; + + struct IReporterRegistry { + using FactoryMap = std::map; + using Listeners = std::vector; + + virtual ~IReporterRegistry(); + virtual IStreamingReporterPtr create( std::string const& name, IConfigPtr const& config ) const = 0; + virtual FactoryMap const& getFactories() const = 0; + virtual Listeners const& getListeners() const = 0; + }; + + void addReporter( IStreamingReporterPtr& existingReporter, IStreamingReporterPtr&& additionalReporter ); + +} // end namespace Catch + +// end catch_interfaces_reporter.h +#include +#include +#include +#include +#include +#include + +namespace Catch { + void prepareExpandedExpression(AssertionResult& result); + + // Returns double formatted as %.3f (format expected on output) + std::string getFormattedDuration( double duration ); + + template + struct StreamingReporterBase : IStreamingReporter { + + StreamingReporterBase( ReporterConfig const& _config ) + : m_config( _config.fullConfig() ), + stream( _config.stream() ) + { + m_reporterPrefs.shouldRedirectStdOut = false; + CATCH_ENFORCE( DerivedT::getSupportedVerbosities().count( m_config->verbosity() ), "Verbosity level not supported by this reporter" ); + } + + ReporterPreferences getPreferences() const override { + return m_reporterPrefs; + } + + static std::set getSupportedVerbosities() { + return { Verbosity::Normal }; + } + + ~StreamingReporterBase() override = default; + + void noMatchingTestCases(std::string const&) override {} + + void testRunStarting(TestRunInfo const& _testRunInfo) override { + currentTestRunInfo = _testRunInfo; + } + void testGroupStarting(GroupInfo const& _groupInfo) override { + currentGroupInfo = _groupInfo; + } + + void testCaseStarting(TestCaseInfo const& _testInfo) override { + currentTestCaseInfo = _testInfo; + } + void sectionStarting(SectionInfo const& _sectionInfo) override { + m_sectionStack.push_back(_sectionInfo); + } + + void sectionEnded(SectionStats const& /* _sectionStats */) override { + m_sectionStack.pop_back(); + } + void testCaseEnded(TestCaseStats const& /* _testCaseStats */) override { + currentTestCaseInfo.reset(); + } + void testGroupEnded(TestGroupStats const& /* _testGroupStats */) override { + currentGroupInfo.reset(); + } + void testRunEnded(TestRunStats const& /* _testRunStats */) override { + currentTestCaseInfo.reset(); + currentGroupInfo.reset(); + currentTestRunInfo.reset(); + } + + void skipTest(TestCaseInfo const&) override { + // Don't do anything with this by default. + // It can optionally be overridden in the derived class. + } + + IConfigPtr m_config; + std::ostream& stream; + + LazyStat currentTestRunInfo; + LazyStat currentGroupInfo; + LazyStat currentTestCaseInfo; + + std::vector m_sectionStack; + ReporterPreferences m_reporterPrefs; + }; + + template + struct CumulativeReporterBase : IStreamingReporter { + template + struct Node { + explicit Node( T const& _value ) : value( _value ) {} + virtual ~Node() {} + + using ChildNodes = std::vector>; + T value; + ChildNodes children; + }; + struct SectionNode { + explicit SectionNode(SectionStats const& _stats) : stats(_stats) {} + virtual ~SectionNode() = default; + + bool operator == (SectionNode const& other) const { + return stats.sectionInfo.lineInfo == other.stats.sectionInfo.lineInfo; + } + bool operator == (std::shared_ptr const& other) const { + return operator==(*other); + } + + SectionStats stats; + using ChildSections = std::vector>; + using Assertions = std::vector; + ChildSections childSections; + Assertions assertions; + std::string stdOut; + std::string stdErr; + }; + + struct BySectionInfo { + BySectionInfo( SectionInfo const& other ) : m_other( other ) {} + BySectionInfo( BySectionInfo const& other ) : m_other( other.m_other ) {} + bool operator() (std::shared_ptr const& node) const { + return ((node->stats.sectionInfo.name == m_other.name) && + (node->stats.sectionInfo.lineInfo == m_other.lineInfo)); + } + void operator=(BySectionInfo const&) = delete; + + private: + SectionInfo const& m_other; + }; + + using TestCaseNode = Node; + using TestGroupNode = Node; + using TestRunNode = Node; + + CumulativeReporterBase( ReporterConfig const& _config ) + : m_config( _config.fullConfig() ), + stream( _config.stream() ) + { + m_reporterPrefs.shouldRedirectStdOut = false; + CATCH_ENFORCE( DerivedT::getSupportedVerbosities().count( m_config->verbosity() ), "Verbosity level not supported by this reporter" ); + } + ~CumulativeReporterBase() override = default; + + ReporterPreferences getPreferences() const override { + return m_reporterPrefs; + } + + static std::set getSupportedVerbosities() { + return { Verbosity::Normal }; + } + + void testRunStarting( TestRunInfo const& ) override {} + void testGroupStarting( GroupInfo const& ) override {} + + void testCaseStarting( TestCaseInfo const& ) override {} + + void sectionStarting( SectionInfo const& sectionInfo ) override { + SectionStats incompleteStats( sectionInfo, Counts(), 0, false ); + std::shared_ptr node; + if( m_sectionStack.empty() ) { + if( !m_rootSection ) + m_rootSection = std::make_shared( incompleteStats ); + node = m_rootSection; + } + else { + SectionNode& parentNode = *m_sectionStack.back(); + auto it = + std::find_if( parentNode.childSections.begin(), + parentNode.childSections.end(), + BySectionInfo( sectionInfo ) ); + if( it == parentNode.childSections.end() ) { + node = std::make_shared( incompleteStats ); + parentNode.childSections.push_back( node ); + } + else + node = *it; + } + m_sectionStack.push_back( node ); + m_deepestSection = std::move(node); + } + + void assertionStarting(AssertionInfo const&) override {} + + bool assertionEnded(AssertionStats const& assertionStats) override { + assert(!m_sectionStack.empty()); + // AssertionResult holds a pointer to a temporary DecomposedExpression, + // which getExpandedExpression() calls to build the expression string. + // Our section stack copy of the assertionResult will likely outlive the + // temporary, so it must be expanded or discarded now to avoid calling + // a destroyed object later. + prepareExpandedExpression(const_cast( assertionStats.assertionResult ) ); + SectionNode& sectionNode = *m_sectionStack.back(); + sectionNode.assertions.push_back(assertionStats); + return true; + } + void sectionEnded(SectionStats const& sectionStats) override { + assert(!m_sectionStack.empty()); + SectionNode& node = *m_sectionStack.back(); + node.stats = sectionStats; + m_sectionStack.pop_back(); + } + void testCaseEnded(TestCaseStats const& testCaseStats) override { + auto node = std::make_shared(testCaseStats); + assert(m_sectionStack.size() == 0); + node->children.push_back(m_rootSection); + m_testCases.push_back(node); + m_rootSection.reset(); + + assert(m_deepestSection); + m_deepestSection->stdOut = testCaseStats.stdOut; + m_deepestSection->stdErr = testCaseStats.stdErr; + } + void testGroupEnded(TestGroupStats const& testGroupStats) override { + auto node = std::make_shared(testGroupStats); + node->children.swap(m_testCases); + m_testGroups.push_back(node); + } + void testRunEnded(TestRunStats const& testRunStats) override { + auto node = std::make_shared(testRunStats); + node->children.swap(m_testGroups); + m_testRuns.push_back(node); + testRunEndedCumulative(); + } + virtual void testRunEndedCumulative() = 0; + + void skipTest(TestCaseInfo const&) override {} + + IConfigPtr m_config; + std::ostream& stream; + std::vector m_assertions; + std::vector>> m_sections; + std::vector> m_testCases; + std::vector> m_testGroups; + + std::vector> m_testRuns; + + std::shared_ptr m_rootSection; + std::shared_ptr m_deepestSection; + std::vector> m_sectionStack; + ReporterPreferences m_reporterPrefs; + }; + + template + char const* getLineOfChars() { + static char line[CATCH_CONFIG_CONSOLE_WIDTH] = {0}; + if( !*line ) { + std::memset( line, C, CATCH_CONFIG_CONSOLE_WIDTH-1 ); + line[CATCH_CONFIG_CONSOLE_WIDTH-1] = 0; + } + return line; + } + + struct TestEventListenerBase : StreamingReporterBase { + TestEventListenerBase( ReporterConfig const& _config ); + + void assertionStarting(AssertionInfo const&) override; + bool assertionEnded(AssertionStats const&) override; + }; + +} // end namespace Catch + +// end catch_reporter_bases.hpp +// start catch_console_colour.h namespace Catch { @@ -5512,462 +3701,105 @@ namespace Catch { // Use constructed object for RAII guard Colour( Code _colourCode ); - Colour( Colour const& other ); + Colour( Colour&& other ) noexcept; + Colour& operator=( Colour&& other ) noexcept; ~Colour(); // Use static method for one-shot changes static void use( Code _colourCode ); private: - bool m_moved; + bool m_moved = false; }; - inline std::ostream& operator << ( std::ostream& os, Colour const& ) { return os; } + std::ostream& operator << ( std::ostream& os, Colour const& ); } // end namespace Catch -// #included from: catch_interfaces_reporter.h -#define TWOBLUECUBES_CATCH_INTERFACES_REPORTER_H_INCLUDED +// end catch_console_colour.h +// start catch_reporter_registrars.hpp -#include -#include -#include - -namespace Catch -{ - struct ReporterConfig { - explicit ReporterConfig( Ptr const& _fullConfig ) - : m_stream( &_fullConfig->stream() ), m_fullConfig( _fullConfig ) {} - - ReporterConfig( Ptr const& _fullConfig, std::ostream& _stream ) - : m_stream( &_stream ), m_fullConfig( _fullConfig ) {} - - std::ostream& stream() const { return *m_stream; } - Ptr fullConfig() const { return m_fullConfig; } - - private: - std::ostream* m_stream; - Ptr m_fullConfig; - }; - - struct ReporterPreferences { - ReporterPreferences() - : shouldRedirectStdOut( false ) - {} - - bool shouldRedirectStdOut; - }; - - template - struct LazyStat : Option { - LazyStat() : used( false ) {} - LazyStat& operator=( T const& _value ) { - Option::operator=( _value ); - used = false; - return *this; - } - void reset() { - Option::reset(); - used = false; - } - bool used; - }; - - struct TestRunInfo { - TestRunInfo( std::string const& _name ) : name( _name ) {} - std::string name; - }; - struct GroupInfo { - GroupInfo( std::string const& _name, - std::size_t _groupIndex, - std::size_t _groupsCount ) - : name( _name ), - groupIndex( _groupIndex ), - groupsCounts( _groupsCount ) - {} - - std::string name; - std::size_t groupIndex; - std::size_t groupsCounts; - }; - - struct AssertionStats { - AssertionStats( AssertionResult const& _assertionResult, - std::vector const& _infoMessages, - Totals const& _totals ) - : assertionResult( _assertionResult ), - infoMessages( _infoMessages ), - totals( _totals ) - { - if( assertionResult.hasMessage() ) { - // Copy message into messages list. - // !TBD This should have been done earlier, somewhere - MessageBuilder builder( assertionResult.getTestMacroName(), assertionResult.getSourceInfo(), assertionResult.getResultType() ); - builder << assertionResult.getMessage(); - builder.m_info.message = builder.m_stream.str(); - - infoMessages.push_back( builder.m_info ); - } - } - virtual ~AssertionStats(); - -# ifdef CATCH_CONFIG_CPP11_GENERATED_METHODS - AssertionStats( AssertionStats const& ) = default; - AssertionStats( AssertionStats && ) = default; - AssertionStats& operator = ( AssertionStats const& ) = default; - AssertionStats& operator = ( AssertionStats && ) = default; -# endif - - AssertionResult assertionResult; - std::vector infoMessages; - Totals totals; - }; - - struct SectionStats { - SectionStats( SectionInfo const& _sectionInfo, - Counts const& _assertions, - double _durationInSeconds, - bool _missingAssertions ) - : sectionInfo( _sectionInfo ), - assertions( _assertions ), - durationInSeconds( _durationInSeconds ), - missingAssertions( _missingAssertions ) - {} - virtual ~SectionStats(); -# ifdef CATCH_CONFIG_CPP11_GENERATED_METHODS - SectionStats( SectionStats const& ) = default; - SectionStats( SectionStats && ) = default; - SectionStats& operator = ( SectionStats const& ) = default; - SectionStats& operator = ( SectionStats && ) = default; -# endif - - SectionInfo sectionInfo; - Counts assertions; - double durationInSeconds; - bool missingAssertions; - }; - - struct TestCaseStats { - TestCaseStats( TestCaseInfo const& _testInfo, - Totals const& _totals, - std::string const& _stdOut, - std::string const& _stdErr, - bool _aborting ) - : testInfo( _testInfo ), - totals( _totals ), - stdOut( _stdOut ), - stdErr( _stdErr ), - aborting( _aborting ) - {} - virtual ~TestCaseStats(); - -# ifdef CATCH_CONFIG_CPP11_GENERATED_METHODS - TestCaseStats( TestCaseStats const& ) = default; - TestCaseStats( TestCaseStats && ) = default; - TestCaseStats& operator = ( TestCaseStats const& ) = default; - TestCaseStats& operator = ( TestCaseStats && ) = default; -# endif - - TestCaseInfo testInfo; - Totals totals; - std::string stdOut; - std::string stdErr; - bool aborting; - }; - - struct TestGroupStats { - TestGroupStats( GroupInfo const& _groupInfo, - Totals const& _totals, - bool _aborting ) - : groupInfo( _groupInfo ), - totals( _totals ), - aborting( _aborting ) - {} - TestGroupStats( GroupInfo const& _groupInfo ) - : groupInfo( _groupInfo ), - aborting( false ) - {} - virtual ~TestGroupStats(); - -# ifdef CATCH_CONFIG_CPP11_GENERATED_METHODS - TestGroupStats( TestGroupStats const& ) = default; - TestGroupStats( TestGroupStats && ) = default; - TestGroupStats& operator = ( TestGroupStats const& ) = default; - TestGroupStats& operator = ( TestGroupStats && ) = default; -# endif - - GroupInfo groupInfo; - Totals totals; - bool aborting; - }; - - struct TestRunStats { - TestRunStats( TestRunInfo const& _runInfo, - Totals const& _totals, - bool _aborting ) - : runInfo( _runInfo ), - totals( _totals ), - aborting( _aborting ) - {} - virtual ~TestRunStats(); - -# ifndef CATCH_CONFIG_CPP11_GENERATED_METHODS - TestRunStats( TestRunStats const& _other ) - : runInfo( _other.runInfo ), - totals( _other.totals ), - aborting( _other.aborting ) - {} -# else - TestRunStats( TestRunStats const& ) = default; - TestRunStats( TestRunStats && ) = default; - TestRunStats& operator = ( TestRunStats const& ) = default; - TestRunStats& operator = ( TestRunStats && ) = default; -# endif - - TestRunInfo runInfo; - Totals totals; - bool aborting; - }; - - class MultipleReporters; - - struct IStreamingReporter : IShared { - virtual ~IStreamingReporter(); - - // Implementing class must also provide the following static method: - // static std::string getDescription(); - - virtual ReporterPreferences getPreferences() const = 0; - - virtual void noMatchingTestCases( std::string const& spec ) = 0; - - virtual void testRunStarting( TestRunInfo const& testRunInfo ) = 0; - virtual void testGroupStarting( GroupInfo const& groupInfo ) = 0; - - virtual void testCaseStarting( TestCaseInfo const& testInfo ) = 0; - virtual void sectionStarting( SectionInfo const& sectionInfo ) = 0; - - virtual void assertionStarting( AssertionInfo const& assertionInfo ) = 0; - - // The return value indicates if the messages buffer should be cleared: - virtual bool assertionEnded( AssertionStats const& assertionStats ) = 0; - - virtual void sectionEnded( SectionStats const& sectionStats ) = 0; - virtual void testCaseEnded( TestCaseStats const& testCaseStats ) = 0; - virtual void testGroupEnded( TestGroupStats const& testGroupStats ) = 0; - virtual void testRunEnded( TestRunStats const& testRunStats ) = 0; - - virtual void skipTest( TestCaseInfo const& testInfo ) = 0; - - virtual MultipleReporters* tryAsMulti() { return CATCH_NULL; } - }; - - struct IReporterFactory : IShared { - virtual ~IReporterFactory(); - virtual IStreamingReporter* create( ReporterConfig const& config ) const = 0; - virtual std::string getDescription() const = 0; - }; - - struct IReporterRegistry { - typedef std::map > FactoryMap; - typedef std::vector > Listeners; - - virtual ~IReporterRegistry(); - virtual IStreamingReporter* create( std::string const& name, Ptr const& config ) const = 0; - virtual FactoryMap const& getFactories() const = 0; - virtual Listeners const& getListeners() const = 0; - }; - - Ptr addReporter( Ptr const& existingReporter, Ptr const& additionalReporter ); - -} - -#include -#include namespace Catch { - inline std::size_t listTests( Config const& config ) { + template + class ReporterRegistrar { - TestSpec testSpec = config.testSpec(); - if( config.testSpec().hasFilters() ) - Catch::cout() << "Matching test cases:\n"; - else { - Catch::cout() << "All available test cases:\n"; - testSpec = TestSpecParser( ITagAliasRegistry::get() ).parse( "*" ).testSpec(); - } + class ReporterFactory : public IReporterFactory { - std::size_t matchedTests = 0; - TextAttributes nameAttr, descAttr, tagsAttr; - nameAttr.setInitialIndent( 2 ).setIndent( 4 ); - descAttr.setIndent( 4 ); - tagsAttr.setIndent( 6 ); - - std::vector matchedTestCases = filterTests( getAllTestCasesSorted( config ), testSpec, config ); - for( std::vector::const_iterator it = matchedTestCases.begin(), itEnd = matchedTestCases.end(); - it != itEnd; - ++it ) { - matchedTests++; - TestCaseInfo const& testCaseInfo = it->getTestCaseInfo(); - Colour::Code colour = testCaseInfo.isHidden() - ? Colour::SecondaryText - : Colour::None; - Colour colourGuard( colour ); - - Catch::cout() << Text( testCaseInfo.name, nameAttr ) << std::endl; - if( config.listExtraInfo() ) { - Catch::cout() << " " << testCaseInfo.lineInfo << std::endl; - std::string description = testCaseInfo.description; - if( description.empty() ) - description = "(NO DESCRIPTION)"; - Catch::cout() << Text( description, descAttr ) << std::endl; + virtual IStreamingReporterPtr create( ReporterConfig const& config ) const override { + return std::unique_ptr( new T( config ) ); } - if( !testCaseInfo.tags.empty() ) - Catch::cout() << Text( testCaseInfo.tagsAsString, tagsAttr ) << std::endl; - } - if( !config.testSpec().hasFilters() ) - Catch::cout() << pluralise( matchedTests, "test case" ) << '\n' << std::endl; - else - Catch::cout() << pluralise( matchedTests, "matching test case" ) << '\n' << std::endl; - return matchedTests; - } + virtual std::string getDescription() const override { + return T::getDescription(); + } + }; - inline std::size_t listTestsNamesOnly( Config const& config ) { - TestSpec testSpec = config.testSpec(); - if( !config.testSpec().hasFilters() ) - testSpec = TestSpecParser( ITagAliasRegistry::get() ).parse( "*" ).testSpec(); - std::size_t matchedTests = 0; - std::vector matchedTestCases = filterTests( getAllTestCasesSorted( config ), testSpec, config ); - for( std::vector::const_iterator it = matchedTestCases.begin(), itEnd = matchedTestCases.end(); - it != itEnd; - ++it ) { - matchedTests++; - TestCaseInfo const& testCaseInfo = it->getTestCaseInfo(); - if( startsWith( testCaseInfo.name, '#' ) ) - Catch::cout() << '"' << testCaseInfo.name << '"'; - else - Catch::cout() << testCaseInfo.name; - if ( config.listExtraInfo() ) - Catch::cout() << "\t@" << testCaseInfo.lineInfo; - Catch::cout() << std::endl; - } - return matchedTests; - } + public: - struct TagInfo { - TagInfo() : count ( 0 ) {} - void add( std::string const& spelling ) { - ++count; - spellings.insert( spelling ); + ReporterRegistrar( std::string const& name ) { + getMutableRegistryHub().registerReporter( name, std::make_shared() ); } - std::string all() const { - std::string out; - for( std::set::const_iterator it = spellings.begin(), itEnd = spellings.end(); - it != itEnd; - ++it ) - out += "[" + *it + "]"; - return out; - } - std::set spellings; - std::size_t count; }; - inline std::size_t listTags( Config const& config ) { - TestSpec testSpec = config.testSpec(); - if( config.testSpec().hasFilters() ) - Catch::cout() << "Tags for matching test cases:\n"; - else { - Catch::cout() << "All available tags:\n"; - testSpec = TestSpecParser( ITagAliasRegistry::get() ).parse( "*" ).testSpec(); - } + template + class ListenerRegistrar { - std::map tagCounts; + class ListenerFactory : public IReporterFactory { - std::vector matchedTestCases = filterTests( getAllTestCasesSorted( config ), testSpec, config ); - for( std::vector::const_iterator it = matchedTestCases.begin(), itEnd = matchedTestCases.end(); - it != itEnd; - ++it ) { - for( std::set::const_iterator tagIt = it->getTestCaseInfo().tags.begin(), - tagItEnd = it->getTestCaseInfo().tags.end(); - tagIt != tagItEnd; - ++tagIt ) { - std::string tagName = *tagIt; - std::string lcaseTagName = toLower( tagName ); - std::map::iterator countIt = tagCounts.find( lcaseTagName ); - if( countIt == tagCounts.end() ) - countIt = tagCounts.insert( std::make_pair( lcaseTagName, TagInfo() ) ).first; - countIt->second.add( tagName ); + virtual IStreamingReporterPtr create( ReporterConfig const& config ) const override { + return std::unique_ptr( new T( config ) ); } + virtual std::string getDescription() const override { + return std::string(); + } + }; + + public: + + ListenerRegistrar() { + getMutableRegistryHub().registerListener( std::make_shared() ); } + }; +} - for( std::map::const_iterator countIt = tagCounts.begin(), - countItEnd = tagCounts.end(); - countIt != countItEnd; - ++countIt ) { - std::ostringstream oss; - oss << " " << std::setw(2) << countIt->second.count << " "; - Text wrapper( countIt->second.all(), TextAttributes() - .setInitialIndent( 0 ) - .setIndent( oss.str().size() ) - .setWidth( CATCH_CONFIG_CONSOLE_WIDTH-10 ) ); - Catch::cout() << oss.str() << wrapper << '\n'; - } - Catch::cout() << pluralise( tagCounts.size(), "tag" ) << '\n' << std::endl; - return tagCounts.size(); - } +#if !defined(CATCH_CONFIG_DISABLE) - inline std::size_t listReporters( Config const& /*config*/ ) { - Catch::cout() << "Available reporters:\n"; - IReporterRegistry::FactoryMap const& factories = getRegistryHub().getReporterRegistry().getFactories(); - IReporterRegistry::FactoryMap::const_iterator itBegin = factories.begin(), itEnd = factories.end(), it; - std::size_t maxNameLen = 0; - for(it = itBegin; it != itEnd; ++it ) - maxNameLen = (std::max)( maxNameLen, it->first.size() ); +#define CATCH_REGISTER_REPORTER( name, reporterType ) \ + CATCH_INTERNAL_SUPPRESS_GLOBALS_WARNINGS \ + namespace{ Catch::ReporterRegistrar catch_internal_RegistrarFor##reporterType( name ); } \ + CATCH_INTERNAL_UNSUPPRESS_GLOBALS_WARNINGS - for(it = itBegin; it != itEnd; ++it ) { - Text wrapper( it->second->getDescription(), TextAttributes() - .setInitialIndent( 0 ) - .setIndent( 7+maxNameLen ) - .setWidth( CATCH_CONFIG_CONSOLE_WIDTH - maxNameLen-8 ) ); - Catch::cout() << " " - << it->first - << ':' - << std::string( maxNameLen - it->first.size() + 2, ' ' ) - << wrapper << '\n'; - } - Catch::cout() << std::endl; - return factories.size(); - } +#define CATCH_REGISTER_LISTENER( listenerType ) \ + CATCH_INTERNAL_SUPPRESS_GLOBALS_WARNINGS \ + namespace{ Catch::ListenerRegistrar catch_internal_RegistrarFor##listenerType; } \ + CATCH_INTERNAL_SUPPRESS_GLOBALS_WARNINGS +#else // CATCH_CONFIG_DISABLE - inline Option list( Config const& config ) { - Option listedCount; - if( config.listTests() || ( config.listExtraInfo() && !config.listTestNamesOnly() ) ) - listedCount = listedCount.valueOr(0) + listTests( config ); - if( config.listTestNamesOnly() ) - listedCount = listedCount.valueOr(0) + listTestsNamesOnly( config ); - if( config.listTags() ) - listedCount = listedCount.valueOr(0) + listTags( config ); - if( config.listReporters() ) - listedCount = listedCount.valueOr(0) + listReporters( config ); - return listedCount; - } +#define CATCH_REGISTER_REPORTER(name, reporterType) +#define CATCH_REGISTER_LISTENER(listenerType) -} // end namespace Catch +#endif // CATCH_CONFIG_DISABLE -// #included from: internal/catch_run_context.hpp -#define TWOBLUECUBES_CATCH_RUNNER_IMPL_HPP_INCLUDED +// end catch_reporter_registrars.hpp +// end catch_external_interfaces.h +#endif -// #included from: catch_test_case_tracker.hpp -#define TWOBLUECUBES_CATCH_TEST_CASE_TRACKER_HPP_INCLUDED +#ifdef CATCH_IMPL +// start catch_impl.hpp + +#ifdef __clang__ +#pragma clang diagnostic push +#pragma clang diagnostic ignored "-Wweak-vtables" +#endif + +// Keep these here for external reporters +// start catch_test_case_tracker.h -#include #include -#include #include -#include - -CATCH_INTERNAL_SUPPRESS_ETD_WARNINGS +#include namespace Catch { namespace TestCaseTracking { @@ -5976,13 +3808,14 @@ namespace TestCaseTracking { std::string name; SourceLineInfo location; - NameAndLocation( std::string const& _name, SourceLineInfo const& _location ) - : name( _name ), - location( _location ) - {} + NameAndLocation( std::string const& _name, SourceLineInfo const& _location ); }; - struct ITracker : SharedImpl<> { + struct ITracker; + + using ITrackerPtr = std::shared_ptr; + + struct ITracker { virtual ~ITracker(); // static queries @@ -6001,8 +3834,8 @@ namespace TestCaseTracking { virtual void fail() = 0; virtual void markAsNeedingAnotherRun() = 0; - virtual void addChild( Ptr const& child ) = 0; - virtual ITracker* findChild( NameAndLocation const& nameAndLocation ) = 0; + virtual void addChild( ITrackerPtr const& child ) = 0; + virtual ITrackerPtr findChild( NameAndLocation const& nameAndLocation ) = 0; virtual void openChild() = 0; // Debug/ checking @@ -6010,7 +3843,7 @@ namespace TestCaseTracking { virtual bool isIndexTracker() const = 0; }; - class TrackerContext { + class TrackerContext { enum RunState { NotStarted, @@ -6018,47 +3851,23 @@ namespace TestCaseTracking { CompletedCycle }; - Ptr m_rootTracker; - ITracker* m_currentTracker; - RunState m_runState; + ITrackerPtr m_rootTracker; + ITracker* m_currentTracker = nullptr; + RunState m_runState = NotStarted; public: - static TrackerContext& instance() { - static TrackerContext s_instance; - return s_instance; - } - - TrackerContext() - : m_currentTracker( CATCH_NULL ), - m_runState( NotStarted ) - {} + static TrackerContext& instance(); ITracker& startRun(); + void endRun(); - void endRun() { - m_rootTracker.reset(); - m_currentTracker = CATCH_NULL; - m_runState = NotStarted; - } + void startCycle(); + void completeCycle(); - void startCycle() { - m_currentTracker = m_rootTracker.get(); - m_runState = Executing; - } - void completeCycle() { - m_runState = CompletedCycle; - } - - bool completedCycle() const { - return m_runState == CompletedCycle; - } - ITracker& currentTracker() { - return *m_currentTracker; - } - void setCurrentTracker( ITracker* tracker ) { - m_currentTracker = tracker; - } + bool completedCycle() const; + ITracker& currentTracker(); + void setCurrentTracker( ITracker* tracker ); }; class TrackerBase : public ITracker { @@ -6071,240 +3880,82 @@ namespace TestCaseTracking { CompletedSuccessfully, Failed }; + class TrackerHasName { NameAndLocation m_nameAndLocation; public: - TrackerHasName( NameAndLocation const& nameAndLocation ) : m_nameAndLocation( nameAndLocation ) {} - bool operator ()( Ptr const& tracker ) { - return - tracker->nameAndLocation().name == m_nameAndLocation.name && - tracker->nameAndLocation().location == m_nameAndLocation.location; - } + TrackerHasName( NameAndLocation const& nameAndLocation ); + bool operator ()( ITrackerPtr const& tracker ) const; }; - typedef std::vector > Children; + + using Children = std::vector; NameAndLocation m_nameAndLocation; TrackerContext& m_ctx; ITracker* m_parent; Children m_children; - CycleState m_runState; + CycleState m_runState = NotStarted; + public: - TrackerBase( NameAndLocation const& nameAndLocation, TrackerContext& ctx, ITracker* parent ) - : m_nameAndLocation( nameAndLocation ), - m_ctx( ctx ), - m_parent( parent ), - m_runState( NotStarted ) - {} - virtual ~TrackerBase(); + TrackerBase( NameAndLocation const& nameAndLocation, TrackerContext& ctx, ITracker* parent ); - virtual NameAndLocation const& nameAndLocation() const CATCH_OVERRIDE { - return m_nameAndLocation; - } - virtual bool isComplete() const CATCH_OVERRIDE { - return m_runState == CompletedSuccessfully || m_runState == Failed; - } - virtual bool isSuccessfullyCompleted() const CATCH_OVERRIDE { - return m_runState == CompletedSuccessfully; - } - virtual bool isOpen() const CATCH_OVERRIDE { - return m_runState != NotStarted && !isComplete(); - } - virtual bool hasChildren() const CATCH_OVERRIDE { - return !m_children.empty(); - } + NameAndLocation const& nameAndLocation() const override; + bool isComplete() const override; + bool isSuccessfullyCompleted() const override; + bool isOpen() const override; + bool hasChildren() const override; - virtual void addChild( Ptr const& child ) CATCH_OVERRIDE { - m_children.push_back( child ); - } + void addChild( ITrackerPtr const& child ) override; - virtual ITracker* findChild( NameAndLocation const& nameAndLocation ) CATCH_OVERRIDE { - Children::const_iterator it = std::find_if( m_children.begin(), m_children.end(), TrackerHasName( nameAndLocation ) ); - return( it != m_children.end() ) - ? it->get() - : CATCH_NULL; - } - virtual ITracker& parent() CATCH_OVERRIDE { - assert( m_parent ); // Should always be non-null except for root - return *m_parent; - } + ITrackerPtr findChild( NameAndLocation const& nameAndLocation ) override; + ITracker& parent() override; - virtual void openChild() CATCH_OVERRIDE { - if( m_runState != ExecutingChildren ) { - m_runState = ExecutingChildren; - if( m_parent ) - m_parent->openChild(); - } - } + void openChild() override; - virtual bool isSectionTracker() const CATCH_OVERRIDE { return false; } - virtual bool isIndexTracker() const CATCH_OVERRIDE { return false; } + bool isSectionTracker() const override; + bool isIndexTracker() const override; - void open() { - m_runState = Executing; - moveToThis(); - if( m_parent ) - m_parent->openChild(); - } + void open(); - virtual void close() CATCH_OVERRIDE { + void close() override; + void fail() override; + void markAsNeedingAnotherRun() override; - // Close any still open children (e.g. generators) - while( &m_ctx.currentTracker() != this ) - m_ctx.currentTracker().close(); - - switch( m_runState ) { - case NotStarted: - case CompletedSuccessfully: - case Failed: - throw std::logic_error( "Illogical state" ); - - case NeedsAnotherRun: - break;; - - case Executing: - m_runState = CompletedSuccessfully; - break; - case ExecutingChildren: - if( m_children.empty() || m_children.back()->isComplete() ) - m_runState = CompletedSuccessfully; - break; - - default: - throw std::logic_error( "Unexpected state" ); - } - moveToParent(); - m_ctx.completeCycle(); - } - virtual void fail() CATCH_OVERRIDE { - m_runState = Failed; - if( m_parent ) - m_parent->markAsNeedingAnotherRun(); - moveToParent(); - m_ctx.completeCycle(); - } - virtual void markAsNeedingAnotherRun() CATCH_OVERRIDE { - m_runState = NeedsAnotherRun; - } private: - void moveToParent() { - assert( m_parent ); - m_ctx.setCurrentTracker( m_parent ); - } - void moveToThis() { - m_ctx.setCurrentTracker( this ); - } + void moveToParent(); + void moveToThis(); }; class SectionTracker : public TrackerBase { std::vector m_filters; public: - SectionTracker( NameAndLocation const& nameAndLocation, TrackerContext& ctx, ITracker* parent ) - : TrackerBase( nameAndLocation, ctx, parent ) - { - if( parent ) { - while( !parent->isSectionTracker() ) - parent = &parent->parent(); + SectionTracker( NameAndLocation const& nameAndLocation, TrackerContext& ctx, ITracker* parent ); - SectionTracker& parentSection = static_cast( *parent ); - addNextFilters( parentSection.m_filters ); - } - } - virtual ~SectionTracker(); + bool isSectionTracker() const override; - virtual bool isSectionTracker() const CATCH_OVERRIDE { return true; } + static SectionTracker& acquire( TrackerContext& ctx, NameAndLocation const& nameAndLocation ); - static SectionTracker& acquire( TrackerContext& ctx, NameAndLocation const& nameAndLocation ) { - SectionTracker* section = CATCH_NULL; + void tryOpen(); - ITracker& currentTracker = ctx.currentTracker(); - if( ITracker* childTracker = currentTracker.findChild( nameAndLocation ) ) { - assert( childTracker ); - assert( childTracker->isSectionTracker() ); - section = static_cast( childTracker ); - } - else { - section = new SectionTracker( nameAndLocation, ctx, ¤tTracker ); - currentTracker.addChild( section ); - } - if( !ctx.completedCycle() ) - section->tryOpen(); - return *section; - } - - void tryOpen() { - if( !isComplete() && (m_filters.empty() || m_filters[0].empty() || m_filters[0] == m_nameAndLocation.name ) ) - open(); - } - - void addInitialFilters( std::vector const& filters ) { - if( !filters.empty() ) { - m_filters.push_back(""); // Root - should never be consulted - m_filters.push_back(""); // Test Case - not a section filter - m_filters.insert( m_filters.end(), filters.begin(), filters.end() ); - } - } - void addNextFilters( std::vector const& filters ) { - if( filters.size() > 1 ) - m_filters.insert( m_filters.end(), ++filters.begin(), filters.end() ); - } + void addInitialFilters( std::vector const& filters ); + void addNextFilters( std::vector const& filters ); }; class IndexTracker : public TrackerBase { int m_size; - int m_index; + int m_index = -1; public: - IndexTracker( NameAndLocation const& nameAndLocation, TrackerContext& ctx, ITracker* parent, int size ) - : TrackerBase( nameAndLocation, ctx, parent ), - m_size( size ), - m_index( -1 ) - {} - virtual ~IndexTracker(); + IndexTracker( NameAndLocation const& nameAndLocation, TrackerContext& ctx, ITracker* parent, int size ); - virtual bool isIndexTracker() const CATCH_OVERRIDE { return true; } + bool isIndexTracker() const override; + void close() override; - static IndexTracker& acquire( TrackerContext& ctx, NameAndLocation const& nameAndLocation, int size ) { - IndexTracker* tracker = CATCH_NULL; + static IndexTracker& acquire( TrackerContext& ctx, NameAndLocation const& nameAndLocation, int size ); - ITracker& currentTracker = ctx.currentTracker(); - if( ITracker* childTracker = currentTracker.findChild( nameAndLocation ) ) { - assert( childTracker ); - assert( childTracker->isIndexTracker() ); - tracker = static_cast( childTracker ); - } - else { - tracker = new IndexTracker( nameAndLocation, ctx, ¤tTracker, size ); - currentTracker.addChild( tracker ); - } + int index() const; - if( !ctx.completedCycle() && !tracker->isComplete() ) { - if( tracker->m_runState != ExecutingChildren && tracker->m_runState != NeedsAnotherRun ) - tracker->moveNext(); - tracker->open(); - } - - return *tracker; - } - - int index() const { return m_index; } - - void moveNext() { - m_index++; - m_children.clear(); - } - - virtual void close() CATCH_OVERRIDE { - TrackerBase::close(); - if( m_runState == CompletedSuccessfully && m_index < m_size-1 ) - m_runState = Executing; - } + void moveNext(); }; - inline ITracker& TrackerContext::startRun() { - m_rootTracker = new SectionTracker( NameAndLocation( "{root}", CATCH_INTERNAL_LINEINFO ), *this, CATCH_NULL ); - m_currentTracker = CATCH_NULL; - m_runState = Executing; - return *m_rootTracker; - } - } // namespace TestCaseTracking using TestCaseTracking::ITracker; @@ -6314,31 +3965,1941 @@ using TestCaseTracking::IndexTracker; } // namespace Catch -CATCH_INTERNAL_UNSUPPRESS_ETD_WARNINGS +// end catch_test_case_tracker.h -// #included from: catch_fatal_condition.hpp -#define TWOBLUECUBES_CATCH_FATAL_CONDITION_H_INCLUDED +// start catch_leak_detector.h namespace Catch { - // Report the error condition - inline void reportFatal( std::string const& message ) { - IContext& context = Catch::getCurrentContext(); - IResultCapture* resultCapture = context.getResultCapture(); - resultCapture->handleFatalErrorCondition( message ); + struct LeakDetector { + LeakDetector(); + }; + +} +// end catch_leak_detector.h +// Cpp files will be included in the single-header file here +// start catch_approx.cpp + +#include +#include + +namespace { + +// Performs equivalent check of std::fabs(lhs - rhs) <= margin +// But without the subtraction to allow for INFINITY in comparison +bool marginComparison(double lhs, double rhs, double margin) { + return (lhs + margin >= rhs) && (rhs + margin >= lhs); +} + +} + +namespace Catch { +namespace Detail { + + Approx::Approx ( double value ) + : m_epsilon( std::numeric_limits::epsilon()*100 ), + m_margin( 0.0 ), + m_scale( 0.0 ), + m_value( value ) + {} + + Approx Approx::custom() { + return Approx( 0 ); + } + + std::string Approx::toString() const { + std::ostringstream oss; + oss << "Approx( " << ::Catch::Detail::stringify( m_value ) << " )"; + return oss.str(); + } + + bool Approx::equalityComparisonImpl(const double other) const { + // First try with fixed margin, then compute margin based on epsilon, scale and Approx's value + // Thanks to Richard Harris for his help refining the scaled margin value + return marginComparison(m_value, other, m_margin) || marginComparison(m_value, other, m_epsilon * (m_scale + std::fabs(m_value))); + } + +} // end namespace Detail + +std::string StringMaker::convert(Catch::Detail::Approx const& value) { + return value.toString(); +} + +} // end namespace Catch +// end catch_approx.cpp +// start catch_assertionhandler.cpp + +// start catch_context.h + +#include + +namespace Catch { + + struct IResultCapture; + struct IRunner; + struct IConfig; + + using IConfigPtr = std::shared_ptr; + + struct IContext + { + virtual ~IContext(); + + virtual IResultCapture* getResultCapture() = 0; + virtual IRunner* getRunner() = 0; + virtual IConfigPtr getConfig() const = 0; + }; + + struct IMutableContext : IContext + { + virtual ~IMutableContext(); + virtual void setResultCapture( IResultCapture* resultCapture ) = 0; + virtual void setRunner( IRunner* runner ) = 0; + virtual void setConfig( IConfigPtr const& config ) = 0; + }; + + IContext& getCurrentContext(); + IMutableContext& getCurrentMutableContext(); + void cleanUpContext(); +} + +// end catch_context.h +#include + +namespace Catch { + + auto operator <<( std::ostream& os, ITransientExpression const& expr ) -> std::ostream& { + expr.streamReconstructedExpression( os ); + return os; + } + + LazyExpression::LazyExpression( bool isNegated ) + : m_isNegated( isNegated ) + {} + + LazyExpression::LazyExpression( LazyExpression const& other ) : m_isNegated( other.m_isNegated ) {} + + LazyExpression::operator bool() const { + return m_transientExpression != nullptr; + } + + auto operator << ( std::ostream& os, LazyExpression const& lazyExpr ) -> std::ostream& { + if( lazyExpr.m_isNegated ) + os << "!"; + + if( lazyExpr ) { + if( lazyExpr.m_isNegated && lazyExpr.m_transientExpression->isBinaryExpression() ) + os << "(" << *lazyExpr.m_transientExpression << ")"; + else + os << *lazyExpr.m_transientExpression; + } + else { + os << "{** error - unchecked empty expression requested **}"; + } + return os; + } + + AssertionHandler::AssertionHandler + ( StringRef macroName, + SourceLineInfo const& lineInfo, + StringRef capturedExpression, + ResultDisposition::Flags resultDisposition ) + : m_assertionInfo{ macroName, lineInfo, capturedExpression, resultDisposition } + { + getCurrentContext().getResultCapture()->assertionStarting( m_assertionInfo ); + } + AssertionHandler::~AssertionHandler() { + if ( m_inExceptionGuard ) { + handle( ResultWas::ThrewException, "Exception translation was disabled by CATCH_CONFIG_FAST_COMPILE" ); + getCurrentContext().getResultCapture()->exceptionEarlyReported(); + } + } + + void AssertionHandler::handle( ITransientExpression const& expr ) { + + bool negated = isFalseTest( m_assertionInfo.resultDisposition ); + bool result = expr.getResult() != negated; + + handle( result ? ResultWas::Ok : ResultWas::ExpressionFailed, &expr, negated ); + } + void AssertionHandler::handle( ResultWas::OfType resultType ) { + handle( resultType, nullptr, false ); + } + void AssertionHandler::handle( ResultWas::OfType resultType, StringRef const& message ) { + AssertionResultData data( resultType, LazyExpression( false ) ); + data.message = message; + handle( data, nullptr ); + } + void AssertionHandler::handle( ResultWas::OfType resultType, ITransientExpression const* expr, bool negated ) { + AssertionResultData data( resultType, LazyExpression( negated ) ); + handle( data, expr ); + } + void AssertionHandler::handle( AssertionResultData const& resultData, ITransientExpression const* expr ) { + + getResultCapture().assertionRun(); + + AssertionResult assertionResult{ m_assertionInfo, resultData }; + assertionResult.m_resultData.lazyExpression.m_transientExpression = expr; + + getResultCapture().assertionEnded( assertionResult ); + + if( !assertionResult.isOk() ) { + m_shouldDebugBreak = getCurrentContext().getConfig()->shouldDebugBreak(); + m_shouldThrow = + getCurrentContext().getRunner()->aborting() || + (m_assertionInfo.resultDisposition & ResultDisposition::Normal); + } + } + + auto AssertionHandler::allowThrows() const -> bool { + 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 + /////////////////////////////////////////////////////////////////// + CATCH_BREAK_INTO_DEBUGGER(); + } + reactWithoutDebugBreak(); + } + void AssertionHandler::reactWithoutDebugBreak() const { + if( m_shouldThrow ) + throw Catch::TestFailureException(); + } + + void AssertionHandler::useActiveException() { + handle( ResultWas::ThrewException, Catch::translateActiveException() ); + } + + void AssertionHandler::setExceptionGuard() { + assert( m_inExceptionGuard == false ); + m_inExceptionGuard = true; + } + void AssertionHandler::unsetExceptionGuard() { + assert( m_inExceptionGuard == true ); + m_inExceptionGuard = false; + } + + // This is the overload that takes a string and infers the Equals matcher from it + // The more general overload, that takes any string matcher, is in catch_capture_matchers.cpp + void handleExceptionMatchExpr( AssertionHandler& handler, std::string const& str, StringRef matcherString ) { + handleExceptionMatchExpr( handler, Matchers::Equals( str ), matcherString ); } } // namespace Catch +// end catch_assertionhandler.cpp +// start catch_assertionresult.cpp -#if defined ( CATCH_PLATFORM_WINDOWS ) ///////////////////////////////////////// -// #included from: catch_windows_h_proxy.h +namespace Catch { + AssertionResultData::AssertionResultData(ResultWas::OfType _resultType, LazyExpression const & _lazyExpression): + lazyExpression(_lazyExpression), + resultType(_resultType) {} -#define TWOBLUECUBES_CATCH_WINDOWS_H_PROXY_H_INCLUDED + std::string AssertionResultData::reconstructExpression() const { -#ifdef CATCH_DEFINES_NOMINMAX + if( reconstructedExpression.empty() ) { + if( lazyExpression ) { + // !TBD Use stringstream for now, but rework above to pass stream in + std::ostringstream oss; + oss << lazyExpression; + reconstructedExpression = oss.str(); + } + } + return reconstructedExpression; + } + + AssertionResult::AssertionResult( AssertionInfo const& info, AssertionResultData const& data ) + : m_info( info ), + m_resultData( data ) + {} + + // Result was a success + bool AssertionResult::succeeded() const { + return Catch::isOk( m_resultData.resultType ); + } + + // Result was a success, or failure is suppressed + bool AssertionResult::isOk() const { + return Catch::isOk( m_resultData.resultType ) || shouldSuppressFailure( m_info.resultDisposition ); + } + + ResultWas::OfType AssertionResult::getResultType() const { + return m_resultData.resultType; + } + + bool AssertionResult::hasExpression() const { + return m_info.capturedExpression[0] != 0; + } + + bool AssertionResult::hasMessage() const { + return !m_resultData.message.empty(); + } + + std::string AssertionResult::getExpression() const { + if( isFalseTest( m_info.resultDisposition ) ) + return "!(" + std::string(m_info.capturedExpression) + ")"; + else + return m_info.capturedExpression; + } + + std::string AssertionResult::getExpressionInMacro() const { + std::string expr; + if( m_info.macroName[0] == 0 ) + expr = m_info.capturedExpression; + else { + expr.reserve( m_info.macroName.size() + m_info.capturedExpression.size() + 4 ); + expr += m_info.macroName; + expr += "( "; + expr += m_info.capturedExpression; + expr += " )"; + } + return expr; + } + + bool AssertionResult::hasExpandedExpression() const { + return hasExpression() && getExpandedExpression() != getExpression(); + } + + std::string AssertionResult::getExpandedExpression() const { + std::string expr = m_resultData.reconstructExpression(); + return expr.empty() + ? getExpression() + : expr; + } + + std::string AssertionResult::getMessage() const { + return m_resultData.message; + } + SourceLineInfo AssertionResult::getSourceInfo() const { + return m_info.lineInfo; + } + + std::string AssertionResult::getTestMacroName() const { + return m_info.macroName; + } + +} // end namespace Catch +// end catch_assertionresult.cpp +// start catch_benchmark.cpp + +namespace Catch { + + auto BenchmarkLooper::getResolution() -> uint64_t { + return getEstimatedClockResolution() * getCurrentContext().getConfig()->benchmarkResolutionMultiple(); + } + + void BenchmarkLooper::reportStart() { + getResultCapture().benchmarkStarting( { m_name } ); + } + auto BenchmarkLooper::needsMoreIterations() -> bool { + auto elapsed = m_timer.getElapsedNanoseconds(); + + // Exponentially increasing iterations until we're confident in our timer resolution + if( elapsed < m_resolution ) { + m_iterationsToRun *= 10; + return true; + } + + getResultCapture().benchmarkEnded( { { m_name }, m_count, elapsed } ); + return false; + } + +} // end namespace Catch +// end catch_benchmark.cpp +// start catch_capture_matchers.cpp + +namespace Catch { + + using StringMatcher = Matchers::Impl::MatcherBase; + + // This is the general overload that takes a any string matcher + // There is another overload, in catch_assertinhandler.h/.cpp, that only takes a string and infers + // the Equals matcher (so the header does not mention matchers) + void handleExceptionMatchExpr( AssertionHandler& handler, StringMatcher const& matcher, StringRef matcherString ) { + std::string exceptionMessage = Catch::translateActiveException(); + MatchExpr expr( exceptionMessage, matcher, matcherString ); + handler.handle( expr ); + } + +} // namespace Catch +// end catch_capture_matchers.cpp +// start catch_commandline.cpp + +// start catch_commandline.h + +// start catch_clara.h + +// Use Catch's value for console width (store Clara's off to the side, if present) +#ifdef CLARA_CONFIG_CONSOLE_WIDTH +#define CATCH_TEMP_CLARA_CONFIG_CONSOLE_WIDTH CATCH_CLARA_TEXTFLOW_CONFIG_CONSOLE_WIDTH +#undef CATCH_CLARA_TEXTFLOW_CONFIG_CONSOLE_WIDTH +#endif +#define CATCH_CLARA_TEXTFLOW_CONFIG_CONSOLE_WIDTH CATCH_CONFIG_CONSOLE_WIDTH-1 + +#ifdef __clang__ +#pragma clang diagnostic push +#pragma clang diagnostic ignored "-Wweak-vtables" +#pragma clang diagnostic ignored "-Wexit-time-destructors" +#pragma clang diagnostic ignored "-Wshadow" +#endif + +// start clara.hpp +// v1.0-develop.2 +// See https://github.com/philsquared/Clara + + +#ifndef CATCH_CLARA_CONFIG_CONSOLE_WIDTH +#define CATCH_CLARA_CONFIG_CONSOLE_WIDTH 80 +#endif + +#ifndef CATCH_CLARA_TEXTFLOW_CONFIG_CONSOLE_WIDTH +#define CATCH_CLARA_TEXTFLOW_CONFIG_CONSOLE_WIDTH CATCH_CLARA_CONFIG_CONSOLE_WIDTH +#endif + +// ----------- #included from clara_textflow.hpp ----------- + +// TextFlowCpp +// +// A single-header library for wrapping and laying out basic text, by Phil Nash +// +// This work is licensed under the BSD 2-Clause license. +// See the accompanying LICENSE file, or the one at https://opensource.org/licenses/BSD-2-Clause +// +// This project is hosted at https://github.com/philsquared/textflowcpp + + +#include +#include +#include +#include + +#ifndef CATCH_CLARA_TEXTFLOW_CONFIG_CONSOLE_WIDTH +#define CATCH_CLARA_TEXTFLOW_CONFIG_CONSOLE_WIDTH 80 +#endif + +namespace Catch { namespace clara { namespace TextFlow { + + inline auto isWhitespace( char c ) -> bool { + static std::string chars = " \t\n\r"; + return chars.find( c ) != std::string::npos; + } + inline auto isBreakableBefore( char c ) -> bool { + static std::string chars = "[({<|"; + return chars.find( c ) != std::string::npos; + } + inline auto isBreakableAfter( char c ) -> bool { + static std::string chars = "])}>.,:;*+-=&/\\"; + return chars.find( c ) != std::string::npos; + } + + class Columns; + + class Column { + std::vector m_strings; + size_t m_width = CATCH_CLARA_TEXTFLOW_CONFIG_CONSOLE_WIDTH; + size_t m_indent = 0; + size_t m_initialIndent = std::string::npos; + + public: + class iterator { + friend Column; + + Column const& m_column; + size_t m_stringIndex = 0; + size_t m_pos = 0; + + size_t m_len = 0; + size_t m_end = 0; + bool m_suffix = false; + + iterator( Column const& column, size_t stringIndex ) + : m_column( column ), + m_stringIndex( stringIndex ) + {} + + auto line() const -> std::string const& { return m_column.m_strings[m_stringIndex]; } + + auto isBoundary( size_t at ) const -> bool { + assert( at > 0 ); + assert( at <= line().size() ); + + return at == line().size() || + ( isWhitespace( line()[at] ) && !isWhitespace( line()[at-1] ) ) || + isBreakableBefore( line()[at] ) || + isBreakableAfter( line()[at-1] ); + } + + void calcLength() { + assert( m_stringIndex < m_column.m_strings.size() ); + + m_suffix = false; + auto width = m_column.m_width-indent(); + m_end = m_pos; + while( m_end < line().size() && line()[m_end] != '\n' ) + ++m_end; + + if( m_end < m_pos + width ) { + m_len = m_end - m_pos; + } + else { + size_t len = width; + while (len > 0 && !isBoundary(m_pos + len)) + --len; + while (len > 0 && isWhitespace( line()[m_pos + len - 1] )) + --len; + + if (len > 0) { + m_len = len; + } else { + m_suffix = true; + m_len = width - 1; + } + } + } + + auto indent() const -> size_t { + auto initial = m_pos == 0 && m_stringIndex == 0 ? m_column.m_initialIndent : std::string::npos; + return initial == std::string::npos ? m_column.m_indent : initial; + } + + auto addIndentAndSuffix(std::string const &plain) const -> std::string { + return std::string( indent(), ' ' ) + (m_suffix ? plain + "-" : plain); + } + + public: + explicit iterator( Column const& column ) : m_column( column ) { + assert( m_column.m_width > m_column.m_indent ); + assert( m_column.m_initialIndent == std::string::npos || m_column.m_width > m_column.m_initialIndent ); + calcLength(); + if( m_len == 0 ) + m_stringIndex++; // Empty string + } + + auto operator *() const -> std::string { + assert( m_stringIndex < m_column.m_strings.size() ); + assert( m_pos <= m_end ); + if( m_pos + m_column.m_width < m_end ) + return addIndentAndSuffix(line().substr(m_pos, m_len)); + else + return addIndentAndSuffix(line().substr(m_pos, m_end - m_pos)); + } + + auto operator ++() -> iterator& { + m_pos += m_len; + if( m_pos < line().size() && line()[m_pos] == '\n' ) + m_pos += 1; + else + while( m_pos < line().size() && isWhitespace( line()[m_pos] ) ) + ++m_pos; + + if( m_pos == line().size() ) { + m_pos = 0; + ++m_stringIndex; + } + if( m_stringIndex < m_column.m_strings.size() ) + calcLength(); + return *this; + } + auto operator ++(int) -> iterator { + iterator prev( *this ); + operator++(); + return prev; + } + + auto operator ==( iterator const& other ) const -> bool { + return + m_pos == other.m_pos && + m_stringIndex == other.m_stringIndex && + &m_column == &other.m_column; + } + auto operator !=( iterator const& other ) const -> bool { + return !operator==( other ); + } + }; + using const_iterator = iterator; + + explicit Column( std::string const& text ) { m_strings.push_back( text ); } + + auto width( size_t newWidth ) -> Column& { + assert( newWidth > 0 ); + m_width = newWidth; + return *this; + } + auto indent( size_t newIndent ) -> Column& { + m_indent = newIndent; + return *this; + } + auto initialIndent( size_t newIndent ) -> Column& { + m_initialIndent = newIndent; + return *this; + } + + auto width() const -> size_t { return m_width; } + auto begin() const -> iterator { return iterator( *this ); } + auto end() const -> iterator { return { *this, m_strings.size() }; } + + inline friend std::ostream& operator << ( std::ostream& os, Column const& col ) { + bool first = true; + for( auto line : col ) { + if( first ) + first = false; + else + os << "\n"; + os << line; + } + return os; + } + + auto operator + ( Column const& other ) -> Columns; + + auto toString() const -> std::string { + std::ostringstream oss; + oss << *this; + return oss.str(); + } + }; + + class Spacer : public Column { + + public: + explicit Spacer( size_t spaceWidth ) : Column( "" ) { + width( spaceWidth ); + } + }; + + class Columns { + std::vector m_columns; + + public: + + class iterator { + friend Columns; + struct EndTag {}; + + std::vector const& m_columns; + std::vector m_iterators; + size_t m_activeIterators; + + iterator( Columns const& columns, EndTag ) + : m_columns( columns.m_columns ), + m_activeIterators( 0 ) + { + m_iterators.reserve( m_columns.size() ); + + for( auto const& col : m_columns ) + m_iterators.push_back( col.end() ); + } + + public: + explicit iterator( Columns const& columns ) + : m_columns( columns.m_columns ), + m_activeIterators( m_columns.size() ) + { + m_iterators.reserve( m_columns.size() ); + + for( auto const& col : m_columns ) + m_iterators.push_back( col.begin() ); + } + + auto operator ==( iterator const& other ) const -> bool { + return m_iterators == other.m_iterators; + } + auto operator !=( iterator const& other ) const -> bool { + return m_iterators != other.m_iterators; + } + auto operator *() const -> std::string { + std::string row, padding; + + for( size_t i = 0; i < m_columns.size(); ++i ) { + auto width = m_columns[i].width(); + if( m_iterators[i] != m_columns[i].end() ) { + std::string col = *m_iterators[i]; + row += padding + col; + if( col.size() < width ) + padding = std::string( width - col.size(), ' ' ); + else + padding = ""; + } + else { + padding += std::string( width, ' ' ); + } + } + return row; + } + auto operator ++() -> iterator& { + for( size_t i = 0; i < m_columns.size(); ++i ) { + if (m_iterators[i] != m_columns[i].end()) + ++m_iterators[i]; + } + return *this; + } + auto operator ++(int) -> iterator { + iterator prev( *this ); + operator++(); + return prev; + } + }; + using const_iterator = iterator; + + auto begin() const -> iterator { return iterator( *this ); } + auto end() const -> iterator { return { *this, iterator::EndTag() }; } + + auto operator += ( Column const& col ) -> Columns& { + m_columns.push_back( col ); + return *this; + } + auto operator + ( Column const& col ) -> Columns { + Columns combined = *this; + combined += col; + return combined; + } + + inline friend std::ostream& operator << ( std::ostream& os, Columns const& cols ) { + + bool first = true; + for( auto line : cols ) { + if( first ) + first = false; + else + os << "\n"; + os << line; + } + return os; + } + + auto toString() const -> std::string { + std::ostringstream oss; + oss << *this; + return oss.str(); + } + }; + + inline auto Column::operator + ( Column const& other ) -> Columns { + Columns cols; + cols += *this; + cols += other; + return cols; + } +}}} // namespace Catch::clara::TextFlow + +// ----------- end of #include from clara_textflow.hpp ----------- +// ........... back in clara.hpp + +#include +#include +#include + +#if !defined(CATCH_PLATFORM_WINDOWS) && ( defined(WIN32) || defined(__WIN32__) || defined(_WIN32) || defined(_MSC_VER) ) +#define CATCH_PLATFORM_WINDOWS +#endif + +namespace Catch { namespace clara { +namespace detail { + + // Traits for extracting arg and return type of lambdas (for single argument lambdas) + template + struct UnaryLambdaTraits : UnaryLambdaTraits {}; + + template + struct UnaryLambdaTraits { + static const bool isValid = false; + }; + + template + struct UnaryLambdaTraits { + static const bool isValid = true; + using ArgType = typename std::remove_const::type>::type;; + using ReturnType = ReturnT; + }; + + class TokenStream; + + // Transport for raw args (copied from main args, or supplied via init list for testing) + class Args { + friend TokenStream; + std::string m_exeName; + std::vector m_args; + + public: + Args( int argc, char *argv[] ) { + m_exeName = argv[0]; + for( int i = 1; i < argc; ++i ) + m_args.push_back( argv[i] ); + } + + Args( std::initializer_list args ) + : m_exeName( *args.begin() ), + m_args( args.begin()+1, args.end() ) + {} + + auto exeName() const -> std::string { + return m_exeName; + } + }; + + // Wraps a token coming from a token stream. These may not directly correspond to strings as a single string + // may encode an option + its argument if the : or = form is used + enum class TokenType { + Option, Argument + }; + struct Token { + TokenType type; + std::string token; + }; + + inline auto isOptPrefix( char c ) -> bool { + return c == '-' +#ifdef CATCH_PLATFORM_WINDOWS + || c == '/' +#endif + ; + } + + // Abstracts iterators into args as a stream of tokens, with option arguments uniformly handled + class TokenStream { + using Iterator = std::vector::const_iterator; + Iterator it; + Iterator itEnd; + std::vector m_tokenBuffer; + + void loadBuffer() { + m_tokenBuffer.resize( 0 ); + + // Skip any empty strings + while( it != itEnd && it->empty() ) + ++it; + + if( it != itEnd ) { + auto const &next = *it; + if( isOptPrefix( next[0] ) ) { + auto delimiterPos = next.find_first_of( " :=" ); + if( delimiterPos != std::string::npos ) { + m_tokenBuffer.push_back( { TokenType::Option, next.substr( 0, delimiterPos ) } ); + m_tokenBuffer.push_back( { TokenType::Argument, next.substr( delimiterPos + 1 ) } ); + } else { + if( next[1] != '-' && next.size() > 2 ) { + std::string opt = "- "; + for( size_t i = 1; i < next.size(); ++i ) { + opt[1] = next[i]; + m_tokenBuffer.push_back( { TokenType::Option, opt } ); + } + } else { + m_tokenBuffer.push_back( { TokenType::Option, next } ); + } + } + } else { + m_tokenBuffer.push_back( { TokenType::Argument, next } ); + } + } + } + + public: + explicit TokenStream( Args const &args ) : TokenStream( args.m_args.begin(), args.m_args.end() ) {} + + TokenStream( Iterator it, Iterator itEnd ) : it( it ), itEnd( itEnd ) { + loadBuffer(); + } + + explicit operator bool() const { + return !m_tokenBuffer.empty() || it != itEnd; + } + + auto count() const -> size_t { return m_tokenBuffer.size() + (itEnd - it); } + + auto operator*() const -> Token { + assert( !m_tokenBuffer.empty() ); + return m_tokenBuffer.front(); + } + + auto operator->() const -> Token const * { + assert( !m_tokenBuffer.empty() ); + return &m_tokenBuffer.front(); + } + + auto operator++() -> TokenStream & { + if( m_tokenBuffer.size() >= 2 ) { + m_tokenBuffer.erase( m_tokenBuffer.begin() ); + } else { + if( it != itEnd ) + ++it; + loadBuffer(); + } + return *this; + } + }; + + class ResultBase { + public: + enum Type { + Ok, LogicError, RuntimeError + }; + + protected: + ResultBase( Type type ) : m_type( type ) {} + virtual ~ResultBase() = default; + + virtual void enforceOk() const = 0; + + Type m_type; + }; + + template + class ResultValueBase : public ResultBase { + public: + auto value() const -> T const & { + enforceOk(); + return m_value; + } + + protected: + ResultValueBase( Type type ) : ResultBase( type ) {} + + ResultValueBase( ResultValueBase const &other ) : ResultBase( other ) { + if( m_type == ResultBase::Ok ) + new( &m_value ) T( other.m_value ); + } + + ResultValueBase( Type, T const &value ) : ResultBase( Ok ) { + new( &m_value ) T( value ); + } + + auto operator=( ResultValueBase const &other ) -> ResultValueBase & { + if( m_type == ResultBase::Ok ) + m_value.~T(); + ResultBase::operator=(other); + if( m_type == ResultBase::Ok ) + new( &m_value ) T( other.m_value ); + return *this; + } + + ~ResultValueBase() { + if( m_type == Ok ) + m_value.~T(); + } + + union { + T m_value; + }; + }; + + template<> + class ResultValueBase : public ResultBase { + protected: + using ResultBase::ResultBase; + }; + + template + class BasicResult : public ResultValueBase { + public: + template + explicit BasicResult( BasicResult const &other ) + : ResultValueBase( other.type() ), + m_errorMessage( other.errorMessage() ) + { + assert( type() != ResultBase::Ok ); + } + + template + static auto ok( U const &value ) -> BasicResult { return { ResultBase::Ok, value }; } + static auto ok() -> BasicResult { return { ResultBase::Ok }; } + static auto logicError( std::string const &message ) -> BasicResult { return { ResultBase::LogicError, message }; } + static auto runtimeError( std::string const &message ) -> BasicResult { return { ResultBase::RuntimeError, message }; } + + explicit operator bool() const { return m_type == ResultBase::Ok; } + auto type() const -> ResultBase::Type { return m_type; } + auto errorMessage() const -> std::string { return m_errorMessage; } + + protected: + virtual void enforceOk() const { + // !TBD: If no exceptions, std::terminate here or something + switch( m_type ) { + case ResultBase::LogicError: + throw std::logic_error( m_errorMessage ); + case ResultBase::RuntimeError: + throw std::runtime_error( m_errorMessage ); + case ResultBase::Ok: + break; + } + } + + std::string m_errorMessage; // Only populated if resultType is an error + + BasicResult( ResultBase::Type type, std::string const &message ) + : ResultValueBase(type), + m_errorMessage(message) + { + assert( m_type != ResultBase::Ok ); + } + + using ResultValueBase::ResultValueBase; + using ResultBase::m_type; + }; + + enum class ParseResultType { + Matched, NoMatch, ShortCircuitAll, ShortCircuitSame + }; + + class ParseState { + public: + + ParseState( ParseResultType type, TokenStream const &remainingTokens ) + : m_type(type), + m_remainingTokens( remainingTokens ) + {} + + auto type() const -> ParseResultType { return m_type; } + auto remainingTokens() const -> TokenStream { return m_remainingTokens; } + + private: + ParseResultType m_type; + TokenStream m_remainingTokens; + }; + + using Result = BasicResult; + using ParserResult = BasicResult; + using InternalParseResult = BasicResult; + + struct HelpColumns { + std::string left; + std::string right; + }; + + template + inline auto convertInto( std::string const &source, T& target ) -> ParserResult { + std::stringstream ss; + ss << source; + ss >> target; + if( ss.fail() ) + return ParserResult::runtimeError( "Unable to convert '" + source + "' to destination type" ); + else + return ParserResult::ok( ParseResultType::Matched ); + } + inline auto convertInto( std::string const &source, std::string& target ) -> ParserResult { + target = source; + return ParserResult::ok( ParseResultType::Matched ); + } + inline auto convertInto( std::string const &source, bool &target ) -> ParserResult { + std::string srcLC = source; + std::transform( srcLC.begin(), srcLC.end(), srcLC.begin(), []( char c ) { return static_cast( ::tolower(c) ); } ); + if (srcLC == "y" || srcLC == "1" || srcLC == "true" || srcLC == "yes" || srcLC == "on") + target = true; + else if (srcLC == "n" || srcLC == "0" || srcLC == "false" || srcLC == "no" || srcLC == "off") + target = false; + else + return ParserResult::runtimeError( "Expected a boolean value but did not recognise: '" + source + "'" ); + return ParserResult::ok( ParseResultType::Matched ); + } + + struct BoundRefBase { + BoundRefBase() = default; + BoundRefBase( BoundRefBase const & ) = delete; + BoundRefBase( BoundRefBase && ) = delete; + BoundRefBase &operator=( BoundRefBase const & ) = delete; + BoundRefBase &operator=( BoundRefBase && ) = delete; + + virtual ~BoundRefBase() = default; + + virtual auto isFlag() const -> bool = 0; + virtual auto isContainer() const -> bool { return false; } + virtual auto setValue( std::string const &arg ) -> ParserResult = 0; + virtual auto setFlag( bool flag ) -> ParserResult = 0; + }; + + struct BoundValueRefBase : BoundRefBase { + auto isFlag() const -> bool override { return false; } + + auto setFlag( bool ) -> ParserResult override { + return ParserResult::logicError( "Flags can only be set on boolean fields" ); + } + }; + + struct BoundFlagRefBase : BoundRefBase { + auto isFlag() const -> bool override { return true; } + + auto setValue( std::string const &arg ) -> ParserResult override { + bool flag; + auto result = convertInto( arg, flag ); + if( result ) + setFlag( flag ); + return result; + } + }; + + template + struct BoundRef : BoundValueRefBase { + T &m_ref; + + explicit BoundRef( T &ref ) : m_ref( ref ) {} + + auto setValue( std::string const &arg ) -> ParserResult override { + return convertInto( arg, m_ref ); + } + }; + + template + struct BoundRef> : BoundValueRefBase { + std::vector &m_ref; + + explicit BoundRef( std::vector &ref ) : m_ref( ref ) {} + + auto isContainer() const -> bool override { return true; } + + auto setValue( std::string const &arg ) -> ParserResult override { + T temp; + auto result = convertInto( arg, temp ); + if( result ) + m_ref.push_back( temp ); + return result; + } + }; + + struct BoundFlagRef : BoundFlagRefBase { + bool &m_ref; + + explicit BoundFlagRef( bool &ref ) : m_ref( ref ) {} + + auto setFlag( bool flag ) -> ParserResult override { + m_ref = flag; + return ParserResult::ok( ParseResultType::Matched ); + } + }; + + template + struct LambdaInvoker { + static_assert( std::is_same::value, "Lambda must return void or clara::ParserResult" ); + + template + static auto invoke( L const &lambda, ArgType const &arg ) -> ParserResult { + return lambda( arg ); + } + }; + + template<> + struct LambdaInvoker { + template + static auto invoke( L const &lambda, ArgType const &arg ) -> ParserResult { + lambda( arg ); + return ParserResult::ok( ParseResultType::Matched ); + } + }; + + template + inline auto invokeLambda( L const &lambda, std::string const &arg ) -> ParserResult { + ArgType temp; + auto result = convertInto( arg, temp ); + return !result + ? result + : LambdaInvoker::ReturnType>::invoke( lambda, temp ); + }; + + template + struct BoundLambda : BoundValueRefBase { + L m_lambda; + + static_assert( UnaryLambdaTraits::isValid, "Supplied lambda must take exactly one argument" ); + explicit BoundLambda( L const &lambda ) : m_lambda( lambda ) {} + + auto setValue( std::string const &arg ) -> ParserResult override { + return invokeLambda::ArgType>( m_lambda, arg ); + } + }; + + template + struct BoundFlagLambda : BoundFlagRefBase { + L m_lambda; + + static_assert( UnaryLambdaTraits::isValid, "Supplied lambda must take exactly one argument" ); + static_assert( std::is_same::ArgType, bool>::value, "flags must be boolean" ); + + explicit BoundFlagLambda( L const &lambda ) : m_lambda( lambda ) {} + + auto setFlag( bool flag ) -> ParserResult override { + return LambdaInvoker::ReturnType>::invoke( m_lambda, flag ); + } + }; + + enum class Optionality { Optional, Required }; + + struct Parser; + + class ParserBase { + public: + virtual ~ParserBase() = default; + virtual auto validate() const -> Result { return Result::ok(); } + virtual auto parse( std::string const& exeName, TokenStream const &tokens) const -> InternalParseResult = 0; + virtual auto cardinality() const -> size_t { return 1; } + + auto parse( Args const &args ) const -> InternalParseResult { + return parse( args.exeName(), TokenStream( args ) ); + } + }; + + template + class ComposableParserImpl : public ParserBase { + public: + template + auto operator|( T const &other ) const -> Parser; + }; + + // Common code and state for Args and Opts + template + class ParserRefImpl : public ComposableParserImpl { + protected: + Optionality m_optionality = Optionality::Optional; + std::shared_ptr m_ref; + std::string m_hint; + std::string m_description; + + explicit ParserRefImpl( std::shared_ptr const &ref ) : m_ref( ref ) {} + + public: + template + ParserRefImpl( T &ref, std::string const &hint ) + : m_ref( std::make_shared>( ref ) ), + m_hint( hint ) + {} + + template + ParserRefImpl( LambdaT const &ref, std::string const &hint ) + : m_ref( std::make_shared>( ref ) ), + m_hint(hint) + {} + + auto operator()( std::string const &description ) -> DerivedT & { + m_description = description; + return static_cast( *this ); + } + + auto optional() -> DerivedT & { + m_optionality = Optionality::Optional; + return static_cast( *this ); + }; + + auto required() -> DerivedT & { + m_optionality = Optionality::Required; + return static_cast( *this ); + }; + + auto isOptional() const -> bool { + return m_optionality == Optionality::Optional; + } + + auto cardinality() const -> size_t override { + if( m_ref->isContainer() ) + return 0; + else + return 1; + } + + auto hint() const -> std::string { return m_hint; } + }; + + class ExeName : public ComposableParserImpl { + std::shared_ptr m_name; + std::shared_ptr m_ref; + + template + static auto makeRef(LambdaT const &lambda) -> std::shared_ptr { + return std::make_shared>( lambda) ; + } + + public: + ExeName() : m_name( std::make_shared( "" ) ) {} + + explicit ExeName( std::string &ref ) : ExeName() { + m_ref = std::make_shared>( ref ); + } + + template + explicit ExeName( LambdaT const& lambda ) : ExeName() { + m_ref = std::make_shared>( lambda ); + } + + // The exe name is not parsed out of the normal tokens, but is handled specially + auto parse( std::string const&, TokenStream const &tokens ) const -> InternalParseResult override { + return InternalParseResult::ok( ParseState( ParseResultType::NoMatch, tokens ) ); + } + + auto name() const -> std::string { return *m_name; } + auto set( std::string const& newName ) -> ParserResult { + + auto lastSlash = newName.find_last_of( "\\/" ); + auto filename = ( lastSlash == std::string::npos ) + ? newName + : newName.substr( lastSlash+1 ); + + *m_name = filename; + if( m_ref ) + return m_ref->setValue( filename ); + else + return ParserResult::ok( ParseResultType::Matched ); + } + }; + + class Arg : public ParserRefImpl { + public: + using ParserRefImpl::ParserRefImpl; + + auto parse( std::string const &, TokenStream const &tokens ) const -> InternalParseResult override { + auto validationResult = validate(); + if( !validationResult ) + return InternalParseResult( validationResult ); + + auto remainingTokens = tokens; + auto const &token = *remainingTokens; + if( token.type != TokenType::Argument ) + return InternalParseResult::ok( ParseState( ParseResultType::NoMatch, remainingTokens ) ); + + auto result = m_ref->setValue( remainingTokens->token ); + if( !result ) + return InternalParseResult( result ); + else + return InternalParseResult::ok( ParseState( ParseResultType::Matched, ++remainingTokens ) ); + } + }; + + inline auto normaliseOpt( std::string const &optName ) -> std::string { +#ifdef CATCH_PLATFORM_WINDOWS + if( optName[0] == '/' ) + return "-" + optName.substr( 1 ); + else +#endif + return optName; + } + + class Opt : public ParserRefImpl { + protected: + std::vector m_optNames; + + public: + template + explicit Opt( LambdaT const &ref ) : ParserRefImpl( std::make_shared>( ref ) ) {} + + explicit Opt( bool &ref ) : ParserRefImpl( std::make_shared( ref ) ) {} + + template + Opt( LambdaT const &ref, std::string const &hint ) : ParserRefImpl( ref, hint ) {} + + template + Opt( T &ref, std::string const &hint ) : ParserRefImpl( ref, hint ) {} + + auto operator[]( std::string const &optName ) -> Opt & { + m_optNames.push_back( optName ); + return *this; + } + + auto getHelpColumns() const -> std::vector { + std::ostringstream oss; + bool first = true; + for( auto const &opt : m_optNames ) { + if (first) + first = false; + else + oss << ", "; + oss << opt; + } + if( !m_hint.empty() ) + oss << " <" << m_hint << ">"; + return { { oss.str(), m_description } }; + } + + auto isMatch( std::string const &optToken ) const -> bool { + auto normalisedToken = normaliseOpt( optToken ); + for( auto const &name : m_optNames ) { + if( normaliseOpt( name ) == normalisedToken ) + return true; + } + return false; + } + + using ParserBase::parse; + + auto parse( std::string const&, TokenStream const &tokens ) const -> InternalParseResult override { + auto validationResult = validate(); + if( !validationResult ) + return InternalParseResult( validationResult ); + + auto remainingTokens = tokens; + if( remainingTokens && remainingTokens->type == TokenType::Option ) { + auto const &token = *remainingTokens; + if( isMatch(token.token ) ) { + if( m_ref->isFlag() ) { + auto result = m_ref->setFlag( true ); + if( !result ) + return InternalParseResult( result ); + if( result.value() == ParseResultType::ShortCircuitAll ) + return InternalParseResult::ok( ParseState( result.value(), remainingTokens ) ); + } else { + ++remainingTokens; + if( !remainingTokens ) + return InternalParseResult::runtimeError( "Expected argument following " + token.token ); + auto const &argToken = *remainingTokens; + if( argToken.type != TokenType::Argument ) + return InternalParseResult::runtimeError( "Expected argument following " + token.token ); + auto result = m_ref->setValue( argToken.token ); + if( !result ) + return InternalParseResult( result ); + if( result.value() == ParseResultType::ShortCircuitAll ) + return InternalParseResult::ok( ParseState( result.value(), remainingTokens ) ); + } + return InternalParseResult::ok( ParseState( ParseResultType::Matched, ++remainingTokens ) ); + } + } + return InternalParseResult::ok( ParseState( ParseResultType::NoMatch, remainingTokens ) ); + } + + auto validate() const -> Result override { + if( m_optNames.empty() ) + return Result::logicError( "No options supplied to Opt" ); + for( auto const &name : m_optNames ) { + if( name.empty() ) + return Result::logicError( "Option name cannot be empty" ); +#ifdef CATCH_PLATFORM_WINDOWS + if( name[0] != '-' && name[0] != '/' ) + return Result::logicError( "Option name must begin with '-' or '/'" ); +#else + if( name[0] != '-' ) + return Result::logicError( "Option name must begin with '-'" ); +#endif + } + return ParserRefImpl::validate(); + } + }; + + struct Help : Opt { + Help( bool &showHelpFlag ) + : Opt([&]( bool flag ) { + showHelpFlag = flag; + return ParserResult::ok( ParseResultType::ShortCircuitAll ); + }) + { + static_cast( *this ) + ("display usage information") + ["-?"]["-h"]["--help"] + .optional(); + } + }; + + struct Parser : ParserBase { + + mutable ExeName m_exeName; + std::vector m_options; + std::vector m_args; + + auto operator|=( ExeName const &exeName ) -> Parser & { + m_exeName = exeName; + return *this; + } + + auto operator|=( Arg const &arg ) -> Parser & { + m_args.push_back(arg); + return *this; + } + + auto operator|=( Opt const &opt ) -> Parser & { + m_options.push_back(opt); + return *this; + } + + auto operator|=( Parser const &other ) -> Parser & { + m_options.insert(m_options.end(), other.m_options.begin(), other.m_options.end()); + m_args.insert(m_args.end(), other.m_args.begin(), other.m_args.end()); + return *this; + } + + template + auto operator|( T const &other ) const -> Parser { + return Parser( *this ) |= other; + } + + auto getHelpColumns() const -> std::vector { + std::vector cols; + for (auto const &o : m_options) { + auto childCols = o.getHelpColumns(); + cols.insert( cols.end(), childCols.begin(), childCols.end() ); + } + return cols; + } + + void writeToStream( std::ostream &os ) const { + if (!m_exeName.name().empty()) { + os << "usage:\n" << " " << m_exeName.name() << " "; + bool required = true, first = true; + for( auto const &arg : m_args ) { + if (first) + first = false; + else + os << " "; + if( arg.isOptional() && required ) { + os << "["; + required = false; + } + os << "<" << arg.hint() << ">"; + if( arg.cardinality() == 0 ) + os << " ... "; + } + if( !required ) + os << "]"; + if( !m_options.empty() ) + os << " options"; + os << "\n\nwhere options are:" << std::endl; + } + + auto rows = getHelpColumns(); + size_t consoleWidth = CATCH_CLARA_CONFIG_CONSOLE_WIDTH; + size_t optWidth = 0; + for( auto const &cols : rows ) + optWidth = (std::max)(optWidth, cols.left.size() + 2); + + for( auto const &cols : rows ) { + auto row = + TextFlow::Column( cols.left ).width( optWidth ).indent( 2 ) + + TextFlow::Spacer(4) + + TextFlow::Column( cols.right ).width( consoleWidth - 7 - optWidth ); + os << row << std::endl; + } + } + + friend auto operator<<( std::ostream &os, Parser const &parser ) -> std::ostream& { + parser.writeToStream( os ); + return os; + } + + auto validate() const -> Result override { + for( auto const &opt : m_options ) { + auto result = opt.validate(); + if( !result ) + return result; + } + for( auto const &arg : m_args ) { + auto result = arg.validate(); + if( !result ) + return result; + } + return Result::ok(); + } + + using ParserBase::parse; + + auto parse( std::string const& exeName, TokenStream const &tokens ) const -> InternalParseResult override { + + struct ParserInfo { + ParserBase const* parser = nullptr; + size_t count = 0; + }; + const size_t totalParsers = m_options.size() + m_args.size(); + assert( totalParsers < 512 ); + // ParserInfo parseInfos[totalParsers]; // <-- this is what we really want to do + ParserInfo parseInfos[512]; + + { + size_t i = 0; + for (auto const &opt : m_options) parseInfos[i++].parser = &opt; + for (auto const &arg : m_args) parseInfos[i++].parser = &arg; + } + + m_exeName.set( exeName ); + + auto result = InternalParseResult::ok( ParseState( ParseResultType::NoMatch, tokens ) ); + while( result.value().remainingTokens() ) { + bool tokenParsed = false; + + for( size_t i = 0; i < totalParsers; ++i ) { + auto& parseInfo = parseInfos[i]; + if( parseInfo.parser->cardinality() == 0 || parseInfo.count < parseInfo.parser->cardinality() ) { + result = parseInfo.parser->parse(exeName, result.value().remainingTokens()); + if (!result) + return result; + if (result.value().type() != ParseResultType::NoMatch) { + tokenParsed = true; + ++parseInfo.count; + break; + } + } + } + + if( result.value().type() == ParseResultType::ShortCircuitAll ) + return result; + if( !tokenParsed ) + return InternalParseResult::runtimeError( "Unrecognised token: " + result.value().remainingTokens()->token ); + } + // !TBD Check missing required options + return result; + } + }; + + template + template + auto ComposableParserImpl::operator|( T const &other ) const -> Parser { + return Parser() | static_cast( *this ) | other; + } +} // namespace detail + +// A Combined parser +using detail::Parser; + +// A parser for options +using detail::Opt; + +// A parser for arguments +using detail::Arg; + +// Wrapper for argc, argv from main() +using detail::Args; + +// Specifies the name of the executable +using detail::ExeName; + +// Convenience wrapper for option parser that specifies the help option +using detail::Help; + +// enum of result types from a parse +using detail::ParseResultType; + +// Result type for parser operation +using detail::ParserResult; + +}} // namespace Catch::clara + +// end clara.hpp +#ifdef __clang__ +#pragma clang diagnostic pop +#endif + +// Restore Clara's value for console width, if present +#ifdef CATCH_TEMP_CLARA_CONFIG_CONSOLE_WIDTH +#define CATCH_CLARA_TEXTFLOW_CONFIG_CONSOLE_WIDTH CATCH_TEMP_CLARA_CONFIG_CONSOLE_WIDTH +#undef CATCH_TEMP_CLARA_CONFIG_CONSOLE_WIDTH +#endif + +// end catch_clara.h +namespace Catch { + + clara::Parser makeCommandLineParser( ConfigData& config ); + +} // end namespace Catch + +// end catch_commandline.h +#include +#include + +namespace Catch { + + clara::Parser makeCommandLineParser( ConfigData& config ) { + + using namespace clara; + + auto const setWarning = [&]( std::string const& warning ) { + if( warning != "NoAssertions" ) + return ParserResult::runtimeError( "Unrecognised warning: '" + warning + "'" ); + config.warnings = static_cast( config.warnings | WarnAbout::NoAssertions ); + return ParserResult::ok( ParseResultType::Matched ); + }; + auto const loadTestNamesFromFile = [&]( std::string const& filename ) { + std::ifstream f( filename.c_str() ); + if( !f.is_open() ) + return ParserResult::runtimeError( "Unable to load input file: '" + filename + "'" ); + + std::string line; + while( std::getline( f, line ) ) { + line = trim(line); + if( !line.empty() && !startsWith( line, '#' ) ) { + if( !startsWith( line, '"' ) ) + line = '"' + line + '"'; + config.testsOrTags.push_back( line + ',' ); + } + } + return ParserResult::ok( ParseResultType::Matched ); + }; + auto const setTestOrder = [&]( std::string const& order ) { + if( startsWith( "declared", order ) ) + config.runOrder = RunTests::InDeclarationOrder; + else if( startsWith( "lexical", order ) ) + config.runOrder = RunTests::InLexicographicalOrder; + else if( startsWith( "random", order ) ) + config.runOrder = RunTests::InRandomOrder; + else + return clara::ParserResult::runtimeError( "Unrecognised ordering: '" + order + "'" ); + return ParserResult::ok( ParseResultType::Matched ); + }; + auto const setRngSeed = [&]( std::string const& seed ) { + if( seed != "time" ) + return clara::detail::convertInto( seed, config.rngSeed ); + config.rngSeed = static_cast( std::time(nullptr) ); + return ParserResult::ok( ParseResultType::Matched ); + }; + auto const setColourUsage = [&]( std::string const& useColour ) { + auto mode = toLower( useColour ); + + if( mode == "yes" ) + config.useColour = UseColour::Yes; + else if( mode == "no" ) + config.useColour = UseColour::No; + else if( mode == "auto" ) + config.useColour = UseColour::Auto; + else + return ParserResult::runtimeError( "colour mode must be one of: auto, yes or no. '" + useColour + "' not recognised" ); + return ParserResult::ok( ParseResultType::Matched ); + }; + auto const setWaitForKeypress = [&]( std::string const& keypress ) { + auto keypressLc = toLower( keypress ); + if( keypressLc == "start" ) + config.waitForKeypress = WaitForKeypress::BeforeStart; + else if( keypressLc == "exit" ) + config.waitForKeypress = WaitForKeypress::BeforeExit; + else if( keypressLc == "both" ) + config.waitForKeypress = WaitForKeypress::BeforeStartAndExit; + else + return ParserResult::runtimeError( "keypress argument must be one of: start, exit or both. '" + keypress + "' not recognised" ); + return ParserResult::ok( ParseResultType::Matched ); + }; + auto const setVerbosity = [&]( std::string const& verbosity ) { + auto lcVerbosity = toLower( verbosity ); + if( lcVerbosity == "quiet" ) + config.verbosity = Verbosity::Quiet; + else if( lcVerbosity == "normal" ) + config.verbosity = Verbosity::Normal; + else if( lcVerbosity == "high" ) + config.verbosity = Verbosity::High; + else + return ParserResult::runtimeError( "Unrecognised verbosity, '" + verbosity + "'" ); + return ParserResult::ok( ParseResultType::Matched ); + }; + + auto cli + = ExeName( config.processName ) + | Help( config.showHelp ) + | Opt( config.listTests ) + ["-l"]["--list-tests"] + ( "list all/matching test cases" ) + | Opt( config.listTags ) + ["-t"]["--list-tags"] + ( "list all/matching tags" ) + | Opt( config.showSuccessfulTests ) + ["-s"]["--success"] + ( "include successful tests in output" ) + | Opt( config.shouldDebugBreak ) + ["-b"]["--break"] + ( "break into debugger on failure" ) + | Opt( config.noThrow ) + ["-e"]["--nothrow"] + ( "skip exception tests" ) + | Opt( config.showInvisibles ) + ["-i"]["--invisibles"] + ( "show invisibles (tabs, newlines)" ) + | Opt( config.outputFilename, "filename" ) + ["-o"]["--out"] + ( "output filename" ) + | Opt( config.reporterNames, "name" ) + ["-r"]["--reporter"] + ( "reporter to use (defaults to console)" ) + | Opt( config.name, "name" ) + ["-n"]["--name"] + ( "suite name" ) + | Opt( [&]( bool ){ config.abortAfter = 1; } ) + ["-a"]["--abort"] + ( "abort at first failure" ) + | Opt( [&]( int x ){ config.abortAfter = x; }, "no. failures" ) + ["-x"]["--abortx"] + ( "abort after x failures" ) + | Opt( setWarning, "warning name" ) + ["-w"]["--warn"] + ( "enable warnings" ) + | Opt( [&]( bool flag ) { config.showDurations = flag ? ShowDurations::Always : ShowDurations::Never; }, "yes|no" ) + ["-d"]["--durations"] + ( "show test durations" ) + | Opt( loadTestNamesFromFile, "filename" ) + ["-f"]["--input-file"] + ( "load test names to run from a file" ) + | Opt( config.filenamesAsTags ) + ["-#"]["--filenames-as-tags"] + ( "adds a tag for the filename" ) + | Opt( config.sectionsToRun, "section name" ) + ["-c"]["--section"] + ( "specify section to run" ) + | Opt( setVerbosity, "quiet|normal|high" ) + ["-v"]["--verbosity"] + ( "set output verbosity" ) + | Opt( config.listTestNamesOnly ) + ["--list-test-names-only"] + ( "list all/matching test cases names only" ) + | Opt( config.listReporters ) + ["--list-reporters"] + ( "list all reporters" ) + | Opt( setTestOrder, "decl|lex|rand" ) + ["--order"] + ( "test case order (defaults to decl)" ) + | Opt( setRngSeed, "'time'|number" ) + ["--rng-seed"] + ( "set a specific seed for random numbers" ) + | Opt( setColourUsage, "yes|no" ) + ["--use-colour"] + ( "should output be colourised" ) + | Opt( config.libIdentify ) + ["--libidentify"] + ( "report name and version according to libidentify standard" ) + | Opt( setWaitForKeypress, "start|exit|both" ) + ["--wait-for-keypress"] + ( "waits for a keypress before exiting" ) + | Opt( config.benchmarkResolutionMultiple, "multiplier" ) + ["--benchmark-resolution-multiple"] + ( "multiple of clock resolution to run benchmarks" ) + + | Arg( config.testsOrTags, "test name|pattern|tags" ) + ( "which test or tests to use" ); + + return cli; + } + +} // end namespace Catch +// end catch_commandline.cpp +// start catch_common.cpp + +#include +#include + +namespace Catch { + + SourceLineInfo::SourceLineInfo( char const* _file, std::size_t _line ) noexcept + : file( _file ), + line( _line ) + {} + bool SourceLineInfo::empty() const noexcept { + return file[0] == '\0'; + } + bool SourceLineInfo::operator == ( SourceLineInfo const& other ) const noexcept { + return line == other.line && (file == other.file || std::strcmp(file, other.file) == 0); + } + bool SourceLineInfo::operator < ( SourceLineInfo const& other ) const noexcept { + return line < other.line || ( line == other.line && (std::strcmp(file, other.file) < 0)); + } + + std::ostream& operator << ( std::ostream& os, SourceLineInfo const& info ) { +#ifndef __GNUG__ + os << info.file << '(' << info.line << ')'; +#else + os << info.file << ':' << info.line; +#endif + return os; + } + + bool isTrue( bool value ){ return value; } + bool alwaysTrue() { return true; } + bool alwaysFalse() { return false; } + + std::string StreamEndStop::operator+() const { + return std::string(); + } + + NonCopyable::NonCopyable() = default; + NonCopyable::~NonCopyable() = default; + +} +// end catch_common.cpp +// start catch_config.cpp + +namespace Catch { + + Config::Config( ConfigData const& data ) + : m_data( data ), + m_stream( openStream() ) + { + if( !data.testsOrTags.empty() ) { + TestSpecParser parser( ITagAliasRegistry::get() ); + for( auto const& testOrTags : data.testsOrTags ) + parser.parse( testOrTags ); + m_testSpec = parser.testSpec(); + } + } + + std::string const& Config::getFilename() const { + return m_data.outputFilename ; + } + + bool Config::listTests() const { return m_data.listTests; } + bool Config::listTestNamesOnly() const { return m_data.listTestNamesOnly; } + bool Config::listTags() const { return m_data.listTags; } + bool Config::listReporters() const { return m_data.listReporters; } + + std::string Config::getProcessName() const { return m_data.processName; } + + std::vector const& Config::getReporterNames() const { return m_data.reporterNames; } + std::vector const& Config::getSectionsToRun() const { return m_data.sectionsToRun; } + + TestSpec const& Config::testSpec() const { return m_testSpec; } + + bool Config::showHelp() const { return m_data.showHelp; } + + // IConfig interface + bool Config::allowThrows() const { return !m_data.noThrow; } + std::ostream& Config::stream() const { return m_stream->stream(); } + std::string Config::name() const { return m_data.name.empty() ? m_data.processName : m_data.name; } + bool Config::includeSuccessfulResults() const { return m_data.showSuccessfulTests; } + bool Config::warnAboutMissingAssertions() const { return m_data.warnings & WarnAbout::NoAssertions; } + ShowDurations::OrNot Config::showDurations() const { return m_data.showDurations; } + RunTests::InWhatOrder Config::runOrder() const { return m_data.runOrder; } + unsigned int Config::rngSeed() const { return m_data.rngSeed; } + int Config::benchmarkResolutionMultiple() const { return m_data.benchmarkResolutionMultiple; } + UseColour::YesOrNo Config::useColour() const { return m_data.useColour; } + bool Config::shouldDebugBreak() const { return m_data.shouldDebugBreak; } + int Config::abortAfter() const { return m_data.abortAfter; } + bool Config::showInvisibles() const { return m_data.showInvisibles; } + Verbosity Config::verbosity() const { return m_data.verbosity; } + + IStream const* Config::openStream() { + if( m_data.outputFilename.empty() ) + return new CoutStream(); + else if( m_data.outputFilename[0] == '%' ) { + if( m_data.outputFilename == "%debug" ) + return new DebugOutStream(); + else + CATCH_ERROR( "Unrecognised stream: '" << m_data.outputFilename << "'" ); + } + else + return new FileStream( m_data.outputFilename ); + } + +} // end namespace Catch +// end catch_config.cpp +// start catch_console_colour.cpp + +#if defined(__clang__) +# pragma clang diagnostic push +# pragma clang diagnostic ignored "-Wexit-time-destructors" +#endif + +// start catch_errno_guard.h + +namespace Catch { + + class ErrnoGuard { + public: + ErrnoGuard(); + ~ErrnoGuard(); + private: + int m_oldErrno; + }; + +} + +// end catch_errno_guard.h +// start catch_windows_h_proxy.h + + +#if defined(CATCH_PLATFORM_WINDOWS) + +#if !defined(NOMINMAX) && !defined(CATCH_CONFIG_NO_NOMINMAX) +# define CATCH_DEFINED_NOMINMAX # define NOMINMAX #endif -#ifdef CATCH_DEFINES_WIN32_LEAN_AND_MEAN +#if !defined(WIN32_LEAN_AND_MEAN) && !defined(CATCH_CONFIG_NO_WIN32_LEAN_AND_MEAN) +# define CATCH_DEFINED_WIN32_LEAN_AND_MEAN # define WIN32_LEAN_AND_MEAN #endif @@ -6348,1491 +5909,21 @@ namespace Catch { #include #endif -#ifdef CATCH_DEFINES_NOMINMAX +#ifdef CATCH_DEFINED_NOMINMAX # undef NOMINMAX #endif -#ifdef CATCH_DEFINES_WIN32_LEAN_AND_MEAN +#ifdef CATCH_DEFINED_WIN32_LEAN_AND_MEAN # undef WIN32_LEAN_AND_MEAN #endif +#endif // defined(CATCH_PLATFORM_WINDOWS) -# if !defined ( CATCH_CONFIG_WINDOWS_SEH ) - -namespace Catch { - struct FatalConditionHandler { - void reset() {} - }; -} - -# else // CATCH_CONFIG_WINDOWS_SEH is defined - -namespace Catch { - - struct SignalDefs { DWORD id; const char* name; }; - extern SignalDefs signalDefs[]; - // There is no 1-1 mapping between signals and windows exceptions. - // Windows can easily distinguish between SO and SigSegV, - // but SigInt, SigTerm, etc are handled differently. - SignalDefs signalDefs[] = { - { EXCEPTION_ILLEGAL_INSTRUCTION, "SIGILL - Illegal instruction signal" }, - { EXCEPTION_STACK_OVERFLOW, "SIGSEGV - Stack overflow" }, - { EXCEPTION_ACCESS_VIOLATION, "SIGSEGV - Segmentation violation signal" }, - { EXCEPTION_INT_DIVIDE_BY_ZERO, "Divide by zero error" }, - }; - - struct FatalConditionHandler { - - static LONG CALLBACK handleVectoredException(PEXCEPTION_POINTERS ExceptionInfo) { - for (int i = 0; i < sizeof(signalDefs) / sizeof(SignalDefs); ++i) { - if (ExceptionInfo->ExceptionRecord->ExceptionCode == signalDefs[i].id) { - reportFatal(signalDefs[i].name); - } - } - // If its not an exception we care about, pass it along. - // This stops us from eating debugger breaks etc. - return EXCEPTION_CONTINUE_SEARCH; - } - - FatalConditionHandler() { - isSet = true; - // 32k seems enough for Catch to handle stack overflow, - // but the value was found experimentally, so there is no strong guarantee - guaranteeSize = 32 * 1024; - exceptionHandlerHandle = CATCH_NULL; - // Register as first handler in current chain - exceptionHandlerHandle = AddVectoredExceptionHandler(1, handleVectoredException); - // Pass in guarantee size to be filled - SetThreadStackGuarantee(&guaranteeSize); - } - - static void reset() { - if (isSet) { - // Unregister handler and restore the old guarantee - RemoveVectoredExceptionHandler(exceptionHandlerHandle); - SetThreadStackGuarantee(&guaranteeSize); - exceptionHandlerHandle = CATCH_NULL; - isSet = false; - } - } - - ~FatalConditionHandler() { - reset(); - } - private: - static bool isSet; - static ULONG guaranteeSize; - static PVOID exceptionHandlerHandle; - }; - - bool FatalConditionHandler::isSet = false; - ULONG FatalConditionHandler::guaranteeSize = 0; - PVOID FatalConditionHandler::exceptionHandlerHandle = CATCH_NULL; - -} // namespace Catch - -# endif // CATCH_CONFIG_WINDOWS_SEH - -#else // Not Windows - assumed to be POSIX compatible ////////////////////////// - -# if !defined(CATCH_CONFIG_POSIX_SIGNALS) - -namespace Catch { - struct FatalConditionHandler { - void reset() {} - }; -} - -# else // CATCH_CONFIG_POSIX_SIGNALS is defined - -#include - -namespace Catch { - - struct SignalDefs { - int id; - const char* name; - }; - extern SignalDefs signalDefs[]; - SignalDefs signalDefs[] = { - { SIGINT, "SIGINT - Terminal interrupt signal" }, - { SIGILL, "SIGILL - Illegal instruction signal" }, - { SIGFPE, "SIGFPE - Floating point error signal" }, - { SIGSEGV, "SIGSEGV - Segmentation violation signal" }, - { SIGTERM, "SIGTERM - Termination request signal" }, - { SIGABRT, "SIGABRT - Abort (abnormal termination) signal" } - }; - - struct FatalConditionHandler { - - static bool isSet; - static struct sigaction oldSigActions [sizeof(signalDefs)/sizeof(SignalDefs)]; - static stack_t oldSigStack; - static char altStackMem[SIGSTKSZ]; - - static void handleSignal( int sig ) { - std::string name = ""; - for (std::size_t i = 0; i < sizeof(signalDefs) / sizeof(SignalDefs); ++i) { - SignalDefs &def = signalDefs[i]; - if (sig == def.id) { - name = def.name; - break; - } - } - reset(); - reportFatal(name); - raise( sig ); - } - - FatalConditionHandler() { - isSet = true; - stack_t sigStack; - sigStack.ss_sp = altStackMem; - sigStack.ss_size = SIGSTKSZ; - sigStack.ss_flags = 0; - sigaltstack(&sigStack, &oldSigStack); - struct sigaction sa = { 0 }; - - sa.sa_handler = handleSignal; - sa.sa_flags = SA_ONSTACK; - for (std::size_t i = 0; i < sizeof(signalDefs)/sizeof(SignalDefs); ++i) { - sigaction(signalDefs[i].id, &sa, &oldSigActions[i]); - } - } - - ~FatalConditionHandler() { - reset(); - } - static void reset() { - if( isSet ) { - // Set signals back to previous values -- hopefully nobody overwrote them in the meantime - for( std::size_t i = 0; i < sizeof(signalDefs)/sizeof(SignalDefs); ++i ) { - sigaction(signalDefs[i].id, &oldSigActions[i], CATCH_NULL); - } - // Return the old stack - sigaltstack(&oldSigStack, CATCH_NULL); - isSet = false; - } - } - }; - - bool FatalConditionHandler::isSet = false; - struct sigaction FatalConditionHandler::oldSigActions[sizeof(signalDefs)/sizeof(SignalDefs)] = {}; - stack_t FatalConditionHandler::oldSigStack = {}; - char FatalConditionHandler::altStackMem[SIGSTKSZ] = {}; - -} // namespace Catch - -# endif // CATCH_CONFIG_POSIX_SIGNALS - -#endif // not Windows - -#include -#include - -namespace Catch { - - class StreamRedirect { - - public: - StreamRedirect( std::ostream& stream, std::string& targetString ) - : m_stream( stream ), - m_prevBuf( stream.rdbuf() ), - m_targetString( targetString ) - { - stream.rdbuf( m_oss.rdbuf() ); - } - - ~StreamRedirect() { - m_targetString += m_oss.str(); - m_stream.rdbuf( m_prevBuf ); - } - - private: - std::ostream& m_stream; - std::streambuf* m_prevBuf; - std::ostringstream 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) - :m_cerrBuf( cerr().rdbuf() ), m_clogBuf(clog().rdbuf()), - m_targetString(targetString){ - cerr().rdbuf(m_oss.rdbuf()); - clog().rdbuf(m_oss.rdbuf()); - } - ~StdErrRedirect() { - m_targetString += m_oss.str(); - cerr().rdbuf(m_cerrBuf); - clog().rdbuf(m_clogBuf); - } - private: - std::streambuf* m_cerrBuf; - std::streambuf* m_clogBuf; - std::ostringstream m_oss; - std::string& m_targetString; - }; - - /////////////////////////////////////////////////////////////////////////// - - class RunContext : public IResultCapture, public IRunner { - - RunContext( RunContext const& ); - void operator =( RunContext const& ); - - public: - - explicit RunContext( Ptr const& _config, Ptr const& reporter ) - : m_runInfo( _config->name() ), - m_context( getCurrentMutableContext() ), - m_activeTestCase( CATCH_NULL ), - m_config( _config ), - m_reporter( reporter ), - m_shouldReportUnexpected ( true ) - { - m_context.setRunner( this ); - m_context.setConfig( m_config ); - m_context.setResultCapture( this ); - m_reporter->testRunStarting( m_runInfo ); - } - - virtual ~RunContext() { - m_reporter->testRunEnded( TestRunStats( m_runInfo, m_totals, aborting() ) ); - } - - void testGroupStarting( std::string const& testSpec, std::size_t groupIndex, std::size_t groupsCount ) { - m_reporter->testGroupStarting( GroupInfo( testSpec, groupIndex, groupsCount ) ); - } - void testGroupEnded( std::string const& testSpec, Totals const& totals, std::size_t groupIndex, std::size_t groupsCount ) { - m_reporter->testGroupEnded( TestGroupStats( GroupInfo( testSpec, groupIndex, groupsCount ), totals, aborting() ) ); - } - - Totals runTest( TestCase const& testCase ) { - Totals prevTotals = m_totals; - - std::string redirectedCout; - std::string redirectedCerr; - - TestCaseInfo testInfo = testCase.getTestCaseInfo(); - - m_reporter->testCaseStarting( testInfo ); - - m_activeTestCase = &testCase; - - do { - ITracker& rootTracker = m_trackerContext.startRun(); - assert( rootTracker.isSectionTracker() ); - static_cast( rootTracker ).addInitialFilters( m_config->getSectionsToRun() ); - do { - m_trackerContext.startCycle(); - m_testCaseTracker = &SectionTracker::acquire( m_trackerContext, TestCaseTracking::NameAndLocation( testInfo.name, testInfo.lineInfo ) ); - runCurrentTest( redirectedCout, redirectedCerr ); - } - while( !m_testCaseTracker->isSuccessfullyCompleted() && !aborting() ); - } - // !TBD: deprecated - this will be replaced by indexed trackers - while( getCurrentContext().advanceGeneratorsForCurrentTest() && !aborting() ); - - Totals deltaTotals = m_totals.delta( prevTotals ); - if( testInfo.expectedToFail() && deltaTotals.testCases.passed > 0 ) { - deltaTotals.assertions.failed++; - deltaTotals.testCases.passed--; - deltaTotals.testCases.failed++; - } - m_totals.testCases += deltaTotals.testCases; - m_reporter->testCaseEnded( TestCaseStats( testInfo, - deltaTotals, - redirectedCout, - redirectedCerr, - aborting() ) ); - - m_activeTestCase = CATCH_NULL; - m_testCaseTracker = CATCH_NULL; - - return deltaTotals; - } - - Ptr config() const { - return m_config; - } - - private: // IResultCapture - - virtual void assertionEnded( AssertionResult const& result ) { - if( result.getResultType() == ResultWas::Ok ) { - m_totals.assertions.passed++; - } - else if( !result.isOk() ) { - if( m_activeTestCase->getTestCaseInfo().okToFail() ) - m_totals.assertions.failedButOk++; - else - m_totals.assertions.failed++; - } - - // 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. - static_cast(m_reporter->assertionEnded(AssertionStats(result, m_messages, m_totals))); - - // Reset working state - m_lastAssertionInfo = AssertionInfo( "", m_lastAssertionInfo.lineInfo, "{Unknown expression after the reported line}" , m_lastAssertionInfo.resultDisposition ); - m_lastResult = result; - } - - virtual bool lastAssertionPassed() - { - return m_totals.assertions.passed == (m_prevPassed + 1); - } - - virtual void assertionPassed() - { - m_totals.assertions.passed++; - m_lastAssertionInfo.capturedExpression = "{Unknown expression after the reported line}"; - m_lastAssertionInfo.macroName = ""; - } - - virtual void assertionRun() - { - m_prevPassed = m_totals.assertions.passed; - } - - virtual bool sectionStarted ( - SectionInfo const& sectionInfo, - Counts& assertions - ) - { - ITracker& sectionTracker = SectionTracker::acquire( m_trackerContext, TestCaseTracking::NameAndLocation( sectionInfo.name, sectionInfo.lineInfo ) ); - if( !sectionTracker.isOpen() ) - return false; - m_activeSections.push_back( §ionTracker ); - - m_lastAssertionInfo.lineInfo = sectionInfo.lineInfo; - - m_reporter->sectionStarting( sectionInfo ); - - assertions = m_totals.assertions; - - return true; - } - bool testForMissingAssertions( Counts& assertions ) { - if( assertions.total() != 0 ) - return false; - if( !m_config->warnAboutMissingAssertions() ) - return false; - if( m_trackerContext.currentTracker().hasChildren() ) - return false; - m_totals.assertions.failed++; - assertions.failed++; - return true; - } - - virtual void sectionEnded( SectionEndInfo const& endInfo ) { - Counts assertions = m_totals.assertions - endInfo.prevAssertions; - bool missingAssertions = testForMissingAssertions( assertions ); - - if( !m_activeSections.empty() ) { - m_activeSections.back()->close(); - m_activeSections.pop_back(); - } - - m_reporter->sectionEnded( SectionStats( endInfo.sectionInfo, assertions, endInfo.durationInSeconds, missingAssertions ) ); - m_messages.clear(); - } - - virtual void sectionEndedEarly( SectionEndInfo const& endInfo ) { - if( m_unfinishedSections.empty() ) - m_activeSections.back()->fail(); - else - m_activeSections.back()->close(); - m_activeSections.pop_back(); - - m_unfinishedSections.push_back( endInfo ); - } - - virtual void pushScopedMessage( MessageInfo const& message ) { - m_messages.push_back( message ); - } - - virtual void popScopedMessage( MessageInfo const& message ) { - m_messages.erase( std::remove( m_messages.begin(), m_messages.end(), message ), m_messages.end() ); - } - - virtual std::string getCurrentTestName() const { - return m_activeTestCase - ? m_activeTestCase->getTestCaseInfo().name - : std::string(); - } - - virtual const AssertionResult* getLastResult() const { - return &m_lastResult; - } - - virtual void exceptionEarlyReported() { - m_shouldReportUnexpected = false; - } - - virtual void handleFatalErrorCondition( std::string const& message ) { - // Don't rebuild the result -- the stringification itself can cause more fatal errors - // Instead, fake a result data. - AssertionResultData tempResult; - tempResult.resultType = ResultWas::FatalErrorCondition; - tempResult.message = message; - AssertionResult result(m_lastAssertionInfo, tempResult); - - getResultCapture().assertionEnded(result); - - handleUnfinishedSections(); - - // Recreate section for test case (as we will lose the one that was in scope) - TestCaseInfo const& testCaseInfo = m_activeTestCase->getTestCaseInfo(); - SectionInfo testCaseSection( testCaseInfo.lineInfo, testCaseInfo.name, testCaseInfo.description ); - - Counts assertions; - assertions.failed = 1; - SectionStats testCaseSectionStats( testCaseSection, assertions, 0, false ); - m_reporter->sectionEnded( testCaseSectionStats ); - - TestCaseInfo testInfo = m_activeTestCase->getTestCaseInfo(); - - Totals deltaTotals; - deltaTotals.testCases.failed = 1; - deltaTotals.assertions.failed = 1; - m_reporter->testCaseEnded( TestCaseStats( testInfo, - deltaTotals, - std::string(), - std::string(), - false ) ); - m_totals.testCases.failed++; - testGroupEnded( std::string(), m_totals, 1, 1 ); - m_reporter->testRunEnded( TestRunStats( m_runInfo, m_totals, false ) ); - } - - public: - // !TBD We need to do this another way! - bool aborting() const { - return m_totals.assertions.failed == static_cast( m_config->abortAfter() ); - } - - private: - - void runCurrentTest( std::string& redirectedCout, std::string& redirectedCerr ) { - TestCaseInfo const& testCaseInfo = m_activeTestCase->getTestCaseInfo(); - SectionInfo testCaseSection( testCaseInfo.lineInfo, testCaseInfo.name, testCaseInfo.description ); - m_reporter->sectionStarting( testCaseSection ); - Counts prevAssertions = m_totals.assertions; - double duration = 0; - m_shouldReportUnexpected = true; - try { - m_lastAssertionInfo = AssertionInfo( "TEST_CASE", testCaseInfo.lineInfo, "", ResultDisposition::Normal ); - - seedRng( *m_config ); - - Timer timer; - timer.start(); - if( m_reporter->getPreferences().shouldRedirectStdOut ) { - StreamRedirect coutRedir( Catch::cout(), redirectedCout ); - StdErrRedirect errRedir( redirectedCerr ); - invokeActiveTestCase(); - } - else { - invokeActiveTestCase(); - } - duration = timer.getElapsedSeconds(); - } - catch( TestFailureException& ) { - // This just means the test was aborted due to failure - } - catch(...) { - // Under CATCH_CONFIG_FAST_COMPILE, unexpected exceptions under REQUIRE assertions - // are reported without translation at the point of origin. - if (m_shouldReportUnexpected) { - makeUnexpectedResultBuilder().useActiveException(); - } - } - m_testCaseTracker->close(); - handleUnfinishedSections(); - m_messages.clear(); - - Counts assertions = m_totals.assertions - prevAssertions; - bool missingAssertions = testForMissingAssertions( assertions ); - - SectionStats testCaseSectionStats( testCaseSection, assertions, duration, missingAssertions ); - m_reporter->sectionEnded( testCaseSectionStats ); - } - - void invokeActiveTestCase() { - FatalConditionHandler fatalConditionHandler; // Handle signals - m_activeTestCase->invoke(); - fatalConditionHandler.reset(); - } - - private: - - ResultBuilder makeUnexpectedResultBuilder() const { - return ResultBuilder( m_lastAssertionInfo.macroName, - m_lastAssertionInfo.lineInfo, - m_lastAssertionInfo.capturedExpression, - m_lastAssertionInfo.resultDisposition ); - } - - void handleUnfinishedSections() { - // If sections ended prematurely due to an exception we stored their - // infos here so we can tear them down outside the unwind process. - for( std::vector::const_reverse_iterator it = m_unfinishedSections.rbegin(), - itEnd = m_unfinishedSections.rend(); - it != itEnd; - ++it ) - sectionEnded( *it ); - m_unfinishedSections.clear(); - } - - TestRunInfo m_runInfo; - IMutableContext& m_context; - TestCase const* m_activeTestCase; - ITracker* m_testCaseTracker; - ITracker* m_currentSectionTracker; - AssertionResult m_lastResult; - - Ptr m_config; - Totals m_totals; - Ptr m_reporter; - std::vector m_messages; - AssertionInfo m_lastAssertionInfo; - std::vector m_unfinishedSections; - std::vector m_activeSections; - TrackerContext m_trackerContext; - size_t m_prevPassed; - bool m_shouldReportUnexpected; - }; - - IResultCapture& getResultCapture() { - if( IResultCapture* capture = getCurrentContext().getResultCapture() ) - return *capture; - else - throw std::logic_error( "No result capture instance" ); - } - -} // end namespace Catch - -// #included from: internal/catch_version.h -#define TWOBLUECUBES_CATCH_VERSION_H_INCLUDED - -namespace Catch { - - // Versioning information - struct Version { - Version( unsigned int _majorVersion, - unsigned int _minorVersion, - unsigned int _patchNumber, - char const * const _branchName, - unsigned int _buildNumber ); - - unsigned int const majorVersion; - unsigned int const minorVersion; - unsigned int const patchNumber; - - // buildNumber is only used if branchName is not null - char const * const branchName; - unsigned int const buildNumber; - - friend std::ostream& operator << ( std::ostream& os, Version const& version ); - - private: - void operator=( Version const& ); - }; - - inline Version libraryVersion(); -} - -#include -#include -#include - -namespace Catch { - - Ptr createReporter( std::string const& reporterName, Ptr const& config ) { - Ptr reporter = getRegistryHub().getReporterRegistry().create( reporterName, config.get() ); - if( !reporter ) { - std::ostringstream oss; - oss << "No reporter registered with name: '" << reporterName << "'"; - throw std::domain_error( oss.str() ); - } - return reporter; - } - -#if !defined(CATCH_CONFIG_DEFAULT_REPORTER) -#define CATCH_CONFIG_DEFAULT_REPORTER "console" -#endif - - Ptr makeReporter( Ptr const& config ) { - std::vector reporters = config->getReporterNames(); - if( reporters.empty() ) - reporters.push_back( CATCH_CONFIG_DEFAULT_REPORTER ); - - Ptr reporter; - for( std::vector::const_iterator it = reporters.begin(), itEnd = reporters.end(); - it != itEnd; - ++it ) - reporter = addReporter( reporter, createReporter( *it, config ) ); - return reporter; - } - Ptr addListeners( Ptr const& config, Ptr reporters ) { - IReporterRegistry::Listeners listeners = getRegistryHub().getReporterRegistry().getListeners(); - for( IReporterRegistry::Listeners::const_iterator it = listeners.begin(), itEnd = listeners.end(); - it != itEnd; - ++it ) - reporters = addReporter(reporters, (*it)->create( ReporterConfig( config ) ) ); - return reporters; - } - - Totals runTests( Ptr const& config ) { - - Ptr iconfig = config.get(); - - Ptr reporter = makeReporter( config ); - reporter = addListeners( iconfig, reporter ); - - RunContext context( iconfig, 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 - - std::vector const& allTestCases = getAllTestCasesSorted( *iconfig ); - for( std::vector::const_iterator it = allTestCases.begin(), itEnd = allTestCases.end(); - it != itEnd; - ++it ) { - if( !context.aborting() && matchTest( *it, testSpec, *iconfig ) ) - totals += context.runTest( *it ); - else - reporter->skipTest( *it ); - } - - context.testGroupEnded( iconfig->name(), totals, 1, 1 ); - return totals; - } - - void applyFilenamesAsTags( IConfig const& config ) { - std::vector const& tests = getAllTestCasesSorted( config ); - for(std::size_t i = 0; i < tests.size(); ++i ) { - TestCase& test = const_cast( tests[i] ); - std::set tags = test.tags; - - std::string filename = test.lineInfo.file; - std::string::size_type lastSlash = filename.find_last_of( "\\/" ); - if( lastSlash != std::string::npos ) - filename = filename.substr( lastSlash+1 ); - - std::string::size_type lastDot = filename.find_last_of( '.' ); - if( lastDot != std::string::npos ) - filename = filename.substr( 0, lastDot ); - - tags.insert( '#' + filename ); - setTags( test, tags ); - } - } - - class Session : NonCopyable { - static bool alreadyInstantiated; - - public: - - struct OnUnusedOptions { enum DoWhat { Ignore, Fail }; }; - - Session() - : m_cli( makeCommandLineParser() ) { - if( alreadyInstantiated ) { - std::string msg = "Only one instance of Catch::Session can ever be used"; - Catch::cerr() << msg << std::endl; - throw std::logic_error( msg ); - } - alreadyInstantiated = true; - } - ~Session() { - Catch::cleanUp(); - } - - void showHelp( std::string const& processName ) { - Catch::cout() << "\nCatch v" << libraryVersion() << "\n"; - - m_cli.usage( Catch::cout(), processName ); - Catch::cout() << "For more detail usage please see the project docs\n" << std::endl; - } - void libIdentify() { - Catch::cout() - << std::left << std::setw(16) << "description: " << "A Catch test executable\n" - << std::left << std::setw(16) << "category: " << "testframework\n" - << std::left << std::setw(16) << "framework: " << "Catch Test\n" - << std::left << std::setw(16) << "version: " << libraryVersion() << std::endl; - } - - int applyCommandLine( int argc, char const* const* const argv, OnUnusedOptions::DoWhat unusedOptionBehaviour = OnUnusedOptions::Fail ) { - try { - m_cli.setThrowOnUnrecognisedTokens( unusedOptionBehaviour == OnUnusedOptions::Fail ); - m_unusedTokens = m_cli.parseInto( Clara::argsToVector( argc, argv ), m_configData ); - if( m_configData.showHelp ) - showHelp( m_configData.processName ); - if( m_configData.libIdentify ) - libIdentify(); - m_config.reset(); - } - catch( std::exception& ex ) { - { - Colour colourGuard( Colour::Red ); - Catch::cerr() - << "\nError(s) in input:\n" - << Text( ex.what(), TextAttributes().setIndent(2) ) - << "\n\n"; - } - m_cli.usage( Catch::cout(), m_configData.processName ); - return (std::numeric_limits::max)(); - } - return 0; - } - - void useConfigData( ConfigData const& _configData ) { - m_configData = _configData; - m_config.reset(); - } - - int run( int argc, char const* const* const argv ) { - - int returnCode = applyCommandLine( argc, argv ); - if( returnCode == 0 ) - returnCode = run(); - return returnCode; - } - - #if defined(WIN32) && defined(UNICODE) - int run( int argc, wchar_t const* const* const argv ) { - - char **utf8Argv = new char *[ argc ]; - - for ( int i = 0; i < argc; ++i ) { - int bufSize = WideCharToMultiByte( CP_UTF8, 0, argv[i], -1, NULL, 0, NULL, NULL ); - - utf8Argv[ i ] = new char[ bufSize ]; - - WideCharToMultiByte( CP_UTF8, 0, argv[i], -1, utf8Argv[i], bufSize, NULL, NULL ); - } - - int returnCode = applyCommandLine( argc, utf8Argv ); - if( returnCode == 0 ) - returnCode = run(); - - for ( int i = 0; i < argc; ++i ) - delete [] utf8Argv[ i ]; - - delete [] utf8Argv; - - return returnCode; - } - #endif - - int run() { - if( ( m_configData.waitForKeypress & WaitForKeypress::BeforeStart ) != 0 ) { - Catch::cout() << "...waiting for enter/ return before starting" << std::endl; - static_cast(std::getchar()); - } - int exitCode = runInternal(); - if( ( m_configData.waitForKeypress & WaitForKeypress::BeforeExit ) != 0 ) { - Catch::cout() << "...waiting for enter/ return before exiting, with code: " << exitCode << std::endl; - static_cast(std::getchar()); - } - return exitCode; - } - - Clara::CommandLine const& cli() const { - return m_cli; - } - std::vector const& unusedTokens() const { - return m_unusedTokens; - } - ConfigData& configData() { - return m_configData; - } - Config& config() { - if( !m_config ) - m_config = new Config( m_configData ); - return *m_config; - } - private: - - int runInternal() { - if( m_configData.showHelp || m_configData.libIdentify ) - return 0; - - try - { - config(); // Force config to be constructed - - seedRng( *m_config ); - - if( m_configData.filenamesAsTags ) - applyFilenamesAsTags( *m_config ); - - // Handle list request - if( Option listed = list( config() ) ) - return static_cast( *listed ); - - return static_cast( runTests( m_config ).assertions.failed ); - } - catch( std::exception& ex ) { - Catch::cerr() << ex.what() << std::endl; - return (std::numeric_limits::max)(); - } - } - - Clara::CommandLine m_cli; - std::vector m_unusedTokens; - ConfigData m_configData; - Ptr m_config; - }; - - bool Session::alreadyInstantiated = false; - -} // end namespace Catch - -// #included from: catch_registry_hub.hpp -#define TWOBLUECUBES_CATCH_REGISTRY_HUB_HPP_INCLUDED - -// #included from: catch_test_case_registry_impl.hpp -#define TWOBLUECUBES_CATCH_TEST_CASE_REGISTRY_IMPL_HPP_INCLUDED - -#include -#include -#include -#include - -namespace Catch { - - struct RandomNumberGenerator { - typedef unsigned int result_type; - - result_type operator()( result_type n ) const { return std::rand() % n; } - -#ifdef CATCH_CONFIG_CPP11_SHUFFLE - static constexpr result_type min() { return 0; } - static constexpr result_type max() { return 1000000; } - result_type operator()() const { return std::rand() % max(); } -#endif - template - static void shuffle( V& vector ) { - RandomNumberGenerator rng; -#ifdef CATCH_CONFIG_CPP11_SHUFFLE - std::shuffle( vector.begin(), vector.end(), rng ); -#else - std::random_shuffle( vector.begin(), vector.end(), rng ); -#endif - } - }; - - inline std::vector sortTests( IConfig const& config, std::vector const& unsortedTestCases ) { - - std::vector sorted = unsortedTestCases; - - switch( config.runOrder() ) { - case RunTests::InLexicographicalOrder: - std::sort( sorted.begin(), sorted.end() ); - break; - case RunTests::InRandomOrder: - { - seedRng( config ); - RandomNumberGenerator::shuffle( sorted ); - } - break; - case RunTests::InDeclarationOrder: - // already in declaration order - break; - } - return sorted; - } - bool matchTest( TestCase const& testCase, TestSpec const& testSpec, IConfig const& config ) { - return testSpec.matches( testCase ) && ( config.allowThrows() || !testCase.throws() ); - } - - void enforceNoDuplicateTestCases( std::vector const& functions ) { - std::set seenFunctions; - for( std::vector::const_iterator it = functions.begin(), itEnd = functions.end(); - it != itEnd; - ++it ) { - std::pair::const_iterator, bool> prev = seenFunctions.insert( *it ); - if( !prev.second ) { - std::ostringstream ss; - - ss << Colour( Colour::Red ) - << "error: TEST_CASE( \"" << it->name << "\" ) already defined.\n" - << "\tFirst seen at " << prev.first->getTestCaseInfo().lineInfo << '\n' - << "\tRedefined at " << it->getTestCaseInfo().lineInfo << std::endl; - - throw std::runtime_error(ss.str()); - } - } - } - - std::vector filterTests( std::vector const& testCases, TestSpec const& testSpec, IConfig const& config ) { - std::vector filtered; - filtered.reserve( testCases.size() ); - for( std::vector::const_iterator it = testCases.begin(), itEnd = testCases.end(); - it != itEnd; - ++it ) - if( matchTest( *it, testSpec, config ) ) - filtered.push_back( *it ); - return filtered; - } - std::vector const& getAllTestCasesSorted( IConfig const& config ) { - return getRegistryHub().getTestCaseRegistry().getAllTestsSorted( config ); - } - - class TestRegistry : public ITestCaseRegistry { - public: - TestRegistry() - : m_currentSortOrder( RunTests::InDeclarationOrder ), - m_unnamedCount( 0 ) - {} - virtual ~TestRegistry(); - - virtual void registerTest( TestCase const& testCase ) { - std::string name = testCase.getTestCaseInfo().name; - if( name.empty() ) { - std::ostringstream oss; - oss << "Anonymous test case " << ++m_unnamedCount; - return registerTest( testCase.withName( oss.str() ) ); - } - m_functions.push_back( testCase ); - } - - virtual std::vector const& getAllTests() const { - return m_functions; - } - virtual std::vector const& getAllTestsSorted( IConfig const& config ) const { - if( m_sortedFunctions.empty() ) - enforceNoDuplicateTestCases( m_functions ); - - if( m_currentSortOrder != config.runOrder() || m_sortedFunctions.empty() ) { - m_sortedFunctions = sortTests( config, m_functions ); - m_currentSortOrder = config.runOrder(); - } - return m_sortedFunctions; - } - - private: - std::vector m_functions; - mutable RunTests::InWhatOrder m_currentSortOrder; - mutable std::vector m_sortedFunctions; - size_t m_unnamedCount; - std::ios_base::Init m_ostreamInit; // Forces cout/ cerr to be initialised - }; - - /////////////////////////////////////////////////////////////////////////// - - class FreeFunctionTestCase : public SharedImpl { - public: - - FreeFunctionTestCase( TestFunction fun ) : m_fun( fun ) {} - - virtual void invoke() const { - m_fun(); - } - - private: - virtual ~FreeFunctionTestCase(); - - TestFunction m_fun; - }; - - inline std::string extractClassName( std::string const& classOrQualifiedMethodName ) { - std::string className = classOrQualifiedMethodName; - if( startsWith( className, '&' ) ) - { - std::size_t lastColons = className.rfind( "::" ); - std::size_t penultimateColons = className.rfind( "::", lastColons-1 ); - if( penultimateColons == std::string::npos ) - penultimateColons = 1; - className = className.substr( penultimateColons, lastColons-penultimateColons ); - } - return className; - } - - void registerTestCase - ( ITestCase* testCase, - char const* classOrQualifiedMethodName, - NameAndDesc const& nameAndDesc, - SourceLineInfo const& lineInfo ) { - - getMutableRegistryHub().registerTest - ( makeTestCase - ( testCase, - extractClassName( classOrQualifiedMethodName ), - nameAndDesc.name, - nameAndDesc.description, - lineInfo ) ); - } - void registerTestCaseFunction - ( TestFunction function, - SourceLineInfo const& lineInfo, - NameAndDesc const& nameAndDesc ) { - registerTestCase( new FreeFunctionTestCase( function ), "", nameAndDesc, lineInfo ); - } - - /////////////////////////////////////////////////////////////////////////// - - AutoReg::AutoReg - ( TestFunction function, - SourceLineInfo const& lineInfo, - NameAndDesc const& nameAndDesc ) { - registerTestCaseFunction( function, lineInfo, nameAndDesc ); - } - - AutoReg::~AutoReg() {} - -} // end namespace Catch - -// #included from: catch_reporter_registry.hpp -#define TWOBLUECUBES_CATCH_REPORTER_REGISTRY_HPP_INCLUDED - -#include - -namespace Catch { - - class ReporterRegistry : public IReporterRegistry { - - public: - - virtual ~ReporterRegistry() CATCH_OVERRIDE {} - - virtual IStreamingReporter* create( std::string const& name, Ptr const& config ) const CATCH_OVERRIDE { - FactoryMap::const_iterator it = m_factories.find( name ); - if( it == m_factories.end() ) - return CATCH_NULL; - return it->second->create( ReporterConfig( config ) ); - } - - void registerReporter( std::string const& name, Ptr const& factory ) { - m_factories.insert( std::make_pair( name, factory ) ); - } - void registerListener( Ptr const& factory ) { - m_listeners.push_back( factory ); - } - - virtual FactoryMap const& getFactories() const CATCH_OVERRIDE { - return m_factories; - } - virtual Listeners const& getListeners() const CATCH_OVERRIDE { - return m_listeners; - } - - private: - FactoryMap m_factories; - Listeners m_listeners; - }; -} - -// #included from: catch_exception_translator_registry.hpp -#define TWOBLUECUBES_CATCH_EXCEPTION_TRANSLATOR_REGISTRY_HPP_INCLUDED - -#ifdef __OBJC__ -#import "Foundation/Foundation.h" -#endif - -namespace Catch { - - class ExceptionTranslatorRegistry : public IExceptionTranslatorRegistry { - public: - ~ExceptionTranslatorRegistry() { - deleteAll( m_translators ); - } - - virtual void registerTranslator( const IExceptionTranslator* translator ) { - m_translators.push_back( translator ); - } - - virtual std::string translateActiveException() const { - try { -#ifdef __OBJC__ - // In Objective-C try objective-c exceptions first - @try { - return tryTranslators(); - } - @catch (NSException *exception) { - return Catch::toString( [exception description] ); - } -#else - return tryTranslators(); -#endif - } - catch( TestFailureException& ) { - throw; - } - catch( std::exception& ex ) { - return ex.what(); - } - catch( std::string& msg ) { - return msg; - } - catch( const char* msg ) { - return msg; - } - catch(...) { - return "Unknown exception"; - } - } - - std::string tryTranslators() const { - if( m_translators.empty() ) - throw; - else - return m_translators[0]->translate( m_translators.begin()+1, m_translators.end() ); - } - - private: - std::vector m_translators; - }; -} - -// #included from: catch_tag_alias_registry.h -#define TWOBLUECUBES_CATCH_TAG_ALIAS_REGISTRY_H_INCLUDED - -#include - -namespace Catch { - - class TagAliasRegistry : public ITagAliasRegistry { - public: - virtual ~TagAliasRegistry(); - virtual Option find( std::string const& alias ) const; - virtual std::string expandAliases( std::string const& unexpandedTestSpec ) const; - void add( std::string const& alias, std::string const& tag, SourceLineInfo const& lineInfo ); - - private: - std::map m_registry; - }; - -} // end namespace Catch - -namespace Catch { - - namespace { - - class RegistryHub : public IRegistryHub, public IMutableRegistryHub { - - RegistryHub( RegistryHub const& ); - void operator=( RegistryHub const& ); - - public: // IRegistryHub - RegistryHub() { - } - virtual IReporterRegistry const& getReporterRegistry() const CATCH_OVERRIDE { - return m_reporterRegistry; - } - virtual ITestCaseRegistry const& getTestCaseRegistry() const CATCH_OVERRIDE { - return m_testCaseRegistry; - } - virtual IExceptionTranslatorRegistry& getExceptionTranslatorRegistry() CATCH_OVERRIDE { - return m_exceptionTranslatorRegistry; - } - virtual ITagAliasRegistry const& getTagAliasRegistry() const CATCH_OVERRIDE { - return m_tagAliasRegistry; - } - - public: // IMutableRegistryHub - virtual void registerReporter( std::string const& name, Ptr const& factory ) CATCH_OVERRIDE { - m_reporterRegistry.registerReporter( name, factory ); - } - virtual void registerListener( Ptr const& factory ) CATCH_OVERRIDE { - m_reporterRegistry.registerListener( factory ); - } - virtual void registerTest( TestCase const& testInfo ) CATCH_OVERRIDE { - m_testCaseRegistry.registerTest( testInfo ); - } - virtual void registerTranslator( const IExceptionTranslator* translator ) CATCH_OVERRIDE { - m_exceptionTranslatorRegistry.registerTranslator( translator ); - } - virtual void registerTagAlias( std::string const& alias, std::string const& tag, SourceLineInfo const& lineInfo ) CATCH_OVERRIDE { - m_tagAliasRegistry.add( alias, tag, lineInfo ); - } - - private: - TestRegistry m_testCaseRegistry; - ReporterRegistry m_reporterRegistry; - ExceptionTranslatorRegistry m_exceptionTranslatorRegistry; - TagAliasRegistry m_tagAliasRegistry; - }; - - // Single, global, instance - inline RegistryHub*& getTheRegistryHub() { - static RegistryHub* theRegistryHub = CATCH_NULL; - if( !theRegistryHub ) - theRegistryHub = new RegistryHub(); - return theRegistryHub; - } - } - - IRegistryHub& getRegistryHub() { - return *getTheRegistryHub(); - } - IMutableRegistryHub& getMutableRegistryHub() { - return *getTheRegistryHub(); - } - void cleanUp() { - delete getTheRegistryHub(); - getTheRegistryHub() = CATCH_NULL; - cleanUpContext(); - } - std::string translateActiveException() { - return getRegistryHub().getExceptionTranslatorRegistry().translateActiveException(); - } - -} // end namespace Catch - -// #included from: catch_notimplemented_exception.hpp -#define TWOBLUECUBES_CATCH_NOTIMPLEMENTED_EXCEPTION_HPP_INCLUDED - -#include - -namespace Catch { - - NotImplementedException::NotImplementedException( SourceLineInfo const& lineInfo ) - : m_lineInfo( lineInfo ) { - std::ostringstream oss; - oss << lineInfo << ": function "; - oss << "not implemented"; - m_what = oss.str(); - } - - const char* NotImplementedException::what() const CATCH_NOEXCEPT { - return m_what.c_str(); - } - -} // end namespace Catch - -// #included from: catch_context_impl.hpp -#define TWOBLUECUBES_CATCH_CONTEXT_IMPL_HPP_INCLUDED - -// #included from: catch_stream.hpp -#define TWOBLUECUBES_CATCH_STREAM_HPP_INCLUDED - -#include -#include -#include - -namespace Catch { - - template - class StreamBufImpl : public StreamBufBase { - char data[bufferSize]; - WriterF m_writer; - - public: - StreamBufImpl() { - setp( data, data + sizeof(data) ); - } - - ~StreamBufImpl() CATCH_NOEXCEPT { - sync(); - } - - private: - int overflow( int c ) { - sync(); - - if( c != EOF ) { - if( pbase() == epptr() ) - m_writer( std::string( 1, static_cast( c ) ) ); - else - sputc( static_cast( c ) ); - } - return 0; - } - - int sync() { - if( pbase() != pptr() ) { - m_writer( std::string( pbase(), static_cast( pptr() - pbase() ) ) ); - setp( pbase(), epptr() ); - } - return 0; - } - }; - - /////////////////////////////////////////////////////////////////////////// - - FileStream::FileStream( std::string const& filename ) { - m_ofs.open( filename.c_str() ); - if( m_ofs.fail() ) { - std::ostringstream oss; - oss << "Unable to open file: '" << filename << '\''; - throw std::domain_error( oss.str() ); - } - } - - std::ostream& FileStream::stream() const { - return m_ofs; - } - - struct OutputDebugWriter { - - void operator()( std::string const&str ) { - writeToDebugConsole( str ); - } - }; - - DebugOutStream::DebugOutStream() - : m_streamBuf( new StreamBufImpl() ), - m_os( m_streamBuf.get() ) - {} - - std::ostream& DebugOutStream::stream() const { - return m_os; - } - - // Store the streambuf from cout up-front because - // cout may get redirected when running tests - CoutStream::CoutStream() - : m_os( Catch::cout().rdbuf() ) - {} - - std::ostream& CoutStream::stream() const { - return m_os; - } - -#ifndef CATCH_CONFIG_NOSTDOUT // If you #define this you must implement these functions - std::ostream& cout() { - return std::cout; - } - std::ostream& cerr() { - return std::cerr; - } - std::ostream& clog() { - return std::clog; - } -#endif -} - -namespace Catch { - - class Context : public IMutableContext { - - Context() : m_config( CATCH_NULL ), m_runner( CATCH_NULL ), m_resultCapture( CATCH_NULL ) {} - Context( Context const& ); - void operator=( Context const& ); - - public: - virtual ~Context() { - deleteAllValues( m_generatorsByTestName ); - } - - public: // IContext - virtual IResultCapture* getResultCapture() { - return m_resultCapture; - } - virtual IRunner* getRunner() { - return m_runner; - } - virtual size_t getGeneratorIndex( std::string const& fileInfo, size_t totalSize ) { - return getGeneratorsForCurrentTest() - .getGeneratorInfo( fileInfo, totalSize ) - .getCurrentIndex(); - } - virtual bool advanceGeneratorsForCurrentTest() { - IGeneratorsForTest* generators = findGeneratorsForCurrentTest(); - return generators && generators->moveNext(); - } - - virtual Ptr getConfig() const { - return m_config; - } - - public: // IMutableContext - virtual void setResultCapture( IResultCapture* resultCapture ) { - m_resultCapture = resultCapture; - } - virtual void setRunner( IRunner* runner ) { - m_runner = runner; - } - virtual void setConfig( Ptr const& config ) { - m_config = config; - } - - friend IMutableContext& getCurrentMutableContext(); - - private: - IGeneratorsForTest* findGeneratorsForCurrentTest() { - std::string testName = getResultCapture()->getCurrentTestName(); - - std::map::const_iterator it = - m_generatorsByTestName.find( testName ); - return it != m_generatorsByTestName.end() - ? it->second - : CATCH_NULL; - } - - IGeneratorsForTest& getGeneratorsForCurrentTest() { - IGeneratorsForTest* generators = findGeneratorsForCurrentTest(); - if( !generators ) { - std::string testName = getResultCapture()->getCurrentTestName(); - generators = createGeneratorsForTest(); - m_generatorsByTestName.insert( std::make_pair( testName, generators ) ); - } - return *generators; - } - - private: - Ptr m_config; - IRunner* m_runner; - IResultCapture* m_resultCapture; - std::map m_generatorsByTestName; - }; - - namespace { - Context* currentContext = CATCH_NULL; - } - IMutableContext& getCurrentMutableContext() { - if( !currentContext ) - currentContext = new Context(); - return *currentContext; - } - IContext& getCurrentContext() { - return getCurrentMutableContext(); - } - - void cleanUpContext() { - delete currentContext; - currentContext = CATCH_NULL; - } -} - -// #included from: catch_console_colour_impl.hpp -#define TWOBLUECUBES_CATCH_CONSOLE_COLOUR_IMPL_HPP_INCLUDED - -// #included from: catch_errno_guard.hpp -#define TWOBLUECUBES_CATCH_ERRNO_GUARD_HPP_INCLUDED - -#include - -namespace Catch { - - class ErrnoGuard { - public: - ErrnoGuard():m_oldErrno(errno){} - ~ErrnoGuard() { errno = m_oldErrno; } - private: - int m_oldErrno; - }; - -} - +// end catch_windows_h_proxy.h namespace Catch { namespace { struct IColourImpl { - virtual ~IColourImpl() {} + virtual ~IColourImpl() = default; virtual void use( Colour::Code _colourCode ) = 0; }; @@ -7871,7 +5962,7 @@ namespace { originalBackgroundAttributes = csbiInfo.wAttributes & ~( FOREGROUND_GREEN | FOREGROUND_RED | FOREGROUND_BLUE | FOREGROUND_INTENSITY ); } - virtual void use( Colour::Code _colourCode ) { + virtual void use( Colour::Code _colourCode ) override { switch( _colourCode ) { case Colour::None: return setTextAttribute( originalForegroundAttributes ); case Colour::White: return setTextAttribute( FOREGROUND_GREEN | FOREGROUND_RED | FOREGROUND_BLUE ); @@ -7887,7 +5978,7 @@ namespace { case Colour::BrightGreen: return setTextAttribute( FOREGROUND_INTENSITY | FOREGROUND_GREEN ); case Colour::BrightWhite: return setTextAttribute( FOREGROUND_INTENSITY | FOREGROUND_GREEN | FOREGROUND_RED | FOREGROUND_BLUE ); - case Colour::Bright: throw std::logic_error( "not a colour" ); + case Colour::Bright: CATCH_INTERNAL_ERROR( "not a colour" ); } } @@ -7903,14 +5994,12 @@ namespace { IColourImpl* platformColourInstance() { static Win32ColourImpl s_instance; - Ptr config = getCurrentContext().getConfig(); + IConfigPtr config = getCurrentContext().getConfig(); UseColour::YesOrNo colourMode = config ? config->useColour() : UseColour::Auto; if( colourMode == UseColour::Auto ) - colourMode = !isDebuggerActive() - ? UseColour::Yes - : UseColour::No; + colourMode = UseColour::Yes; return colourMode == UseColour::Yes ? &s_instance : NoColourImpl::instance(); @@ -7932,7 +6021,7 @@ namespace { // https://github.com/philsquared/Catch/pull/131 class PosixColourImpl : public IColourImpl { public: - virtual void use( Colour::Code _colourCode ) { + virtual void use( Colour::Code _colourCode ) override { switch( _colourCode ) { case Colour::None: case Colour::White: return setColour( "[0m" ); @@ -7948,7 +6037,7 @@ namespace { case Colour::BrightGreen: return setColour( "[1;32m" ); case Colour::BrightWhite: return setColour( "[1;37m" ); - case Colour::Bright: throw std::logic_error( "not a colour" ); + case Colour::Bright: CATCH_INTERNAL_ERROR( "not a colour" ); } } static IColourImpl* instance() { @@ -7962,14 +6051,21 @@ namespace { } }; + bool useColourOnPlatform() { + return +#ifdef CATCH_PLATFORM_MAC + !isDebuggerActive() && +#endif + isatty(STDOUT_FILENO); + } IColourImpl* platformColourInstance() { ErrnoGuard guard; - Ptr config = getCurrentContext().getConfig(); + IConfigPtr config = getCurrentContext().getConfig(); UseColour::YesOrNo colourMode = config ? config->useColour() : UseColour::Auto; if( colourMode == UseColour::Auto ) - colourMode = (!isDebuggerActive() && isatty(STDOUT_FILENO) ) + colourMode = useColourOnPlatform() ? UseColour::Yes : UseColour::No; return colourMode == UseColour::Yes @@ -7992,8 +6088,17 @@ namespace Catch { namespace Catch { - Colour::Colour( Code _colourCode ) : m_moved( false ) { use( _colourCode ); } - Colour::Colour( Colour const& _other ) : m_moved( false ) { const_cast( _other ).m_moved = true; } + Colour::Colour( Code _colourCode ) { use( _colourCode ); } + Colour::Colour( Colour&& rhs ) noexcept { + m_moved = rhs.m_moved; + rhs.m_moved = true; + } + Colour& Colour::operator=( Colour&& rhs ) noexcept { + m_moved = rhs.m_moved; + rhs.m_moved = true; + return *this; + } + Colour::~Colour(){ if( !m_moved ) use( None ); } void Colour::use( Code _colourCode ) { @@ -8001,776 +6106,105 @@ namespace Catch { impl->use( _colourCode ); } -} // end namespace Catch - -// #included from: catch_generators_impl.hpp -#define TWOBLUECUBES_CATCH_GENERATORS_IMPL_HPP_INCLUDED - -#include -#include -#include - -namespace Catch { - - struct GeneratorInfo : IGeneratorInfo { - - GeneratorInfo( std::size_t size ) - : m_size( size ), - m_currentIndex( 0 ) - {} - - bool moveNext() { - if( ++m_currentIndex == m_size ) { - m_currentIndex = 0; - return false; - } - return true; - } - - std::size_t getCurrentIndex() const { - return m_currentIndex; - } - - std::size_t m_size; - std::size_t m_currentIndex; - }; - - /////////////////////////////////////////////////////////////////////////// - - class GeneratorsForTest : public IGeneratorsForTest { - - public: - ~GeneratorsForTest() { - deleteAll( m_generatorsInOrder ); - } - - IGeneratorInfo& getGeneratorInfo( std::string const& fileInfo, std::size_t size ) { - std::map::const_iterator it = m_generatorsByName.find( fileInfo ); - if( it == m_generatorsByName.end() ) { - IGeneratorInfo* info = new GeneratorInfo( size ); - m_generatorsByName.insert( std::make_pair( fileInfo, info ) ); - m_generatorsInOrder.push_back( info ); - return *info; - } - return *it->second; - } - - bool moveNext() { - std::vector::const_iterator it = m_generatorsInOrder.begin(); - std::vector::const_iterator itEnd = m_generatorsInOrder.end(); - for(; it != itEnd; ++it ) { - if( (*it)->moveNext() ) - return true; - } - return false; - } - - private: - std::map m_generatorsByName; - std::vector m_generatorsInOrder; - }; - - IGeneratorsForTest* createGeneratorsForTest() - { - return new GeneratorsForTest(); - } - -} // end namespace Catch - -// #included from: catch_assertionresult.hpp -#define TWOBLUECUBES_CATCH_ASSERTIONRESULT_HPP_INCLUDED - -namespace Catch { - - AssertionInfo::AssertionInfo():macroName(""), capturedExpression(""), resultDisposition(ResultDisposition::Normal), secondArg(""){} - - AssertionInfo::AssertionInfo( char const * _macroName, - SourceLineInfo const& _lineInfo, - char const * _capturedExpression, - ResultDisposition::Flags _resultDisposition, - char const * _secondArg) - : macroName( _macroName ), - lineInfo( _lineInfo ), - capturedExpression( _capturedExpression ), - resultDisposition( _resultDisposition ), - secondArg( _secondArg ) - {} - - AssertionResult::AssertionResult() {} - - AssertionResult::AssertionResult( AssertionInfo const& info, AssertionResultData const& data ) - : m_info( info ), - m_resultData( data ) - {} - - AssertionResult::~AssertionResult() {} - - // Result was a success - bool AssertionResult::succeeded() const { - return Catch::isOk( m_resultData.resultType ); - } - - // Result was a success, or failure is suppressed - bool AssertionResult::isOk() const { - return Catch::isOk( m_resultData.resultType ) || shouldSuppressFailure( m_info.resultDisposition ); - } - - ResultWas::OfType AssertionResult::getResultType() const { - return m_resultData.resultType; - } - - bool AssertionResult::hasExpression() const { - return m_info.capturedExpression[0] != 0; - } - - bool AssertionResult::hasMessage() const { - return !m_resultData.message.empty(); - } - - std::string capturedExpressionWithSecondArgument( char const * capturedExpression, char const * secondArg ) { - return (secondArg[0] == 0 || secondArg[0] == '"' && secondArg[1] == '"') - ? capturedExpression - : std::string(capturedExpression) + ", " + secondArg; - } - - std::string AssertionResult::getExpression() const { - if( isFalseTest( m_info.resultDisposition ) ) - return "!(" + capturedExpressionWithSecondArgument(m_info.capturedExpression, m_info.secondArg) + ")"; - else - return capturedExpressionWithSecondArgument(m_info.capturedExpression, m_info.secondArg); - } - std::string AssertionResult::getExpressionInMacro() const { - if( m_info.macroName[0] == 0 ) - return capturedExpressionWithSecondArgument(m_info.capturedExpression, m_info.secondArg); - else - return std::string(m_info.macroName) + "( " + capturedExpressionWithSecondArgument(m_info.capturedExpression, m_info.secondArg) + " )"; - } - - bool AssertionResult::hasExpandedExpression() const { - return hasExpression() && getExpandedExpression() != getExpression(); - } - - std::string AssertionResult::getExpandedExpression() const { - return m_resultData.reconstructExpression(); - } - - std::string AssertionResult::getMessage() const { - return m_resultData.message; - } - SourceLineInfo AssertionResult::getSourceInfo() const { - return m_info.lineInfo; - } - - std::string AssertionResult::getTestMacroName() const { - return m_info.macroName; - } - - void AssertionResult::discardDecomposedExpression() const { - m_resultData.decomposedExpression = CATCH_NULL; - } - - void AssertionResult::expandDecomposedExpression() const { - m_resultData.reconstructExpression(); - } - -} // end namespace Catch - -// #included from: catch_test_case_info.hpp -#define TWOBLUECUBES_CATCH_TEST_CASE_INFO_HPP_INCLUDED - -#include - -namespace Catch { - - inline TestCaseInfo::SpecialProperties parseSpecialTag( std::string const& tag ) { - if( startsWith( tag, '.' ) || - tag == "hide" || - tag == "!hide" ) - return TestCaseInfo::IsHidden; - else if( tag == "!throws" ) - return TestCaseInfo::Throws; - else if( tag == "!shouldfail" ) - return TestCaseInfo::ShouldFail; - else if( tag == "!mayfail" ) - return TestCaseInfo::MayFail; - else if( tag == "!nonportable" ) - return TestCaseInfo::NonPortable; - else - return TestCaseInfo::None; - } - inline bool isReservedTag( std::string const& tag ) { - return parseSpecialTag( tag ) == TestCaseInfo::None && tag.size() > 0 && !std::isalnum( tag[0] ); - } - inline void enforceNotReservedTag( std::string const& tag, SourceLineInfo const& _lineInfo ) { - if( isReservedTag( tag ) ) { - std::ostringstream ss; - ss << Colour(Colour::Red) - << "Tag name [" << tag << "] not allowed.\n" - << "Tag names starting with non alpha-numeric characters are reserved\n" - << Colour(Colour::FileName) - << _lineInfo << '\n'; - throw std::runtime_error(ss.str()); - } - } - - TestCase makeTestCase( ITestCase* _testCase, - std::string const& _className, - std::string const& _name, - std::string const& _descOrTags, - SourceLineInfo const& _lineInfo ) - { - bool isHidden( startsWith( _name, "./" ) ); // Legacy support - - // Parse out tags - std::set tags; - std::string desc, tag; - bool inTag = false; - for( std::size_t i = 0; i < _descOrTags.size(); ++i ) { - char c = _descOrTags[i]; - if( !inTag ) { - if( c == '[' ) - inTag = true; - else - desc += c; - } - else { - if( c == ']' ) { - TestCaseInfo::SpecialProperties prop = parseSpecialTag( tag ); - if( prop == TestCaseInfo::IsHidden ) - isHidden = true; - else if( prop == TestCaseInfo::None ) - enforceNotReservedTag( tag, _lineInfo ); - - tags.insert( tag ); - tag.clear(); - inTag = false; - } - else - tag += c; - } - } - if( isHidden ) { - tags.insert( "hide" ); - tags.insert( "." ); - } - - TestCaseInfo info( _name, _className, desc, tags, _lineInfo ); - return TestCase( _testCase, info ); - } - - void setTags( TestCaseInfo& testCaseInfo, std::set const& tags ) - { - testCaseInfo.tags = tags; - testCaseInfo.lcaseTags.clear(); - - std::ostringstream oss; - for( std::set::const_iterator it = tags.begin(), itEnd = tags.end(); it != itEnd; ++it ) { - oss << '[' << *it << ']'; - std::string lcaseTag = toLower( *it ); - testCaseInfo.properties = static_cast( testCaseInfo.properties | parseSpecialTag( lcaseTag ) ); - testCaseInfo.lcaseTags.insert( lcaseTag ); - } - testCaseInfo.tagsAsString = oss.str(); - } - - TestCaseInfo::TestCaseInfo( std::string const& _name, - std::string const& _className, - std::string const& _description, - std::set const& _tags, - SourceLineInfo const& _lineInfo ) - : name( _name ), - className( _className ), - description( _description ), - lineInfo( _lineInfo ), - properties( None ) - { - setTags( *this, _tags ); - } - - TestCaseInfo::TestCaseInfo( TestCaseInfo const& other ) - : name( other.name ), - className( other.className ), - description( other.description ), - tags( other.tags ), - lcaseTags( other.lcaseTags ), - tagsAsString( other.tagsAsString ), - lineInfo( other.lineInfo ), - properties( other.properties ) - {} - - bool TestCaseInfo::isHidden() const { - return ( properties & IsHidden ) != 0; - } - bool TestCaseInfo::throws() const { - return ( properties & Throws ) != 0; - } - bool TestCaseInfo::okToFail() const { - return ( properties & (ShouldFail | MayFail ) ) != 0; - } - bool TestCaseInfo::expectedToFail() const { - return ( properties & (ShouldFail ) ) != 0; - } - - TestCase::TestCase( ITestCase* testCase, TestCaseInfo const& info ) : TestCaseInfo( info ), test( testCase ) {} - - TestCase::TestCase( TestCase const& other ) - : TestCaseInfo( other ), - test( other.test ) - {} - - TestCase TestCase::withName( std::string const& _newName ) const { - TestCase other( *this ); - other.name = _newName; - return other; - } - - void TestCase::swap( TestCase& other ) { - test.swap( other.test ); - name.swap( other.name ); - className.swap( other.className ); - description.swap( other.description ); - tags.swap( other.tags ); - lcaseTags.swap( other.lcaseTags ); - tagsAsString.swap( other.tagsAsString ); - std::swap( TestCaseInfo::properties, static_cast( other ).properties ); - std::swap( lineInfo, other.lineInfo ); - } - - void TestCase::invoke() const { - test->invoke(); - } - - bool TestCase::operator == ( TestCase const& other ) const { - return test.get() == other.test.get() && - name == other.name && - className == other.className; - } - - bool TestCase::operator < ( TestCase const& other ) const { - return name < other.name; - } - TestCase& TestCase::operator = ( TestCase const& other ) { - TestCase temp( other ); - swap( temp ); - return *this; - } - - TestCaseInfo const& TestCase::getTestCaseInfo() const - { - return *this; - } - -} // end namespace Catch - -// #included from: catch_version.hpp -#define TWOBLUECUBES_CATCH_VERSION_HPP_INCLUDED - -namespace Catch { - - Version::Version - ( unsigned int _majorVersion, - unsigned int _minorVersion, - unsigned int _patchNumber, - char const * const _branchName, - unsigned int _buildNumber ) - : majorVersion( _majorVersion ), - minorVersion( _minorVersion ), - patchNumber( _patchNumber ), - branchName( _branchName ), - buildNumber( _buildNumber ) - {} - - std::ostream& operator << ( std::ostream& os, Version const& version ) { - os << version.majorVersion << '.' - << version.minorVersion << '.' - << version.patchNumber; - // branchName is never null -> 0th char is \0 if it is empty - if (version.branchName[0]) { - os << '-' << version.branchName - << '.' << version.buildNumber; - } + std::ostream& operator << ( std::ostream& os, Colour const& ) { return os; } - inline Version libraryVersion() { - static Version version( 1, 11, 0, "", 0 ); - return version; - } - -} - -// #included from: catch_message.hpp -#define TWOBLUECUBES_CATCH_MESSAGE_HPP_INCLUDED - -namespace Catch { - - MessageInfo::MessageInfo( std::string const& _macroName, - SourceLineInfo const& _lineInfo, - ResultWas::OfType _type ) - : macroName( _macroName ), - lineInfo( _lineInfo ), - type( _type ), - sequence( ++globalCount ) - {} - - // This may need protecting if threading support is added - unsigned int MessageInfo::globalCount = 0; - - //////////////////////////////////////////////////////////////////////////// - - ScopedMessage::ScopedMessage( MessageBuilder const& builder ) - : m_info( builder.m_info ) - { - m_info.message = builder.m_stream.str(); - getResultCapture().pushScopedMessage( m_info ); - } - ScopedMessage::ScopedMessage( ScopedMessage const& other ) - : m_info( other.m_info ) - {} - - ScopedMessage::~ScopedMessage() { - if ( !std::uncaught_exception() ){ - getResultCapture().popScopedMessage(m_info); - } - } - } // end namespace Catch -// #included from: catch_legacy_reporter_adapter.hpp -#define TWOBLUECUBES_CATCH_LEGACY_REPORTER_ADAPTER_HPP_INCLUDED - -// #included from: catch_legacy_reporter_adapter.h -#define TWOBLUECUBES_CATCH_LEGACY_REPORTER_ADAPTER_H_INCLUDED - -namespace Catch -{ - // Deprecated - struct IReporter : IShared { - virtual ~IReporter(); - - virtual bool shouldRedirectStdout() const = 0; - - virtual void StartTesting() = 0; - virtual void EndTesting( Totals const& totals ) = 0; - virtual void StartGroup( std::string const& groupName ) = 0; - virtual void EndGroup( std::string const& groupName, Totals const& totals ) = 0; - virtual void StartTestCase( TestCaseInfo const& testInfo ) = 0; - virtual void EndTestCase( TestCaseInfo const& testInfo, Totals const& totals, std::string const& stdOut, std::string const& stdErr ) = 0; - virtual void StartSection( std::string const& sectionName, std::string const& description ) = 0; - virtual void EndSection( std::string const& sectionName, Counts const& assertions ) = 0; - virtual void NoAssertionsInSection( std::string const& sectionName ) = 0; - virtual void NoAssertionsInTestCase( std::string const& testName ) = 0; - virtual void Aborted() = 0; - virtual void Result( AssertionResult const& result ) = 0; - }; - - class LegacyReporterAdapter : public SharedImpl - { - public: - LegacyReporterAdapter( Ptr const& legacyReporter ); - virtual ~LegacyReporterAdapter(); - - virtual ReporterPreferences getPreferences() const; - virtual void noMatchingTestCases( std::string const& ); - virtual void testRunStarting( TestRunInfo const& ); - virtual void testGroupStarting( GroupInfo const& groupInfo ); - virtual void testCaseStarting( TestCaseInfo const& testInfo ); - virtual void sectionStarting( SectionInfo const& sectionInfo ); - virtual void assertionStarting( AssertionInfo const& ); - virtual bool assertionEnded( AssertionStats const& assertionStats ); - virtual void sectionEnded( SectionStats const& sectionStats ); - virtual void testCaseEnded( TestCaseStats const& testCaseStats ); - virtual void testGroupEnded( TestGroupStats const& testGroupStats ); - virtual void testRunEnded( TestRunStats const& testRunStats ); - virtual void skipTest( TestCaseInfo const& ); - - private: - Ptr m_legacyReporter; - }; -} - -namespace Catch -{ - LegacyReporterAdapter::LegacyReporterAdapter( Ptr const& legacyReporter ) - : m_legacyReporter( legacyReporter ) - {} - LegacyReporterAdapter::~LegacyReporterAdapter() {} - - ReporterPreferences LegacyReporterAdapter::getPreferences() const { - ReporterPreferences prefs; - prefs.shouldRedirectStdOut = m_legacyReporter->shouldRedirectStdout(); - return prefs; - } - - void LegacyReporterAdapter::noMatchingTestCases( std::string const& ) {} - void LegacyReporterAdapter::testRunStarting( TestRunInfo const& ) { - m_legacyReporter->StartTesting(); - } - void LegacyReporterAdapter::testGroupStarting( GroupInfo const& groupInfo ) { - m_legacyReporter->StartGroup( groupInfo.name ); - } - void LegacyReporterAdapter::testCaseStarting( TestCaseInfo const& testInfo ) { - m_legacyReporter->StartTestCase( testInfo ); - } - void LegacyReporterAdapter::sectionStarting( SectionInfo const& sectionInfo ) { - m_legacyReporter->StartSection( sectionInfo.name, sectionInfo.description ); - } - void LegacyReporterAdapter::assertionStarting( AssertionInfo const& ) { - // Not on legacy interface - } - - bool LegacyReporterAdapter::assertionEnded( AssertionStats const& assertionStats ) { - if( assertionStats.assertionResult.getResultType() != ResultWas::Ok ) { - for( std::vector::const_iterator it = assertionStats.infoMessages.begin(), itEnd = assertionStats.infoMessages.end(); - it != itEnd; - ++it ) { - if( it->type == ResultWas::Info ) { - ResultBuilder rb( it->macroName.c_str(), it->lineInfo, "", ResultDisposition::Normal ); - rb << it->message; - rb.setResultType( ResultWas::Info ); - AssertionResult result = rb.build(); - m_legacyReporter->Result( result ); - } - } - } - m_legacyReporter->Result( assertionStats.assertionResult ); - return true; - } - void LegacyReporterAdapter::sectionEnded( SectionStats const& sectionStats ) { - if( sectionStats.missingAssertions ) - m_legacyReporter->NoAssertionsInSection( sectionStats.sectionInfo.name ); - m_legacyReporter->EndSection( sectionStats.sectionInfo.name, sectionStats.assertions ); - } - void LegacyReporterAdapter::testCaseEnded( TestCaseStats const& testCaseStats ) { - m_legacyReporter->EndTestCase - ( testCaseStats.testInfo, - testCaseStats.totals, - testCaseStats.stdOut, - testCaseStats.stdErr ); - } - void LegacyReporterAdapter::testGroupEnded( TestGroupStats const& testGroupStats ) { - if( testGroupStats.aborting ) - m_legacyReporter->Aborted(); - m_legacyReporter->EndGroup( testGroupStats.groupInfo.name, testGroupStats.totals ); - } - void LegacyReporterAdapter::testRunEnded( TestRunStats const& testRunStats ) { - m_legacyReporter->EndTesting( testRunStats.totals ); - } - void LegacyReporterAdapter::skipTest( TestCaseInfo const& ) { - } -} - -// #included from: catch_timer.hpp - -#ifdef __clang__ -#pragma clang diagnostic push -#pragma clang diagnostic ignored "-Wc++11-long-long" +#if defined(__clang__) +# pragma clang diagnostic pop #endif -#ifdef CATCH_PLATFORM_WINDOWS - -#else - -#include - -#endif +// end catch_console_colour.cpp +// start catch_context.cpp namespace Catch { + class Context : public IMutableContext, NonCopyable { + + public: // IContext + virtual IResultCapture* getResultCapture() override { + return m_resultCapture; + } + virtual IRunner* getRunner() override { + return m_runner; + } + + virtual IConfigPtr getConfig() const override { + return m_config; + } + + virtual ~Context() override; + + public: // IMutableContext + virtual void setResultCapture( IResultCapture* resultCapture ) override { + m_resultCapture = resultCapture; + } + virtual void setRunner( IRunner* runner ) override { + m_runner = runner; + } + virtual void setConfig( IConfigPtr const& config ) override { + m_config = config; + } + + friend IMutableContext& getCurrentMutableContext(); + + private: + IConfigPtr m_config; + IRunner* m_runner = nullptr; + IResultCapture* m_resultCapture = nullptr; + }; + namespace { -#ifdef CATCH_PLATFORM_WINDOWS - UInt64 getCurrentTicks() { - static UInt64 hz=0, hzo=0; - if (!hz) { - QueryPerformanceFrequency( reinterpret_cast( &hz ) ); - QueryPerformanceCounter( reinterpret_cast( &hzo ) ); - } - UInt64 t; - QueryPerformanceCounter( reinterpret_cast( &t ) ); - return ((t-hzo)*1000000)/hz; - } -#else - UInt64 getCurrentTicks() { - timeval t; - gettimeofday(&t,CATCH_NULL); - return static_cast( t.tv_sec ) * 1000000ull + static_cast( t.tv_usec ); - } -#endif + Context* currentContext = nullptr; + } + IMutableContext& getCurrentMutableContext() { + if( !currentContext ) + currentContext = new Context(); + return *currentContext; + } + IContext& getCurrentContext() { + return getCurrentMutableContext(); } - void Timer::start() { - m_ticks = getCurrentTicks(); - } - unsigned int Timer::getElapsedMicroseconds() const { - return static_cast(getCurrentTicks() - m_ticks); - } - unsigned int Timer::getElapsedMilliseconds() const { - return static_cast(getElapsedMicroseconds()/1000); - } - double Timer::getElapsedSeconds() const { - return getElapsedMicroseconds()/1000000.0; + void cleanUpContext() { + delete currentContext; + currentContext = nullptr; } + IContext::~IContext() = default; + IMutableContext::~IMutableContext() = default; + Context::~Context() = default; +} +// end catch_context.cpp +// start catch_debug_console.cpp -} // namespace Catch +// start catch_debug_console.h -#ifdef __clang__ -#pragma clang diagnostic pop -#endif -// #included from: catch_common.hpp -#define TWOBLUECUBES_CATCH_COMMON_HPP_INCLUDED - -#include -#include +#include namespace Catch { - - bool startsWith( std::string const& s, std::string const& prefix ) { - return s.size() >= prefix.size() && std::equal(prefix.begin(), prefix.end(), s.begin()); - } - bool startsWith( std::string const& s, char prefix ) { - return !s.empty() && s[0] == prefix; - } - bool endsWith( std::string const& s, std::string const& suffix ) { - return s.size() >= suffix.size() && std::equal(suffix.rbegin(), suffix.rend(), s.rbegin()); - } - bool endsWith( std::string const& s, char suffix ) { - return !s.empty() && s[s.size()-1] == suffix; - } - bool contains( std::string const& s, std::string const& infix ) { - return s.find( infix ) != std::string::npos; - } - char toLowerCh(char c) { - return static_cast( std::tolower( c ) ); - } - void toLowerInPlace( std::string& s ) { - std::transform( s.begin(), s.end(), s.begin(), toLowerCh ); - } - std::string toLower( std::string const& s ) { - std::string lc = s; - toLowerInPlace( lc ); - return lc; - } - std::string trim( std::string const& str ) { - static char const* whitespaceChars = "\n\r\t "; - std::string::size_type start = str.find_first_not_of( whitespaceChars ); - std::string::size_type end = str.find_last_not_of( whitespaceChars ); - - return start != std::string::npos ? str.substr( start, 1+end-start ) : std::string(); - } - - bool replaceInPlace( std::string& str, std::string const& replaceThis, std::string const& withThis ) { - bool replaced = false; - std::size_t i = str.find( replaceThis ); - while( i != std::string::npos ) { - replaced = true; - str = str.substr( 0, i ) + withThis + str.substr( i+replaceThis.size() ); - if( i < str.size()-withThis.size() ) - i = str.find( replaceThis, i+withThis.size() ); - else - i = std::string::npos; - } - return replaced; - } - - pluralise::pluralise( std::size_t count, std::string const& label ) - : m_count( count ), - m_label( label ) - {} - - std::ostream& operator << ( std::ostream& os, pluralise const& pluraliser ) { - os << pluraliser.m_count << ' ' << pluraliser.m_label; - if( pluraliser.m_count != 1 ) - os << 's'; - return os; - } - - SourceLineInfo::SourceLineInfo() : file(""), line( 0 ){} - SourceLineInfo::SourceLineInfo( char const* _file, std::size_t _line ) - : file( _file ), - line( _line ) - {} - bool SourceLineInfo::empty() const { - return file[0] == '\0'; - } - bool SourceLineInfo::operator == ( SourceLineInfo const& other ) const { - return line == other.line && (file == other.file || std::strcmp(file, other.file) == 0); - } - bool SourceLineInfo::operator < ( SourceLineInfo const& other ) const { - return line < other.line || ( line == other.line && (std::strcmp(file, other.file) < 0)); - } - - void seedRng( IConfig const& config ) { - if( config.rngSeed() != 0 ) - std::srand( config.rngSeed() ); - } - unsigned int rngSeed() { - return getCurrentContext().getConfig()->rngSeed(); - } - - std::ostream& operator << ( std::ostream& os, SourceLineInfo const& info ) { -#ifndef __GNUG__ - os << info.file << '(' << info.line << ')'; -#else - os << info.file << ':' << info.line; -#endif - return os; - } - - void throwLogicError( std::string const& message, SourceLineInfo const& locationInfo ) { - std::ostringstream oss; - oss << locationInfo << ": Internal Catch error: '" << message << '\''; - if( alwaysTrue() ) - throw std::logic_error( oss.str() ); - } + void writeToDebugConsole( std::string const& text ); } -// #included from: catch_section.hpp -#define TWOBLUECUBES_CATCH_SECTION_HPP_INCLUDED +// end catch_debug_console.h +#ifdef CATCH_PLATFORM_WINDOWS -namespace Catch { - - SectionInfo::SectionInfo - ( SourceLineInfo const& _lineInfo, - std::string const& _name, - std::string const& _description ) - : name( _name ), - description( _description ), - lineInfo( _lineInfo ) - {} - - Section::Section( SectionInfo const& info ) - : m_info( info ), - m_sectionIncluded( getResultCapture().sectionStarted( m_info, m_assertions ) ) - { - m_timer.start(); - } - -#if defined(_MSC_VER) -#pragma warning(push) -#pragma warning(disable:4996) // std::uncaught_exception is deprecated in C++17 -#endif - Section::~Section() { - if( m_sectionIncluded ) { - SectionEndInfo endInfo( m_info, m_assertions, m_timer.getElapsedSeconds() ); - if( std::uncaught_exception() ) - getResultCapture().sectionEndedEarly( endInfo ); - else - getResultCapture().sectionEnded( endInfo ); + namespace Catch { + void writeToDebugConsole( std::string const& text ) { + ::OutputDebugStringA( text.c_str() ); } } -#if defined(_MSC_VER) -#pragma warning(pop) -#endif - - // This indicates whether the section should be executed or not - Section::operator bool() const { - return m_sectionIncluded; +#else + namespace Catch { + void writeToDebugConsole( std::string const& text ) { + // !TBD: Need a version for Mac/ XCode and other IDEs + Catch::cout() << text; + } } - -} // end namespace Catch - -// #included from: catch_debugger.hpp -#define TWOBLUECUBES_CATCH_DEBUGGER_HPP_INCLUDED +#endif // Platform +// end catch_debug_console.cpp +// start catch_debugger.cpp #ifdef CATCH_PLATFORM_MAC @@ -8780,7 +6214,7 @@ namespace Catch { #include #include - namespace Catch{ + namespace Catch { // The following function is taken directly from the following technical note: // http://developer.apple.com/library/mac/#qa/qa2004/qa1361.html @@ -8791,7 +6225,7 @@ namespace Catch { int mib[4]; struct kinfo_proc info; - size_t size; + std::size_t size; // Initialize the flags so that, if sysctl fails for some bizarre // reason, we get a predictable result. @@ -8809,7 +6243,7 @@ namespace Catch { // Call sysctl. size = sizeof(info); - if( sysctl(mib, sizeof(mib) / sizeof(*mib), &info, &size, CATCH_NULL, 0) != 0 ) { + if( sysctl(mib, sizeof(mib) / sizeof(*mib), &info, &size, nullptr, 0) != 0 ) { Catch::cerr() << "\n** Call to sysctl failed - unable to determine if debugger is active **\n" << std::endl; return false; } @@ -8866,449 +6300,778 @@ namespace Catch { } #else namespace Catch { - inline bool isDebuggerActive() { return false; } + bool isDebuggerActive() { return false; } } #endif // Platform - -#ifdef CATCH_PLATFORM_WINDOWS - - namespace Catch { - void writeToDebugConsole( std::string const& text ) { - ::OutputDebugStringA( text.c_str() ); - } - } -#else - namespace Catch { - void writeToDebugConsole( std::string const& text ) { - // !TBD: Need a version for Mac/ XCode and other IDEs - Catch::cout() << text; - } - } -#endif // Platform - -// #included from: catch_tostring.hpp -#define TWOBLUECUBES_CATCH_TOSTRING_HPP_INCLUDED +// end catch_debugger.cpp +// start catch_decomposer.cpp namespace Catch { -namespace Detail { + ITransientExpression::~ITransientExpression() = default; - const std::string unprintableString = "{?}"; - - namespace { - const int hexThreshold = 255; - - struct Endianness { - enum Arch { Big, Little }; - - static Arch which() { - union _{ - int asInt; - char asChar[sizeof (int)]; - } u; - - u.asInt = 1; - return ( u.asChar[sizeof(int)-1] == 1 ) ? Big : Little; - } - }; - } - - std::string rawMemoryToString( const void *object, std::size_t size ) - { - // Reverse order for little endian architectures - int i = 0, end = static_cast( size ), inc = 1; - if( Endianness::which() == Endianness::Little ) { - i = end-1; - end = inc = -1; - } - - unsigned char const *bytes = static_cast(object); - std::ostringstream os; - os << "0x" << std::setfill('0') << std::hex; - for( ; i != end; i += inc ) - os << std::setw(2) << static_cast(bytes[i]); - return os.str(); + void formatReconstructedExpression( std::ostream &os, std::string const& lhs, StringRef op, std::string const& rhs ) { + if( lhs.size() + rhs.size() < 40 && + lhs.find('\n') == std::string::npos && + rhs.find('\n') == std::string::npos ) + os << lhs << " " << op << " " << rhs; + else + os << lhs << "\n" << op << "\n" << rhs; } } +// end catch_decomposer.cpp +// start catch_errno_guard.cpp -std::string toString( std::string const& value ) { - std::string s = value; - if( getCurrentContext().getConfig()->showInvisibles() ) { - for(size_t i = 0; i < s.size(); ++i ) { - std::string subs; - switch( s[i] ) { - case '\n': subs = "\\n"; break; - case '\t': subs = "\\t"; break; - default: break; - } - if( !subs.empty() ) { - s = s.substr( 0, i ) + subs + s.substr( i+1 ); - ++i; - } - } - } - return '"' + s + '"'; +#include + +namespace Catch { + ErrnoGuard::ErrnoGuard():m_oldErrno(errno){} + ErrnoGuard::~ErrnoGuard() { errno = m_oldErrno; } } -std::string toString( std::wstring const& value ) { +// end catch_errno_guard.cpp +// start catch_exception_translator_registry.cpp - std::string s; - s.reserve( value.size() ); - for(size_t i = 0; i < value.size(); ++i ) - s += value[i] <= 0xff ? static_cast( value[i] ) : '?'; - return Catch::toString( s ); +// start catch_exception_translator_registry.h + +#include +#include +#include + +namespace Catch { + + class ExceptionTranslatorRegistry : public IExceptionTranslatorRegistry { + public: + ~ExceptionTranslatorRegistry(); + virtual void registerTranslator( const IExceptionTranslator* translator ); + virtual std::string translateActiveException() const override; + std::string tryTranslators() const; + + private: + std::vector> m_translators; + }; } -std::string toString( const char* const value ) { - return value ? Catch::toString( std::string( value ) ) : std::string( "{null string}" ); -} - -std::string toString( char* const value ) { - return Catch::toString( static_cast( value ) ); -} - -std::string toString( const wchar_t* const value ) -{ - return value ? Catch::toString( std::wstring(value) ) : std::string( "{null string}" ); -} - -std::string toString( wchar_t* const value ) -{ - return Catch::toString( static_cast( value ) ); -} - -std::string toString( int value ) { - std::ostringstream oss; - oss << value; - if( value > Detail::hexThreshold ) - oss << " (0x" << std::hex << value << ')'; - return oss.str(); -} - -std::string toString( unsigned long value ) { - std::ostringstream oss; - oss << value; - if( value > Detail::hexThreshold ) - oss << " (0x" << std::hex << value << ')'; - return oss.str(); -} - -std::string toString( unsigned int value ) { - return Catch::toString( static_cast( value ) ); -} - -template -std::string fpToString( T value, int precision ) { - std::ostringstream oss; - oss << std::setprecision( precision ) - << std::fixed - << value; - std::string d = oss.str(); - std::size_t i = d.find_last_not_of( '0' ); - if( i != std::string::npos && i != d.size()-1 ) { - if( d[i] == '.' ) - i++; - d = d.substr( 0, i+1 ); - } - return d; -} - -std::string toString( const double value ) { - return fpToString( value, 10 ); -} -std::string toString( const float value ) { - return fpToString( value, 5 ) + 'f'; -} - -std::string toString( bool value ) { - return value ? "true" : "false"; -} - -std::string toString( char value ) { - if ( value == '\r' ) - return "'\\r'"; - if ( value == '\f' ) - return "'\\f'"; - if ( value == '\n' ) - return "'\\n'"; - if ( value == '\t' ) - return "'\\t'"; - if ( '\0' <= value && value < ' ' ) - return toString( static_cast( value ) ); - char chstr[] = "' '"; - chstr[1] = value; - return chstr; -} - -std::string toString( signed char value ) { - return toString( static_cast( value ) ); -} - -std::string toString( unsigned char value ) { - return toString( static_cast( value ) ); -} - -#ifdef CATCH_CONFIG_CPP11_LONG_LONG -std::string toString( long long value ) { - std::ostringstream oss; - oss << value; - if( value > Detail::hexThreshold ) - oss << " (0x" << std::hex << value << ')'; - return oss.str(); -} -std::string toString( unsigned long long value ) { - std::ostringstream oss; - oss << value; - if( value > Detail::hexThreshold ) - oss << " (0x" << std::hex << value << ')'; - return oss.str(); -} -#endif - -#ifdef CATCH_CONFIG_CPP11_NULLPTR -std::string toString( std::nullptr_t ) { - return "nullptr"; -} -#endif - +// end catch_exception_translator_registry.h #ifdef __OBJC__ - std::string toString( NSString const * const& nsstring ) { - if( !nsstring ) - return "nil"; - return "@" + toString([nsstring UTF8String]); - } - std::string toString( NSString * CATCH_ARC_STRONG & nsstring ) { - if( !nsstring ) - return "nil"; - return "@" + toString([nsstring UTF8String]); - } - std::string toString( NSObject* const& nsObject ) { - return toString( [nsObject description] ); - } +#import "Foundation/Foundation.h" #endif +namespace Catch { + + ExceptionTranslatorRegistry::~ExceptionTranslatorRegistry() { + } + + void ExceptionTranslatorRegistry::registerTranslator( const IExceptionTranslator* translator ) { + m_translators.push_back( std::unique_ptr( translator ) ); + } + + std::string ExceptionTranslatorRegistry::translateActiveException() const { + try { +#ifdef __OBJC__ + // In Objective-C try objective-c exceptions first + @try { + return tryTranslators(); + } + @catch (NSException *exception) { + return Catch::Detail::stringify( [exception description] ); + } +#else + return tryTranslators(); +#endif + } + catch( TestFailureException& ) { + std::rethrow_exception(std::current_exception()); + } + catch( std::exception& ex ) { + return ex.what(); + } + catch( std::string& msg ) { + return msg; + } + catch( const char* msg ) { + return msg; + } + catch(...) { + return "Unknown exception"; + } + } + + std::string ExceptionTranslatorRegistry::tryTranslators() const { + if( m_translators.empty() ) + std::rethrow_exception(std::current_exception()); + else + return m_translators[0]->translate( m_translators.begin()+1, m_translators.end() ); + } +} +// end catch_exception_translator_registry.cpp +// start catch_fatal_condition.cpp + +// start catch_fatal_condition.h + +#include + +#if defined ( CATCH_PLATFORM_WINDOWS ) ///////////////////////////////////////// + +# if !defined ( CATCH_CONFIG_WINDOWS_SEH ) + +namespace Catch { + struct FatalConditionHandler { + void reset(); + }; +} + +# else // CATCH_CONFIG_WINDOWS_SEH is defined + +namespace Catch { + + struct FatalConditionHandler { + + static LONG CALLBACK handleVectoredException(PEXCEPTION_POINTERS ExceptionInfo); + FatalConditionHandler(); + static void reset(); + ~FatalConditionHandler(); + + private: + static bool isSet; + static ULONG guaranteeSize; + static PVOID exceptionHandlerHandle; + }; + +} // namespace Catch + +# endif // CATCH_CONFIG_WINDOWS_SEH + +#else // Not Windows - assumed to be POSIX compatible ////////////////////////// + +# if !defined(CATCH_CONFIG_POSIX_SIGNALS) + +namespace Catch { + struct FatalConditionHandler { + void reset(); + }; +} + +# else // CATCH_CONFIG_POSIX_SIGNALS is defined + +#include + +namespace Catch { + + struct FatalConditionHandler { + + static bool isSet; + static struct sigaction oldSigActions[];// [sizeof(signalDefs) / sizeof(SignalDefs)]; + static stack_t oldSigStack; + static char altStackMem[]; + + static void handleSignal( int sig ); + + FatalConditionHandler(); + ~FatalConditionHandler(); + static void reset(); + }; + +} // namespace Catch + +# endif // CATCH_CONFIG_POSIX_SIGNALS + +#endif // not Windows + +// end catch_fatal_condition.h +namespace { + // Report the error condition + void reportFatal( char const * const message ) { + Catch::getCurrentContext().getResultCapture()->handleFatalErrorCondition( message ); + } +} + +#if defined ( CATCH_PLATFORM_WINDOWS ) ///////////////////////////////////////// + +# if !defined ( CATCH_CONFIG_WINDOWS_SEH ) + +namespace Catch { + void FatalConditionHandler::reset() {} +} + +# else // CATCH_CONFIG_WINDOWS_SEH is defined + +namespace Catch { + struct SignalDefs { DWORD id; const char* name; }; + + // There is no 1-1 mapping between signals and windows exceptions. + // Windows can easily distinguish between SO and SigSegV, + // but SigInt, SigTerm, etc are handled differently. + static SignalDefs signalDefs[] = { + { EXCEPTION_ILLEGAL_INSTRUCTION, "SIGILL - Illegal instruction signal" }, + { EXCEPTION_STACK_OVERFLOW, "SIGSEGV - Stack overflow" }, + { EXCEPTION_ACCESS_VIOLATION, "SIGSEGV - Segmentation violation signal" }, + { EXCEPTION_INT_DIVIDE_BY_ZERO, "Divide by zero error" }, + }; + + LONG CALLBACK FatalConditionHandler::handleVectoredException(PEXCEPTION_POINTERS ExceptionInfo) { + for (auto const& def : signalDefs) { + if (ExceptionInfo->ExceptionRecord->ExceptionCode == def.id) { + reportFatal(def.name); + } + } + // If its not an exception we care about, pass it along. + // This stops us from eating debugger breaks etc. + return EXCEPTION_CONTINUE_SEARCH; + } + + FatalConditionHandler::FatalConditionHandler() { + isSet = true; + // 32k seems enough for Catch to handle stack overflow, + // but the value was found experimentally, so there is no strong guarantee + guaranteeSize = 32 * 1024; + exceptionHandlerHandle = nullptr; + // Register as first handler in current chain + exceptionHandlerHandle = AddVectoredExceptionHandler(1, handleVectoredException); + // Pass in guarantee size to be filled + SetThreadStackGuarantee(&guaranteeSize); + } + + void FatalConditionHandler::reset() { + if (isSet) { + // Unregister handler and restore the old guarantee + RemoveVectoredExceptionHandler(exceptionHandlerHandle); + SetThreadStackGuarantee(&guaranteeSize); + exceptionHandlerHandle = nullptr; + isSet = false; + } + } + + FatalConditionHandler::~FatalConditionHandler() { + reset(); + } + +bool FatalConditionHandler::isSet = false; +ULONG FatalConditionHandler::guaranteeSize = 0; +PVOID FatalConditionHandler::exceptionHandlerHandle = nullptr; + +} // namespace Catch + +# endif // CATCH_CONFIG_WINDOWS_SEH + +#else // Not Windows - assumed to be POSIX compatible ////////////////////////// + +# if !defined(CATCH_CONFIG_POSIX_SIGNALS) + +namespace Catch { + void FatalConditionHandler::reset() {} +} + +# else // CATCH_CONFIG_POSIX_SIGNALS is defined + +#include + +namespace Catch { + + struct SignalDefs { + int id; + const char* name; + }; + static SignalDefs signalDefs[] = { + { SIGINT, "SIGINT - Terminal interrupt signal" }, + { SIGILL, "SIGILL - Illegal instruction signal" }, + { SIGFPE, "SIGFPE - Floating point error signal" }, + { SIGSEGV, "SIGSEGV - Segmentation violation signal" }, + { SIGTERM, "SIGTERM - Termination request signal" }, + { SIGABRT, "SIGABRT - Abort (abnormal termination) signal" } + }; + + void FatalConditionHandler::handleSignal( int sig ) { + char const * name = ""; + for (auto const& def : signalDefs) { + if (sig == def.id) { + name = def.name; + break; + } + } + reset(); + reportFatal(name); + raise( sig ); + } + + FatalConditionHandler::FatalConditionHandler() { + isSet = true; + stack_t sigStack; + sigStack.ss_sp = altStackMem; + sigStack.ss_size = SIGSTKSZ; + sigStack.ss_flags = 0; + sigaltstack(&sigStack, &oldSigStack); + struct sigaction sa = { }; + + sa.sa_handler = handleSignal; + sa.sa_flags = SA_ONSTACK; + for (std::size_t i = 0; i < sizeof(signalDefs)/sizeof(SignalDefs); ++i) { + sigaction(signalDefs[i].id, &sa, &oldSigActions[i]); + } + } + + FatalConditionHandler::~FatalConditionHandler() { + reset(); + } + + void FatalConditionHandler::reset() { + if( isSet ) { + // Set signals back to previous values -- hopefully nobody overwrote them in the meantime + for( std::size_t i = 0; i < sizeof(signalDefs)/sizeof(SignalDefs); ++i ) { + sigaction(signalDefs[i].id, &oldSigActions[i], nullptr); + } + // Return the old stack + sigaltstack(&oldSigStack, nullptr); + isSet = false; + } + } + + bool FatalConditionHandler::isSet = false; + struct sigaction FatalConditionHandler::oldSigActions[sizeof(signalDefs)/sizeof(SignalDefs)] = {}; + stack_t FatalConditionHandler::oldSigStack = {}; + char FatalConditionHandler::altStackMem[SIGSTKSZ] = {}; + +} // namespace Catch + +# endif // CATCH_CONFIG_POSIX_SIGNALS + +#endif // not Windows +// end catch_fatal_condition.cpp +// start catch_interfaces_capture.cpp + +namespace Catch { + IResultCapture::~IResultCapture() = default; +} +// end catch_interfaces_capture.cpp +// start catch_interfaces_config.cpp + +namespace Catch { + IConfig::~IConfig() = default; +} +// end catch_interfaces_config.cpp +// start catch_interfaces_exception.cpp + +namespace Catch { + IExceptionTranslator::~IExceptionTranslator() = default; + IExceptionTranslatorRegistry::~IExceptionTranslatorRegistry() = default; +} +// end catch_interfaces_exception.cpp +// start catch_interfaces_registry_hub.cpp + +namespace Catch { + IRegistryHub::~IRegistryHub() = default; + IMutableRegistryHub::~IMutableRegistryHub() = default; +} +// end catch_interfaces_registry_hub.cpp +// start catch_interfaces_reporter.cpp + +// start catch_reporter_multi.h + +namespace Catch { + + class MultipleReporters : public IStreamingReporter { + using Reporters = std::vector; + Reporters m_reporters; + + public: + void add( IStreamingReporterPtr&& reporter ); + + public: // IStreamingReporter + + ReporterPreferences getPreferences() const override; + + void noMatchingTestCases( std::string const& spec ) override; + + static std::set getSupportedVerbosities(); + + void benchmarkStarting( BenchmarkInfo const& benchmarkInfo ) override; + void benchmarkEnded( BenchmarkStats const& benchmarkStats ) override; + + void testRunStarting( TestRunInfo const& testRunInfo ) override; + void testGroupStarting( GroupInfo const& groupInfo ) override; + void testCaseStarting( TestCaseInfo const& testInfo ) override; + void sectionStarting( SectionInfo const& sectionInfo ) override; + void assertionStarting( AssertionInfo const& assertionInfo ) override; + + // The return value indicates if the messages buffer should be cleared: + bool assertionEnded( AssertionStats const& assertionStats ) override; + void sectionEnded( SectionStats const& sectionStats ) override; + void testCaseEnded( TestCaseStats const& testCaseStats ) override; + void testGroupEnded( TestGroupStats const& testGroupStats ) override; + void testRunEnded( TestRunStats const& testRunStats ) override; + + void skipTest( TestCaseInfo const& testInfo ) override; + bool isMulti() const override; + + }; + } // end namespace Catch -// #included from: catch_result_builder.hpp -#define TWOBLUECUBES_CATCH_RESULT_BUILDER_HPP_INCLUDED - +// end catch_reporter_multi.h namespace Catch { - ResultBuilder::ResultBuilder( char const* macroName, - SourceLineInfo const& lineInfo, - char const* capturedExpression, - ResultDisposition::Flags resultDisposition, - char const* secondArg ) - : m_assertionInfo( macroName, lineInfo, capturedExpression, resultDisposition, secondArg ), - m_shouldDebugBreak( false ), - m_shouldThrow( false ), - m_guardException( false ), - m_usedStream( false ) + ReporterConfig::ReporterConfig( IConfigPtr const& _fullConfig ) + : m_stream( &_fullConfig->stream() ), m_fullConfig( _fullConfig ) {} + + ReporterConfig::ReporterConfig( IConfigPtr const& _fullConfig, std::ostream& _stream ) + : m_stream( &_stream ), m_fullConfig( _fullConfig ) {} + + std::ostream& ReporterConfig::stream() const { return *m_stream; } + IConfigPtr ReporterConfig::fullConfig() const { return m_fullConfig; } + + TestRunInfo::TestRunInfo( std::string const& _name ) : name( _name ) {} + + GroupInfo::GroupInfo( std::string const& _name, + std::size_t _groupIndex, + std::size_t _groupsCount ) + : name( _name ), + groupIndex( _groupIndex ), + groupsCounts( _groupsCount ) {} - ResultBuilder::~ResultBuilder() { -#if defined(CATCH_CONFIG_FAST_COMPILE) - if ( m_guardException ) { - stream().oss << "Exception translation was disabled by CATCH_CONFIG_FAST_COMPILE"; - captureResult( ResultWas::ThrewException ); - getCurrentContext().getResultCapture()->exceptionEarlyReported(); - } -#endif - } - - ResultBuilder& ResultBuilder::setResultType( ResultWas::OfType result ) { - m_data.resultType = result; - return *this; - } - ResultBuilder& ResultBuilder::setResultType( bool result ) { - m_data.resultType = result ? ResultWas::Ok : ResultWas::ExpressionFailed; - return *this; - } - - void ResultBuilder::endExpression( DecomposedExpression const& expr ) { - // Flip bool results if FalseTest flag is set - if( isFalseTest( m_assertionInfo.resultDisposition ) ) { - m_data.negate( expr.isBinaryExpression() ); - } - - getResultCapture().assertionRun(); - - if(getCurrentContext().getConfig()->includeSuccessfulResults() || m_data.resultType != ResultWas::Ok) - { - AssertionResult result = build( expr ); - handleResult( result ); - } - else - getResultCapture().assertionPassed(); - } - - void ResultBuilder::useActiveException( ResultDisposition::Flags resultDisposition ) { - m_assertionInfo.resultDisposition = resultDisposition; - stream().oss << Catch::translateActiveException(); - captureResult( ResultWas::ThrewException ); - } - - void ResultBuilder::captureResult( ResultWas::OfType resultType ) { - setResultType( resultType ); - captureExpression(); - } - - void ResultBuilder::captureExpectedException( std::string const& expectedMessage ) { - if( expectedMessage.empty() ) - captureExpectedException( Matchers::Impl::MatchAllOf() ); - else - captureExpectedException( Matchers::Equals( expectedMessage ) ); - } - - void ResultBuilder::captureExpectedException( Matchers::Impl::MatcherBase const& matcher ) { - - assert( !isFalseTest( m_assertionInfo.resultDisposition ) ); - AssertionResultData data = m_data; - data.resultType = ResultWas::Ok; - data.reconstructedExpression = capturedExpressionWithSecondArgument(m_assertionInfo.capturedExpression, m_assertionInfo.secondArg); - - std::string actualMessage = Catch::translateActiveException(); - if( !matcher.match( actualMessage ) ) { - data.resultType = ResultWas::ExpressionFailed; - data.reconstructedExpression = actualMessage; - } - AssertionResult result( m_assertionInfo, data ); - handleResult( result ); - } - - void ResultBuilder::captureExpression() { - AssertionResult result = build(); - handleResult( result ); - } - - void ResultBuilder::handleResult( AssertionResult const& result ) + AssertionStats::AssertionStats( AssertionResult const& _assertionResult, + std::vector const& _infoMessages, + Totals const& _totals ) + : assertionResult( _assertionResult ), + infoMessages( _infoMessages ), + totals( _totals ) { - getResultCapture().assertionEnded( result ); + assertionResult.m_resultData.lazyExpression.m_transientExpression = _assertionResult.m_resultData.lazyExpression.m_transientExpression; - if( !result.isOk() ) { - if( getCurrentContext().getConfig()->shouldDebugBreak() ) - m_shouldDebugBreak = true; - if( getCurrentContext().getRunner()->aborting() || (m_assertionInfo.resultDisposition & ResultDisposition::Normal) ) - m_shouldThrow = true; + if( assertionResult.hasMessage() ) { + // Copy message into messages list. + // !TBD This should have been done earlier, somewhere + MessageBuilder builder( assertionResult.getTestMacroName(), assertionResult.getSourceInfo(), assertionResult.getResultType() ); + builder << assertionResult.getMessage(); + builder.m_info.message = builder.m_stream.str(); + + infoMessages.push_back( builder.m_info ); } } - void ResultBuilder::react() { -#if defined(CATCH_CONFIG_FAST_COMPILE) - 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 throw statement - /////////////////////////////////////////////////////////////////// - CATCH_BREAK_INTO_DEBUGGER(); + AssertionStats::~AssertionStats() = default; + + SectionStats::SectionStats( SectionInfo const& _sectionInfo, + Counts const& _assertions, + double _durationInSeconds, + bool _missingAssertions ) + : sectionInfo( _sectionInfo ), + assertions( _assertions ), + durationInSeconds( _durationInSeconds ), + missingAssertions( _missingAssertions ) + {} + + SectionStats::~SectionStats() = default; + + TestCaseStats::TestCaseStats( TestCaseInfo const& _testInfo, + Totals const& _totals, + std::string const& _stdOut, + std::string const& _stdErr, + bool _aborting ) + : testInfo( _testInfo ), + totals( _totals ), + stdOut( _stdOut ), + stdErr( _stdErr ), + aborting( _aborting ) + {} + + TestCaseStats::~TestCaseStats() = default; + + TestGroupStats::TestGroupStats( GroupInfo const& _groupInfo, + Totals const& _totals, + bool _aborting ) + : groupInfo( _groupInfo ), + totals( _totals ), + aborting( _aborting ) + {} + + TestGroupStats::TestGroupStats( GroupInfo const& _groupInfo ) + : groupInfo( _groupInfo ), + aborting( false ) + {} + + TestGroupStats::~TestGroupStats() = default; + + TestRunStats::TestRunStats( TestRunInfo const& _runInfo, + Totals const& _totals, + bool _aborting ) + : runInfo( _runInfo ), + totals( _totals ), + aborting( _aborting ) + {} + + TestRunStats::~TestRunStats() = default; + + void IStreamingReporter::fatalErrorEncountered( StringRef ) {} + bool IStreamingReporter::isMulti() const { return false; } + + IReporterFactory::~IReporterFactory() = default; + IReporterRegistry::~IReporterRegistry() = default; + + void addReporter( IStreamingReporterPtr& existingReporter, IStreamingReporterPtr&& additionalReporter ) { + + if( !existingReporter ) { + existingReporter = std::move( additionalReporter ); + return; } -#endif - if( m_shouldThrow ) - throw Catch::TestFailureException(); - } - bool ResultBuilder::shouldDebugBreak() const { return m_shouldDebugBreak; } - bool ResultBuilder::allowThrows() const { return getCurrentContext().getConfig()->allowThrows(); } + MultipleReporters* multi = nullptr; - AssertionResult ResultBuilder::build() const - { - return build( *this ); - } - - // CAVEAT: The returned AssertionResult stores a pointer to the argument expr, - // a temporary DecomposedExpression, which in turn holds references to - // operands, possibly temporary as well. - // It should immediately be passed to handleResult; if the expression - // needs to be reported, its string expansion must be composed before - // the temporaries are destroyed. - AssertionResult ResultBuilder::build( DecomposedExpression const& expr ) const - { - assert( m_data.resultType != ResultWas::Unknown ); - AssertionResultData data = m_data; - - if(m_usedStream) - data.message = m_stream().oss.str(); - data.decomposedExpression = &expr; // for lazy reconstruction - return AssertionResult( m_assertionInfo, data ); - } - - void ResultBuilder::reconstructExpression( std::string& dest ) const { - dest = capturedExpressionWithSecondArgument(m_assertionInfo.capturedExpression, m_assertionInfo.secondArg); - } - - void ResultBuilder::setExceptionGuard() { - m_guardException = true; - } - void ResultBuilder::unsetExceptionGuard() { - m_guardException = false; + if( existingReporter->isMulti() ) { + multi = static_cast( existingReporter.get() ); + } + else { + auto newMulti = std::unique_ptr( new MultipleReporters ); + newMulti->add( std::move( existingReporter ) ); + multi = newMulti.get(); + existingReporter = std::move( newMulti ); + } + multi->add( std::move( additionalReporter ) ); } } // end namespace Catch +// end catch_interfaces_reporter.cpp +// start catch_interfaces_runner.cpp -// #included from: catch_tag_alias_registry.hpp -#define TWOBLUECUBES_CATCH_TAG_ALIAS_REGISTRY_HPP_INCLUDED +namespace Catch { + IRunner::~IRunner() = default; +} +// end catch_interfaces_runner.cpp +// start catch_interfaces_testcase.cpp + +namespace Catch { + ITestInvoker::~ITestInvoker() = default; + ITestCaseRegistry::~ITestCaseRegistry() = default; +} +// end catch_interfaces_testcase.cpp +// start catch_leak_detector.cpp namespace Catch { - TagAliasRegistry::~TagAliasRegistry() {} +#ifdef CATCH_CONFIG_WINDOWS_CRTDBG +#include - Option TagAliasRegistry::find( std::string const& alias ) const { - std::map::const_iterator it = m_registry.find( alias ); - if( it != m_registry.end() ) - return it->second; - else - return Option(); - } + LeakDetector::LeakDetector() { + int flag = _CrtSetDbgFlag(_CRTDBG_REPORT_FLAG); + flag |= _CRTDBG_LEAK_CHECK_DF; + flag |= _CRTDBG_ALLOC_MEM_DF; + _CrtSetDbgFlag(flag); + _CrtSetReportMode(_CRT_WARN, _CRTDBG_MODE_FILE | _CRTDBG_MODE_DEBUG); + _CrtSetReportFile(_CRT_WARN, _CRTDBG_FILE_STDERR); + // Change this to leaking allocation's number to break there + _CrtSetBreakAlloc(-1); + } - std::string TagAliasRegistry::expandAliases( std::string const& unexpandedTestSpec ) const { - std::string expandedTestSpec = unexpandedTestSpec; - for( std::map::const_iterator it = m_registry.begin(), itEnd = m_registry.end(); - it != itEnd; - ++it ) { - std::size_t pos = expandedTestSpec.find( it->first ); - if( pos != std::string::npos ) { - expandedTestSpec = expandedTestSpec.substr( 0, pos ) + - it->second.tag + - expandedTestSpec.substr( pos + it->first.size() ); - } - } - return expandedTestSpec; - } +#else - void TagAliasRegistry::add( std::string const& alias, std::string const& tag, SourceLineInfo const& lineInfo ) { + LeakDetector::LeakDetector(){} - if( !startsWith( alias, "[@" ) || !endsWith( alias, ']' ) ) { - std::ostringstream oss; - oss << Colour( Colour::Red ) - << "error: tag alias, \"" << alias << "\" is not of the form [@alias name].\n" - << Colour( Colour::FileName ) - << lineInfo << '\n'; - throw std::domain_error( oss.str().c_str() ); - } - if( !m_registry.insert( std::make_pair( alias, TagAlias( tag, lineInfo ) ) ).second ) { - std::ostringstream oss; - oss << Colour( Colour::Red ) - << "error: tag alias, \"" << alias << "\" already registered.\n" - << "\tFirst seen at " - << Colour( Colour::Red ) << find(alias)->lineInfo << '\n' - << Colour( Colour::Red ) << "\tRedefined at " - << Colour( Colour::FileName) << lineInfo << '\n'; - throw std::domain_error( oss.str().c_str() ); - } - } +#endif - ITagAliasRegistry::~ITagAliasRegistry() {} +} +// end catch_leak_detector.cpp +// start catch_list.cpp - ITagAliasRegistry const& ITagAliasRegistry::get() { - return getRegistryHub().getTagAliasRegistry(); - } +// start catch_list.h - RegistrarForTagAliases::RegistrarForTagAliases( char const* alias, char const* tag, SourceLineInfo const& lineInfo ) { - getMutableRegistryHub().registerTagAlias( alias, tag, lineInfo ); - } +#include + +namespace Catch { + + std::size_t listTests( Config const& config ); + + std::size_t listTestsNamesOnly( Config const& config ); + + struct TagInfo { + void add( std::string const& spelling ); + std::string all() const; + + std::set spellings; + std::size_t count = 0; + }; + + std::size_t listTags( Config const& config ); + + std::size_t listReporters( Config const& /*config*/ ); + + Option list( Config const& config ); } // end namespace Catch -// #included from: catch_matchers_string.hpp +// end catch_list.h +// start catch_text.h + +namespace Catch { + using namespace clara::TextFlow; +} + +// end catch_text.h +#include +#include +#include + +namespace Catch { + + std::size_t listTests( Config const& config ) { + TestSpec testSpec = config.testSpec(); + if( config.testSpec().hasFilters() ) + Catch::cout() << "Matching test cases:\n"; + else { + Catch::cout() << "All available test cases:\n"; + testSpec = TestSpecParser( ITagAliasRegistry::get() ).parse( "*" ).testSpec(); + } + + auto matchedTestCases = filterTests( getAllTestCasesSorted( config ), testSpec, config ); + for( auto const& testCaseInfo : matchedTestCases ) { + Colour::Code colour = testCaseInfo.isHidden() + ? Colour::SecondaryText + : Colour::None; + Colour colourGuard( colour ); + + Catch::cout() << Column( testCaseInfo.name ).initialIndent( 2 ).indent( 4 ) << "\n"; + if( config.verbosity() >= Verbosity::High ) { + Catch::cout() << Column( Catch::Detail::stringify( testCaseInfo.lineInfo ) ).indent(4) << std::endl; + std::string description = testCaseInfo.description; + if( description.empty() ) + description = "(NO DESCRIPTION)"; + Catch::cout() << Column( description ).indent(4) << std::endl; + } + if( !testCaseInfo.tags.empty() ) + Catch::cout() << Column( testCaseInfo.tagsAsString() ).indent( 6 ) << "\n"; + } + + if( !config.testSpec().hasFilters() ) + Catch::cout() << pluralise( matchedTestCases.size(), "test case" ) << '\n' << std::endl; + else + Catch::cout() << pluralise( matchedTestCases.size(), "matching test case" ) << '\n' << std::endl; + return matchedTestCases.size(); + } + + std::size_t listTestsNamesOnly( Config const& config ) { + TestSpec testSpec = config.testSpec(); + if( !config.testSpec().hasFilters() ) + testSpec = TestSpecParser( ITagAliasRegistry::get() ).parse( "*" ).testSpec(); + std::size_t matchedTests = 0; + std::vector matchedTestCases = filterTests( getAllTestCasesSorted( config ), testSpec, config ); + for( auto const& testCaseInfo : matchedTestCases ) { + matchedTests++; + if( startsWith( testCaseInfo.name, '#' ) ) + Catch::cout() << '"' << testCaseInfo.name << '"'; + else + Catch::cout() << testCaseInfo.name; + if ( config.verbosity() >= Verbosity::High ) + Catch::cout() << "\t@" << testCaseInfo.lineInfo; + Catch::cout() << std::endl; + } + return matchedTests; + } + + void TagInfo::add( std::string const& spelling ) { + ++count; + spellings.insert( spelling ); + } + + std::string TagInfo::all() const { + std::string out; + for( auto const& spelling : spellings ) + out += "[" + spelling + "]"; + return out; + } + + std::size_t listTags( Config const& config ) { + TestSpec testSpec = config.testSpec(); + if( config.testSpec().hasFilters() ) + Catch::cout() << "Tags for matching test cases:\n"; + else { + Catch::cout() << "All available tags:\n"; + testSpec = TestSpecParser( ITagAliasRegistry::get() ).parse( "*" ).testSpec(); + } + + std::map tagCounts; + + std::vector matchedTestCases = filterTests( getAllTestCasesSorted( config ), testSpec, config ); + for( auto const& testCase : matchedTestCases ) { + for( auto const& tagName : testCase.getTestCaseInfo().tags ) { + std::string lcaseTagName = toLower( tagName ); + auto countIt = tagCounts.find( lcaseTagName ); + if( countIt == tagCounts.end() ) + countIt = tagCounts.insert( std::make_pair( lcaseTagName, TagInfo() ) ).first; + countIt->second.add( tagName ); + } + } + + for( auto const& tagCount : tagCounts ) { + std::ostringstream oss; + oss << " " << std::setw(2) << tagCount.second.count << " "; + auto wrapper = Column( tagCount.second.all() ) + .initialIndent( 0 ) + .indent( oss.str().size() ) + .width( CATCH_CONFIG_CONSOLE_WIDTH-10 ); + Catch::cout() << oss.str() << wrapper << '\n'; + } + Catch::cout() << pluralise( tagCounts.size(), "tag" ) << '\n' << std::endl; + return tagCounts.size(); + } + + std::size_t listReporters( Config const& /*config*/ ) { + Catch::cout() << "Available reporters:\n"; + IReporterRegistry::FactoryMap const& factories = getRegistryHub().getReporterRegistry().getFactories(); + std::size_t maxNameLen = 0; + for( auto const& factoryKvp : factories ) + maxNameLen = (std::max)( maxNameLen, factoryKvp.first.size() ); + + for( auto const& factoryKvp : factories ) { + Catch::cout() + << Column( factoryKvp.first + ":" ) + .indent(2) + .width( 5+maxNameLen ) + + Column( factoryKvp.second->getDescription() ) + .initialIndent(0) + .indent(2) + .width( CATCH_CONFIG_CONSOLE_WIDTH - maxNameLen-8 ) + << "\n"; + } + Catch::cout() << std::endl; + return factories.size(); + } + + Option list( Config const& config ) { + Option listedCount; + if( config.listTests() ) + listedCount = listedCount.valueOr(0) + listTests( config ); + if( config.listTestNamesOnly() ) + listedCount = listedCount.valueOr(0) + listTestsNamesOnly( config ); + if( config.listTags() ) + listedCount = listedCount.valueOr(0) + listTags( config ); + if( config.listReporters() ) + listedCount = listedCount.valueOr(0) + listReporters( config ); + return listedCount; + } + +} // end namespace Catch +// end catch_list.cpp +// start catch_matchers.cpp + +namespace Catch { +namespace Matchers { + namespace Impl { + + std::string MatcherUntypedBase::toString() const { + if( m_cachedToString.empty() ) + m_cachedToString = describe(); + return m_cachedToString; + } + + MatcherUntypedBase::~MatcherUntypedBase() = default; + + } // namespace Impl +} // namespace Matchers + +using namespace Matchers; +using Matchers::Impl::MatcherBase; + +} // namespace Catch +// end catch_matchers.cpp +// start catch_matchers_string.cpp namespace Catch { namespace Matchers { @@ -9388,573 +7151,2744 @@ namespace Matchers { } // namespace Matchers } // namespace Catch -// #included from: ../reporters/catch_reporter_multi.hpp -#define TWOBLUECUBES_CATCH_REPORTER_MULTI_HPP_INCLUDED +// end catch_matchers_string.cpp +// start catch_message.cpp namespace Catch { -class MultipleReporters : public SharedImpl { - typedef std::vector > Reporters; - Reporters m_reporters; + MessageInfo::MessageInfo( std::string const& _macroName, + SourceLineInfo const& _lineInfo, + ResultWas::OfType _type ) + : macroName( _macroName ), + lineInfo( _lineInfo ), + type( _type ), + sequence( ++globalCount ) + {} -public: - void add( Ptr const& reporter ) { - m_reporters.push_back( reporter ); + bool MessageInfo::operator==( MessageInfo const& other ) const { + return sequence == other.sequence; } -public: // IStreamingReporter - - virtual ReporterPreferences getPreferences() const CATCH_OVERRIDE { - return m_reporters[0]->getPreferences(); + bool MessageInfo::operator<( MessageInfo const& other ) const { + return sequence < other.sequence; } - virtual void noMatchingTestCases( std::string const& spec ) CATCH_OVERRIDE { - for( Reporters::const_iterator it = m_reporters.begin(), itEnd = m_reporters.end(); - it != itEnd; - ++it ) - (*it)->noMatchingTestCases( spec ); + // This may need protecting if threading support is added + unsigned int MessageInfo::globalCount = 0; + + //////////////////////////////////////////////////////////////////////////// + + Catch::MessageBuilder::MessageBuilder( std::string const& macroName, + SourceLineInfo const& lineInfo, + ResultWas::OfType type ) + :m_info(macroName, lineInfo, type) {} + + //////////////////////////////////////////////////////////////////////////// + + ScopedMessage::ScopedMessage( MessageBuilder const& builder ) + : m_info( builder.m_info ) + { + m_info.message = builder.m_stream.str(); + getResultCapture().pushScopedMessage( m_info ); } - virtual void testRunStarting( TestRunInfo const& testRunInfo ) CATCH_OVERRIDE { - for( Reporters::const_iterator it = m_reporters.begin(), itEnd = m_reporters.end(); - it != itEnd; - ++it ) - (*it)->testRunStarting( testRunInfo ); - } - - virtual void testGroupStarting( GroupInfo const& groupInfo ) CATCH_OVERRIDE { - for( Reporters::const_iterator it = m_reporters.begin(), itEnd = m_reporters.end(); - it != itEnd; - ++it ) - (*it)->testGroupStarting( groupInfo ); - } - - virtual void testCaseStarting( TestCaseInfo const& testInfo ) CATCH_OVERRIDE { - for( Reporters::const_iterator it = m_reporters.begin(), itEnd = m_reporters.end(); - it != itEnd; - ++it ) - (*it)->testCaseStarting( testInfo ); - } - - virtual void sectionStarting( SectionInfo const& sectionInfo ) CATCH_OVERRIDE { - for( Reporters::const_iterator it = m_reporters.begin(), itEnd = m_reporters.end(); - it != itEnd; - ++it ) - (*it)->sectionStarting( sectionInfo ); - } - - virtual void assertionStarting( AssertionInfo const& assertionInfo ) CATCH_OVERRIDE { - for( Reporters::const_iterator it = m_reporters.begin(), itEnd = m_reporters.end(); - it != itEnd; - ++it ) - (*it)->assertionStarting( assertionInfo ); - } - - // The return value indicates if the messages buffer should be cleared: - virtual bool assertionEnded( AssertionStats const& assertionStats ) CATCH_OVERRIDE { - bool clearBuffer = false; - for( Reporters::const_iterator it = m_reporters.begin(), itEnd = m_reporters.end(); - it != itEnd; - ++it ) - clearBuffer |= (*it)->assertionEnded( assertionStats ); - return clearBuffer; - } - - virtual void sectionEnded( SectionStats const& sectionStats ) CATCH_OVERRIDE { - for( Reporters::const_iterator it = m_reporters.begin(), itEnd = m_reporters.end(); - it != itEnd; - ++it ) - (*it)->sectionEnded( sectionStats ); - } - - virtual void testCaseEnded( TestCaseStats const& testCaseStats ) CATCH_OVERRIDE { - for( Reporters::const_iterator it = m_reporters.begin(), itEnd = m_reporters.end(); - it != itEnd; - ++it ) - (*it)->testCaseEnded( testCaseStats ); - } - - virtual void testGroupEnded( TestGroupStats const& testGroupStats ) CATCH_OVERRIDE { - for( Reporters::const_iterator it = m_reporters.begin(), itEnd = m_reporters.end(); - it != itEnd; - ++it ) - (*it)->testGroupEnded( testGroupStats ); - } - - virtual void testRunEnded( TestRunStats const& testRunStats ) CATCH_OVERRIDE { - for( Reporters::const_iterator it = m_reporters.begin(), itEnd = m_reporters.end(); - it != itEnd; - ++it ) - (*it)->testRunEnded( testRunStats ); - } - - virtual void skipTest( TestCaseInfo const& testInfo ) CATCH_OVERRIDE { - for( Reporters::const_iterator it = m_reporters.begin(), itEnd = m_reporters.end(); - it != itEnd; - ++it ) - (*it)->skipTest( testInfo ); - } - - virtual MultipleReporters* tryAsMulti() CATCH_OVERRIDE { - return this; - } - -}; - -Ptr addReporter( Ptr const& existingReporter, Ptr const& additionalReporter ) { - Ptr resultingReporter; - - if( existingReporter ) { - MultipleReporters* multi = existingReporter->tryAsMulti(); - if( !multi ) { - multi = new MultipleReporters; - resultingReporter = Ptr( multi ); - if( existingReporter ) - multi->add( existingReporter ); + ScopedMessage::~ScopedMessage() { + if ( !std::uncaught_exception() ){ + getResultCapture().popScopedMessage(m_info); } - else - resultingReporter = existingReporter; - multi->add( additionalReporter ); } - else - resultingReporter = additionalReporter; - return resultingReporter; +} // end namespace Catch +// end catch_message.cpp +// start catch_random_number_generator.cpp + +// start catch_random_number_generator.h + +#include + +namespace Catch { + + struct IConfig; + + void seedRng( IConfig const& config ); + + unsigned int rngSeed(); + + struct RandomNumberGenerator { + using result_type = unsigned int; + + static constexpr result_type (min)() { return 0; } + static constexpr result_type (max)() { return 1000000; } + + result_type operator()( result_type n ) const; + result_type operator()() const; + + template + static void shuffle( V& vector ) { + RandomNumberGenerator rng; + std::shuffle( vector.begin(), vector.end(), rng ); + } + }; + } +// end catch_random_number_generator.h +#include + +namespace Catch { + + void seedRng( IConfig const& config ) { + if( config.rngSeed() != 0 ) + std::srand( config.rngSeed() ); + } + unsigned int rngSeed() { + return getCurrentContext().getConfig()->rngSeed(); + } + + RandomNumberGenerator::result_type RandomNumberGenerator::operator()( result_type n ) const { + return std::rand() % n; + } + RandomNumberGenerator::result_type RandomNumberGenerator::operator()() const { + return std::rand() % (max)(); + } + +} +// end catch_random_number_generator.cpp +// start catch_registry_hub.cpp + +// start catch_test_case_registry_impl.h + +#include +#include +#include +#include + +namespace Catch { + + class TestCase; + struct IConfig; + + std::vector sortTests( IConfig const& config, std::vector const& unsortedTestCases ); + bool matchTest( TestCase const& testCase, TestSpec const& testSpec, IConfig const& config ); + + void enforceNoDuplicateTestCases( std::vector const& functions ); + + std::vector filterTests( std::vector const& testCases, TestSpec const& testSpec, IConfig const& config ); + std::vector const& getAllTestCasesSorted( IConfig const& config ); + + class TestRegistry : public ITestCaseRegistry { + public: + virtual ~TestRegistry() = default; + + virtual void registerTest( TestCase const& testCase ); + + std::vector const& getAllTests() const override; + std::vector const& getAllTestsSorted( IConfig const& config ) const override; + + private: + std::vector m_functions; + mutable RunTests::InWhatOrder m_currentSortOrder = RunTests::InDeclarationOrder; + mutable std::vector m_sortedFunctions; + std::size_t m_unnamedCount = 0; + std::ios_base::Init m_ostreamInit; // Forces cout/ cerr to be initialised + }; + + /////////////////////////////////////////////////////////////////////////// + + class TestInvokerAsFunction : public ITestInvoker { + void(*m_testAsFunction)(); + public: + TestInvokerAsFunction( void(*testAsFunction)() ) noexcept; + + void invoke() const override; + }; + + std::string extractClassName( std::string const& classOrQualifiedMethodName ); + + /////////////////////////////////////////////////////////////////////////// + } // end namespace Catch -// #included from: ../reporters/catch_reporter_xml.hpp -#define TWOBLUECUBES_CATCH_REPORTER_XML_HPP_INCLUDED +// end catch_test_case_registry_impl.h +// start catch_reporter_registry.h -// #included from: catch_reporter_bases.hpp -#define TWOBLUECUBES_CATCH_REPORTER_BASES_HPP_INCLUDED +#include -#include -#include -#include -#include +namespace Catch { + class ReporterRegistry : public IReporterRegistry { + + public: + + ~ReporterRegistry() override; + + IStreamingReporterPtr create( std::string const& name, IConfigPtr const& config ) const override; + + void registerReporter( std::string const& name, IReporterFactoryPtr const& factory ); + void registerListener( IReporterFactoryPtr const& factory ); + + FactoryMap const& getFactories() const override; + Listeners const& getListeners() const override; + + private: + FactoryMap m_factories; + Listeners m_listeners; + }; +} + +// end catch_reporter_registry.h +// start catch_tag_alias_registry.h + +// start catch_tag_alias.h + +#include + +namespace Catch { + + struct TagAlias { + TagAlias(std::string const& _tag, SourceLineInfo _lineInfo); + + std::string tag; + SourceLineInfo lineInfo; + }; + +} // end namespace Catch + +// end catch_tag_alias.h +#include + +namespace Catch { + + class TagAliasRegistry : public ITagAliasRegistry { + public: + ~TagAliasRegistry() override; + TagAlias const* find( std::string const& alias ) const override; + std::string expandAliases( std::string const& unexpandedTestSpec ) const override; + void add( std::string const& alias, std::string const& tag, SourceLineInfo const& lineInfo ); + + private: + std::map m_registry; + }; + +} // end namespace Catch + +// end catch_tag_alias_registry.h +// start catch_startup_exception_registry.h + +#include +#include + +namespace Catch { + + class StartupExceptionRegistry { + public: + void add(std::exception_ptr const& exception) noexcept; + std::vector const& getExceptions() const noexcept; + private: + std::vector m_exceptions; + }; + +} // end namespace Catch + +// end catch_startup_exception_registry.h namespace Catch { namespace { - // Because formatting using c++ streams is stateful, drop down to C is required - // Alternatively we could use stringstream, but its performance is... not good. - std::string getFormattedDuration( double duration ) { - // Max exponent + 1 is required to represent the whole part - // + 1 for decimal point - // + 3 for the 3 decimal places - // + 1 for null terminator - const size_t maxDoubleSize = DBL_MAX_10_EXP + 1 + 1 + 3 + 1; - char buffer[maxDoubleSize]; - // Save previous errno, to prevent sprintf from overwriting it - ErrnoGuard guard; -#ifdef _MSC_VER - sprintf_s(buffer, "%.3f", duration); -#else - sprintf(buffer, "%.3f", duration); -#endif - return std::string(buffer); - } - } + class RegistryHub : public IRegistryHub, public IMutableRegistryHub, + private NonCopyable { - struct StreamingReporterBase : SharedImpl { - - StreamingReporterBase( ReporterConfig const& _config ) - : m_config( _config.fullConfig() ), - stream( _config.stream() ) - { - m_reporterPrefs.shouldRedirectStdOut = false; - } - - virtual ReporterPreferences getPreferences() const CATCH_OVERRIDE { - return m_reporterPrefs; - } - - virtual ~StreamingReporterBase() CATCH_OVERRIDE; - - virtual void noMatchingTestCases( std::string const& ) CATCH_OVERRIDE {} - - virtual void testRunStarting( TestRunInfo const& _testRunInfo ) CATCH_OVERRIDE { - currentTestRunInfo = _testRunInfo; - } - virtual void testGroupStarting( GroupInfo const& _groupInfo ) CATCH_OVERRIDE { - currentGroupInfo = _groupInfo; - } - - virtual void testCaseStarting( TestCaseInfo const& _testInfo ) CATCH_OVERRIDE { - currentTestCaseInfo = _testInfo; - } - virtual void sectionStarting( SectionInfo const& _sectionInfo ) CATCH_OVERRIDE { - m_sectionStack.push_back( _sectionInfo ); - } - - virtual void sectionEnded( SectionStats const& /* _sectionStats */ ) CATCH_OVERRIDE { - m_sectionStack.pop_back(); - } - virtual void testCaseEnded( TestCaseStats const& /* _testCaseStats */ ) CATCH_OVERRIDE { - currentTestCaseInfo.reset(); - } - virtual void testGroupEnded( TestGroupStats const& /* _testGroupStats */ ) CATCH_OVERRIDE { - currentGroupInfo.reset(); - } - virtual void testRunEnded( TestRunStats const& /* _testRunStats */ ) CATCH_OVERRIDE { - currentTestCaseInfo.reset(); - currentGroupInfo.reset(); - currentTestRunInfo.reset(); - } - - virtual void skipTest( TestCaseInfo const& ) CATCH_OVERRIDE { - // Don't do anything with this by default. - // It can optionally be overridden in the derived class. - } - - Ptr m_config; - std::ostream& stream; - - LazyStat currentTestRunInfo; - LazyStat currentGroupInfo; - LazyStat currentTestCaseInfo; - - std::vector m_sectionStack; - ReporterPreferences m_reporterPrefs; - }; - - struct CumulativeReporterBase : SharedImpl { - template - struct Node : SharedImpl<> { - explicit Node( T const& _value ) : value( _value ) {} - virtual ~Node() {} - - typedef std::vector > ChildNodes; - T value; - ChildNodes children; - }; - struct SectionNode : SharedImpl<> { - explicit SectionNode( SectionStats const& _stats ) : stats( _stats ) {} - virtual ~SectionNode(); - - bool operator == ( SectionNode const& other ) const { - return stats.sectionInfo.lineInfo == other.stats.sectionInfo.lineInfo; + public: // IRegistryHub + RegistryHub() = default; + IReporterRegistry const& getReporterRegistry() const override { + return m_reporterRegistry; } - bool operator == ( Ptr const& other ) const { - return operator==( *other ); + ITestCaseRegistry const& getTestCaseRegistry() const override { + return m_testCaseRegistry; + } + IExceptionTranslatorRegistry& getExceptionTranslatorRegistry() override { + return m_exceptionTranslatorRegistry; + } + ITagAliasRegistry const& getTagAliasRegistry() const override { + return m_tagAliasRegistry; + } + StartupExceptionRegistry const& getStartupExceptionRegistry() const override { + return m_exceptionRegistry; } - SectionStats stats; - typedef std::vector > ChildSections; - typedef std::vector Assertions; - ChildSections childSections; - Assertions assertions; - std::string stdOut; - std::string stdErr; - }; - - struct BySectionInfo { - BySectionInfo( SectionInfo const& other ) : m_other( other ) {} - BySectionInfo( BySectionInfo const& other ) : m_other( other.m_other ) {} - bool operator() ( Ptr const& node ) const { - return ((node->stats.sectionInfo.name == m_other.name) && - (node->stats.sectionInfo.lineInfo == m_other.lineInfo)); + public: // IMutableRegistryHub + void registerReporter( std::string const& name, IReporterFactoryPtr const& factory ) override { + m_reporterRegistry.registerReporter( name, factory ); } + void registerListener( IReporterFactoryPtr const& factory ) override { + m_reporterRegistry.registerListener( factory ); + } + void registerTest( TestCase const& testInfo ) override { + m_testCaseRegistry.registerTest( testInfo ); + } + void registerTranslator( const IExceptionTranslator* translator ) override { + m_exceptionTranslatorRegistry.registerTranslator( translator ); + } + void registerTagAlias( std::string const& alias, std::string const& tag, SourceLineInfo const& lineInfo ) override { + m_tagAliasRegistry.add( alias, tag, lineInfo ); + } + void registerStartupException() noexcept override { + m_exceptionRegistry.add(std::current_exception()); + } + private: - void operator=( BySectionInfo const& ); - SectionInfo const& m_other; + TestRegistry m_testCaseRegistry; + ReporterRegistry m_reporterRegistry; + ExceptionTranslatorRegistry m_exceptionTranslatorRegistry; + TagAliasRegistry m_tagAliasRegistry; + StartupExceptionRegistry m_exceptionRegistry; }; - typedef Node TestCaseNode; - typedef Node TestGroupNode; - typedef Node TestRunNode; - - CumulativeReporterBase( ReporterConfig const& _config ) - : m_config( _config.fullConfig() ), - stream( _config.stream() ) - { - m_reporterPrefs.shouldRedirectStdOut = false; + // Single, global, instance + RegistryHub*& getTheRegistryHub() { + static RegistryHub* theRegistryHub = nullptr; + if( !theRegistryHub ) + theRegistryHub = new RegistryHub(); + return theRegistryHub; } - ~CumulativeReporterBase(); - - virtual ReporterPreferences getPreferences() const CATCH_OVERRIDE { - return m_reporterPrefs; - } - - virtual void testRunStarting( TestRunInfo const& ) CATCH_OVERRIDE {} - virtual void testGroupStarting( GroupInfo const& ) CATCH_OVERRIDE {} - - virtual void testCaseStarting( TestCaseInfo const& ) CATCH_OVERRIDE {} - - virtual void sectionStarting( SectionInfo const& sectionInfo ) CATCH_OVERRIDE { - SectionStats incompleteStats( sectionInfo, Counts(), 0, false ); - Ptr node; - if( m_sectionStack.empty() ) { - if( !m_rootSection ) - m_rootSection = new SectionNode( incompleteStats ); - node = m_rootSection; - } - else { - SectionNode& parentNode = *m_sectionStack.back(); - SectionNode::ChildSections::const_iterator it = - std::find_if( parentNode.childSections.begin(), - parentNode.childSections.end(), - BySectionInfo( sectionInfo ) ); - if( it == parentNode.childSections.end() ) { - node = new SectionNode( incompleteStats ); - parentNode.childSections.push_back( node ); - } - else - node = *it; - } - m_sectionStack.push_back( node ); - m_deepestSection = node; - } - - virtual void assertionStarting( AssertionInfo const& ) CATCH_OVERRIDE {} - - virtual bool assertionEnded( AssertionStats const& assertionStats ) CATCH_OVERRIDE { - assert( !m_sectionStack.empty() ); - SectionNode& sectionNode = *m_sectionStack.back(); - sectionNode.assertions.push_back( assertionStats ); - // AssertionResult holds a pointer to a temporary DecomposedExpression, - // which getExpandedExpression() calls to build the expression string. - // Our section stack copy of the assertionResult will likely outlive the - // temporary, so it must be expanded or discarded now to avoid calling - // a destroyed object later. - prepareExpandedExpression( sectionNode.assertions.back().assertionResult ); - return true; - } - virtual void sectionEnded( SectionStats const& sectionStats ) CATCH_OVERRIDE { - assert( !m_sectionStack.empty() ); - SectionNode& node = *m_sectionStack.back(); - node.stats = sectionStats; - m_sectionStack.pop_back(); - } - virtual void testCaseEnded( TestCaseStats const& testCaseStats ) CATCH_OVERRIDE { - Ptr node = new TestCaseNode( testCaseStats ); - assert( m_sectionStack.size() == 0 ); - node->children.push_back( m_rootSection ); - m_testCases.push_back( node ); - m_rootSection.reset(); - - assert( m_deepestSection ); - m_deepestSection->stdOut = testCaseStats.stdOut; - m_deepestSection->stdErr = testCaseStats.stdErr; - } - virtual void testGroupEnded( TestGroupStats const& testGroupStats ) CATCH_OVERRIDE { - Ptr node = new TestGroupNode( testGroupStats ); - node->children.swap( m_testCases ); - m_testGroups.push_back( node ); - } - virtual void testRunEnded( TestRunStats const& testRunStats ) CATCH_OVERRIDE { - Ptr node = new TestRunNode( testRunStats ); - node->children.swap( m_testGroups ); - m_testRuns.push_back( node ); - testRunEndedCumulative(); - } - virtual void testRunEndedCumulative() = 0; - - virtual void skipTest( TestCaseInfo const& ) CATCH_OVERRIDE {} - - virtual void prepareExpandedExpression( AssertionResult& result ) const { - if( result.isOk() ) - result.discardDecomposedExpression(); - else - result.expandDecomposedExpression(); - } - - Ptr m_config; - std::ostream& stream; - std::vector m_assertions; - std::vector > > m_sections; - std::vector > m_testCases; - std::vector > m_testGroups; - - std::vector > m_testRuns; - - Ptr m_rootSection; - Ptr m_deepestSection; - std::vector > m_sectionStack; - ReporterPreferences m_reporterPrefs; - - }; - - template - char const* getLineOfChars() { - static char line[CATCH_CONFIG_CONSOLE_WIDTH] = {0}; - if( !*line ) { - std::memset( line, C, CATCH_CONFIG_CONSOLE_WIDTH-1 ); - line[CATCH_CONFIG_CONSOLE_WIDTH-1] = 0; - } - return line; } - struct TestEventListenerBase : StreamingReporterBase { - TestEventListenerBase( ReporterConfig const& _config ) - : StreamingReporterBase( _config ) - {} + IRegistryHub& getRegistryHub() { + return *getTheRegistryHub(); + } + IMutableRegistryHub& getMutableRegistryHub() { + return *getTheRegistryHub(); + } + void cleanUp() { + delete getTheRegistryHub(); + getTheRegistryHub() = nullptr; + cleanUpContext(); + } + std::string translateActiveException() { + return getRegistryHub().getExceptionTranslatorRegistry().translateActiveException(); + } - virtual void assertionStarting( AssertionInfo const& ) CATCH_OVERRIDE {} - virtual bool assertionEnded( AssertionStats const& ) CATCH_OVERRIDE { - return false; +} // end namespace Catch +// end catch_registry_hub.cpp +// start catch_reporter_registry.cpp + +namespace Catch { + + ReporterRegistry::~ReporterRegistry() = default; + + IStreamingReporterPtr ReporterRegistry::create( std::string const& name, IConfigPtr const& config ) const { + auto it = m_factories.find( name ); + if( it == m_factories.end() ) + return nullptr; + return it->second->create( ReporterConfig( config ) ); + } + + void ReporterRegistry::registerReporter( std::string const& name, IReporterFactoryPtr const& factory ) { + m_factories.emplace(name, factory); + } + void ReporterRegistry::registerListener( IReporterFactoryPtr const& factory ) { + m_listeners.push_back( factory ); + } + + IReporterRegistry::FactoryMap const& ReporterRegistry::getFactories() const { + return m_factories; + } + IReporterRegistry::Listeners const& ReporterRegistry::getListeners() const { + return m_listeners; + } + +} +// end catch_reporter_registry.cpp +// start catch_result_type.cpp + +namespace Catch { + + bool isOk( ResultWas::OfType resultType ) { + return ( resultType & ResultWas::FailureBit ) == 0; + } + bool isJustInfo( int flags ) { + return flags == ResultWas::Info; + } + + ResultDisposition::Flags operator | ( ResultDisposition::Flags lhs, ResultDisposition::Flags rhs ) { + return static_cast( static_cast( lhs ) | static_cast( rhs ) ); + } + + bool shouldContinueOnFailure( int flags ) { return ( flags & ResultDisposition::ContinueOnFailure ) != 0; } + bool isFalseTest( int flags ) { return ( flags & ResultDisposition::FalseTest ) != 0; } + bool shouldSuppressFailure( int flags ) { return ( flags & ResultDisposition::SuppressFail ) != 0; } + +} // end namespace Catch +// end catch_result_type.cpp +// start catch_run_context.cpp +// start catch_run_context.h + +#include + +namespace Catch { + + struct IMutableContext; + + class StreamRedirect { + + public: + StreamRedirect(std::ostream& stream, std::string& targetString); + + ~StreamRedirect(); + + private: + std::ostream& m_stream; + std::streambuf* m_prevBuf; + std::ostringstream 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; + std::ostringstream m_oss; + std::string& m_targetString; + }; + + /////////////////////////////////////////////////////////////////////////// + + class RunContext : public IResultCapture, public IRunner { + + public: + RunContext( RunContext const& ) = delete; + RunContext& operator =( RunContext const& ) = delete; + + explicit RunContext(IConfigPtr const& _config, IStreamingReporterPtr&& reporter); + + virtual ~RunContext(); + + 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 + + void assertionStarting(AssertionInfo const& info) override; + void assertionEnded(AssertionResult const& result) 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 benchmarkStarting( BenchmarkInfo const& info ) override; + void benchmarkEnded( BenchmarkStats const& stats ) override; + + void pushScopedMessage(MessageInfo const& message) override; + void popScopedMessage(MessageInfo const& message) override; + + std::string getCurrentTestName() const override; + + const AssertionResult* getLastResult() const override; + + void exceptionEarlyReported() override; + + void handleFatalErrorCondition( StringRef message ) override; + + bool lastAssertionPassed() override; + + 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 invokeActiveTestCase(); + + private: + + void handleUnfinishedSections(); + + TestRunInfo m_runInfo; + IMutableContext& m_context; + TestCase const* m_activeTestCase = nullptr; + ITracker* m_testCaseTracker; + Option m_lastResult; + + IConfigPtr m_config; + Totals m_totals; + IStreamingReporterPtr m_reporter; + std::vector m_messages; + AssertionInfo m_lastAssertionInfo; + std::vector m_unfinishedSections; + std::vector m_activeSections; + TrackerContext m_trackerContext; + std::size_t m_prevPassed = 0; + bool m_shouldReportUnexpected = true; + }; + + IResultCapture& getResultCapture(); + +} // end namespace Catch + +// end catch_run_context.h + +#include +#include + +namespace Catch { + + StreamRedirect::StreamRedirect(std::ostream& stream, std::string& targetString) + : m_stream(stream), + m_prevBuf(stream.rdbuf()), + m_targetString(targetString) { + stream.rdbuf(m_oss.rdbuf()); + } + + StreamRedirect::~StreamRedirect() { + m_targetString += m_oss.str(); + m_stream.rdbuf(m_prevBuf); + } + + StdErrRedirect::StdErrRedirect(std::string & targetString) + :m_cerrBuf(cerr().rdbuf()), m_clogBuf(clog().rdbuf()), + m_targetString(targetString) { + cerr().rdbuf(m_oss.rdbuf()); + clog().rdbuf(m_oss.rdbuf()); + } + + 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_context.setRunner(this); + m_context.setConfig(m_config); + m_context.setResultCapture(this); + m_reporter->testRunStarting(m_runInfo); + } + + RunContext::~RunContext() { + m_reporter->testRunEnded(TestRunStats(m_runInfo, m_totals, aborting())); + } + + void RunContext::testGroupStarting(std::string const& testSpec, std::size_t groupIndex, std::size_t groupsCount) { + m_reporter->testGroupStarting(GroupInfo(testSpec, groupIndex, groupsCount)); + } + + void RunContext::testGroupEnded(std::string const& testSpec, Totals const& totals, std::size_t groupIndex, std::size_t groupsCount) { + m_reporter->testGroupEnded(TestGroupStats(GroupInfo(testSpec, groupIndex, groupsCount), totals, aborting())); + } + + Totals RunContext::runTest(TestCase const& testCase) { + Totals prevTotals = m_totals; + + std::string redirectedCout; + std::string redirectedCerr; + + TestCaseInfo testInfo = testCase.getTestCaseInfo(); + + m_reporter->testCaseStarting(testInfo); + + m_activeTestCase = &testCase; + + ITracker& rootTracker = m_trackerContext.startRun(); + assert(rootTracker.isSectionTracker()); + static_cast(rootTracker).addInitialFilters(m_config->getSectionsToRun()); + do { + m_trackerContext.startCycle(); + m_testCaseTracker = &SectionTracker::acquire(m_trackerContext, TestCaseTracking::NameAndLocation(testInfo.name, testInfo.lineInfo)); + runCurrentTest(redirectedCout, redirectedCerr); + } while (!m_testCaseTracker->isSuccessfullyCompleted() && !aborting()); + + Totals deltaTotals = m_totals.delta(prevTotals); + if (testInfo.expectedToFail() && deltaTotals.testCases.passed > 0) { + deltaTotals.assertions.failed++; + deltaTotals.testCases.passed--; + deltaTotals.testCases.failed++; } + m_totals.testCases += deltaTotals.testCases; + m_reporter->testCaseEnded(TestCaseStats(testInfo, + deltaTotals, + redirectedCout, + redirectedCerr, + aborting())); + + m_activeTestCase = nullptr; + m_testCaseTracker = nullptr; + + return deltaTotals; + } + + IConfigPtr RunContext::config() const { + return m_config; + } + + IStreamingReporter& RunContext::reporter() const { + 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++; + } else if (!result.isOk()) { + if( m_activeTestCase->getTestCaseInfo().okToFail() ) + m_totals.assertions.failedButOk++; + else + m_totals.assertions.failed++; + } + + // 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. + static_cast(m_reporter->assertionEnded(AssertionStats(result, m_messages, m_totals))); + + // Reset working state + m_lastAssertionInfo = { "", m_lastAssertionInfo.lineInfo, "{Unknown expression after the reported line}", m_lastAssertionInfo.resultDisposition }; + m_lastResult = result; + } + + bool RunContext::sectionStarted(SectionInfo const & sectionInfo, Counts & assertions) { + ITracker& sectionTracker = SectionTracker::acquire(m_trackerContext, TestCaseTracking::NameAndLocation(sectionInfo.name, sectionInfo.lineInfo)); + if (!sectionTracker.isOpen()) + return false; + m_activeSections.push_back(§ionTracker); + + m_lastAssertionInfo.lineInfo = sectionInfo.lineInfo; + + m_reporter->sectionStarting(sectionInfo); + + assertions = m_totals.assertions; + + return true; + } + + bool RunContext::testForMissingAssertions(Counts& assertions) { + if (assertions.total() != 0) + return false; + if (!m_config->warnAboutMissingAssertions()) + return false; + if (m_trackerContext.currentTracker().hasChildren()) + return false; + m_totals.assertions.failed++; + assertions.failed++; + return true; + } + + void RunContext::sectionEnded(SectionEndInfo const & endInfo) { + Counts assertions = m_totals.assertions - endInfo.prevAssertions; + bool missingAssertions = testForMissingAssertions(assertions); + + if (!m_activeSections.empty()) { + m_activeSections.back()->close(); + m_activeSections.pop_back(); + } + + m_reporter->sectionEnded(SectionStats(endInfo.sectionInfo, assertions, endInfo.durationInSeconds, missingAssertions)); + m_messages.clear(); + } + + void RunContext::sectionEndedEarly(SectionEndInfo const & endInfo) { + if (m_unfinishedSections.empty()) + m_activeSections.back()->fail(); + else + m_activeSections.back()->close(); + m_activeSections.pop_back(); + + m_unfinishedSections.push_back(endInfo); + } + void RunContext::benchmarkStarting( BenchmarkInfo const& info ) { + m_reporter->benchmarkStarting( info ); + } + void RunContext::benchmarkEnded( BenchmarkStats const& stats ) { + m_reporter->benchmarkEnded( stats ); + } + + void RunContext::pushScopedMessage(MessageInfo const & message) { + m_messages.push_back(message); + } + + void RunContext::popScopedMessage(MessageInfo const & message) { + m_messages.erase(std::remove(m_messages.begin(), m_messages.end(), message), m_messages.end()); + } + + std::string RunContext::getCurrentTestName() const { + return m_activeTestCase + ? m_activeTestCase->getTestCaseInfo().name + : std::string(); + } + + const AssertionResult * RunContext::getLastResult() const { + return &(*m_lastResult); + } + + void RunContext::exceptionEarlyReported() { + m_shouldReportUnexpected = false; + } + + void RunContext::handleFatalErrorCondition( StringRef message ) { + // First notify reporter that bad things happened + m_reporter->fatalErrorEncountered(message); + + // Don't rebuild the result -- the stringification itself can cause more fatal errors + // Instead, fake a result data. + AssertionResultData tempResult( ResultWas::FatalErrorCondition, { false } ); + tempResult.message = message; + AssertionResult result(m_lastAssertionInfo, tempResult); + + getResultCapture().assertionEnded(result); + + handleUnfinishedSections(); + + // Recreate section for test case (as we will lose the one that was in scope) + auto const& testCaseInfo = m_activeTestCase->getTestCaseInfo(); + SectionInfo testCaseSection(testCaseInfo.lineInfo, testCaseInfo.name, testCaseInfo.description); + + Counts assertions; + assertions.failed = 1; + SectionStats testCaseSectionStats(testCaseSection, assertions, 0, false); + m_reporter->sectionEnded(testCaseSectionStats); + + auto const& testInfo = m_activeTestCase->getTestCaseInfo(); + + Totals deltaTotals; + deltaTotals.testCases.failed = 1; + deltaTotals.assertions.failed = 1; + m_reporter->testCaseEnded(TestCaseStats(testInfo, + deltaTotals, + std::string(), + std::string(), + false)); + m_totals.testCases.failed++; + testGroupEnded(std::string(), m_totals, 1, 1); + m_reporter->testRunEnded(TestRunStats(m_runInfo, m_totals, false)); + } + + bool RunContext::lastAssertionPassed() { + return m_totals.assertions.passed == (m_prevPassed + 1); + } + + void RunContext::assertionPassed() { + ++m_totals.assertions.passed; + m_lastAssertionInfo.capturedExpression = "{Unknown expression after the reported line}"; + m_lastAssertionInfo.macroName = ""; + } + + void RunContext::assertionRun() { + m_prevPassed = m_totals.assertions.passed; + } + + bool RunContext::aborting() const { + return m_totals.assertions.failed == static_cast(m_config->abortAfter()); + } + + void RunContext::runCurrentTest(std::string & redirectedCout, std::string & redirectedCerr) { + auto const& testCaseInfo = m_activeTestCase->getTestCaseInfo(); + SectionInfo testCaseSection(testCaseInfo.lineInfo, testCaseInfo.name, testCaseInfo.description); + m_reporter->sectionStarting(testCaseSection); + Counts prevAssertions = m_totals.assertions; + double duration = 0; + m_shouldReportUnexpected = true; + try { + m_lastAssertionInfo = { "TEST_CASE", testCaseInfo.lineInfo, "", ResultDisposition::Normal }; + + seedRng(*m_config); + + Timer timer; + timer.start(); + if (m_reporter->getPreferences().shouldRedirectStdOut) { + StreamRedirect coutRedir(cout(), redirectedCout); + StdErrRedirect errRedir(redirectedCerr); + invokeActiveTestCase(); + } else { + invokeActiveTestCase(); + } + duration = timer.getElapsedSeconds(); + } catch (TestFailureException&) { + // This just means the test was aborted due to failure + } 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(); + } + } + m_testCaseTracker->close(); + handleUnfinishedSections(); + m_messages.clear(); + + Counts assertions = m_totals.assertions - prevAssertions; + bool missingAssertions = testForMissingAssertions(assertions); + SectionStats testCaseSectionStats(testCaseSection, assertions, duration, missingAssertions); + m_reporter->sectionEnded(testCaseSectionStats); + } + + void RunContext::invokeActiveTestCase() { + FatalConditionHandler fatalConditionHandler; // Handle signals + m_activeTestCase->invoke(); + fatalConditionHandler.reset(); + } + + void RunContext::handleUnfinishedSections() { + // If sections ended prematurely due to an exception we stored their + // infos here so we can tear them down outside the unwind process. + for (auto it = m_unfinishedSections.rbegin(), + itEnd = m_unfinishedSections.rend(); + it != itEnd; + ++it) + sectionEnded(*it); + m_unfinishedSections.clear(); + } + + IResultCapture& getResultCapture() { + if (auto* capture = getCurrentContext().getResultCapture()) + return *capture; + else + CATCH_INTERNAL_ERROR("No result capture instance"); + } +} +// end catch_run_context.cpp +// start catch_section.cpp + +namespace Catch { + + Section::Section( SectionInfo const& info ) + : m_info( info ), + m_sectionIncluded( getResultCapture().sectionStarted( m_info, m_assertions ) ) + { + m_timer.start(); + } + +#if defined(_MSC_VER) +#pragma warning(push) +#pragma warning(disable:4996) // std::uncaught_exception is deprecated in C++17 +#endif + Section::~Section() { + if( m_sectionIncluded ) { + SectionEndInfo endInfo( m_info, m_assertions, m_timer.getElapsedSeconds() ); + if( std::uncaught_exception() ) + getResultCapture().sectionEndedEarly( endInfo ); + else + getResultCapture().sectionEnded( endInfo ); + } + } +#if defined(_MSC_VER) +#pragma warning(pop) +#endif + + // This indicates whether the section should be executed or not + Section::operator bool() const { + return m_sectionIncluded; + } + +} // end namespace Catch +// end catch_section.cpp +// start catch_section_info.cpp + +namespace Catch { + + SectionInfo::SectionInfo + ( SourceLineInfo const& _lineInfo, + std::string const& _name, + std::string const& _description ) + : name( _name ), + description( _description ), + lineInfo( _lineInfo ) + {} + + SectionEndInfo::SectionEndInfo( SectionInfo const& _sectionInfo, Counts const& _prevAssertions, double _durationInSeconds ) + : sectionInfo( _sectionInfo ), prevAssertions( _prevAssertions ), durationInSeconds( _durationInSeconds ) + {} + +} // end namespace Catch +// end catch_section_info.cpp +// start catch_session.cpp + +// start catch_session.h + +#include + +namespace Catch { + + class Session : NonCopyable { + public: + + Session(); + ~Session() override; + + void showHelp() const; + void libIdentify(); + + int applyCommandLine( int argc, char* argv[] ); + + void useConfigData( ConfigData const& configData ); + + int run( int argc, char* argv[] ); + #if defined(WIN32) && defined(UNICODE) + int run( int argc, wchar_t* const argv[] ); + #endif + int run(); + + clara::Parser const& cli() const; + void cli( clara::Parser const& newParser ); + ConfigData& configData(); + Config& config(); + private: + int runInternal(); + + clara::Parser m_cli; + ConfigData m_configData; + std::shared_ptr m_config; + bool m_startupExceptions = false; }; } // end namespace Catch -// #included from: ../internal/catch_reporter_registrars.hpp -#define TWOBLUECUBES_CATCH_REPORTER_REGISTRARS_HPP_INCLUDED +// end catch_session.h +// start catch_version.h + +#include namespace Catch { - template - class LegacyReporterRegistrar { + // Versioning information + struct Version { + Version( Version const& ) = delete; + Version& operator=( Version const& ) = delete; + Version( unsigned int _majorVersion, + unsigned int _minorVersion, + unsigned int _patchNumber, + char const * const _branchName, + unsigned int _buildNumber ); - class ReporterFactory : public IReporterFactory { - virtual IStreamingReporter* create( ReporterConfig const& config ) const { - return new LegacyReporterAdapter( new T( config ) ); - } + unsigned int const majorVersion; + unsigned int const minorVersion; + unsigned int const patchNumber; - virtual std::string getDescription() const { - return T::getDescription(); - } - }; + // buildNumber is only used if branchName is not null + char const * const branchName; + unsigned int const buildNumber; - public: - - LegacyReporterRegistrar( std::string const& name ) { - getMutableRegistryHub().registerReporter( name, new ReporterFactory() ); - } + friend std::ostream& operator << ( std::ostream& os, Version const& version ); }; - template - class ReporterRegistrar { - - class ReporterFactory : public SharedImpl { - - // *** Please Note ***: - // - If you end up here looking at a compiler error because it's trying to register - // your custom reporter class be aware that the native reporter interface has changed - // to IStreamingReporter. The "legacy" interface, IReporter, is still supported via - // an adapter. Just use REGISTER_LEGACY_REPORTER to take advantage of the adapter. - // However please consider updating to the new interface as the old one is now - // deprecated and will probably be removed quite soon! - // Please contact me via github if you have any questions at all about this. - // In fact, ideally, please contact me anyway to let me know you've hit this - as I have - // no idea who is actually using custom reporters at all (possibly no-one!). - // The new interface is designed to minimise exposure to interface changes in the future. - virtual IStreamingReporter* create( ReporterConfig const& config ) const { - return new T( config ); - } - - virtual std::string getDescription() const { - return T::getDescription(); - } - }; - - public: - - ReporterRegistrar( std::string const& name ) { - getMutableRegistryHub().registerReporter( name, new ReporterFactory() ); - } - }; - - template - class ListenerRegistrar { - - class ListenerFactory : public SharedImpl { - - virtual IStreamingReporter* create( ReporterConfig const& config ) const { - return new T( config ); - } - virtual std::string getDescription() const { - return std::string(); - } - }; - - public: - - ListenerRegistrar() { - getMutableRegistryHub().registerListener( new ListenerFactory() ); - } - }; + Version const& libraryVersion(); } -#define INTERNAL_CATCH_REGISTER_LEGACY_REPORTER( name, reporterType ) \ - namespace{ Catch::LegacyReporterRegistrar catch_internal_RegistrarFor##reporterType( name ); } +// end catch_version.h +#include +#include -#define INTERNAL_CATCH_REGISTER_REPORTER( name, reporterType ) \ - namespace{ Catch::ReporterRegistrar catch_internal_RegistrarFor##reporterType( name ); } +namespace { + const int MaxExitCode = 255; + using Catch::IStreamingReporterPtr; + using Catch::IConfigPtr; + using Catch::Config; -// Deprecated - use the form without INTERNAL_ -#define INTERNAL_CATCH_REGISTER_LISTENER( listenerType ) \ - namespace{ Catch::ListenerRegistrar catch_internal_RegistrarFor##listenerType; } + 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 << "'"); -#define CATCH_REGISTER_LISTENER( listenerType ) \ - namespace{ Catch::ListenerRegistrar catch_internal_RegistrarFor##listenerType; } + return reporter; + } -// #included from: ../internal/catch_xmlwriter.hpp -#define TWOBLUECUBES_CATCH_XMLWRITER_HPP_INCLUDED +#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 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); + } + + 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; + + 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); + } + } + +} + +namespace Catch { + + Session::Session() { + static bool alreadyInstantiated = false; + if( alreadyInstantiated ) { + try { CATCH_INTERNAL_ERROR( "Only one instance of Catch::Session can ever be used" ); } + catch(...) { getMutableRegistryHub().registerStartupException(); } + } + + const auto& exceptions = getRegistryHub().getStartupExceptionRegistry().getExceptions(); + if ( !exceptions.empty() ) { + m_startupExceptions = true; + Colour colourGuard( Colour::Red ); + Catch::cerr() << "Errors occured during startup!" << '\n'; + // iterate over all exceptions and notify user + for ( const auto& ex_ptr : exceptions ) { + try { + std::rethrow_exception(ex_ptr); + } catch ( std::exception const& ex ) { + Catch::cerr() << Column( ex.what() ).indent(2) << '\n'; + } + } + } + + alreadyInstantiated = true; + m_cli = makeCommandLineParser( m_configData ); + } + Session::~Session() { + Catch::cleanUp(); + } + + void Session::showHelp() const { + Catch::cout() + << "\nCatch v" << libraryVersion() << "\n" + << m_cli << std::endl + << "For more detailed usage please see the project docs\n" << std::endl; + } + void Session::libIdentify() { + Catch::cout() + << std::left << std::setw(16) << "description: " << "A Catch test executable\n" + << std::left << std::setw(16) << "category: " << "testframework\n" + << std::left << std::setw(16) << "framework: " << "Catch Test\n" + << std::left << std::setw(16) << "version: " << libraryVersion() << std::endl; + } + + int Session::applyCommandLine( int argc, char* argv[] ) { + if( m_startupExceptions ) + return 1; + + auto result = m_cli.parse( clara::Args( argc, argv ) ); + if( !result ) { + Catch::cerr() + << Colour( Colour::Red ) + << "\nError(s) in input:\n" + << Column( result.errorMessage() ).indent( 2 ) + << "\n\n"; + Catch::cerr() << "Run with -? for usage\n" << std::endl; + return MaxExitCode; + } + + if( m_configData.showHelp ) + showHelp(); + if( m_configData.libIdentify ) + libIdentify(); + m_config.reset(); + return 0; + } + + void Session::useConfigData( ConfigData const& configData ) { + m_configData = configData; + m_config.reset(); + } + + int Session::run( int argc, char* argv[] ) { + if( m_startupExceptions ) + return 1; + int returnCode = applyCommandLine( argc, argv ); + if( returnCode == 0 ) + returnCode = run(); + return returnCode; + } + +#if defined(WIN32) && defined(UNICODE) + int Session::run( int argc, wchar_t* const argv[] ) { + + char **utf8Argv = new char *[ argc ]; + + for ( int i = 0; i < argc; ++i ) { + int bufSize = WideCharToMultiByte( CP_UTF8, 0, argv[i], -1, NULL, 0, NULL, NULL ); + + utf8Argv[ i ] = new char[ bufSize ]; + + WideCharToMultiByte( CP_UTF8, 0, argv[i], -1, utf8Argv[i], bufSize, NULL, NULL ); + } + + int returnCode = run( argc, utf8Argv ); + + for ( int i = 0; i < argc; ++i ) + delete [] utf8Argv[ i ]; + + delete [] utf8Argv; + + return returnCode; + } +#endif + int Session::run() { + if( ( m_configData.waitForKeypress & WaitForKeypress::BeforeStart ) != 0 ) { + Catch::cout() << "...waiting for enter/ return before starting" << std::endl; + static_cast(std::getchar()); + } + int exitCode = runInternal(); + if( ( m_configData.waitForKeypress & WaitForKeypress::BeforeExit ) != 0 ) { + Catch::cout() << "...waiting for enter/ return before exiting, with code: " << exitCode << std::endl; + static_cast(std::getchar()); + } + return exitCode; + } + + clara::Parser const& Session::cli() const { + return m_cli; + } + void Session::cli( clara::Parser const& newParser ) { + m_cli = newParser; + } + ConfigData& Session::configData() { + return m_configData; + } + Config& Session::config() { + if( !m_config ) + m_config = std::make_shared( m_configData ); + return *m_config; + } + + int Session::runInternal() { + if( m_startupExceptions ) + return 1; + + if( m_configData.showHelp || m_configData.libIdentify ) + return 0; + + try + { + config(); // Force config to be constructed + + seedRng( *m_config ); + + if( m_configData.filenamesAsTags ) + applyFilenamesAsTags( *m_config ); + + // Handle list request + if( Option listed = list( config() ) ) + return static_cast( *listed ); + + return (std::min)( MaxExitCode, static_cast( runTests( m_config ).assertions.failed ) ); + } + catch( std::exception& ex ) { + Catch::cerr() << ex.what() << std::endl; + return MaxExitCode; + } + } + +} // end namespace Catch +// end catch_session.cpp +// start catch_startup_exception_registry.cpp + +namespace Catch { + void StartupExceptionRegistry::add( std::exception_ptr const& exception ) noexcept { + try { + m_exceptions.push_back(exception); + } + catch(...) { + // If we run out of memory during start-up there's really not a lot more we can do about it + std::terminate(); + } + } + + std::vector const& StartupExceptionRegistry::getExceptions() const noexcept { + return m_exceptions; + } + +} // end namespace Catch +// end catch_startup_exception_registry.cpp +// start catch_stream.cpp + +#include +#include +#include + +namespace Catch { + + template + class StreamBufImpl : public StreamBufBase { + char data[bufferSize]; + WriterF m_writer; + + public: + StreamBufImpl() { + setp( data, data + sizeof(data) ); + } + + ~StreamBufImpl() noexcept { + StreamBufImpl::sync(); + } + + private: + int overflow( int c ) override { + sync(); + + if( c != EOF ) { + if( pbase() == epptr() ) + m_writer( std::string( 1, static_cast( c ) ) ); + else + sputc( static_cast( c ) ); + } + return 0; + } + + int sync() override { + if( pbase() != pptr() ) { + m_writer( std::string( pbase(), static_cast( pptr() - pbase() ) ) ); + setp( pbase(), epptr() ); + } + return 0; + } + }; + + /////////////////////////////////////////////////////////////////////////// + + Catch::IStream::~IStream() = default; + + FileStream::FileStream( std::string const& filename ) { + m_ofs.open( filename.c_str() ); + CATCH_ENFORCE( !m_ofs.fail(), "Unable to open file: '" << filename << "'" ); + } + + std::ostream& FileStream::stream() const { + return m_ofs; + } + + struct OutputDebugWriter { + + void operator()( std::string const&str ) { + writeToDebugConsole( str ); + } + }; + + DebugOutStream::DebugOutStream() + : m_streamBuf( new StreamBufImpl() ), + m_os( m_streamBuf.get() ) + {} + + std::ostream& DebugOutStream::stream() const { + return m_os; + } + + // Store the streambuf from cout up-front because + // cout may get redirected when running tests + CoutStream::CoutStream() + : m_os( Catch::cout().rdbuf() ) + {} + + std::ostream& CoutStream::stream() const { + return m_os; + } + +#ifndef CATCH_CONFIG_NOSTDOUT // If you #define this you must implement these functions + std::ostream& cout() { + return std::cout; + } + std::ostream& cerr() { + return std::cerr; + } + std::ostream& clog() { + return std::clog; + } +#endif +} +// end catch_stream.cpp +// start catch_streambuf.cpp + +namespace Catch { + StreamBufBase::~StreamBufBase() = default; +} +// end catch_streambuf.cpp +// start catch_string_manip.cpp + +#include +#include +#include +#include + +namespace Catch { + + bool startsWith( std::string const& s, std::string const& prefix ) { + return s.size() >= prefix.size() && std::equal(prefix.begin(), prefix.end(), s.begin()); + } + bool startsWith( std::string const& s, char prefix ) { + return !s.empty() && s[0] == prefix; + } + bool endsWith( std::string const& s, std::string const& suffix ) { + return s.size() >= suffix.size() && std::equal(suffix.rbegin(), suffix.rend(), s.rbegin()); + } + bool endsWith( std::string const& s, char suffix ) { + return !s.empty() && s[s.size()-1] == suffix; + } + bool contains( std::string const& s, std::string const& infix ) { + return s.find( infix ) != std::string::npos; + } + char toLowerCh(char c) { + return static_cast( std::tolower( c ) ); + } + void toLowerInPlace( std::string& s ) { + std::transform( s.begin(), s.end(), s.begin(), toLowerCh ); + } + std::string toLower( std::string const& s ) { + std::string lc = s; + toLowerInPlace( lc ); + return lc; + } + std::string trim( std::string const& str ) { + static char const* whitespaceChars = "\n\r\t "; + std::string::size_type start = str.find_first_not_of( whitespaceChars ); + std::string::size_type end = str.find_last_not_of( whitespaceChars ); + + return start != std::string::npos ? str.substr( start, 1+end-start ) : std::string(); + } + + bool replaceInPlace( std::string& str, std::string const& replaceThis, std::string const& withThis ) { + bool replaced = false; + std::size_t i = str.find( replaceThis ); + while( i != std::string::npos ) { + replaced = true; + str = str.substr( 0, i ) + withThis + str.substr( i+replaceThis.size() ); + if( i < str.size()-withThis.size() ) + i = str.find( replaceThis, i+withThis.size() ); + else + i = std::string::npos; + } + return replaced; + } + + pluralise::pluralise( std::size_t count, std::string const& label ) + : m_count( count ), + m_label( label ) + {} + + std::ostream& operator << ( std::ostream& os, pluralise const& pluraliser ) { + os << pluraliser.m_count << ' ' << pluraliser.m_label; + if( pluraliser.m_count != 1 ) + os << 's'; + return os; + } + +} +// end catch_string_manip.cpp +// start catch_stringref.cpp + +#if defined(__clang__) +# pragma clang diagnostic push +# pragma clang diagnostic ignored "-Wexit-time-destructors" +#endif + +#include +#include +#include + +namespace Catch { + + auto getEmptyStringRef() -> StringRef { + static StringRef s_emptyStringRef(""); + return s_emptyStringRef; + } + + StringRef::StringRef() noexcept + : StringRef( getEmptyStringRef() ) + {} + + StringRef::StringRef( StringRef const& other ) noexcept + : m_start( other.m_start ), + m_size( other.m_size ) + {} + + StringRef::StringRef( StringRef&& other ) noexcept + : m_start( other.m_start ), + m_size( other.m_size ), + m_data( other.m_data ) + { + other.m_data = nullptr; + } + + StringRef::StringRef( char const* rawChars ) noexcept + : m_start( rawChars ), + m_size( static_cast( std::strlen( rawChars ) ) ) + { + assert( rawChars != nullptr ); + } + + StringRef::StringRef( char const* rawChars, size_type size ) noexcept + : m_start( rawChars ), + m_size( size ) + { + size_type rawSize = rawChars == nullptr ? 0 : static_cast( std::strlen( rawChars ) ); + if( rawSize < size ) + m_size = rawSize; + } + + StringRef::StringRef( std::string const& stdString ) noexcept + : m_start( stdString.c_str() ), + m_size( stdString.size() ) + {} + + StringRef::~StringRef() noexcept { + delete[] m_data; + } + + auto StringRef::operator = ( StringRef other ) noexcept -> StringRef& { + swap( other ); + return *this; + } + StringRef::operator std::string() const { + return std::string( m_start, m_size ); + } + + void StringRef::swap( StringRef& other ) noexcept { + std::swap( m_start, other.m_start ); + std::swap( m_size, other.m_size ); + std::swap( m_data, other.m_data ); + } + + auto StringRef::c_str() const -> char const* { + if( isSubstring() ) + const_cast( this )->takeOwnership(); + return m_start; + } + auto StringRef::data() const noexcept -> char const* { + return m_start; + } + + auto StringRef::isOwned() const noexcept -> bool { + return m_data != nullptr; + } + auto StringRef::isSubstring() const noexcept -> bool { + return m_start[m_size] != '\0'; + } + + void StringRef::takeOwnership() { + if( !isOwned() ) { + m_data = new char[m_size+1]; + memcpy( m_data, m_start, m_size ); + m_data[m_size] = '\0'; + m_start = m_data; + } + } + auto StringRef::substr( size_type start, size_type size ) const noexcept -> StringRef { + if( start < m_size ) + return StringRef( m_start+start, size ); + else + return StringRef(); + } + auto StringRef::operator == ( StringRef const& other ) const noexcept -> bool { + return + size() == other.size() && + (std::strncmp( m_start, other.m_start, size() ) == 0); + } + auto StringRef::operator != ( StringRef const& other ) const noexcept -> bool { + return !operator==( other ); + } + + auto StringRef::operator[](size_type index) const noexcept -> char { + return m_start[index]; + } + + auto StringRef::empty() const noexcept -> bool { + return m_size == 0; + } + + auto StringRef::size() const noexcept -> size_type { + return m_size; + } + auto StringRef::numberOfCharacters() const noexcept -> size_type { + size_type noChars = m_size; + // Make adjustments for uft encodings + for( size_type i=0; i < m_size; ++i ) { + char c = m_start[i]; + if( ( c & 0b11000000 ) == 0b11000000 ) { + if( ( c & 0b11100000 ) == 0b11000000 ) + noChars--; + else if( ( c & 0b11110000 ) == 0b11100000 ) + noChars-=2; + else if( ( c & 0b11111000 ) == 0b11110000 ) + noChars-=3; + } + } + return noChars; + } + + auto operator + ( StringRef const& lhs, StringRef const& rhs ) -> std::string { + std::string str; + str.reserve( lhs.size() + rhs.size() ); + str += lhs; + str += rhs; + return str; + } + auto operator + ( StringRef const& lhs, const char* rhs ) -> std::string { + return std::string( lhs ) + std::string( rhs ); + } + auto operator + ( char const* lhs, StringRef const& rhs ) -> std::string { + return std::string( lhs ) + std::string( rhs ); + } + + auto operator << ( std::ostream& os, StringRef const& str ) -> std::ostream& { + return os << str.c_str(); + } + +} // namespace Catch + +#if defined(__clang__) +# pragma clang diagnostic pop +#endif +// end catch_stringref.cpp +// start catch_tag_alias.cpp + +namespace Catch { + TagAlias::TagAlias(std::string const & _tag, SourceLineInfo _lineInfo): tag(_tag), lineInfo(_lineInfo) {} +} +// end catch_tag_alias.cpp +// start catch_tag_alias_autoregistrar.cpp + +namespace Catch { + + RegistrarForTagAliases::RegistrarForTagAliases(char const* alias, char const* tag, SourceLineInfo const& lineInfo) { + try { + getMutableRegistryHub().registerTagAlias(alias, tag, lineInfo); + } catch (...) { + // Do not throw when constructing global objects, instead register the exception to be processed later + getMutableRegistryHub().registerStartupException(); + } + } + +} +// end catch_tag_alias_autoregistrar.cpp +// start catch_tag_alias_registry.cpp + +namespace Catch { + + TagAliasRegistry::~TagAliasRegistry() {} + + TagAlias const* TagAliasRegistry::find( std::string const& alias ) const { + auto it = m_registry.find( alias ); + if( it != m_registry.end() ) + return &(it->second); + else + return nullptr; + } + + std::string TagAliasRegistry::expandAliases( std::string const& unexpandedTestSpec ) const { + std::string expandedTestSpec = unexpandedTestSpec; + for( auto const& registryKvp : m_registry ) { + std::size_t pos = expandedTestSpec.find( registryKvp.first ); + if( pos != std::string::npos ) { + expandedTestSpec = expandedTestSpec.substr( 0, pos ) + + registryKvp.second.tag + + expandedTestSpec.substr( pos + registryKvp.first.size() ); + } + } + return expandedTestSpec; + } + + void TagAliasRegistry::add( std::string const& alias, std::string const& tag, SourceLineInfo const& lineInfo ) { + CATCH_ENFORCE( startsWith(alias, "[@") && endsWith(alias, ']'), + "error: tag alias, '" << alias << "' is not of the form [@alias name].\n" << lineInfo ); + + CATCH_ENFORCE( m_registry.insert(std::make_pair(alias, TagAlias(tag, lineInfo))).second, + "error: tag alias, '" << alias << "' already registered.\n" + << "\tFirst seen at: " << find(alias)->lineInfo << "\n" + << "\tRedefined at: " << lineInfo ); + } + + ITagAliasRegistry::~ITagAliasRegistry() {} + + ITagAliasRegistry const& ITagAliasRegistry::get() { + return getRegistryHub().getTagAliasRegistry(); + } + +} // end namespace Catch +// end catch_tag_alias_registry.cpp +// start catch_test_case_info.cpp + +#include +#include +#include + +namespace Catch { + + TestCaseInfo::SpecialProperties parseSpecialTag( std::string const& tag ) { + if( startsWith( tag, '.' ) || + tag == "!hide" ) + return TestCaseInfo::IsHidden; + else if( tag == "!throws" ) + return TestCaseInfo::Throws; + else if( tag == "!shouldfail" ) + return TestCaseInfo::ShouldFail; + else if( tag == "!mayfail" ) + return TestCaseInfo::MayFail; + else if( tag == "!nonportable" ) + return TestCaseInfo::NonPortable; + else if( tag == "!benchmark" ) + return static_cast( TestCaseInfo::Benchmark | TestCaseInfo::IsHidden ); + else + return TestCaseInfo::None; + } + bool isReservedTag( std::string const& tag ) { + return parseSpecialTag( tag ) == TestCaseInfo::None && tag.size() > 0 && !std::isalnum( tag[0] ); + } + void enforceNotReservedTag( std::string const& tag, SourceLineInfo const& _lineInfo ) { + CATCH_ENFORCE( !isReservedTag(tag), + "Tag name: [" << tag << "] is not allowed.\n" + << "Tag names starting with non alpha-numeric characters are reserved\n" + << _lineInfo ); + } + + TestCase makeTestCase( ITestInvoker* _testCase, + std::string const& _className, + std::string const& _name, + std::string const& _descOrTags, + SourceLineInfo const& _lineInfo ) + { + bool isHidden = false; + + // Parse out tags + std::vector tags; + std::string desc, tag; + bool inTag = false; + for (char c : _descOrTags) { + if( !inTag ) { + if( c == '[' ) + inTag = true; + else + desc += c; + } + else { + if( c == ']' ) { + TestCaseInfo::SpecialProperties prop = parseSpecialTag( tag ); + if( ( prop & TestCaseInfo::IsHidden ) != 0 ) + isHidden = true; + else if( prop == TestCaseInfo::None ) + enforceNotReservedTag( tag, _lineInfo ); + + tags.push_back( tag ); + tag.clear(); + inTag = false; + } + else + tag += c; + } + } + if( isHidden ) { + tags.push_back( "." ); + } + + TestCaseInfo info( _name, _className, desc, tags, _lineInfo ); + return TestCase( _testCase, info ); + } + + void setTags( TestCaseInfo& testCaseInfo, std::vector tags ) { + std::sort(begin(tags), end(tags)); + tags.erase(std::unique(begin(tags), end(tags)), end(tags)); + testCaseInfo.lcaseTags.clear(); + + for( auto const& tag : tags ) { + std::string lcaseTag = toLower( tag ); + testCaseInfo.properties = static_cast( testCaseInfo.properties | parseSpecialTag( lcaseTag ) ); + testCaseInfo.lcaseTags.push_back( lcaseTag ); + } + testCaseInfo.tags = std::move(tags); + } + + TestCaseInfo::TestCaseInfo( std::string const& _name, + std::string const& _className, + std::string const& _description, + std::vector const& _tags, + SourceLineInfo const& _lineInfo ) + : name( _name ), + className( _className ), + description( _description ), + lineInfo( _lineInfo ), + properties( None ) + { + setTags( *this, _tags ); + } + + bool TestCaseInfo::isHidden() const { + return ( properties & IsHidden ) != 0; + } + bool TestCaseInfo::throws() const { + return ( properties & Throws ) != 0; + } + bool TestCaseInfo::okToFail() const { + return ( properties & (ShouldFail | MayFail ) ) != 0; + } + bool TestCaseInfo::expectedToFail() const { + return ( properties & (ShouldFail ) ) != 0; + } + + std::string TestCaseInfo::tagsAsString() const { + std::string ret; + // '[' and ']' per tag + std::size_t full_size = 2 * tags.size(); + for (const auto& tag : tags) { + full_size += tag.size(); + } + ret.reserve(full_size); + for (const auto& tag : tags) { + ret.push_back('['); + ret.append(tag); + ret.push_back(']'); + } + + return ret; + } + + TestCase::TestCase( ITestInvoker* testCase, TestCaseInfo const& info ) : TestCaseInfo( info ), test( testCase ) {} + + TestCase TestCase::withName( std::string const& _newName ) const { + TestCase other( *this ); + other.name = _newName; + return other; + } + + void TestCase::invoke() const { + test->invoke(); + } + + bool TestCase::operator == ( TestCase const& other ) const { + return test.get() == other.test.get() && + name == other.name && + className == other.className; + } + + bool TestCase::operator < ( TestCase const& other ) const { + return name < other.name; + } + + TestCaseInfo const& TestCase::getTestCaseInfo() const + { + return *this; + } + +} // end namespace Catch +// end catch_test_case_info.cpp +// start catch_test_case_registry_impl.cpp #include + +namespace Catch { + + std::vector sortTests( IConfig const& config, std::vector const& unsortedTestCases ) { + + std::vector sorted = unsortedTestCases; + + switch( config.runOrder() ) { + case RunTests::InLexicographicalOrder: + std::sort( sorted.begin(), sorted.end() ); + break; + case RunTests::InRandomOrder: + seedRng( config ); + RandomNumberGenerator::shuffle( sorted ); + break; + case RunTests::InDeclarationOrder: + // already in declaration order + break; + } + return sorted; + } + bool matchTest( TestCase const& testCase, TestSpec const& testSpec, IConfig const& config ) { + return testSpec.matches( testCase ) && ( config.allowThrows() || !testCase.throws() ); + } + + void enforceNoDuplicateTestCases( std::vector const& functions ) { + std::set seenFunctions; + for( auto const& function : functions ) { + auto prev = seenFunctions.insert( function ); + CATCH_ENFORCE( prev.second, + "error: TEST_CASE( \"" << function.name << "\" ) already defined.\n" + << "\tFirst seen at " << prev.first->getTestCaseInfo().lineInfo << "\n" + << "\tRedefined at " << function.getTestCaseInfo().lineInfo ); + } + } + + std::vector filterTests( std::vector const& testCases, TestSpec const& testSpec, IConfig const& config ) { + std::vector filtered; + filtered.reserve( testCases.size() ); + for( auto const& testCase : testCases ) + if( matchTest( testCase, testSpec, config ) ) + filtered.push_back( testCase ); + return filtered; + } + std::vector const& getAllTestCasesSorted( IConfig const& config ) { + return getRegistryHub().getTestCaseRegistry().getAllTestsSorted( config ); + } + + void TestRegistry::registerTest( TestCase const& testCase ) { + std::string name = testCase.getTestCaseInfo().name; + if( name.empty() ) { + std::ostringstream oss; + oss << "Anonymous test case " << ++m_unnamedCount; + return registerTest( testCase.withName( oss.str() ) ); + } + m_functions.push_back( testCase ); + } + + std::vector const& TestRegistry::getAllTests() const { + return m_functions; + } + std::vector const& TestRegistry::getAllTestsSorted( IConfig const& config ) const { + if( m_sortedFunctions.empty() ) + enforceNoDuplicateTestCases( m_functions ); + + if( m_currentSortOrder != config.runOrder() || m_sortedFunctions.empty() ) { + m_sortedFunctions = sortTests( config, m_functions ); + m_currentSortOrder = config.runOrder(); + } + return m_sortedFunctions; + } + + /////////////////////////////////////////////////////////////////////////// + TestInvokerAsFunction::TestInvokerAsFunction( void(*testAsFunction)() ) noexcept : m_testAsFunction( testAsFunction ) {} + + void TestInvokerAsFunction::invoke() const { + m_testAsFunction(); + } + + std::string extractClassName( std::string const& classOrQualifiedMethodName ) { + std::string className = classOrQualifiedMethodName; + if( startsWith( className, '&' ) ) + { + std::size_t lastColons = className.rfind( "::" ); + std::size_t penultimateColons = className.rfind( "::", lastColons-1 ); + if( penultimateColons == std::string::npos ) + penultimateColons = 1; + className = className.substr( penultimateColons, lastColons-penultimateColons ); + } + return className; + } + +} // end namespace Catch +// end catch_test_case_registry_impl.cpp +// start catch_test_case_tracker.cpp + +#include +#include +#include +#include + +#if defined(__clang__) +# pragma clang diagnostic push +# pragma clang diagnostic ignored "-Wexit-time-destructors" +#endif + +namespace Catch { +namespace TestCaseTracking { + + NameAndLocation::NameAndLocation( std::string const& _name, SourceLineInfo const& _location ) + : name( _name ), + location( _location ) + {} + + ITracker::~ITracker() = default; + + TrackerContext& TrackerContext::instance() { + static TrackerContext s_instance; + return s_instance; + } + + ITracker& TrackerContext::startRun() { + m_rootTracker = std::make_shared( NameAndLocation( "{root}", CATCH_INTERNAL_LINEINFO ), *this, nullptr ); + m_currentTracker = nullptr; + m_runState = Executing; + return *m_rootTracker; + } + + void TrackerContext::endRun() { + m_rootTracker.reset(); + m_currentTracker = nullptr; + m_runState = NotStarted; + } + + void TrackerContext::startCycle() { + m_currentTracker = m_rootTracker.get(); + m_runState = Executing; + } + void TrackerContext::completeCycle() { + m_runState = CompletedCycle; + } + + bool TrackerContext::completedCycle() const { + return m_runState == CompletedCycle; + } + ITracker& TrackerContext::currentTracker() { + return *m_currentTracker; + } + void TrackerContext::setCurrentTracker( ITracker* tracker ) { + m_currentTracker = tracker; + } + + TrackerBase::TrackerHasName::TrackerHasName( NameAndLocation const& nameAndLocation ) : m_nameAndLocation( nameAndLocation ) {} + bool TrackerBase::TrackerHasName::operator ()( ITrackerPtr const& tracker ) const { + return + tracker->nameAndLocation().name == m_nameAndLocation.name && + tracker->nameAndLocation().location == m_nameAndLocation.location; + } + + TrackerBase::TrackerBase( NameAndLocation const& nameAndLocation, TrackerContext& ctx, ITracker* parent ) + : m_nameAndLocation( nameAndLocation ), + m_ctx( ctx ), + m_parent( parent ) + {} + + NameAndLocation const& TrackerBase::nameAndLocation() const { + return m_nameAndLocation; + } + bool TrackerBase::isComplete() const { + return m_runState == CompletedSuccessfully || m_runState == Failed; + } + bool TrackerBase::isSuccessfullyCompleted() const { + return m_runState == CompletedSuccessfully; + } + bool TrackerBase::isOpen() const { + return m_runState != NotStarted && !isComplete(); + } + bool TrackerBase::hasChildren() const { + return !m_children.empty(); + } + + void TrackerBase::addChild( ITrackerPtr const& child ) { + m_children.push_back( child ); + } + + ITrackerPtr TrackerBase::findChild( NameAndLocation const& nameAndLocation ) { + auto it = std::find_if( m_children.begin(), m_children.end(), TrackerHasName( nameAndLocation ) ); + return( it != m_children.end() ) + ? *it + : nullptr; + } + ITracker& TrackerBase::parent() { + assert( m_parent ); // Should always be non-null except for root + return *m_parent; + } + + void TrackerBase::openChild() { + if( m_runState != ExecutingChildren ) { + m_runState = ExecutingChildren; + if( m_parent ) + m_parent->openChild(); + } + } + + bool TrackerBase::isSectionTracker() const { return false; } + bool TrackerBase::isIndexTracker() const { return false; } + + void TrackerBase::open() { + m_runState = Executing; + moveToThis(); + if( m_parent ) + m_parent->openChild(); + } + + void TrackerBase::close() { + + // Close any still open children (e.g. generators) + while( &m_ctx.currentTracker() != this ) + m_ctx.currentTracker().close(); + + switch( m_runState ) { + case NeedsAnotherRun: + break; + + case Executing: + m_runState = CompletedSuccessfully; + break; + case ExecutingChildren: + if( m_children.empty() || m_children.back()->isComplete() ) + m_runState = CompletedSuccessfully; + break; + + case NotStarted: + case CompletedSuccessfully: + case Failed: + CATCH_INTERNAL_ERROR( "Illogical state: " << m_runState ); + + default: + CATCH_INTERNAL_ERROR( "Unknown state: " << m_runState ); + } + moveToParent(); + m_ctx.completeCycle(); + } + void TrackerBase::fail() { + m_runState = Failed; + if( m_parent ) + m_parent->markAsNeedingAnotherRun(); + moveToParent(); + m_ctx.completeCycle(); + } + void TrackerBase::markAsNeedingAnotherRun() { + m_runState = NeedsAnotherRun; + } + + void TrackerBase::moveToParent() { + assert( m_parent ); + m_ctx.setCurrentTracker( m_parent ); + } + void TrackerBase::moveToThis() { + m_ctx.setCurrentTracker( this ); + } + + SectionTracker::SectionTracker( NameAndLocation const& nameAndLocation, TrackerContext& ctx, ITracker* parent ) + : TrackerBase( nameAndLocation, ctx, parent ) + { + if( parent ) { + while( !parent->isSectionTracker() ) + parent = &parent->parent(); + + SectionTracker& parentSection = static_cast( *parent ); + addNextFilters( parentSection.m_filters ); + } + } + + bool SectionTracker::isSectionTracker() const { return true; } + + SectionTracker& SectionTracker::acquire( TrackerContext& ctx, NameAndLocation const& nameAndLocation ) { + std::shared_ptr section; + + ITracker& currentTracker = ctx.currentTracker(); + if( ITrackerPtr childTracker = currentTracker.findChild( nameAndLocation ) ) { + assert( childTracker ); + assert( childTracker->isSectionTracker() ); + section = std::static_pointer_cast( childTracker ); + } + else { + section = std::make_shared( nameAndLocation, ctx, ¤tTracker ); + currentTracker.addChild( section ); + } + if( !ctx.completedCycle() ) + section->tryOpen(); + return *section; + } + + void SectionTracker::tryOpen() { + if( !isComplete() && (m_filters.empty() || m_filters[0].empty() || m_filters[0] == m_nameAndLocation.name ) ) + open(); + } + + void SectionTracker::addInitialFilters( std::vector const& filters ) { + if( !filters.empty() ) { + m_filters.push_back(""); // Root - should never be consulted + m_filters.push_back(""); // Test Case - not a section filter + m_filters.insert( m_filters.end(), filters.begin(), filters.end() ); + } + } + void SectionTracker::addNextFilters( std::vector const& filters ) { + if( filters.size() > 1 ) + m_filters.insert( m_filters.end(), ++filters.begin(), filters.end() ); + } + + IndexTracker::IndexTracker( NameAndLocation const& nameAndLocation, TrackerContext& ctx, ITracker* parent, int size ) + : TrackerBase( nameAndLocation, ctx, parent ), + m_size( size ) + {} + + bool IndexTracker::isIndexTracker() const { return true; } + + IndexTracker& IndexTracker::acquire( TrackerContext& ctx, NameAndLocation const& nameAndLocation, int size ) { + std::shared_ptr tracker; + + ITracker& currentTracker = ctx.currentTracker(); + if( ITrackerPtr childTracker = currentTracker.findChild( nameAndLocation ) ) { + assert( childTracker ); + assert( childTracker->isIndexTracker() ); + tracker = std::static_pointer_cast( childTracker ); + } + else { + tracker = std::make_shared( nameAndLocation, ctx, ¤tTracker, size ); + currentTracker.addChild( tracker ); + } + + if( !ctx.completedCycle() && !tracker->isComplete() ) { + if( tracker->m_runState != ExecutingChildren && tracker->m_runState != NeedsAnotherRun ) + tracker->moveNext(); + tracker->open(); + } + + return *tracker; + } + + int IndexTracker::index() const { return m_index; } + + void IndexTracker::moveNext() { + m_index++; + m_children.clear(); + } + + void IndexTracker::close() { + TrackerBase::close(); + if( m_runState == CompletedSuccessfully && m_index < m_size-1 ) + m_runState = Executing; + } + +} // namespace TestCaseTracking + +using TestCaseTracking::ITracker; +using TestCaseTracking::TrackerContext; +using TestCaseTracking::SectionTracker; +using TestCaseTracking::IndexTracker; + +} // namespace Catch + +#if defined(__clang__) +# pragma clang diagnostic pop +#endif +// end catch_test_case_tracker.cpp +// start catch_test_registry.cpp + +namespace Catch { + + auto makeTestInvoker( void(*testAsFunction)() ) noexcept -> ITestInvoker* { + return new(std::nothrow) TestInvokerAsFunction( testAsFunction ); + } + + NameAndTags::NameAndTags( StringRef name_ , StringRef tags_ ) noexcept : name( name_ ), tags( tags_ ) {} + + AutoReg::AutoReg( ITestInvoker* invoker, SourceLineInfo const& lineInfo, StringRef classOrMethod, NameAndTags const& nameAndTags ) noexcept { + try { + getMutableRegistryHub() + .registerTest( + makeTestCase( + invoker, + extractClassName( classOrMethod ), + nameAndTags.name, + nameAndTags.tags, + lineInfo)); + } catch (...) { + // Do not throw when constructing global objects, instead register the exception to be processed later + getMutableRegistryHub().registerStartupException(); + } + } + + AutoReg::~AutoReg() = default; +} +// end catch_test_registry.cpp +// start catch_test_spec.cpp + +#include #include #include +#include + +namespace Catch { + + TestSpec::Pattern::~Pattern() = default; + TestSpec::NamePattern::~NamePattern() = default; + TestSpec::TagPattern::~TagPattern() = default; + TestSpec::ExcludedPattern::~ExcludedPattern() = default; + + TestSpec::NamePattern::NamePattern( std::string const& name ) + : m_wildcardPattern( toLower( name ), CaseSensitive::No ) + {} + bool TestSpec::NamePattern::matches( TestCaseInfo const& testCase ) const { + return m_wildcardPattern.matches( toLower( testCase.name ) ); + } + + TestSpec::TagPattern::TagPattern( std::string const& tag ) : m_tag( toLower( tag ) ) {} + bool TestSpec::TagPattern::matches( TestCaseInfo const& testCase ) const { + return std::find(begin(testCase.lcaseTags), + end(testCase.lcaseTags), + m_tag) != end(testCase.lcaseTags); + } + + TestSpec::ExcludedPattern::ExcludedPattern( PatternPtr const& underlyingPattern ) : m_underlyingPattern( underlyingPattern ) {} + bool TestSpec::ExcludedPattern::matches( TestCaseInfo const& testCase ) const { return !m_underlyingPattern->matches( testCase ); } + + bool TestSpec::Filter::matches( TestCaseInfo const& testCase ) const { + // All patterns in a filter must match for the filter to be a match + for( auto const& pattern : m_patterns ) { + if( !pattern->matches( testCase ) ) + return false; + } + return true; + } + + bool TestSpec::hasFilters() const { + return !m_filters.empty(); + } + bool TestSpec::matches( TestCaseInfo const& testCase ) const { + // A TestSpec matches if any filter matches + for( auto const& filter : m_filters ) + if( filter.matches( testCase ) ) + return true; + return false; + } +} +// end catch_test_spec.cpp +// start catch_test_spec_parser.cpp + +namespace Catch { + + TestSpecParser::TestSpecParser( ITagAliasRegistry const& tagAliases ) : m_tagAliases( &tagAliases ) {} + + TestSpecParser& TestSpecParser::parse( std::string const& arg ) { + m_mode = None; + m_exclusion = false; + m_start = std::string::npos; + m_arg = m_tagAliases->expandAliases( arg ); + m_escapeChars.clear(); + for( m_pos = 0; m_pos < m_arg.size(); ++m_pos ) + visitChar( m_arg[m_pos] ); + if( m_mode == Name ) + addPattern(); + return *this; + } + TestSpec TestSpecParser::testSpec() { + addFilter(); + return m_testSpec; + } + + void TestSpecParser::visitChar( char c ) { + if( m_mode == None ) { + switch( c ) { + case ' ': return; + case '~': m_exclusion = true; return; + case '[': return startNewMode( Tag, ++m_pos ); + case '"': return startNewMode( QuotedName, ++m_pos ); + case '\\': return escape(); + default: startNewMode( Name, m_pos ); break; + } + } + if( m_mode == Name ) { + if( c == ',' ) { + addPattern(); + addFilter(); + } + else if( c == '[' ) { + if( subString() == "exclude:" ) + m_exclusion = true; + else + addPattern(); + startNewMode( Tag, ++m_pos ); + } + else if( c == '\\' ) + escape(); + } + else if( m_mode == EscapedName ) + m_mode = Name; + else if( m_mode == QuotedName && c == '"' ) + addPattern(); + else if( m_mode == Tag && c == ']' ) + addPattern(); + } + void TestSpecParser::startNewMode( Mode mode, std::size_t start ) { + m_mode = mode; + m_start = start; + } + void TestSpecParser::escape() { + if( m_mode == None ) + m_start = m_pos; + m_mode = EscapedName; + m_escapeChars.push_back( m_pos ); + } + std::string TestSpecParser::subString() const { return m_arg.substr( m_start, m_pos - m_start ); } + + void TestSpecParser::addFilter() { + if( !m_currentFilter.m_patterns.empty() ) { + m_testSpec.m_filters.push_back( m_currentFilter ); + m_currentFilter = TestSpec::Filter(); + } + } + + TestSpec parseTestSpec( std::string const& arg ) { + return TestSpecParser( ITagAliasRegistry::get() ).parse( arg ).testSpec(); + } + +} // namespace Catch +// end catch_test_spec_parser.cpp +// start catch_timer.cpp + +#include + +namespace Catch { + + auto getCurrentNanosecondsSinceEpoch() -> uint64_t { + return std::chrono::duration_cast( std::chrono::high_resolution_clock::now().time_since_epoch() ).count(); + } + + auto estimateClockResolution() -> uint64_t { + uint64_t sum = 0; + static const uint64_t iterations = 1000000; + + for( std::size_t i = 0; i < iterations; ++i ) { + + uint64_t ticks; + uint64_t baseTicks = getCurrentNanosecondsSinceEpoch(); + do { + ticks = getCurrentNanosecondsSinceEpoch(); + } + while( ticks == baseTicks ); + + auto delta = ticks - baseTicks; + sum += delta; + } + + // We're just taking the mean, here. To do better we could take the std. dev and exclude outliers + // - and potentially do more iterations if there's a high variance. + return sum/iterations; + } + auto getEstimatedClockResolution() -> uint64_t { + static auto s_resolution = estimateClockResolution(); + return s_resolution; + } + + void Timer::start() { + m_nanoseconds = getCurrentNanosecondsSinceEpoch(); + } + auto Timer::getElapsedNanoseconds() const -> unsigned int { + return static_cast(getCurrentNanosecondsSinceEpoch() - m_nanoseconds); + } + auto Timer::getElapsedMicroseconds() const -> unsigned int { + return static_cast(getElapsedNanoseconds()/1000); + } + auto Timer::getElapsedMilliseconds() const -> unsigned int { + return static_cast(getElapsedMicroseconds()/1000); + } + auto Timer::getElapsedSeconds() const -> double { + return getElapsedMicroseconds()/1000000.0; + } + +} // namespace Catch +// end catch_timer.cpp +// start catch_tostring.cpp + +#if defined(__clang__) +# pragma clang diagnostic push +# pragma clang diagnostic ignored "-Wexit-time-destructors" +# pragma clang diagnostic ignored "-Wglobal-constructors" +#endif + #include +namespace Catch { + +namespace Detail { + + const std::string unprintableString = "{?}"; + + namespace { + const int hexThreshold = 255; + + struct Endianness { + enum Arch { Big, Little }; + + static Arch which() { + union _{ + int asInt; + char asChar[sizeof (int)]; + } u; + + u.asInt = 1; + return ( u.asChar[sizeof(int)-1] == 1 ) ? Big : Little; + } + }; + } + + std::string rawMemoryToString( const void *object, std::size_t size ) { + // Reverse order for little endian architectures + int i = 0, end = static_cast( size ), inc = 1; + if( Endianness::which() == Endianness::Little ) { + i = end-1; + end = inc = -1; + } + + unsigned char const *bytes = static_cast(object); + std::ostringstream os; + os << "0x" << std::setfill('0') << std::hex; + for( ; i != end; i += inc ) + os << std::setw(2) << static_cast(bytes[i]); + return os.str(); + } +} + +template +std::string fpToString( T value, int precision ) { + std::ostringstream oss; + oss << std::setprecision( precision ) + << std::fixed + << value; + std::string d = oss.str(); + std::size_t i = d.find_last_not_of( '0' ); + if( i != std::string::npos && i != d.size()-1 ) { + if( d[i] == '.' ) + i++; + d = d.substr( 0, i+1 ); + } + return d; +} + +//// ======================================================= //// +// +// Out-of-line defs for full specialization of StringMaker +// +//// ======================================================= //// + +std::string StringMaker::convert(const std::string& str) { + if (!getCurrentContext().getConfig()->showInvisibles()) { + return '"' + str + '"'; + } + + std::string s("\""); + for (char c : str) { + switch (c) { + case '\n': + s.append("\\n"); + break; + case '\t': + s.append("\\t"); + break; + default: + s.push_back(c); + break; + } + } + s.append("\""); + return s; +} + +std::string StringMaker::convert(const std::wstring& wstr) { + std::string s; + s.reserve(wstr.size()); + for (auto c : wstr) { + s += (c <= 0xff) ? static_cast(c) : '?'; + } + return ::Catch::Detail::stringify(s); +} + +std::string StringMaker::convert(char const* str) { + if (str) { + return ::Catch::Detail::stringify(std::string{ str }); + } else { + return{ "{null string}" }; + } +} +std::string StringMaker::convert(char* str) { + if (str) { + return ::Catch::Detail::stringify(std::string{ str }); + } else { + return{ "{null string}" }; + } +} +std::string StringMaker::convert(wchar_t const * str) { + if (str) { + return ::Catch::Detail::stringify(std::wstring{ str }); + } else { + return{ "{null string}" }; + } +} +std::string StringMaker::convert(wchar_t * str) { + if (str) { + return ::Catch::Detail::stringify(std::wstring{ str }); + } else { + return{ "{null string}" }; + } +} + +std::string StringMaker::convert(int value) { + return ::Catch::Detail::stringify(static_cast(value)); +} +std::string StringMaker::convert(long value) { + return ::Catch::Detail::stringify(static_cast(value)); +} +std::string StringMaker::convert(long long value) { + std::ostringstream oss; + oss << value; + if (value > Detail::hexThreshold) { + oss << " (0x" << std::hex << value << ')'; + } + return oss.str(); +} + +std::string StringMaker::convert(unsigned int value) { + return ::Catch::Detail::stringify(static_cast(value)); +} +std::string StringMaker::convert(unsigned long value) { + return ::Catch::Detail::stringify(static_cast(value)); +} +std::string StringMaker::convert(unsigned long long value) { + std::ostringstream oss; + oss << value; + if (value > Detail::hexThreshold) { + oss << " (0x" << std::hex << value << ')'; + } + return oss.str(); +} + +std::string StringMaker::convert(bool b) { + return b ? "true" : "false"; +} + +std::string StringMaker::convert(char value) { + if (value == '\r') { + return "'\\r'"; + } else if (value == '\f') { + return "'\\f'"; + } else if (value == '\n') { + return "'\\n'"; + } else if (value == '\t') { + return "'\\t'"; + } else if ('\0' <= value && value < ' ') { + return ::Catch::Detail::stringify(static_cast(value)); + } else { + char chstr[] = "' '"; + chstr[1] = value; + return chstr; + } +} +std::string StringMaker::convert(signed char c) { + return ::Catch::Detail::stringify(static_cast(c)); +} +std::string StringMaker::convert(unsigned char c) { + return ::Catch::Detail::stringify(static_cast(c)); +} + +std::string StringMaker::convert(std::nullptr_t) { + return "nullptr"; +} + +std::string StringMaker::convert(float value) { + return fpToString(value, 5) + 'f'; +} +std::string StringMaker::convert(double value) { + return fpToString(value, 10); +} + +} // end namespace Catch + +#if defined(__clang__) +# pragma clang diagnostic pop +#endif + +// end catch_tostring.cpp +// start catch_totals.cpp + +namespace Catch { + + Counts Counts::operator - ( Counts const& other ) const { + Counts diff; + diff.passed = passed - other.passed; + diff.failed = failed - other.failed; + diff.failedButOk = failedButOk - other.failedButOk; + return diff; + } + + Counts& Counts::operator += ( Counts const& other ) { + passed += other.passed; + failed += other.failed; + failedButOk += other.failedButOk; + return *this; + } + + std::size_t Counts::total() const { + return passed + failed + failedButOk; + } + bool Counts::allPassed() const { + return failed == 0 && failedButOk == 0; + } + bool Counts::allOk() const { + return failed == 0; + } + + Totals Totals::operator - ( Totals const& other ) const { + Totals diff; + diff.assertions = assertions - other.assertions; + diff.testCases = testCases - other.testCases; + return diff; + } + + Totals& Totals::operator += ( Totals const& other ) { + assertions += other.assertions; + testCases += other.testCases; + return *this; + } + + Totals Totals::delta( Totals const& prevTotals ) const { + Totals diff = *this - prevTotals; + if( diff.assertions.failed > 0 ) + ++diff.testCases.failed; + else if( diff.assertions.failedButOk > 0 ) + ++diff.testCases.failedButOk; + else + ++diff.testCases.passed; + return diff; + } + +} +// end catch_totals.cpp +// start catch_version.cpp + +#include + +namespace Catch { + + Version::Version + ( unsigned int _majorVersion, + unsigned int _minorVersion, + unsigned int _patchNumber, + char const * const _branchName, + unsigned int _buildNumber ) + : majorVersion( _majorVersion ), + minorVersion( _minorVersion ), + patchNumber( _patchNumber ), + branchName( _branchName ), + buildNumber( _buildNumber ) + {} + + std::ostream& operator << ( std::ostream& os, Version const& version ) { + os << version.majorVersion << '.' + << version.minorVersion << '.' + << version.patchNumber; + // branchName is never null -> 0th char is \0 if it is empty + if (version.branchName[0]) { + os << '-' << version.branchName + << '.' << version.buildNumber; + } + return os; + } + + Version const& libraryVersion() { + static Version version( 2, 0, 1, "", 0 ); + return version; + } + +} +// end catch_version.cpp +// start catch_wildcard_pattern.cpp + +namespace Catch { + + WildcardPattern::WildcardPattern( std::string const& pattern, + CaseSensitive::Choice caseSensitivity ) + : m_caseSensitivity( caseSensitivity ), + m_pattern( adjustCase( pattern ) ) + { + if( startsWith( m_pattern, '*' ) ) { + m_pattern = m_pattern.substr( 1 ); + m_wildcard = WildcardAtStart; + } + if( endsWith( m_pattern, '*' ) ) { + m_pattern = m_pattern.substr( 0, m_pattern.size()-1 ); + m_wildcard = static_cast( m_wildcard | WildcardAtEnd ); + } + } + + bool WildcardPattern::matches( std::string const& str ) const { + switch( m_wildcard ) { + case NoWildcard: + return m_pattern == adjustCase( str ); + case WildcardAtStart: + return endsWith( adjustCase( str ), m_pattern ); + case WildcardAtEnd: + return startsWith( adjustCase( str ), m_pattern ); + case WildcardAtBothEnds: + return contains( adjustCase( str ), m_pattern ); + default: + CATCH_INTERNAL_ERROR( "Unknown enum" ); + } + } + + std::string WildcardPattern::adjustCase( std::string const& str ) const { + return m_caseSensitivity == CaseSensitive::No ? toLower( str ) : str; + } +} +// end catch_wildcard_pattern.cpp +// start catch_xmlwriter.cpp + +// start catch_xmlwriter.h + +#include +#include + namespace Catch { class XmlEncode { public: enum ForWhat { ForTextNodes, ForAttributes }; - XmlEncode( std::string const& str, ForWhat forWhat = ForTextNodes ) - : m_str( str ), - m_forWhat( forWhat ) - {} + XmlEncode( std::string const& str, ForWhat forWhat = ForTextNodes ); - void encodeTo( std::ostream& os ) const { + void encodeTo( std::ostream& os ) const; - // Apostrophe escaping not necessary if we always use " to write attributes - // (see: http://www.w3.org/TR/xml/#syntax) - - for( std::size_t i = 0; i < m_str.size(); ++ i ) { - char c = m_str[i]; - switch( c ) { - case '<': os << "<"; break; - case '&': os << "&"; break; - - case '>': - // See: http://www.w3.org/TR/xml/#syntax - if( i > 2 && m_str[i-1] == ']' && m_str[i-2] == ']' ) - os << ">"; - else - os << c; - break; - - case '\"': - if( m_forWhat == ForAttributes ) - os << """; - else - os << c; - break; - - default: - // Escape control chars - based on contribution by @espenalb in PR #465 and - // by @mrpi PR #588 - if ( ( c >= 0 && c < '\x09' ) || ( c > '\x0D' && c < '\x20') || c=='\x7F' ) { - // see http://stackoverflow.com/questions/404107/why-are-control-characters-illegal-in-xml-1-0 - os << "\\x" << std::uppercase << std::hex << std::setfill('0') << std::setw(2) - << static_cast( c ); - } - else - os << c; - } - } - } - - friend std::ostream& operator << ( std::ostream& os, XmlEncode const& xmlEncode ) { - xmlEncode.encodeTo( os ); - return os; - } + friend std::ostream& operator << ( std::ostream& os, XmlEncode const& xmlEncode ); private: std::string m_str; @@ -9966,24 +9900,14 @@ namespace Catch { class ScopedElement { public: - ScopedElement( XmlWriter* writer ) - : m_writer( writer ) - {} + ScopedElement( XmlWriter* writer ); - ScopedElement( ScopedElement const& other ) - : m_writer( other.m_writer ){ - other.m_writer = CATCH_NULL; - } + ScopedElement( ScopedElement&& other ) noexcept; + ScopedElement& operator=( ScopedElement&& other ) noexcept; - ~ScopedElement() { - if( m_writer ) - m_writer->endElement(); - } + ~ScopedElement(); - ScopedElement& writeText( std::string const& text, bool indent = true ) { - m_writer->writeText( text, indent ); - return *this; - } + ScopedElement& writeText( std::string const& text, bool indent = true ); template ScopedElement& writeAttribute( std::string const& name, T const& attribute ) { @@ -9992,622 +9916,790 @@ namespace Catch { } private: - mutable XmlWriter* m_writer; + mutable XmlWriter* m_writer = nullptr; }; - XmlWriter() - : m_tagIsOpen( false ), - m_needsNewline( false ), - m_os( Catch::cout() ) - { - writeDeclaration(); - } + XmlWriter( std::ostream& os = Catch::cout() ); + ~XmlWriter(); - XmlWriter( std::ostream& os ) - : m_tagIsOpen( false ), - m_needsNewline( false ), - m_os( os ) - { - writeDeclaration(); - } + XmlWriter( XmlWriter const& ) = delete; + XmlWriter& operator=( XmlWriter const& ) = delete; - ~XmlWriter() { - while( !m_tags.empty() ) - endElement(); - } + XmlWriter& startElement( std::string const& name ); - XmlWriter& startElement( std::string const& name ) { - ensureTagClosed(); - newlineIfNecessary(); - m_os << m_indent << '<' << name; - m_tags.push_back( name ); - m_indent += " "; - m_tagIsOpen = true; - return *this; - } + ScopedElement scopedElement( std::string const& name ); - ScopedElement scopedElement( std::string const& name ) { - ScopedElement scoped( this ); - startElement( name ); - return scoped; - } + XmlWriter& endElement(); - XmlWriter& endElement() { - newlineIfNecessary(); - m_indent = m_indent.substr( 0, m_indent.size()-2 ); - if( m_tagIsOpen ) { - m_os << "/>"; - m_tagIsOpen = false; - } - else { - m_os << m_indent << ""; - } - m_os << std::endl; - m_tags.pop_back(); - return *this; - } + XmlWriter& writeAttribute( std::string const& name, std::string const& attribute ); - XmlWriter& writeAttribute( std::string const& name, std::string const& attribute ) { - if( !name.empty() && !attribute.empty() ) - m_os << ' ' << name << "=\"" << XmlEncode( attribute, XmlEncode::ForAttributes ) << '"'; - return *this; - } - - XmlWriter& writeAttribute( std::string const& name, bool attribute ) { - m_os << ' ' << name << "=\"" << ( attribute ? "true" : "false" ) << '"'; - return *this; - } + XmlWriter& writeAttribute( std::string const& name, bool attribute ); template XmlWriter& writeAttribute( std::string const& name, T const& attribute ) { - std::ostringstream oss; - oss << attribute; - return writeAttribute( name, oss.str() ); + m_oss.clear(); + m_oss.str(std::string()); + m_oss << attribute; + return writeAttribute( name, m_oss.str() ); } - XmlWriter& writeText( std::string const& text, bool indent = true ) { - if( !text.empty() ){ - bool tagWasOpen = m_tagIsOpen; - ensureTagClosed(); - if( tagWasOpen && indent ) - m_os << m_indent; - m_os << XmlEncode( text ); - m_needsNewline = true; - } - return *this; - } + XmlWriter& writeText( std::string const& text, bool indent = true ); - XmlWriter& writeComment( std::string const& text ) { - ensureTagClosed(); - m_os << m_indent << ""; - m_needsNewline = true; - return *this; - } + XmlWriter& writeComment( std::string const& text ); - void writeStylesheetRef( std::string const& url ) { - m_os << "\n"; - } + void writeStylesheetRef( std::string const& url ); - XmlWriter& writeBlankLine() { - ensureTagClosed(); - m_os << '\n'; - return *this; - } + XmlWriter& writeBlankLine(); - void ensureTagClosed() { - if( m_tagIsOpen ) { - m_os << ">" << std::endl; - m_tagIsOpen = false; - } - } + void ensureTagClosed(); private: - XmlWriter( XmlWriter const& ); - void operator=( XmlWriter const& ); - void writeDeclaration() { - m_os << "\n"; - } + void writeDeclaration(); - void newlineIfNecessary() { - if( m_needsNewline ) { - m_os << std::endl; - m_needsNewline = false; - } - } + void newlineIfNecessary(); - bool m_tagIsOpen; - bool m_needsNewline; + bool m_tagIsOpen = false; + bool m_needsNewline = false; std::vector m_tags; std::string m_indent; std::ostream& m_os; + std::ostringstream m_oss; }; } -namespace Catch { - class XmlReporter : public StreamingReporterBase { - public: - XmlReporter( ReporterConfig const& _config ) - : StreamingReporterBase( _config ), - m_xml(_config.stream()), - m_sectionDepth( 0 ) - { - m_reporterPrefs.shouldRedirectStdOut = true; - } +// end catch_xmlwriter.h +#include - virtual ~XmlReporter() CATCH_OVERRIDE; +namespace Catch { + + XmlEncode::XmlEncode( std::string const& str, ForWhat forWhat ) + : m_str( str ), + m_forWhat( forWhat ) + {} + + void XmlEncode::encodeTo( std::ostream& os ) const { + + // Apostrophe escaping not necessary if we always use " to write attributes + // (see: http://www.w3.org/TR/xml/#syntax) + + for( std::size_t i = 0; i < m_str.size(); ++ i ) { + char c = m_str[i]; + switch( c ) { + case '<': os << "<"; break; + case '&': os << "&"; break; + + case '>': + // See: http://www.w3.org/TR/xml/#syntax + if( i > 2 && m_str[i-1] == ']' && m_str[i-2] == ']' ) + os << ">"; + else + os << c; + break; + + case '\"': + if( m_forWhat == ForAttributes ) + os << """; + else + os << c; + break; + + default: + // Escape control chars - based on contribution by @espenalb in PR #465 and + // by @mrpi PR #588 + if ( ( c >= 0 && c < '\x09' ) || ( c > '\x0D' && c < '\x20') || c=='\x7F' ) { + // see http://stackoverflow.com/questions/404107/why-are-control-characters-illegal-in-xml-1-0 + os << "\\x" << std::uppercase << std::hex << std::setfill('0') << std::setw(2) + << static_cast( c ); + } + else + os << c; + } + } + } + + std::ostream& operator << ( std::ostream& os, XmlEncode const& xmlEncode ) { + xmlEncode.encodeTo( os ); + return os; + } + + XmlWriter::ScopedElement::ScopedElement( XmlWriter* writer ) + : m_writer( writer ) + {} + + XmlWriter::ScopedElement::ScopedElement( ScopedElement&& other ) noexcept + : m_writer( other.m_writer ){ + other.m_writer = nullptr; + } + XmlWriter::ScopedElement& XmlWriter::ScopedElement::operator=( ScopedElement&& other ) noexcept { + if ( m_writer ) { + m_writer->endElement(); + } + m_writer = other.m_writer; + other.m_writer = nullptr; + return *this; + } + + XmlWriter::ScopedElement::~ScopedElement() { + if( m_writer ) + m_writer->endElement(); + } + + XmlWriter::ScopedElement& XmlWriter::ScopedElement::writeText( std::string const& text, bool indent ) { + m_writer->writeText( text, indent ); + return *this; + } + + XmlWriter::XmlWriter( std::ostream& os ) : m_os( os ) + { + writeDeclaration(); + } + + XmlWriter::~XmlWriter() { + while( !m_tags.empty() ) + endElement(); + } + + XmlWriter& XmlWriter::startElement( std::string const& name ) { + ensureTagClosed(); + newlineIfNecessary(); + m_os << m_indent << '<' << name; + m_tags.push_back( name ); + m_indent += " "; + m_tagIsOpen = true; + return *this; + } + + XmlWriter::ScopedElement XmlWriter::scopedElement( std::string const& name ) { + ScopedElement scoped( this ); + startElement( name ); + return scoped; + } + + XmlWriter& XmlWriter::endElement() { + newlineIfNecessary(); + m_indent = m_indent.substr( 0, m_indent.size()-2 ); + if( m_tagIsOpen ) { + m_os << "/>"; + m_tagIsOpen = false; + } + else { + m_os << m_indent << ""; + } + m_os << std::endl; + m_tags.pop_back(); + return *this; + } + + XmlWriter& XmlWriter::writeAttribute( std::string const& name, std::string const& attribute ) { + if( !name.empty() && !attribute.empty() ) + m_os << ' ' << name << "=\"" << XmlEncode( attribute, XmlEncode::ForAttributes ) << '"'; + return *this; + } + + XmlWriter& XmlWriter::writeAttribute( std::string const& name, bool attribute ) { + m_os << ' ' << name << "=\"" << ( attribute ? "true" : "false" ) << '"'; + return *this; + } + + XmlWriter& XmlWriter::writeText( std::string const& text, bool indent ) { + if( !text.empty() ){ + bool tagWasOpen = m_tagIsOpen; + ensureTagClosed(); + if( tagWasOpen && indent ) + m_os << m_indent; + m_os << XmlEncode( text ); + m_needsNewline = true; + } + return *this; + } + + XmlWriter& XmlWriter::writeComment( std::string const& text ) { + ensureTagClosed(); + m_os << m_indent << ""; + m_needsNewline = true; + return *this; + } + + void XmlWriter::writeStylesheetRef( std::string const& url ) { + m_os << "\n"; + } + + XmlWriter& XmlWriter::writeBlankLine() { + ensureTagClosed(); + m_os << '\n'; + return *this; + } + + void XmlWriter::ensureTagClosed() { + if( m_tagIsOpen ) { + m_os << ">" << std::endl; + m_tagIsOpen = false; + } + } + + void XmlWriter::writeDeclaration() { + m_os << "\n"; + } + + void XmlWriter::newlineIfNecessary() { + if( m_needsNewline ) { + m_os << std::endl; + m_needsNewline = false; + } + } +} +// end catch_xmlwriter.cpp +// start catch_reporter_bases.cpp + +#include +#include +#include +#include +#include + +namespace Catch { + void prepareExpandedExpression(AssertionResult& result) { + result.getExpandedExpression(); + } + + // Because formatting using c++ streams is stateful, drop down to C is required + // Alternatively we could use stringstream, but its performance is... not good. + std::string getFormattedDuration( double duration ) { + // Max exponent + 1 is required to represent the whole part + // + 1 for decimal point + // + 3 for the 3 decimal places + // + 1 for null terminator + const std::size_t maxDoubleSize = DBL_MAX_10_EXP + 1 + 1 + 3 + 1; + char buffer[maxDoubleSize]; + + // Save previous errno, to prevent sprintf from overwriting it + ErrnoGuard guard; +#ifdef _MSC_VER + sprintf_s(buffer, "%.3f", duration); +#else + sprintf(buffer, "%.3f", duration); +#endif + return std::string(buffer); + } + + TestEventListenerBase::TestEventListenerBase(ReporterConfig const & _config) + :StreamingReporterBase(_config) {} + + void TestEventListenerBase::assertionStarting(AssertionInfo const &) {} + + bool TestEventListenerBase::assertionEnded(AssertionStats const &) { + return false; + } + +} // end namespace Catch +// end catch_reporter_bases.cpp +// start catch_reporter_compact.cpp + +namespace { + +#ifdef CATCH_PLATFORM_MAC + const char* failedString() { return "FAILED"; } + const char* passedString() { return "PASSED"; } +#else + const char* failedString() { return "failed"; } + const char* passedString() { return "passed"; } +#endif + + // Colour::LightGrey + Catch::Colour::Code dimColour() { return Catch::Colour::FileName; } + + std::string bothOrAll( std::size_t count ) { + return count == 1 ? std::string() : + count == 2 ? "both " : "all " ; + } +} + +namespace Catch { + + struct CompactReporter : StreamingReporterBase { + + using StreamingReporterBase::StreamingReporterBase; + + ~CompactReporter() override; static std::string getDescription() { - return "Reports test results as an XML document"; + return "Reports test results on a single line, suitable for IDEs"; } - virtual std::string getStylesheetRef() const { - return std::string(); + ReporterPreferences getPreferences() const override { + ReporterPreferences prefs; + prefs.shouldRedirectStdOut = false; + return prefs; } - void writeSourceInfo( SourceLineInfo const& sourceInfo ) { - m_xml - .writeAttribute( "filename", sourceInfo.file ) - .writeAttribute( "line", sourceInfo.line ); + void noMatchingTestCases( std::string const& spec ) override { + stream << "No test cases matched '" << spec << '\'' << std::endl; } - public: // StreamingReporterBase + void assertionStarting( AssertionInfo const& ) override {} - virtual void noMatchingTestCases( std::string const& s ) CATCH_OVERRIDE { - StreamingReporterBase::noMatchingTestCases( s ); + bool assertionEnded( AssertionStats const& _assertionStats ) override { + AssertionResult const& result = _assertionStats.assertionResult; + + bool printInfoMessages = true; + + // Drop out if result was successful and we're not printing those + if( !m_config->includeSuccessfulResults() && result.isOk() ) { + if( result.getResultType() != ResultWas::Warning ) + return false; + printInfoMessages = false; + } + + AssertionPrinter printer( stream, _assertionStats, printInfoMessages ); + printer.print(); + + stream << std::endl; + return true; } - virtual void testRunStarting( TestRunInfo const& testInfo ) CATCH_OVERRIDE { - StreamingReporterBase::testRunStarting( testInfo ); - std::string stylesheetRef = getStylesheetRef(); - if( !stylesheetRef.empty() ) - m_xml.writeStylesheetRef( stylesheetRef ); - m_xml.startElement( "Catch" ); - if( !m_config->name().empty() ) - m_xml.writeAttribute( "name", m_config->name() ); - } - - virtual void testGroupStarting( GroupInfo const& groupInfo ) CATCH_OVERRIDE { - StreamingReporterBase::testGroupStarting( groupInfo ); - m_xml.startElement( "Group" ) - .writeAttribute( "name", groupInfo.name ); - } - - virtual void testCaseStarting( TestCaseInfo const& testInfo ) CATCH_OVERRIDE { - StreamingReporterBase::testCaseStarting(testInfo); - m_xml.startElement( "TestCase" ) - .writeAttribute( "name", trim( testInfo.name ) ) - .writeAttribute( "description", testInfo.description ) - .writeAttribute( "tags", testInfo.tagsAsString ); - - writeSourceInfo( testInfo.lineInfo ); - - if ( m_config->showDurations() == ShowDurations::Always ) - m_testCaseTimer.start(); - m_xml.ensureTagClosed(); - } - - virtual void sectionStarting( SectionInfo const& sectionInfo ) CATCH_OVERRIDE { - StreamingReporterBase::sectionStarting( sectionInfo ); - if( m_sectionDepth++ > 0 ) { - m_xml.startElement( "Section" ) - .writeAttribute( "name", trim( sectionInfo.name ) ) - .writeAttribute( "description", sectionInfo.description ); - writeSourceInfo( sectionInfo.lineInfo ); - m_xml.ensureTagClosed(); + void sectionEnded(SectionStats const& _sectionStats) override { + if (m_config->showDurations() == ShowDurations::Always) { + stream << getFormattedDuration(_sectionStats.durationInSeconds) << " s: " << _sectionStats.sectionInfo.name << std::endl; } } - virtual void assertionStarting( AssertionInfo const& ) CATCH_OVERRIDE { } + void testRunEnded( TestRunStats const& _testRunStats ) override { + printTotals( _testRunStats.totals ); + stream << '\n' << std::endl; + StreamingReporterBase::testRunEnded( _testRunStats ); + } - virtual bool assertionEnded( AssertionStats const& assertionStats ) CATCH_OVERRIDE { + private: + class AssertionPrinter { + public: + AssertionPrinter& operator= ( AssertionPrinter const& ) = delete; + AssertionPrinter( AssertionPrinter const& ) = delete; + AssertionPrinter( std::ostream& _stream, AssertionStats const& _stats, bool _printInfoMessages ) + : stream( _stream ) + , result( _stats.assertionResult ) + , messages( _stats.infoMessages ) + , itMessage( _stats.infoMessages.begin() ) + , printInfoMessages( _printInfoMessages ) + {} - AssertionResult const& result = assertionStats.assertionResult; + void print() { + printSourceInfo(); - bool includeResults = m_config->includeSuccessfulResults() || !result.isOk(); + itMessage = messages.begin(); - if( includeResults ) { - // Print any info messages in tags. - for( std::vector::const_iterator it = assertionStats.infoMessages.begin(), itEnd = assertionStats.infoMessages.end(); - it != itEnd; - ++it ) { - if( it->type == ResultWas::Info ) { - m_xml.scopedElement( "Info" ) - .writeText( it->message ); - } else if ( it->type == ResultWas::Warning ) { - m_xml.scopedElement( "Warning" ) - .writeText( it->message ); + switch( result.getResultType() ) { + case ResultWas::Ok: + printResultType( Colour::ResultSuccess, passedString() ); + printOriginalExpression(); + printReconstructedExpression(); + if ( ! result.hasExpression() ) + printRemainingMessages( Colour::None ); + else + printRemainingMessages(); + break; + case ResultWas::ExpressionFailed: + if( result.isOk() ) + printResultType( Colour::ResultSuccess, failedString() + std::string( " - but was ok" ) ); + else + printResultType( Colour::Error, failedString() ); + printOriginalExpression(); + printReconstructedExpression(); + printRemainingMessages(); + break; + case ResultWas::ThrewException: + printResultType( Colour::Error, failedString() ); + printIssue( "unexpected exception with message:" ); + printMessage(); + printExpressionWas(); + printRemainingMessages(); + break; + case ResultWas::FatalErrorCondition: + printResultType( Colour::Error, failedString() ); + printIssue( "fatal error condition with message:" ); + printMessage(); + printExpressionWas(); + printRemainingMessages(); + break; + case ResultWas::DidntThrowException: + printResultType( Colour::Error, failedString() ); + printIssue( "expected exception, got none" ); + printExpressionWas(); + printRemainingMessages(); + break; + case ResultWas::Info: + printResultType( Colour::None, "info" ); + printMessage(); + printRemainingMessages(); + break; + case ResultWas::Warning: + printResultType( Colour::None, "warning" ); + printMessage(); + printRemainingMessages(); + break; + case ResultWas::ExplicitFailure: + printResultType( Colour::Error, failedString() ); + printIssue( "explicitly" ); + printRemainingMessages( Colour::None ); + break; + // These cases are here to prevent compiler warnings + case ResultWas::Unknown: + case ResultWas::FailureBit: + case ResultWas::Exception: + printResultType( Colour::Error, "** internal error **" ); + break; + } + } + + private: + void printSourceInfo() const { + Colour colourGuard( Colour::FileName ); + stream << result.getSourceInfo() << ':'; + } + + void printResultType( Colour::Code colour, std::string const& passOrFail ) const { + if( !passOrFail.empty() ) { + { + Colour colourGuard( colour ); + stream << ' ' << passOrFail; + } + stream << ':'; + } + } + + void printIssue( std::string const& issue ) const { + stream << ' ' << issue; + } + + void printExpressionWas() { + if( result.hasExpression() ) { + stream << ';'; + { + Colour colour( dimColour() ); + stream << " expression was:"; + } + printOriginalExpression(); + } + } + + void printOriginalExpression() const { + if( result.hasExpression() ) { + stream << ' ' << result.getExpression(); + } + } + + void printReconstructedExpression() const { + if( result.hasExpandedExpression() ) { + { + Colour colour( dimColour() ); + stream << " for: "; + } + stream << result.getExpandedExpression(); + } + } + + void printMessage() { + if ( itMessage != messages.end() ) { + stream << " '" << itMessage->message << '\''; + ++itMessage; + } + } + + void printRemainingMessages( Colour::Code colour = dimColour() ) { + if ( itMessage == messages.end() ) + return; + + // using messages.end() directly yields (or auto) compilation error: + std::vector::const_iterator itEnd = messages.end(); + const std::size_t N = static_cast( std::distance( itMessage, itEnd ) ); + + { + Colour colourGuard( colour ); + stream << " with " << pluralise( N, "message" ) << ':'; + } + + for(; itMessage != itEnd; ) { + // If this assertion is a warning ignore any INFO messages + if( printInfoMessages || itMessage->type != ResultWas::Info ) { + stream << " '" << itMessage->message << '\''; + if ( ++itMessage != itEnd ) { + Colour colourGuard( dimColour() ); + stream << " and"; + } } } } - // Drop out if result was successful but we're not printing them. - if( !includeResults && result.getResultType() != ResultWas::Warning ) - return true; + private: + std::ostream& stream; + AssertionResult const& result; + std::vector messages; + std::vector::const_iterator itMessage; + bool printInfoMessages; + }; - // Print the expression if there is one. - if( result.hasExpression() ) { - m_xml.startElement( "Expression" ) - .writeAttribute( "success", result.succeeded() ) - .writeAttribute( "type", result.getTestMacroName() ); + // Colour, message variants: + // - white: No tests ran. + // - red: Failed [both/all] N test cases, failed [both/all] M assertions. + // - white: Passed [both/all] N test cases (no assertions). + // - red: Failed N tests cases, failed M assertions. + // - green: Passed [both/all] N tests cases with M assertions. - writeSourceInfo( result.getSourceInfo() ); - - m_xml.scopedElement( "Original" ) - .writeText( result.getExpression() ); - m_xml.scopedElement( "Expanded" ) - .writeText( result.getExpandedExpression() ); + void printTotals( const Totals& totals ) const { + if( totals.testCases.total() == 0 ) { + stream << "No tests ran."; } - - // And... Print a result applicable to each result type. - switch( result.getResultType() ) { - case ResultWas::ThrewException: - m_xml.startElement( "Exception" ); - writeSourceInfo( result.getSourceInfo() ); - m_xml.writeText( result.getMessage() ); - m_xml.endElement(); - break; - case ResultWas::FatalErrorCondition: - m_xml.startElement( "FatalErrorCondition" ); - writeSourceInfo( result.getSourceInfo() ); - m_xml.writeText( result.getMessage() ); - m_xml.endElement(); - break; - case ResultWas::Info: - m_xml.scopedElement( "Info" ) - .writeText( result.getMessage() ); - break; - case ResultWas::Warning: - // Warning will already have been written - break; - case ResultWas::ExplicitFailure: - m_xml.startElement( "Failure" ); - writeSourceInfo( result.getSourceInfo() ); - m_xml.writeText( result.getMessage() ); - m_xml.endElement(); - break; - default: - break; + else if( totals.testCases.failed == totals.testCases.total() ) { + Colour colour( Colour::ResultError ); + const std::string qualify_assertions_failed = + totals.assertions.failed == totals.assertions.total() ? + bothOrAll( totals.assertions.failed ) : std::string(); + stream << + "Failed " << bothOrAll( totals.testCases.failed ) + << pluralise( totals.testCases.failed, "test case" ) << ", " + "failed " << qualify_assertions_failed << + pluralise( totals.assertions.failed, "assertion" ) << '.'; } - - if( result.hasExpression() ) - m_xml.endElement(); - - return true; - } - - virtual void sectionEnded( SectionStats const& sectionStats ) CATCH_OVERRIDE { - StreamingReporterBase::sectionEnded( sectionStats ); - if( --m_sectionDepth > 0 ) { - XmlWriter::ScopedElement e = m_xml.scopedElement( "OverallResults" ); - e.writeAttribute( "successes", sectionStats.assertions.passed ); - e.writeAttribute( "failures", sectionStats.assertions.failed ); - e.writeAttribute( "expectedFailures", sectionStats.assertions.failedButOk ); - - if ( m_config->showDurations() == ShowDurations::Always ) - e.writeAttribute( "durationInSeconds", sectionStats.durationInSeconds ); - - m_xml.endElement(); + else if( totals.assertions.total() == 0 ) { + stream << + "Passed " << bothOrAll( totals.testCases.total() ) + << pluralise( totals.testCases.total(), "test case" ) + << " (no assertions)."; + } + else if( totals.assertions.failed ) { + Colour colour( Colour::ResultError ); + stream << + "Failed " << pluralise( totals.testCases.failed, "test case" ) << ", " + "failed " << pluralise( totals.assertions.failed, "assertion" ) << '.'; + } + else { + Colour colour( Colour::ResultSuccess ); + stream << + "Passed " << bothOrAll( totals.testCases.passed ) + << pluralise( totals.testCases.passed, "test case" ) << + " with " << pluralise( totals.assertions.passed, "assertion" ) << '.'; } } - - virtual void testCaseEnded( TestCaseStats const& testCaseStats ) CATCH_OVERRIDE { - StreamingReporterBase::testCaseEnded( testCaseStats ); - XmlWriter::ScopedElement e = m_xml.scopedElement( "OverallResult" ); - e.writeAttribute( "success", testCaseStats.totals.assertions.allOk() ); - - if ( m_config->showDurations() == ShowDurations::Always ) - e.writeAttribute( "durationInSeconds", m_testCaseTimer.getElapsedSeconds() ); - - if( !testCaseStats.stdOut.empty() ) - m_xml.scopedElement( "StdOut" ).writeText( trim( testCaseStats.stdOut ), false ); - if( !testCaseStats.stdErr.empty() ) - m_xml.scopedElement( "StdErr" ).writeText( trim( testCaseStats.stdErr ), false ); - - m_xml.endElement(); - } - - virtual void testGroupEnded( TestGroupStats const& testGroupStats ) CATCH_OVERRIDE { - StreamingReporterBase::testGroupEnded( testGroupStats ); - // TODO: Check testGroupStats.aborting and act accordingly. - m_xml.scopedElement( "OverallResults" ) - .writeAttribute( "successes", testGroupStats.totals.assertions.passed ) - .writeAttribute( "failures", testGroupStats.totals.assertions.failed ) - .writeAttribute( "expectedFailures", testGroupStats.totals.assertions.failedButOk ); - m_xml.endElement(); - } - - virtual void testRunEnded( TestRunStats const& testRunStats ) CATCH_OVERRIDE { - StreamingReporterBase::testRunEnded( testRunStats ); - m_xml.scopedElement( "OverallResults" ) - .writeAttribute( "successes", testRunStats.totals.assertions.passed ) - .writeAttribute( "failures", testRunStats.totals.assertions.failed ) - .writeAttribute( "expectedFailures", testRunStats.totals.assertions.failedButOk ); - m_xml.endElement(); - } - - private: - Timer m_testCaseTimer; - XmlWriter m_xml; - int m_sectionDepth; }; - INTERNAL_CATCH_REGISTER_REPORTER( "xml", XmlReporter ) + CompactReporter::~CompactReporter() {} + + CATCH_REGISTER_REPORTER( "compact", CompactReporter ) } // end namespace Catch - -// #included from: ../reporters/catch_reporter_junit.hpp -#define TWOBLUECUBES_CATCH_REPORTER_JUNIT_HPP_INCLUDED - -#include - -namespace Catch { - - namespace { - std::string getCurrentTimestamp() { - // Beware, this is not reentrant because of backward compatibility issues - // Also, UTC only, again because of backward compatibility (%z is C++11) - time_t rawtime; - std::time(&rawtime); - const size_t timeStampSize = sizeof("2017-01-16T17:06:45Z"); - -#ifdef _MSC_VER - std::tm timeInfo = {}; - gmtime_s(&timeInfo, &rawtime); -#else - std::tm* timeInfo; - timeInfo = std::gmtime(&rawtime); -#endif - - char timeStamp[timeStampSize]; - const char * const fmt = "%Y-%m-%dT%H:%M:%SZ"; - -#ifdef _MSC_VER - std::strftime(timeStamp, timeStampSize, fmt, &timeInfo); -#else - std::strftime(timeStamp, timeStampSize, fmt, timeInfo); -#endif - return std::string(timeStamp); - } - - } - - class JunitReporter : public CumulativeReporterBase { - public: - JunitReporter( ReporterConfig const& _config ) - : CumulativeReporterBase( _config ), - xml( _config.stream() ), - unexpectedExceptions( 0 ), - m_okToFail( false ) - { - m_reporterPrefs.shouldRedirectStdOut = true; - } - - virtual ~JunitReporter() CATCH_OVERRIDE; - - static std::string getDescription() { - return "Reports test results in an XML format that looks like Ant's junitreport target"; - } - - virtual void noMatchingTestCases( std::string const& /*spec*/ ) CATCH_OVERRIDE {} - - virtual void testRunStarting( TestRunInfo const& runInfo ) CATCH_OVERRIDE { - CumulativeReporterBase::testRunStarting( runInfo ); - xml.startElement( "testsuites" ); - } - - virtual void testGroupStarting( GroupInfo const& groupInfo ) CATCH_OVERRIDE { - suiteTimer.start(); - stdOutForSuite.str(""); - stdErrForSuite.str(""); - unexpectedExceptions = 0; - CumulativeReporterBase::testGroupStarting( groupInfo ); - } - - virtual void testCaseStarting( TestCaseInfo const& testCaseInfo ) CATCH_OVERRIDE { - m_okToFail = testCaseInfo.okToFail(); - } - virtual bool assertionEnded( AssertionStats const& assertionStats ) CATCH_OVERRIDE { - if( assertionStats.assertionResult.getResultType() == ResultWas::ThrewException && !m_okToFail ) - unexpectedExceptions++; - return CumulativeReporterBase::assertionEnded( assertionStats ); - } - - virtual void testCaseEnded( TestCaseStats const& testCaseStats ) CATCH_OVERRIDE { - stdOutForSuite << testCaseStats.stdOut; - stdErrForSuite << testCaseStats.stdErr; - CumulativeReporterBase::testCaseEnded( testCaseStats ); - } - - virtual void testGroupEnded( TestGroupStats const& testGroupStats ) CATCH_OVERRIDE { - double suiteTime = suiteTimer.getElapsedSeconds(); - CumulativeReporterBase::testGroupEnded( testGroupStats ); - writeGroup( *m_testGroups.back(), suiteTime ); - } - - virtual void testRunEndedCumulative() CATCH_OVERRIDE { - xml.endElement(); - } - - void writeGroup( TestGroupNode const& groupNode, double suiteTime ) { - XmlWriter::ScopedElement e = xml.scopedElement( "testsuite" ); - TestGroupStats const& stats = groupNode.value; - xml.writeAttribute( "name", stats.groupInfo.name ); - xml.writeAttribute( "errors", unexpectedExceptions ); - xml.writeAttribute( "failures", stats.totals.assertions.failed-unexpectedExceptions ); - xml.writeAttribute( "tests", stats.totals.assertions.total() ); - xml.writeAttribute( "hostname", "tbd" ); // !TBD - if( m_config->showDurations() == ShowDurations::Never ) - xml.writeAttribute( "time", "" ); - else - xml.writeAttribute( "time", suiteTime ); - xml.writeAttribute( "timestamp", getCurrentTimestamp() ); - - // Write test cases - for( TestGroupNode::ChildNodes::const_iterator - it = groupNode.children.begin(), itEnd = groupNode.children.end(); - it != itEnd; - ++it ) - writeTestCase( **it ); - - xml.scopedElement( "system-out" ).writeText( trim( stdOutForSuite.str() ), false ); - xml.scopedElement( "system-err" ).writeText( trim( stdErrForSuite.str() ), false ); - } - - void writeTestCase( TestCaseNode const& testCaseNode ) { - TestCaseStats const& stats = testCaseNode.value; - - // All test cases have exactly one section - which represents the - // test case itself. That section may have 0-n nested sections - assert( testCaseNode.children.size() == 1 ); - SectionNode const& rootSection = *testCaseNode.children.front(); - - std::string className = stats.testInfo.className; - - if( className.empty() ) { - if( rootSection.childSections.empty() ) - className = "global"; - } - writeSection( className, "", rootSection ); - } - - void writeSection( std::string const& className, - std::string const& rootName, - SectionNode const& sectionNode ) { - std::string name = trim( sectionNode.stats.sectionInfo.name ); - if( !rootName.empty() ) - name = rootName + '/' + name; - - if( !sectionNode.assertions.empty() || - !sectionNode.stdOut.empty() || - !sectionNode.stdErr.empty() ) { - XmlWriter::ScopedElement e = xml.scopedElement( "testcase" ); - if( className.empty() ) { - xml.writeAttribute( "classname", name ); - xml.writeAttribute( "name", "root" ); - } - else { - xml.writeAttribute( "classname", className ); - xml.writeAttribute( "name", name ); - } - xml.writeAttribute( "time", Catch::toString( sectionNode.stats.durationInSeconds ) ); - - writeAssertions( sectionNode ); - - if( !sectionNode.stdOut.empty() ) - xml.scopedElement( "system-out" ).writeText( trim( sectionNode.stdOut ), false ); - if( !sectionNode.stdErr.empty() ) - xml.scopedElement( "system-err" ).writeText( trim( sectionNode.stdErr ), false ); - } - for( SectionNode::ChildSections::const_iterator - it = sectionNode.childSections.begin(), - itEnd = sectionNode.childSections.end(); - it != itEnd; - ++it ) - if( className.empty() ) - writeSection( name, "", **it ); - else - writeSection( className, name, **it ); - } - - void writeAssertions( SectionNode const& sectionNode ) { - for( SectionNode::Assertions::const_iterator - it = sectionNode.assertions.begin(), itEnd = sectionNode.assertions.end(); - it != itEnd; - ++it ) - writeAssertion( *it ); - } - void writeAssertion( AssertionStats const& stats ) { - AssertionResult const& result = stats.assertionResult; - if( !result.isOk() ) { - std::string elementName; - switch( result.getResultType() ) { - case ResultWas::ThrewException: - case ResultWas::FatalErrorCondition: - elementName = "error"; - break; - case ResultWas::ExplicitFailure: - elementName = "failure"; - break; - case ResultWas::ExpressionFailed: - elementName = "failure"; - break; - case ResultWas::DidntThrowException: - elementName = "failure"; - break; - - // We should never see these here: - case ResultWas::Info: - case ResultWas::Warning: - case ResultWas::Ok: - case ResultWas::Unknown: - case ResultWas::FailureBit: - case ResultWas::Exception: - elementName = "internalError"; - break; - } - - XmlWriter::ScopedElement e = xml.scopedElement( elementName ); - - xml.writeAttribute( "message", result.getExpandedExpression() ); - xml.writeAttribute( "type", result.getTestMacroName() ); - - std::ostringstream oss; - if( !result.getMessage().empty() ) - oss << result.getMessage() << '\n'; - for( std::vector::const_iterator - it = stats.infoMessages.begin(), - itEnd = stats.infoMessages.end(); - it != itEnd; - ++it ) - if( it->type == ResultWas::Info ) - oss << it->message << '\n'; - - oss << "at " << result.getSourceInfo(); - xml.writeText( oss.str(), false ); - } - } - - XmlWriter xml; - Timer suiteTimer; - std::ostringstream stdOutForSuite; - std::ostringstream stdErrForSuite; - unsigned int unexpectedExceptions; - bool m_okToFail; - }; - - INTERNAL_CATCH_REGISTER_REPORTER( "junit", JunitReporter ) - -} // end namespace Catch - -// #included from: ../reporters/catch_reporter_console.hpp -#define TWOBLUECUBES_CATCH_REPORTER_CONSOLE_HPP_INCLUDED +// end catch_reporter_compact.cpp +// start catch_reporter_console.cpp #include #include +#if defined(_MSC_VER) +#pragma warning(push) +#pragma warning(disable:4061) // Not all labels are EXPLICITLY handled in switch + // Note that 4062 (not all labels are handled + // and default is missing) is enabled +#endif + namespace Catch { - struct ConsoleReporter : StreamingReporterBase { - ConsoleReporter( ReporterConfig const& _config ) - : StreamingReporterBase( _config ), - m_headerPrinted( false ) - {} + namespace { + std::size_t makeRatio( std::size_t number, std::size_t total ) { + std::size_t ratio = total > 0 ? CATCH_CONFIG_CONSOLE_WIDTH * number/ total : 0; + return ( ratio == 0 && number > 0 ) ? 1 : ratio; + } - virtual ~ConsoleReporter() CATCH_OVERRIDE; + std::size_t& findMax( std::size_t& i, std::size_t& j, std::size_t& k ) { + if( i > j && i > k ) + return i; + else if( j > k ) + return j; + else + return k; + } + + struct ColumnInfo { + enum Justification { Left, Right }; + std::string name; + int width; + Justification justification; + }; + struct ColumnBreak {}; + struct RowBreak {}; + + class TablePrinter { + std::ostream& m_os; + std::vector m_columnInfos; + std::ostringstream m_oss; + int m_currentColumn = -1; + bool m_isOpen = false; + + public: + TablePrinter( std::ostream& os, std::vector const& columnInfos ) + : m_os( os ), + m_columnInfos( columnInfos ) + {} + + auto columnInfos() const -> std::vector const& { + return m_columnInfos; + } + + void open() { + if( !m_isOpen ) { + m_isOpen = true; + *this << RowBreak(); + for( auto const& info : m_columnInfos ) + *this << info.name << ColumnBreak(); + *this << RowBreak(); + m_os << Catch::getLineOfChars<'-'>() << "\n"; + } + } + void close() { + if( m_isOpen ) { + *this << RowBreak(); + m_os << std::endl; + m_isOpen = false; + } + } + + template + friend TablePrinter& operator << ( TablePrinter& tp, T const& value ) { + tp.m_oss << value; + return tp; + } + + friend TablePrinter& operator << ( TablePrinter& tp, ColumnBreak ) { + auto colStr = tp.m_oss.str(); + // This takes account of utf8 encodings + auto strSize = Catch::StringRef( colStr ).numberOfCharacters(); + tp.m_oss.str(""); + tp.open(); + if( tp.m_currentColumn == static_cast(tp.m_columnInfos.size()-1) ) { + tp.m_currentColumn = -1; + tp.m_os << "\n"; + } + tp.m_currentColumn++; + + auto colInfo = tp.m_columnInfos[tp.m_currentColumn]; + auto padding = ( strSize+2 < static_cast( colInfo.width ) ) + ? std::string( colInfo.width-(strSize+2), ' ' ) + : std::string(); + if( colInfo.justification == ColumnInfo::Left ) + tp.m_os << colStr << padding << " "; + else + tp.m_os << padding << colStr << " "; + return tp; + } + + friend TablePrinter& operator << ( TablePrinter& tp, RowBreak ) { + if( tp.m_currentColumn > 0 ) { + tp.m_os << "\n"; + tp.m_currentColumn = -1; + } + return tp; + } + }; + + class Duration { + enum class Unit { + Auto, + Nanoseconds, + Microseconds, + Milliseconds, + Seconds, + Minutes + }; + static const uint64_t s_nanosecondsInAMicrosecond = 1000; + static const uint64_t s_nanosecondsInAMillisecond = 1000*s_nanosecondsInAMicrosecond; + static const uint64_t s_nanosecondsInASecond = 1000*s_nanosecondsInAMillisecond; + static const uint64_t s_nanosecondsInAMinute = 60*s_nanosecondsInASecond; + + uint64_t m_inNanoseconds; + Unit m_units; + + public: + Duration( uint64_t inNanoseconds, Unit units = Unit::Auto ) + : m_inNanoseconds( inNanoseconds ), + m_units( units ) + { + if( m_units == Unit::Auto ) { + if( m_inNanoseconds < s_nanosecondsInAMicrosecond ) + m_units = Unit::Nanoseconds; + else if( m_inNanoseconds < s_nanosecondsInAMillisecond ) + m_units = Unit::Microseconds; + else if( m_inNanoseconds < s_nanosecondsInASecond ) + m_units = Unit::Milliseconds; + else if( m_inNanoseconds < s_nanosecondsInAMinute ) + m_units = Unit::Seconds; + else + m_units = Unit::Minutes; + } + + } + + auto value() const -> double { + switch( m_units ) { + case Unit::Microseconds: + return m_inNanoseconds / static_cast( s_nanosecondsInAMicrosecond ); + case Unit::Milliseconds: + return m_inNanoseconds / static_cast( s_nanosecondsInAMillisecond ); + case Unit::Seconds: + return m_inNanoseconds / static_cast( s_nanosecondsInASecond ); + case Unit::Minutes: + return m_inNanoseconds / static_cast( s_nanosecondsInAMinute ); + default: + return static_cast( m_inNanoseconds ); + } + } + auto unitsAsString() const -> std::string { + switch( m_units ) { + case Unit::Nanoseconds: + return "ns"; + case Unit::Microseconds: + return "µs"; + case Unit::Milliseconds: + return "ms"; + case Unit::Seconds: + return "s"; + case Unit::Minutes: + return "m"; + default: + return "** internal error **"; + } + + } + friend auto operator << ( std::ostream& os, Duration const& duration ) -> std::ostream& { + return os << duration.value() << " " << duration.unitsAsString(); + } + }; + } // end anon namespace + + struct ConsoleReporter : StreamingReporterBase { + TablePrinter m_tablePrinter; + + ConsoleReporter( ReporterConfig const& config ) + : StreamingReporterBase( config ), + m_tablePrinter( config.stream(), + { + { "benchmark name", CATCH_CONFIG_CONSOLE_WIDTH-32, ColumnInfo::Left }, + { "iters", 8, ColumnInfo::Right }, + { "elapsed ns", 14, ColumnInfo::Right }, + { "average", 14, ColumnInfo::Right } + } ) + {} + ~ConsoleReporter() override; static std::string getDescription() { return "Reports test results as plain lines of text"; } - virtual void noMatchingTestCases( std::string const& spec ) CATCH_OVERRIDE { + void noMatchingTestCases( std::string const& spec ) override { stream << "No test cases matched '" << spec << '\'' << std::endl; } - virtual void assertionStarting( AssertionInfo const& ) CATCH_OVERRIDE { + void assertionStarting( AssertionInfo const& ) override { } - virtual bool assertionEnded( AssertionStats const& _assertionStats ) CATCH_OVERRIDE { + bool assertionEnded( AssertionStats const& _assertionStats ) override { AssertionResult const& result = _assertionStats.assertionResult; bool includeResults = m_config->includeSuccessfulResults() || !result.isOk(); @@ -10624,11 +10716,12 @@ namespace Catch { return true; } - virtual void sectionStarting( SectionInfo const& _sectionInfo ) CATCH_OVERRIDE { + void sectionStarting( SectionInfo const& _sectionInfo ) override { m_headerPrinted = false; StreamingReporterBase::sectionStarting( _sectionInfo ); } - virtual void sectionEnded( SectionStats const& _sectionStats ) CATCH_OVERRIDE { + void sectionEnded( SectionStats const& _sectionStats ) override { + m_tablePrinter.close(); if( _sectionStats.missingAssertions ) { lazyPrint(); Colour colour( Colour::ResultError ); @@ -10647,11 +10740,35 @@ namespace Catch { StreamingReporterBase::sectionEnded( _sectionStats ); } - virtual void testCaseEnded( TestCaseStats const& _testCaseStats ) CATCH_OVERRIDE { + void benchmarkStarting( BenchmarkInfo const& info ) override { + lazyPrintWithoutClosingBenchmarkTable(); + + auto nameCol = Column( info.name ).width( m_tablePrinter.columnInfos()[0].width-2 ); + + bool firstLine = true; + for( auto line : nameCol ) { + if( !firstLine ) + m_tablePrinter << ColumnBreak() << ColumnBreak() << ColumnBreak(); + else + firstLine = false; + + m_tablePrinter << line << ColumnBreak(); + } + } + void benchmarkEnded( BenchmarkStats const& stats ) override { + Duration average( stats.elapsedTimeInNanoseconds/stats.iterations ); + m_tablePrinter + << stats.iterations << ColumnBreak() + << stats.elapsedTimeInNanoseconds << ColumnBreak() + << average << ColumnBreak(); + } + + void testCaseEnded( TestCaseStats const& _testCaseStats ) override { + m_tablePrinter.close(); StreamingReporterBase::testCaseEnded( _testCaseStats ); m_headerPrinted = false; } - virtual void testGroupEnded( TestGroupStats const& _testGroupStats ) CATCH_OVERRIDE { + void testGroupEnded( TestGroupStats const& _testGroupStats ) override { if( currentGroupInfo.used ) { printSummaryDivider(); stream << "Summary for group '" << _testGroupStats.groupInfo.name << "':\n"; @@ -10660,7 +10777,7 @@ namespace Catch { } StreamingReporterBase::testGroupEnded( _testGroupStats ); } - virtual void testRunEnded( TestRunStats const& _testRunStats ) CATCH_OVERRIDE { + void testRunEnded( TestRunStats const& _testRunStats ) override { printTotalsDivider( _testRunStats.totals ); printTotals( _testRunStats.totals ); stream << std::endl; @@ -10670,8 +10787,9 @@ namespace Catch { private: class AssertionPrinter { - void operator= ( AssertionPrinter const& ); public: + AssertionPrinter& operator= ( AssertionPrinter const& ) = delete; + AssertionPrinter( AssertionPrinter const& ) = delete; AssertionPrinter( std::ostream& _stream, AssertionStats const& _stats, bool _printInfoMessages ) : stream( _stream ), stats( _stats ), @@ -10782,18 +10900,16 @@ namespace Catch { if( result.hasExpandedExpression() ) { stream << "with expansion:\n"; Colour colourGuard( Colour::ReconstructedExpression ); - stream << Text( result.getExpandedExpression(), TextAttributes().setIndent(2) ) << '\n'; + stream << Column( result.getExpandedExpression() ).indent(2) << '\n'; } } void printMessage() const { if( !messageLabel.empty() ) stream << messageLabel << ':' << '\n'; - for( std::vector::const_iterator it = messages.begin(), itEnd = messages.end(); - it != itEnd; - ++it ) { + for( auto const& msg : messages ) { // If this assertion is a warning ignore any INFO messages - if( printInfoMessages || it->type != ResultWas::Info ) - stream << Text( it->message, TextAttributes().setIndent(2) ) << '\n'; + if( printInfoMessages || msg.type != ResultWas::Info ) + stream << Column( msg.message ).indent(2) << '\n'; } } void printSourceInfo() const { @@ -10814,6 +10930,12 @@ namespace Catch { void lazyPrint() { + m_tablePrinter.close(); + lazyPrintWithoutClosingBenchmarkTable(); + } + + void lazyPrintWithoutClosingBenchmarkTable() { + if( !currentTestRunInfo.used ) lazyPrintRunInfo(); if( !currentGroupInfo.used ) @@ -10849,7 +10971,7 @@ namespace Catch { if( m_sectionStack.size() > 1 ) { Colour colourGuard( Colour::Headers ); - std::vector::const_iterator + auto it = m_sectionStack.begin()+1, // Skip first section (test case) itEnd = m_sectionStack.end(); for( ; it != itEnd; ++it ) @@ -10886,9 +11008,7 @@ namespace Catch { i+=2; else i = 0; - stream << Text( _string, TextAttributes() - .setIndent( indent+i) - .setInitialIndent( indent ) ) << '\n'; + stream << Column( _string ).indent( indent+i ).initialIndent( indent ) << '\n'; } struct SummaryColumn { @@ -10901,10 +11021,10 @@ namespace Catch { std::ostringstream oss; oss << count; std::string row = oss.str(); - for( std::vector::iterator it = rows.begin(); it != rows.end(); ++it ) { - while( it->size() < row.size() ) - *it = ' ' + *it; - while( it->size() > row.size() ) + for( auto& oldRow : rows ) { + while( oldRow.size() < row.size() ) + oldRow = ' ' + oldRow; + while( oldRow.size() > row.size() ) row = ' ' + row; } rows.push_back( row ); @@ -10949,9 +11069,9 @@ namespace Catch { } } void printSummaryRow( std::string const& label, std::vector const& cols, std::size_t row ) { - for( std::vector::const_iterator it = cols.begin(); it != cols.end(); ++it ) { - std::string value = it->rows[row]; - if( it->label.empty() ) { + for( auto col : cols ) { + std::string value = col.rows[row]; + if( col.label.empty() ) { stream << label << ": "; if( value != "0" ) stream << value; @@ -10960,26 +11080,13 @@ namespace Catch { } else if( value != "0" ) { stream << Colour( Colour::LightGrey ) << " | "; - stream << Colour( it->colour ) - << value << ' ' << it->label; + stream << Colour( col.colour ) + << value << ' ' << col.label; } } stream << '\n'; } - static std::size_t makeRatio( std::size_t number, std::size_t total ) { - std::size_t ratio = total > 0 ? CATCH_CONFIG_CONSOLE_WIDTH * number/ total : 0; - return ( ratio == 0 && number > 0 ) ? 1 : ratio; - } - static std::size_t& findMax( std::size_t& i, std::size_t& j, std::size_t& k ) { - if( i > j && i > k ) - return i; - else if( j > k ) - return j; - else - return k; - } - void printTotalsDivider( Totals const& totals ) { if( totals.testCases.total() > 0 ) { std::size_t failedRatio = makeRatio( totals.testCases.failed, totals.testCases.total() ); @@ -11007,374 +11114,594 @@ namespace Catch { } private: - bool m_headerPrinted; + bool m_headerPrinted = false; }; - INTERNAL_CATCH_REGISTER_REPORTER( "console", ConsoleReporter ) + CATCH_REGISTER_REPORTER( "console", ConsoleReporter ) + + ConsoleReporter::~ConsoleReporter() {} } // end namespace Catch -// #included from: ../reporters/catch_reporter_compact.hpp -#define TWOBLUECUBES_CATCH_REPORTER_COMPACT_HPP_INCLUDED +#if defined(_MSC_VER) +#pragma warning(pop) +#endif +// end catch_reporter_console.cpp +// start catch_reporter_junit.cpp + +#include + +#include +#include namespace Catch { - struct CompactReporter : StreamingReporterBase { + namespace { + std::string getCurrentTimestamp() { + // Beware, this is not reentrant because of backward compatibility issues + // Also, UTC only, again because of backward compatibility (%z is C++11) + time_t rawtime; + std::time(&rawtime); + auto const timeStampSize = sizeof("2017-01-16T17:06:45Z"); - CompactReporter( ReporterConfig const& _config ) - : StreamingReporterBase( _config ) - {} +#ifdef _MSC_VER + std::tm timeInfo = {}; + gmtime_s(&timeInfo, &rawtime); +#else + std::tm* timeInfo; + timeInfo = std::gmtime(&rawtime); +#endif - virtual ~CompactReporter(); + char timeStamp[timeStampSize]; + const char * const fmt = "%Y-%m-%dT%H:%M:%SZ"; + +#ifdef _MSC_VER + std::strftime(timeStamp, timeStampSize, fmt, &timeInfo); +#else + std::strftime(timeStamp, timeStampSize, fmt, timeInfo); +#endif + return std::string(timeStamp); + } + + std::string fileNameTag(const std::vector &tags) { + auto it = std::find_if(begin(tags), + end(tags), + [] (std::string const& tag) {return tag.front() == '#'; }); + if (it != tags.end()) + return it->substr(1); + return std::string(); + } + } + + class JunitReporter : public CumulativeReporterBase { + public: + JunitReporter( ReporterConfig const& _config ) + : CumulativeReporterBase( _config ), + xml( _config.stream() ) + { + m_reporterPrefs.shouldRedirectStdOut = true; + } + + ~JunitReporter() override; static std::string getDescription() { - return "Reports test results on a single line, suitable for IDEs"; + return "Reports test results in an XML format that looks like Ant's junitreport target"; } - virtual ReporterPreferences getPreferences() const { - ReporterPreferences prefs; - prefs.shouldRedirectStdOut = false; - return prefs; + void noMatchingTestCases( std::string const& /*spec*/ ) override {} + + void testRunStarting( TestRunInfo const& runInfo ) override { + CumulativeReporterBase::testRunStarting( runInfo ); + xml.startElement( "testsuites" ); } - virtual void noMatchingTestCases( std::string const& spec ) { - stream << "No test cases matched '" << spec << '\'' << std::endl; + void testGroupStarting( GroupInfo const& groupInfo ) override { + suiteTimer.start(); + stdOutForSuite.str(""); + stdErrForSuite.str(""); + unexpectedExceptions = 0; + CumulativeReporterBase::testGroupStarting( groupInfo ); } - virtual void assertionStarting( AssertionInfo const& ) {} + void testCaseStarting( TestCaseInfo const& testCaseInfo ) override { + m_okToFail = testCaseInfo.okToFail(); + } + bool assertionEnded( AssertionStats const& assertionStats ) override { + if( assertionStats.assertionResult.getResultType() == ResultWas::ThrewException && !m_okToFail ) + unexpectedExceptions++; + return CumulativeReporterBase::assertionEnded( assertionStats ); + } - virtual bool assertionEnded( AssertionStats const& _assertionStats ) { - AssertionResult const& result = _assertionStats.assertionResult; + void testCaseEnded( TestCaseStats const& testCaseStats ) override { + stdOutForSuite << testCaseStats.stdOut; + stdErrForSuite << testCaseStats.stdErr; + CumulativeReporterBase::testCaseEnded( testCaseStats ); + } - bool printInfoMessages = true; + void testGroupEnded( TestGroupStats const& testGroupStats ) override { + double suiteTime = suiteTimer.getElapsedSeconds(); + CumulativeReporterBase::testGroupEnded( testGroupStats ); + writeGroup( *m_testGroups.back(), suiteTime ); + } - // Drop out if result was successful and we're not printing those - if( !m_config->includeSuccessfulResults() && result.isOk() ) { - if( result.getResultType() != ResultWas::Warning ) - return false; - printInfoMessages = false; + void testRunEndedCumulative() override { + xml.endElement(); + } + + void writeGroup( TestGroupNode const& groupNode, double suiteTime ) { + XmlWriter::ScopedElement e = xml.scopedElement( "testsuite" ); + TestGroupStats const& stats = groupNode.value; + xml.writeAttribute( "name", stats.groupInfo.name ); + xml.writeAttribute( "errors", unexpectedExceptions ); + xml.writeAttribute( "failures", stats.totals.assertions.failed-unexpectedExceptions ); + xml.writeAttribute( "tests", stats.totals.assertions.total() ); + xml.writeAttribute( "hostname", "tbd" ); // !TBD + if( m_config->showDurations() == ShowDurations::Never ) + xml.writeAttribute( "time", "" ); + else + xml.writeAttribute( "time", suiteTime ); + xml.writeAttribute( "timestamp", getCurrentTimestamp() ); + + // Write test cases + for( auto const& child : groupNode.children ) + writeTestCase( *child ); + + xml.scopedElement( "system-out" ).writeText( trim( stdOutForSuite.str() ), false ); + xml.scopedElement( "system-err" ).writeText( trim( stdErrForSuite.str() ), false ); + } + + void writeTestCase( TestCaseNode const& testCaseNode ) { + TestCaseStats const& stats = testCaseNode.value; + + // All test cases have exactly one section - which represents the + // test case itself. That section may have 0-n nested sections + assert( testCaseNode.children.size() == 1 ); + SectionNode const& rootSection = *testCaseNode.children.front(); + + std::string className = stats.testInfo.className; + + if( className.empty() ) { + className = fileNameTag(stats.testInfo.tags); + if ( className.empty() ) + className = "global"; } - AssertionPrinter printer( stream, _assertionStats, printInfoMessages ); - printer.print(); + if ( !m_config->name().empty() ) + className = m_config->name() + "." + className; - stream << std::endl; - return true; + writeSection( className, "", rootSection ); } - virtual void sectionEnded(SectionStats const& _sectionStats) CATCH_OVERRIDE { - if (m_config->showDurations() == ShowDurations::Always) { - stream << getFormattedDuration(_sectionStats.durationInSeconds) << " s: " << _sectionStats.sectionInfo.name << std::endl; + void writeSection( std::string const& className, + std::string const& rootName, + SectionNode const& sectionNode ) { + std::string name = trim( sectionNode.stats.sectionInfo.name ); + if( !rootName.empty() ) + name = rootName + '/' + name; + + if( !sectionNode.assertions.empty() || + !sectionNode.stdOut.empty() || + !sectionNode.stdErr.empty() ) { + XmlWriter::ScopedElement e = xml.scopedElement( "testcase" ); + if( className.empty() ) { + xml.writeAttribute( "classname", name ); + xml.writeAttribute( "name", "root" ); + } + else { + xml.writeAttribute( "classname", className ); + xml.writeAttribute( "name", name ); + } + xml.writeAttribute( "time", ::Catch::Detail::stringify( sectionNode.stats.durationInSeconds ) ); + + writeAssertions( sectionNode ); + + if( !sectionNode.stdOut.empty() ) + xml.scopedElement( "system-out" ).writeText( trim( sectionNode.stdOut ), false ); + if( !sectionNode.stdErr.empty() ) + xml.scopedElement( "system-err" ).writeText( trim( sectionNode.stdErr ), false ); } + for( auto const& childNode : sectionNode.childSections ) + if( className.empty() ) + writeSection( name, "", *childNode ); + else + writeSection( className, name, *childNode ); } - virtual void testRunEnded( TestRunStats const& _testRunStats ) { - printTotals( _testRunStats.totals ); - stream << '\n' << std::endl; - StreamingReporterBase::testRunEnded( _testRunStats ); + void writeAssertions( SectionNode const& sectionNode ) { + for( auto const& assertion : sectionNode.assertions ) + writeAssertion( assertion ); } - - private: - class AssertionPrinter { - void operator= ( AssertionPrinter const& ); - public: - AssertionPrinter( std::ostream& _stream, AssertionStats const& _stats, bool _printInfoMessages ) - : stream( _stream ) - , stats( _stats ) - , result( _stats.assertionResult ) - , messages( _stats.infoMessages ) - , itMessage( _stats.infoMessages.begin() ) - , printInfoMessages( _printInfoMessages ) - {} - - void print() { - printSourceInfo(); - - itMessage = messages.begin(); - + void writeAssertion( AssertionStats const& stats ) { + AssertionResult const& result = stats.assertionResult; + if( !result.isOk() ) { + std::string elementName; switch( result.getResultType() ) { - case ResultWas::Ok: - printResultType( Colour::ResultSuccess, passedString() ); - printOriginalExpression(); - printReconstructedExpression(); - if ( ! result.hasExpression() ) - printRemainingMessages( Colour::None ); - else - printRemainingMessages(); - break; - case ResultWas::ExpressionFailed: - if( result.isOk() ) - printResultType( Colour::ResultSuccess, failedString() + std::string( " - but was ok" ) ); - else - printResultType( Colour::Error, failedString() ); - printOriginalExpression(); - printReconstructedExpression(); - printRemainingMessages(); - break; case ResultWas::ThrewException: - printResultType( Colour::Error, failedString() ); - printIssue( "unexpected exception with message:" ); - printMessage(); - printExpressionWas(); - printRemainingMessages(); - break; case ResultWas::FatalErrorCondition: - printResultType( Colour::Error, failedString() ); - printIssue( "fatal error condition with message:" ); - printMessage(); - printExpressionWas(); - printRemainingMessages(); - break; - case ResultWas::DidntThrowException: - printResultType( Colour::Error, failedString() ); - printIssue( "expected exception, got none" ); - printExpressionWas(); - printRemainingMessages(); - break; - case ResultWas::Info: - printResultType( Colour::None, "info" ); - printMessage(); - printRemainingMessages(); - break; - case ResultWas::Warning: - printResultType( Colour::None, "warning" ); - printMessage(); - printRemainingMessages(); + elementName = "error"; break; case ResultWas::ExplicitFailure: - printResultType( Colour::Error, failedString() ); - printIssue( "explicitly" ); - printRemainingMessages( Colour::None ); + elementName = "failure"; break; - // These cases are here to prevent compiler warnings + case ResultWas::ExpressionFailed: + elementName = "failure"; + break; + case ResultWas::DidntThrowException: + elementName = "failure"; + break; + + // We should never see these here: + case ResultWas::Info: + case ResultWas::Warning: + case ResultWas::Ok: case ResultWas::Unknown: case ResultWas::FailureBit: case ResultWas::Exception: - printResultType( Colour::Error, "** internal error **" ); + elementName = "internalError"; break; } - } - private: - // Colour::LightGrey + XmlWriter::ScopedElement e = xml.scopedElement( elementName ); - static Colour::Code dimColour() { return Colour::FileName; } + xml.writeAttribute( "message", result.getExpandedExpression() ); + xml.writeAttribute( "type", result.getTestMacroName() ); -#ifdef CATCH_PLATFORM_MAC - static const char* failedString() { return "FAILED"; } - static const char* passedString() { return "PASSED"; } -#else - static const char* failedString() { return "failed"; } - static const char* passedString() { return "passed"; } -#endif + std::ostringstream oss; + if( !result.getMessage().empty() ) + oss << result.getMessage() << '\n'; + for( auto const& msg : stats.infoMessages ) + if( msg.type == ResultWas::Info ) + oss << msg.message << '\n'; - void printSourceInfo() const { - Colour colourGuard( Colour::FileName ); - stream << result.getSourceInfo() << ':'; - } - - void printResultType( Colour::Code colour, std::string const& passOrFail ) const { - if( !passOrFail.empty() ) { - { - Colour colourGuard( colour ); - stream << ' ' << passOrFail; - } - stream << ':'; - } - } - - void printIssue( std::string const& issue ) const { - stream << ' ' << issue; - } - - void printExpressionWas() { - if( result.hasExpression() ) { - stream << ';'; - { - Colour colour( dimColour() ); - stream << " expression was:"; - } - printOriginalExpression(); - } - } - - void printOriginalExpression() const { - if( result.hasExpression() ) { - stream << ' ' << result.getExpression(); - } - } - - void printReconstructedExpression() const { - if( result.hasExpandedExpression() ) { - { - Colour colour( dimColour() ); - stream << " for: "; - } - stream << result.getExpandedExpression(); - } - } - - void printMessage() { - if ( itMessage != messages.end() ) { - stream << " '" << itMessage->message << '\''; - ++itMessage; - } - } - - void printRemainingMessages( Colour::Code colour = dimColour() ) { - if ( itMessage == messages.end() ) - return; - - // using messages.end() directly yields compilation error: - std::vector::const_iterator itEnd = messages.end(); - const std::size_t N = static_cast( std::distance( itMessage, itEnd ) ); - - { - Colour colourGuard( colour ); - stream << " with " << pluralise( N, "message" ) << ':'; - } - - for(; itMessage != itEnd; ) { - // If this assertion is a warning ignore any INFO messages - if( printInfoMessages || itMessage->type != ResultWas::Info ) { - stream << " '" << itMessage->message << '\''; - if ( ++itMessage != itEnd ) { - Colour colourGuard( dimColour() ); - stream << " and"; - } - } - } - } - - private: - std::ostream& stream; - AssertionStats const& stats; - AssertionResult const& result; - std::vector messages; - std::vector::const_iterator itMessage; - bool printInfoMessages; - }; - - // Colour, message variants: - // - white: No tests ran. - // - red: Failed [both/all] N test cases, failed [both/all] M assertions. - // - white: Passed [both/all] N test cases (no assertions). - // - red: Failed N tests cases, failed M assertions. - // - green: Passed [both/all] N tests cases with M assertions. - - std::string bothOrAll( std::size_t count ) const { - return count == 1 ? std::string() : count == 2 ? "both " : "all " ; - } - - void printTotals( const Totals& totals ) const { - if( totals.testCases.total() == 0 ) { - stream << "No tests ran."; - } - else if( totals.testCases.failed == totals.testCases.total() ) { - Colour colour( Colour::ResultError ); - const std::string qualify_assertions_failed = - totals.assertions.failed == totals.assertions.total() ? - bothOrAll( totals.assertions.failed ) : std::string(); - stream << - "Failed " << bothOrAll( totals.testCases.failed ) - << pluralise( totals.testCases.failed, "test case" ) << ", " - "failed " << qualify_assertions_failed << - pluralise( totals.assertions.failed, "assertion" ) << '.'; - } - else if( totals.assertions.total() == 0 ) { - stream << - "Passed " << bothOrAll( totals.testCases.total() ) - << pluralise( totals.testCases.total(), "test case" ) - << " (no assertions)."; - } - else if( totals.assertions.failed ) { - Colour colour( Colour::ResultError ); - stream << - "Failed " << pluralise( totals.testCases.failed, "test case" ) << ", " - "failed " << pluralise( totals.assertions.failed, "assertion" ) << '.'; - } - else { - Colour colour( Colour::ResultSuccess ); - stream << - "Passed " << bothOrAll( totals.testCases.passed ) - << pluralise( totals.testCases.passed, "test case" ) << - " with " << pluralise( totals.assertions.passed, "assertion" ) << '.'; + oss << "at " << result.getSourceInfo(); + xml.writeText( oss.str(), false ); } } + + XmlWriter xml; + Timer suiteTimer; + std::ostringstream stdOutForSuite; + std::ostringstream stdErrForSuite; + unsigned int unexpectedExceptions = 0; + bool m_okToFail = false; }; - INTERNAL_CATCH_REGISTER_REPORTER( "compact", CompactReporter ) + JunitReporter::~JunitReporter() {} + CATCH_REGISTER_REPORTER( "junit", JunitReporter ) + +} // end namespace Catch +// end catch_reporter_junit.cpp +// start catch_reporter_multi.cpp + +namespace Catch { + + void MultipleReporters::add( IStreamingReporterPtr&& reporter ) { + m_reporters.push_back( std::move( reporter ) ); + } + + ReporterPreferences MultipleReporters::getPreferences() const { + return m_reporters[0]->getPreferences(); + } + + std::set MultipleReporters::getSupportedVerbosities() { + return std::set{ }; + } + + void MultipleReporters::noMatchingTestCases( std::string const& spec ) { + for( auto const& reporter : m_reporters ) + reporter->noMatchingTestCases( spec ); + } + + void MultipleReporters::benchmarkStarting( BenchmarkInfo const& benchmarkInfo ) { + for( auto const& reporter : m_reporters ) + reporter->benchmarkStarting( benchmarkInfo ); + } + void MultipleReporters::benchmarkEnded( BenchmarkStats const& benchmarkStats ) { + for( auto const& reporter : m_reporters ) + reporter->benchmarkEnded( benchmarkStats ); + } + + void MultipleReporters::testRunStarting( TestRunInfo const& testRunInfo ) { + for( auto const& reporter : m_reporters ) + reporter->testRunStarting( testRunInfo ); + } + + void MultipleReporters::testGroupStarting( GroupInfo const& groupInfo ) { + for( auto const& reporter : m_reporters ) + reporter->testGroupStarting( groupInfo ); + } + + void MultipleReporters::testCaseStarting( TestCaseInfo const& testInfo ) { + for( auto const& reporter : m_reporters ) + reporter->testCaseStarting( testInfo ); + } + + void MultipleReporters::sectionStarting( SectionInfo const& sectionInfo ) { + for( auto const& reporter : m_reporters ) + reporter->sectionStarting( sectionInfo ); + } + + void MultipleReporters::assertionStarting( AssertionInfo const& assertionInfo ) { + for( auto const& reporter : m_reporters ) + reporter->assertionStarting( assertionInfo ); + } + + // The return value indicates if the messages buffer should be cleared: + bool MultipleReporters::assertionEnded( AssertionStats const& assertionStats ) { + bool clearBuffer = false; + for( auto const& reporter : m_reporters ) + clearBuffer |= reporter->assertionEnded( assertionStats ); + return clearBuffer; + } + + void MultipleReporters::sectionEnded( SectionStats const& sectionStats ) { + for( auto const& reporter : m_reporters ) + reporter->sectionEnded( sectionStats ); + } + + void MultipleReporters::testCaseEnded( TestCaseStats const& testCaseStats ) { + for( auto const& reporter : m_reporters ) + reporter->testCaseEnded( testCaseStats ); + } + + void MultipleReporters::testGroupEnded( TestGroupStats const& testGroupStats ) { + for( auto const& reporter : m_reporters ) + reporter->testGroupEnded( testGroupStats ); + } + + void MultipleReporters::testRunEnded( TestRunStats const& testRunStats ) { + for( auto const& reporter : m_reporters ) + reporter->testRunEnded( testRunStats ); + } + + void MultipleReporters::skipTest( TestCaseInfo const& testInfo ) { + for( auto const& reporter : m_reporters ) + reporter->skipTest( testInfo ); + } + + bool MultipleReporters::isMulti() const { + return true; + } + +} // end namespace Catch +// end catch_reporter_multi.cpp +// start catch_reporter_xml.cpp + +#if defined(_MSC_VER) +#pragma warning(push) +#pragma warning(disable:4061) // Not all labels are EXPLICITLY handled in switch + // Note that 4062 (not all labels are handled + // and default is missing) is enabled +#endif + +namespace Catch { + class XmlReporter : public StreamingReporterBase { + public: + XmlReporter( ReporterConfig const& _config ) + : StreamingReporterBase( _config ), + m_xml(_config.stream()) + { + m_reporterPrefs.shouldRedirectStdOut = true; + } + + ~XmlReporter() override; + + static std::string getDescription() { + return "Reports test results as an XML document"; + } + + virtual std::string getStylesheetRef() const { + return std::string(); + } + + void writeSourceInfo( SourceLineInfo const& sourceInfo ) { + m_xml + .writeAttribute( "filename", sourceInfo.file ) + .writeAttribute( "line", sourceInfo.line ); + } + + public: // StreamingReporterBase + + void noMatchingTestCases( std::string const& s ) override { + StreamingReporterBase::noMatchingTestCases( s ); + } + + void testRunStarting( TestRunInfo const& testInfo ) override { + StreamingReporterBase::testRunStarting( testInfo ); + std::string stylesheetRef = getStylesheetRef(); + if( !stylesheetRef.empty() ) + m_xml.writeStylesheetRef( stylesheetRef ); + m_xml.startElement( "Catch" ); + if( !m_config->name().empty() ) + m_xml.writeAttribute( "name", m_config->name() ); + } + + void testGroupStarting( GroupInfo const& groupInfo ) override { + StreamingReporterBase::testGroupStarting( groupInfo ); + m_xml.startElement( "Group" ) + .writeAttribute( "name", groupInfo.name ); + } + + void testCaseStarting( TestCaseInfo const& testInfo ) override { + StreamingReporterBase::testCaseStarting(testInfo); + m_xml.startElement( "TestCase" ) + .writeAttribute( "name", trim( testInfo.name ) ) + .writeAttribute( "description", testInfo.description ) + .writeAttribute( "tags", testInfo.tagsAsString() ); + + writeSourceInfo( testInfo.lineInfo ); + + if ( m_config->showDurations() == ShowDurations::Always ) + m_testCaseTimer.start(); + m_xml.ensureTagClosed(); + } + + void sectionStarting( SectionInfo const& sectionInfo ) override { + StreamingReporterBase::sectionStarting( sectionInfo ); + if( m_sectionDepth++ > 0 ) { + m_xml.startElement( "Section" ) + .writeAttribute( "name", trim( sectionInfo.name ) ) + .writeAttribute( "description", sectionInfo.description ); + writeSourceInfo( sectionInfo.lineInfo ); + m_xml.ensureTagClosed(); + } + } + + void assertionStarting( AssertionInfo const& ) override { } + + bool assertionEnded( AssertionStats const& assertionStats ) override { + + AssertionResult const& result = assertionStats.assertionResult; + + bool includeResults = m_config->includeSuccessfulResults() || !result.isOk(); + + if( includeResults ) { + // Print any info messages in tags. + for( auto const& msg : assertionStats.infoMessages ) { + if( msg.type == ResultWas::Info ) { + m_xml.scopedElement( "Info" ) + .writeText( msg.message ); + } else if ( msg.type == ResultWas::Warning ) { + m_xml.scopedElement( "Warning" ) + .writeText( msg.message ); + } + } + } + + // Drop out if result was successful but we're not printing them. + if( !includeResults && result.getResultType() != ResultWas::Warning ) + return true; + + // Print the expression if there is one. + if( result.hasExpression() ) { + m_xml.startElement( "Expression" ) + .writeAttribute( "success", result.succeeded() ) + .writeAttribute( "type", result.getTestMacroName() ); + + writeSourceInfo( result.getSourceInfo() ); + + m_xml.scopedElement( "Original" ) + .writeText( result.getExpression() ); + m_xml.scopedElement( "Expanded" ) + .writeText( result.getExpandedExpression() ); + } + + // And... Print a result applicable to each result type. + switch( result.getResultType() ) { + case ResultWas::ThrewException: + m_xml.startElement( "Exception" ); + writeSourceInfo( result.getSourceInfo() ); + m_xml.writeText( result.getMessage() ); + m_xml.endElement(); + break; + case ResultWas::FatalErrorCondition: + m_xml.startElement( "FatalErrorCondition" ); + writeSourceInfo( result.getSourceInfo() ); + m_xml.writeText( result.getMessage() ); + m_xml.endElement(); + break; + case ResultWas::Info: + m_xml.scopedElement( "Info" ) + .writeText( result.getMessage() ); + break; + case ResultWas::Warning: + // Warning will already have been written + break; + case ResultWas::ExplicitFailure: + m_xml.startElement( "Failure" ); + writeSourceInfo( result.getSourceInfo() ); + m_xml.writeText( result.getMessage() ); + m_xml.endElement(); + break; + default: + break; + } + + if( result.hasExpression() ) + m_xml.endElement(); + + return true; + } + + void sectionEnded( SectionStats const& sectionStats ) override { + StreamingReporterBase::sectionEnded( sectionStats ); + if( --m_sectionDepth > 0 ) { + XmlWriter::ScopedElement e = m_xml.scopedElement( "OverallResults" ); + e.writeAttribute( "successes", sectionStats.assertions.passed ); + e.writeAttribute( "failures", sectionStats.assertions.failed ); + e.writeAttribute( "expectedFailures", sectionStats.assertions.failedButOk ); + + if ( m_config->showDurations() == ShowDurations::Always ) + e.writeAttribute( "durationInSeconds", sectionStats.durationInSeconds ); + + m_xml.endElement(); + } + } + + void testCaseEnded( TestCaseStats const& testCaseStats ) override { + StreamingReporterBase::testCaseEnded( testCaseStats ); + XmlWriter::ScopedElement e = m_xml.scopedElement( "OverallResult" ); + e.writeAttribute( "success", testCaseStats.totals.assertions.allOk() ); + + if ( m_config->showDurations() == ShowDurations::Always ) + e.writeAttribute( "durationInSeconds", m_testCaseTimer.getElapsedSeconds() ); + + if( !testCaseStats.stdOut.empty() ) + m_xml.scopedElement( "StdOut" ).writeText( trim( testCaseStats.stdOut ), false ); + if( !testCaseStats.stdErr.empty() ) + m_xml.scopedElement( "StdErr" ).writeText( trim( testCaseStats.stdErr ), false ); + + m_xml.endElement(); + } + + void testGroupEnded( TestGroupStats const& testGroupStats ) override { + StreamingReporterBase::testGroupEnded( testGroupStats ); + // TODO: Check testGroupStats.aborting and act accordingly. + m_xml.scopedElement( "OverallResults" ) + .writeAttribute( "successes", testGroupStats.totals.assertions.passed ) + .writeAttribute( "failures", testGroupStats.totals.assertions.failed ) + .writeAttribute( "expectedFailures", testGroupStats.totals.assertions.failedButOk ); + m_xml.endElement(); + } + + void testRunEnded( TestRunStats const& testRunStats ) override { + StreamingReporterBase::testRunEnded( testRunStats ); + m_xml.scopedElement( "OverallResults" ) + .writeAttribute( "successes", testRunStats.totals.assertions.passed ) + .writeAttribute( "failures", testRunStats.totals.assertions.failed ) + .writeAttribute( "expectedFailures", testRunStats.totals.assertions.failedButOk ); + m_xml.endElement(); + } + + private: + Timer m_testCaseTimer; + XmlWriter m_xml; + int m_sectionDepth = 0; + }; + + XmlReporter::~XmlReporter() {} + CATCH_REGISTER_REPORTER( "xml", XmlReporter ) } // end namespace Catch +#if defined(_MSC_VER) +#pragma warning(pop) +#endif +// end catch_reporter_xml.cpp + namespace Catch { - // These are all here to avoid warnings about not having any out of line - // virtual methods - NonCopyable::~NonCopyable() {} - IShared::~IShared() {} - IStream::~IStream() CATCH_NOEXCEPT {} - FileStream::~FileStream() CATCH_NOEXCEPT {} - CoutStream::~CoutStream() CATCH_NOEXCEPT {} - DebugOutStream::~DebugOutStream() CATCH_NOEXCEPT {} - StreamBufBase::~StreamBufBase() CATCH_NOEXCEPT {} - IContext::~IContext() {} - IResultCapture::~IResultCapture() {} - ITestCase::~ITestCase() {} - ITestCaseRegistry::~ITestCaseRegistry() {} - IRegistryHub::~IRegistryHub() {} - IMutableRegistryHub::~IMutableRegistryHub() {} - IExceptionTranslator::~IExceptionTranslator() {} - IExceptionTranslatorRegistry::~IExceptionTranslatorRegistry() {} - IReporter::~IReporter() {} - IReporterFactory::~IReporterFactory() {} - IReporterRegistry::~IReporterRegistry() {} - IStreamingReporter::~IStreamingReporter() {} - AssertionStats::~AssertionStats() {} - SectionStats::~SectionStats() {} - TestCaseStats::~TestCaseStats() {} - TestGroupStats::~TestGroupStats() {} - TestRunStats::~TestRunStats() {} - CumulativeReporterBase::SectionNode::~SectionNode() {} - CumulativeReporterBase::~CumulativeReporterBase() {} - - StreamingReporterBase::~StreamingReporterBase() {} - ConsoleReporter::~ConsoleReporter() {} - CompactReporter::~CompactReporter() {} - IRunner::~IRunner() {} - IMutableContext::~IMutableContext() {} - IConfig::~IConfig() {} - XmlReporter::~XmlReporter() {} - JunitReporter::~JunitReporter() {} - TestRegistry::~TestRegistry() {} - FreeFunctionTestCase::~FreeFunctionTestCase() {} - IGeneratorInfo::~IGeneratorInfo() {} - IGeneratorsForTest::~IGeneratorsForTest() {} - WildcardPattern::~WildcardPattern() {} - TestSpec::Pattern::~Pattern() {} - TestSpec::NamePattern::~NamePattern() {} - TestSpec::TagPattern::~TagPattern() {} - TestSpec::ExcludedPattern::~ExcludedPattern() {} - Matchers::Impl::MatcherUntypedBase::~MatcherUntypedBase() {} - - void Config::dummy() {} - - namespace TestCaseTracking { - ITracker::~ITracker() {} - TrackerBase::~TrackerBase() {} - SectionTracker::~SectionTracker() {} - IndexTracker::~IndexTracker() {} - } + LeakDetector leakDetector; } #ifdef __clang__ #pragma clang diagnostic pop #endif +// end catch_impl.hpp #endif #ifdef CATCH_CONFIG_MAIN -// #included from: internal/catch_default_main.hpp -#define TWOBLUECUBES_CATCH_DEFAULT_MAIN_HPP_INCLUDED +// start catch_default_main.hpp #ifndef __OBJC__ @@ -11386,8 +11713,7 @@ extern "C" int wmain (int argc, wchar_t * argv[], wchar_t * []) { int main (int argc, char * argv[]) { #endif - int result = Catch::Session().run( argc, argv ); - return ( result < 0xff ? result : 0xff ); + return Catch::Session().run( argc, argv ); } #else // __OBJC__ @@ -11399,149 +11725,122 @@ int main (int argc, char * const argv[]) { #endif Catch::registerTestMethods(); - int result = Catch::Session().run( argc, (char* const*)argv ); + int result = Catch::Session().run( argc, (char**)argv ); #if !CATCH_ARC_ENABLED [pool drain]; #endif - return ( result < 0xff ? result : 0xff ); + return result; } #endif // __OBJC__ +// end catch_default_main.hpp #endif #ifdef CLARA_CONFIG_MAIN_NOT_DEFINED # undef CLARA_CONFIG_MAIN #endif +#if !defined(CATCH_CONFIG_DISABLE) ////// - // If this config identifier is defined then all CATCH macros are prefixed with CATCH_ #ifdef CATCH_CONFIG_PREFIX_ALL -#if defined(CATCH_CONFIG_FAST_COMPILE) -#define CATCH_REQUIRE( expr ) INTERNAL_CATCH_TEST_NO_TRY( "CATCH_REQUIRE", Catch::ResultDisposition::Normal, expr ) -#define CATCH_REQUIRE_FALSE( expr ) INTERNAL_CATCH_TEST_NO_TRY( "CATCH_REQUIRE_FALSE", Catch::ResultDisposition::Normal | Catch::ResultDisposition::FalseTest, expr ) -#else -#define CATCH_REQUIRE( expr ) INTERNAL_CATCH_TEST( "CATCH_REQUIRE", Catch::ResultDisposition::Normal, expr ) -#define CATCH_REQUIRE_FALSE( expr ) INTERNAL_CATCH_TEST( "CATCH_REQUIRE_FALSE", Catch::ResultDisposition::Normal | Catch::ResultDisposition::FalseTest, expr ) -#endif +#define CATCH_REQUIRE( ... ) INTERNAL_CATCH_TEST( "CATCH_REQUIRE", Catch::ResultDisposition::Normal, __VA_ARGS__ ) +#define CATCH_REQUIRE_FALSE( ... ) INTERNAL_CATCH_TEST( "CATCH_REQUIRE_FALSE", Catch::ResultDisposition::Normal | Catch::ResultDisposition::FalseTest, __VA_ARGS__ ) -#define CATCH_REQUIRE_THROWS( expr ) INTERNAL_CATCH_THROWS( "CATCH_REQUIRE_THROWS", Catch::ResultDisposition::Normal, "", expr ) +#define CATCH_REQUIRE_THROWS( ... ) INTERNAL_CATCH_THROWS( "CATCH_REQUIRE_THROWS", Catch::ResultDisposition::Normal, "", __VA_ARGS__ ) #define CATCH_REQUIRE_THROWS_AS( expr, exceptionType ) INTERNAL_CATCH_THROWS_AS( "CATCH_REQUIRE_THROWS_AS", exceptionType, Catch::ResultDisposition::Normal, expr ) -#define CATCH_REQUIRE_THROWS_WITH( expr, matcher ) INTERNAL_CATCH_THROWS( "CATCH_REQUIRE_THROWS_WITH", Catch::ResultDisposition::Normal, matcher, expr ) -#define CATCH_REQUIRE_NOTHROW( expr ) INTERNAL_CATCH_NO_THROW( "CATCH_REQUIRE_NOTHROW", Catch::ResultDisposition::Normal, expr ) +#define CATCH_REQUIRE_THROWS_WITH( expr, matcher ) INTERNAL_CATCH_THROWS_STR_MATCHES( "CATCH_REQUIRE_THROWS_WITH", Catch::ResultDisposition::Normal, matcher, expr ) +#if !defined(CATCH_CONFIG_DISABLE_MATCHERS) +#define CATCH_REQUIRE_THROWS_MATCHES( expr, exceptionType, matcher ) INTERNAL_CATCH_THROWS_MATCHES( "CATCH_REQUIRE_THROWS_MATCHES", exceptionType, Catch::ResultDisposition::Normal, matcher, expr ) +#endif// CATCH_CONFIG_DISABLE_MATCHERS +#define CATCH_REQUIRE_NOTHROW( ... ) INTERNAL_CATCH_NO_THROW( "CATCH_REQUIRE_NOTHROW", Catch::ResultDisposition::Normal, __VA_ARGS__ ) -#define CATCH_CHECK( expr ) INTERNAL_CATCH_TEST( "CATCH_CHECK", Catch::ResultDisposition::ContinueOnFailure, expr ) -#define CATCH_CHECK_FALSE( expr ) INTERNAL_CATCH_TEST( "CATCH_CHECK_FALSE", Catch::ResultDisposition::ContinueOnFailure | Catch::ResultDisposition::FalseTest, expr ) -#define CATCH_CHECKED_IF( expr ) INTERNAL_CATCH_IF( "CATCH_CHECKED_IF", Catch::ResultDisposition::ContinueOnFailure, expr ) -#define CATCH_CHECKED_ELSE( expr ) INTERNAL_CATCH_ELSE( "CATCH_CHECKED_ELSE", Catch::ResultDisposition::ContinueOnFailure, expr ) -#define CATCH_CHECK_NOFAIL( expr ) INTERNAL_CATCH_TEST( "CATCH_CHECK_NOFAIL", Catch::ResultDisposition::ContinueOnFailure | Catch::ResultDisposition::SuppressFail, expr ) +#define CATCH_CHECK( ... ) INTERNAL_CATCH_TEST( "CATCH_CHECK", Catch::ResultDisposition::ContinueOnFailure, __VA_ARGS__ ) +#define CATCH_CHECK_FALSE( ... ) INTERNAL_CATCH_TEST( "CATCH_CHECK_FALSE", Catch::ResultDisposition::ContinueOnFailure | Catch::ResultDisposition::FalseTest, __VA_ARGS__ ) +#define CATCH_CHECKED_IF( ... ) INTERNAL_CATCH_IF( "CATCH_CHECKED_IF", Catch::ResultDisposition::ContinueOnFailure, __VA_ARGS__ ) +#define CATCH_CHECKED_ELSE( ... ) INTERNAL_CATCH_ELSE( "CATCH_CHECKED_ELSE", Catch::ResultDisposition::ContinueOnFailure, __VA_ARGS__ ) +#define CATCH_CHECK_NOFAIL( ... ) INTERNAL_CATCH_TEST( "CATCH_CHECK_NOFAIL", Catch::ResultDisposition::ContinueOnFailure | Catch::ResultDisposition::SuppressFail, __VA_ARGS__ ) -#define CATCH_CHECK_THROWS( expr ) INTERNAL_CATCH_THROWS( "CATCH_CHECK_THROWS", Catch::ResultDisposition::ContinueOnFailure, "", expr ) +#define CATCH_CHECK_THROWS( ... ) INTERNAL_CATCH_THROWS( "CATCH_CHECK_THROWS", Catch::ResultDisposition::ContinueOnFailure, "", __VA_ARGS__ ) #define CATCH_CHECK_THROWS_AS( expr, exceptionType ) INTERNAL_CATCH_THROWS_AS( "CATCH_CHECK_THROWS_AS", exceptionType, Catch::ResultDisposition::ContinueOnFailure, expr ) -#define CATCH_CHECK_THROWS_WITH( expr, matcher ) INTERNAL_CATCH_THROWS( "CATCH_CHECK_THROWS_WITH", Catch::ResultDisposition::ContinueOnFailure, matcher, expr ) -#define CATCH_CHECK_NOTHROW( expr ) INTERNAL_CATCH_NO_THROW( "CATCH_CHECK_NOTHROW", Catch::ResultDisposition::ContinueOnFailure, expr ) +#define CATCH_CHECK_THROWS_WITH( expr, matcher ) INTERNAL_CATCH_THROWS_STR_MATCHES( "CATCH_CHECK_THROWS_WITH", Catch::ResultDisposition::ContinueOnFailure, matcher, expr ) +#if !defined(CATCH_CONFIG_DISABLE_MATCHERS) +#define CATCH_CHECK_THROWS_MATCHES( expr, exceptionType, matcher ) INTERNAL_CATCH_THROWS_MATCHES( "CATCH_CHECK_THROWS_MATCHES", exceptionType, Catch::ResultDisposition::ContinueOnFailure, matcher, expr ) +#endif // CATCH_CONFIG_DISABLE_MATCHERS +#define CATCH_CHECK_NOTHROW( ... ) INTERNAL_CATCH_NO_THROW( "CATCH_CHECK_NOTHROW", Catch::ResultDisposition::ContinueOnFailure, __VA_ARGS__ ) +#if !defined(CATCH_CONFIG_DISABLE_MATCHERS) #define CATCH_CHECK_THAT( arg, matcher ) INTERNAL_CHECK_THAT( "CATCH_CHECK_THAT", matcher, Catch::ResultDisposition::ContinueOnFailure, arg ) -#if defined(CATCH_CONFIG_FAST_COMPILE) -#define CATCH_REQUIRE_THAT( arg, matcher ) INTERNAL_CHECK_THAT_NO_TRY( "CATCH_REQUIRE_THAT", matcher, Catch::ResultDisposition::Normal, arg ) -#else #define CATCH_REQUIRE_THAT( arg, matcher ) INTERNAL_CHECK_THAT( "CATCH_REQUIRE_THAT", matcher, Catch::ResultDisposition::Normal, arg ) -#endif +#endif // CATCH_CONFIG_DISABLE_MATCHERS #define CATCH_INFO( msg ) INTERNAL_CATCH_INFO( "CATCH_INFO", msg ) #define CATCH_WARN( msg ) INTERNAL_CATCH_MSG( "CATCH_WARN", Catch::ResultWas::Warning, Catch::ResultDisposition::ContinueOnFailure, msg ) -#define CATCH_SCOPED_INFO( msg ) INTERNAL_CATCH_INFO( "CATCH_INFO", msg ) -#define CATCH_CAPTURE( msg ) INTERNAL_CATCH_INFO( "CATCH_CAPTURE", #msg " := " << Catch::toString(msg) ) -#define CATCH_SCOPED_CAPTURE( msg ) INTERNAL_CATCH_INFO( "CATCH_CAPTURE", #msg " := " << Catch::toString(msg) ) +#define CATCH_CAPTURE( msg ) INTERNAL_CATCH_INFO( "CATCH_CAPTURE", #msg " := " << ::Catch::Detail::stringify(msg) ) -#ifdef CATCH_CONFIG_VARIADIC_MACROS - #define CATCH_TEST_CASE( ... ) INTERNAL_CATCH_TESTCASE( __VA_ARGS__ ) - #define CATCH_TEST_CASE_METHOD( className, ... ) INTERNAL_CATCH_TEST_CASE_METHOD( className, __VA_ARGS__ ) - #define CATCH_METHOD_AS_TEST_CASE( method, ... ) INTERNAL_CATCH_METHOD_AS_TEST_CASE( method, __VA_ARGS__ ) - #define CATCH_REGISTER_TEST_CASE( Function, ... ) INTERNAL_CATCH_REGISTER_TESTCASE( Function, __VA_ARGS__ ) - #define CATCH_SECTION( ... ) INTERNAL_CATCH_SECTION( __VA_ARGS__ ) - #define CATCH_FAIL( ... ) INTERNAL_CATCH_MSG( "CATCH_FAIL", Catch::ResultWas::ExplicitFailure, Catch::ResultDisposition::Normal, __VA_ARGS__ ) - #define CATCH_FAIL_CHECK( ... ) INTERNAL_CATCH_MSG( "CATCH_FAIL_CHECK", Catch::ResultWas::ExplicitFailure, Catch::ResultDisposition::ContinueOnFailure, __VA_ARGS__ ) - #define CATCH_SUCCEED( ... ) INTERNAL_CATCH_MSG( "CATCH_SUCCEED", Catch::ResultWas::Ok, Catch::ResultDisposition::ContinueOnFailure, __VA_ARGS__ ) -#else - #define CATCH_TEST_CASE( name, description ) INTERNAL_CATCH_TESTCASE( name, description ) - #define CATCH_TEST_CASE_METHOD( className, name, description ) INTERNAL_CATCH_TEST_CASE_METHOD( className, name, description ) - #define CATCH_METHOD_AS_TEST_CASE( method, name, description ) INTERNAL_CATCH_METHOD_AS_TEST_CASE( method, name, description ) - #define CATCH_REGISTER_TEST_CASE( function, name, description ) INTERNAL_CATCH_REGISTER_TESTCASE( function, name, description ) - #define CATCH_SECTION( name, description ) INTERNAL_CATCH_SECTION( name, description ) - #define CATCH_FAIL( msg ) INTERNAL_CATCH_MSG( "CATCH_FAIL", Catch::ResultWas::ExplicitFailure, Catch::ResultDisposition::Normal, msg ) - #define CATCH_FAIL_CHECK( msg ) INTERNAL_CATCH_MSG( "CATCH_FAIL_CHECK", Catch::ResultWas::ExplicitFailure, Catch::ResultDisposition::ContinueOnFailure, msg ) - #define CATCH_SUCCEED( msg ) INTERNAL_CATCH_MSG( "CATCH_SUCCEED", Catch::ResultWas::Ok, Catch::ResultDisposition::ContinueOnFailure, msg ) -#endif -#define CATCH_ANON_TEST_CASE() INTERNAL_CATCH_TESTCASE( "", "" ) +#define CATCH_TEST_CASE( ... ) INTERNAL_CATCH_TESTCASE( __VA_ARGS__ ) +#define CATCH_TEST_CASE_METHOD( className, ... ) INTERNAL_CATCH_TEST_CASE_METHOD( className, __VA_ARGS__ ) +#define CATCH_METHOD_AS_TEST_CASE( method, ... ) INTERNAL_CATCH_METHOD_AS_TEST_CASE( method, __VA_ARGS__ ) +#define CATCH_REGISTER_TEST_CASE( Function, ... ) INTERNAL_CATCH_REGISTER_TESTCASE( Function, __VA_ARGS__ ) +#define CATCH_SECTION( ... ) INTERNAL_CATCH_SECTION( __VA_ARGS__ ) +#define CATCH_FAIL( ... ) INTERNAL_CATCH_MSG( "CATCH_FAIL", Catch::ResultWas::ExplicitFailure, Catch::ResultDisposition::Normal, __VA_ARGS__ ) +#define CATCH_FAIL_CHECK( ... ) INTERNAL_CATCH_MSG( "CATCH_FAIL_CHECK", Catch::ResultWas::ExplicitFailure, Catch::ResultDisposition::ContinueOnFailure, __VA_ARGS__ ) +#define CATCH_SUCCEED( ... ) INTERNAL_CATCH_MSG( "CATCH_SUCCEED", Catch::ResultWas::Ok, Catch::ResultDisposition::ContinueOnFailure, __VA_ARGS__ ) -#define CATCH_REGISTER_REPORTER( name, reporterType ) INTERNAL_CATCH_REGISTER_REPORTER( name, reporterType ) -#define CATCH_REGISTER_LEGACY_REPORTER( name, reporterType ) INTERNAL_CATCH_REGISTER_LEGACY_REPORTER( name, reporterType ) - -#define CATCH_GENERATE( expr) INTERNAL_CATCH_GENERATE( expr ) +#define CATCH_ANON_TEST_CASE() INTERNAL_CATCH_TESTCASE() // "BDD-style" convenience wrappers -#ifdef CATCH_CONFIG_VARIADIC_MACROS #define CATCH_SCENARIO( ... ) CATCH_TEST_CASE( "Scenario: " __VA_ARGS__ ) #define CATCH_SCENARIO_METHOD( className, ... ) INTERNAL_CATCH_TEST_CASE_METHOD( className, "Scenario: " __VA_ARGS__ ) -#else -#define CATCH_SCENARIO( name, tags ) CATCH_TEST_CASE( "Scenario: " name, tags ) -#define CATCH_SCENARIO_METHOD( className, name, tags ) INTERNAL_CATCH_TEST_CASE_METHOD( className, "Scenario: " name, tags ) -#endif -#define CATCH_GIVEN( desc ) CATCH_SECTION( std::string( "Given: ") + desc, "" ) -#define CATCH_WHEN( desc ) CATCH_SECTION( std::string( " When: ") + desc, "" ) -#define CATCH_AND_WHEN( desc ) CATCH_SECTION( std::string( " And: ") + desc, "" ) -#define CATCH_THEN( desc ) CATCH_SECTION( std::string( " Then: ") + desc, "" ) -#define CATCH_AND_THEN( desc ) CATCH_SECTION( std::string( " And: ") + desc, "" ) +#define CATCH_GIVEN( desc ) CATCH_SECTION( std::string( "Given: ") + desc ) +#define CATCH_WHEN( desc ) CATCH_SECTION( std::string( " When: ") + desc ) +#define CATCH_AND_WHEN( desc ) CATCH_SECTION( std::string( " And: ") + desc ) +#define CATCH_THEN( desc ) CATCH_SECTION( std::string( " Then: ") + desc ) +#define CATCH_AND_THEN( desc ) CATCH_SECTION( std::string( " And: ") + desc ) // If CATCH_CONFIG_PREFIX_ALL is not defined then the CATCH_ prefix is not required #else -#if defined(CATCH_CONFIG_FAST_COMPILE) -#define REQUIRE( expr ) INTERNAL_CATCH_TEST_NO_TRY( "REQUIRE", Catch::ResultDisposition::Normal, expr ) -#define REQUIRE_FALSE( expr ) INTERNAL_CATCH_TEST_NO_TRY( "REQUIRE_FALSE", Catch::ResultDisposition::Normal | Catch::ResultDisposition::FalseTest, expr ) +#define REQUIRE( ... ) INTERNAL_CATCH_TEST( "REQUIRE", Catch::ResultDisposition::Normal, __VA_ARGS__ ) +#define REQUIRE_FALSE( ... ) INTERNAL_CATCH_TEST( "REQUIRE_FALSE", Catch::ResultDisposition::Normal | Catch::ResultDisposition::FalseTest, __VA_ARGS__ ) -#else -#define REQUIRE( expr ) INTERNAL_CATCH_TEST( "REQUIRE", Catch::ResultDisposition::Normal, expr ) -#define REQUIRE_FALSE( expr ) INTERNAL_CATCH_TEST( "REQUIRE_FALSE", Catch::ResultDisposition::Normal | Catch::ResultDisposition::FalseTest, expr ) -#endif - -#define REQUIRE_THROWS( expr ) INTERNAL_CATCH_THROWS( "REQUIRE_THROWS", Catch::ResultDisposition::Normal, "", expr ) +#define REQUIRE_THROWS( ... ) INTERNAL_CATCH_THROWS( "REQUIRE_THROWS", Catch::ResultDisposition::Normal, __VA_ARGS__ ) #define REQUIRE_THROWS_AS( expr, exceptionType ) INTERNAL_CATCH_THROWS_AS( "REQUIRE_THROWS_AS", exceptionType, Catch::ResultDisposition::Normal, expr ) -#define REQUIRE_THROWS_WITH( expr, matcher ) INTERNAL_CATCH_THROWS( "REQUIRE_THROWS_WITH", Catch::ResultDisposition::Normal, matcher, expr ) -#define REQUIRE_NOTHROW( expr ) INTERNAL_CATCH_NO_THROW( "REQUIRE_NOTHROW", Catch::ResultDisposition::Normal, expr ) +#define REQUIRE_THROWS_WITH( expr, matcher ) INTERNAL_CATCH_THROWS_STR_MATCHES( "REQUIRE_THROWS_WITH", Catch::ResultDisposition::Normal, matcher, expr ) +#if !defined(CATCH_CONFIG_DISABLE_MATCHERS) +#define REQUIRE_THROWS_MATCHES( expr, exceptionType, matcher ) INTERNAL_CATCH_THROWS_MATCHES( "REQUIRE_THROWS_MATCHES", exceptionType, Catch::ResultDisposition::Normal, matcher, expr ) +#endif // CATCH_CONFIG_DISABLE_MATCHERS +#define REQUIRE_NOTHROW( ... ) INTERNAL_CATCH_NO_THROW( "REQUIRE_NOTHROW", Catch::ResultDisposition::Normal, __VA_ARGS__ ) -#define CHECK( expr ) INTERNAL_CATCH_TEST( "CHECK", Catch::ResultDisposition::ContinueOnFailure, expr ) -#define CHECK_FALSE( expr ) INTERNAL_CATCH_TEST( "CHECK_FALSE", Catch::ResultDisposition::ContinueOnFailure | Catch::ResultDisposition::FalseTest, expr ) -#define CHECKED_IF( expr ) INTERNAL_CATCH_IF( "CHECKED_IF", Catch::ResultDisposition::ContinueOnFailure, expr ) -#define CHECKED_ELSE( expr ) INTERNAL_CATCH_ELSE( "CHECKED_ELSE", Catch::ResultDisposition::ContinueOnFailure, expr ) -#define CHECK_NOFAIL( expr ) INTERNAL_CATCH_TEST( "CHECK_NOFAIL", Catch::ResultDisposition::ContinueOnFailure | Catch::ResultDisposition::SuppressFail, expr ) +#define CHECK( ... ) INTERNAL_CATCH_TEST( "CHECK", Catch::ResultDisposition::ContinueOnFailure, __VA_ARGS__ ) +#define CHECK_FALSE( ... ) INTERNAL_CATCH_TEST( "CHECK_FALSE", Catch::ResultDisposition::ContinueOnFailure | Catch::ResultDisposition::FalseTest, __VA_ARGS__ ) +#define CHECKED_IF( ... ) INTERNAL_CATCH_IF( "CHECKED_IF", Catch::ResultDisposition::ContinueOnFailure, __VA_ARGS__ ) +#define CHECKED_ELSE( ... ) INTERNAL_CATCH_ELSE( "CHECKED_ELSE", Catch::ResultDisposition::ContinueOnFailure, __VA_ARGS__ ) +#define CHECK_NOFAIL( ... ) INTERNAL_CATCH_TEST( "CHECK_NOFAIL", Catch::ResultDisposition::ContinueOnFailure | Catch::ResultDisposition::SuppressFail, __VA_ARGS__ ) -#define CHECK_THROWS( expr ) INTERNAL_CATCH_THROWS( "CHECK_THROWS", Catch::ResultDisposition::ContinueOnFailure, "", expr ) +#define CHECK_THROWS( ... ) INTERNAL_CATCH_THROWS( "CHECK_THROWS", Catch::ResultDisposition::ContinueOnFailure, __VA_ARGS__ ) #define CHECK_THROWS_AS( expr, exceptionType ) INTERNAL_CATCH_THROWS_AS( "CHECK_THROWS_AS", exceptionType, Catch::ResultDisposition::ContinueOnFailure, expr ) -#define CHECK_THROWS_WITH( expr, matcher ) INTERNAL_CATCH_THROWS( "CHECK_THROWS_WITH", Catch::ResultDisposition::ContinueOnFailure, matcher, expr ) -#define CHECK_NOTHROW( expr ) INTERNAL_CATCH_NO_THROW( "CHECK_NOTHROW", Catch::ResultDisposition::ContinueOnFailure, expr ) +#define CHECK_THROWS_WITH( expr, matcher ) INTERNAL_CATCH_THROWS_STR_MATCHES( "CHECK_THROWS_WITH", Catch::ResultDisposition::ContinueOnFailure, matcher, expr ) +#if !defined(CATCH_CONFIG_DISABLE_MATCHERS) +#define CHECK_THROWS_MATCHES( expr, exceptionType, matcher ) INTERNAL_CATCH_THROWS_MATCHES( "CHECK_THROWS_MATCHES", exceptionType, Catch::ResultDisposition::ContinueOnFailure, matcher, expr ) +#endif // CATCH_CONFIG_DISABLE_MATCHERS +#define CHECK_NOTHROW( ... ) INTERNAL_CATCH_NO_THROW( "CHECK_NOTHROW", Catch::ResultDisposition::ContinueOnFailure, __VA_ARGS__ ) +#if !defined(CATCH_CONFIG_DISABLE_MATCHERS) #define CHECK_THAT( arg, matcher ) INTERNAL_CHECK_THAT( "CHECK_THAT", matcher, Catch::ResultDisposition::ContinueOnFailure, arg ) -#if defined(CATCH_CONFIG_FAST_COMPILE) -#define REQUIRE_THAT( arg, matcher ) INTERNAL_CHECK_THAT_NO_TRY( "REQUIRE_THAT", matcher, Catch::ResultDisposition::Normal, arg ) -#else #define REQUIRE_THAT( arg, matcher ) INTERNAL_CHECK_THAT( "REQUIRE_THAT", matcher, Catch::ResultDisposition::Normal, arg ) -#endif +#endif // CATCH_CONFIG_DISABLE_MATCHERS #define INFO( msg ) INTERNAL_CATCH_INFO( "INFO", msg ) #define WARN( msg ) INTERNAL_CATCH_MSG( "WARN", Catch::ResultWas::Warning, Catch::ResultDisposition::ContinueOnFailure, msg ) -#define SCOPED_INFO( msg ) INTERNAL_CATCH_INFO( "INFO", msg ) -#define CAPTURE( msg ) INTERNAL_CATCH_INFO( "CAPTURE", #msg " := " << Catch::toString(msg) ) -#define SCOPED_CAPTURE( msg ) INTERNAL_CATCH_INFO( "CAPTURE", #msg " := " << Catch::toString(msg) ) +#define CAPTURE( msg ) INTERNAL_CATCH_INFO( "CAPTURE", #msg " := " << ::Catch::Detail::stringify(msg) ) -#ifdef CATCH_CONFIG_VARIADIC_MACROS #define TEST_CASE( ... ) INTERNAL_CATCH_TESTCASE( __VA_ARGS__ ) #define TEST_CASE_METHOD( className, ... ) INTERNAL_CATCH_TEST_CASE_METHOD( className, __VA_ARGS__ ) #define METHOD_AS_TEST_CASE( method, ... ) INTERNAL_CATCH_METHOD_AS_TEST_CASE( method, __VA_ARGS__ ) @@ -11550,46 +11849,152 @@ int main (int argc, char * const argv[]) { #define FAIL( ... ) INTERNAL_CATCH_MSG( "FAIL", Catch::ResultWas::ExplicitFailure, Catch::ResultDisposition::Normal, __VA_ARGS__ ) #define FAIL_CHECK( ... ) INTERNAL_CATCH_MSG( "FAIL_CHECK", Catch::ResultWas::ExplicitFailure, Catch::ResultDisposition::ContinueOnFailure, __VA_ARGS__ ) #define SUCCEED( ... ) INTERNAL_CATCH_MSG( "SUCCEED", Catch::ResultWas::Ok, Catch::ResultDisposition::ContinueOnFailure, __VA_ARGS__ ) -#else -#define TEST_CASE( name, description ) INTERNAL_CATCH_TESTCASE( name, description ) - #define TEST_CASE_METHOD( className, name, description ) INTERNAL_CATCH_TEST_CASE_METHOD( className, name, description ) - #define METHOD_AS_TEST_CASE( method, name, description ) INTERNAL_CATCH_METHOD_AS_TEST_CASE( method, name, description ) - #define REGISTER_TEST_CASE( method, name, description ) INTERNAL_CATCH_REGISTER_TESTCASE( method, name, description ) - #define SECTION( name, description ) INTERNAL_CATCH_SECTION( name, description ) - #define FAIL( msg ) INTERNAL_CATCH_MSG( "FAIL", Catch::ResultWas::ExplicitFailure, Catch::ResultDisposition::Normal, msg ) - #define FAIL_CHECK( msg ) INTERNAL_CATCH_MSG( "FAIL_CHECK", Catch::ResultWas::ExplicitFailure, Catch::ResultDisposition::ContinueOnFailure, msg ) - #define SUCCEED( msg ) INTERNAL_CATCH_MSG( "SUCCEED", Catch::ResultWas::Ok, Catch::ResultDisposition::ContinueOnFailure, msg ) -#endif -#define ANON_TEST_CASE() INTERNAL_CATCH_TESTCASE( "", "" ) - -#define REGISTER_REPORTER( name, reporterType ) INTERNAL_CATCH_REGISTER_REPORTER( name, reporterType ) -#define REGISTER_LEGACY_REPORTER( name, reporterType ) INTERNAL_CATCH_REGISTER_LEGACY_REPORTER( name, reporterType ) - -#define GENERATE( expr) INTERNAL_CATCH_GENERATE( expr ) +#define ANON_TEST_CASE() INTERNAL_CATCH_TESTCASE() #endif #define CATCH_TRANSLATE_EXCEPTION( signature ) INTERNAL_CATCH_TRANSLATE_EXCEPTION( signature ) // "BDD-style" convenience wrappers -#ifdef CATCH_CONFIG_VARIADIC_MACROS #define SCENARIO( ... ) TEST_CASE( "Scenario: " __VA_ARGS__ ) #define SCENARIO_METHOD( className, ... ) INTERNAL_CATCH_TEST_CASE_METHOD( className, "Scenario: " __VA_ARGS__ ) -#else -#define SCENARIO( name, tags ) TEST_CASE( "Scenario: " name, tags ) -#define SCENARIO_METHOD( className, name, tags ) INTERNAL_CATCH_TEST_CASE_METHOD( className, "Scenario: " name, tags ) -#endif -#define GIVEN( desc ) SECTION( std::string(" Given: ") + desc, "" ) -#define WHEN( desc ) SECTION( std::string(" When: ") + desc, "" ) -#define AND_WHEN( desc ) SECTION( std::string("And when: ") + desc, "" ) -#define THEN( desc ) SECTION( std::string(" Then: ") + desc, "" ) -#define AND_THEN( desc ) SECTION( std::string(" And: ") + desc, "" ) + +#define GIVEN( desc ) SECTION( std::string(" Given: ") + desc ) +#define WHEN( desc ) SECTION( std::string(" When: ") + desc ) +#define AND_WHEN( desc ) SECTION( std::string("And when: ") + desc ) +#define THEN( desc ) SECTION( std::string(" Then: ") + desc ) +#define AND_THEN( desc ) SECTION( std::string(" And: ") + desc ) using Catch::Detail::Approx; -// #included from: internal/catch_reenable_warnings.h +#else +////// +// If this config identifier is defined then all CATCH macros are prefixed with CATCH_ +#ifdef CATCH_CONFIG_PREFIX_ALL + +#define CATCH_REQUIRE( ... ) (void)(0) +#define CATCH_REQUIRE_FALSE( ... ) (void)(0) + +#define CATCH_REQUIRE_THROWS( ... ) (void)(0) +#define CATCH_REQUIRE_THROWS_AS( expr, exceptionType ) (void)(0) +#define CATCH_REQUIRE_THROWS_WITH( expr, matcher ) (void)(0) +#if !defined(CATCH_CONFIG_DISABLE_MATCHERS) +#define CATCH_REQUIRE_THROWS_MATCHES( expr, exceptionType, matcher ) (void)(0) +#endif// CATCH_CONFIG_DISABLE_MATCHERS +#define CATCH_REQUIRE_NOTHROW( ... ) (void)(0) + +#define CATCH_CHECK( ... ) (void)(0) +#define CATCH_CHECK_FALSE( ... ) (void)(0) +#define CATCH_CHECKED_IF( ... ) if (__VA_ARGS__) +#define CATCH_CHECKED_ELSE( ... ) if (!(__VA_ARGS__)) +#define CATCH_CHECK_NOFAIL( ... ) (void)(0) + +#define CATCH_CHECK_THROWS( ... ) (void)(0) +#define CATCH_CHECK_THROWS_AS( expr, exceptionType ) (void)(0) +#define CATCH_CHECK_THROWS_WITH( expr, matcher ) (void)(0) +#if !defined(CATCH_CONFIG_DISABLE_MATCHERS) +#define CATCH_CHECK_THROWS_MATCHES( expr, exceptionType, matcher ) (void)(0) +#endif // CATCH_CONFIG_DISABLE_MATCHERS +#define CATCH_CHECK_NOTHROW( ... ) (void)(0) + +#if !defined(CATCH_CONFIG_DISABLE_MATCHERS) +#define CATCH_CHECK_THAT( arg, matcher ) (void)(0) + +#define CATCH_REQUIRE_THAT( arg, matcher ) (void)(0) +#endif // CATCH_CONFIG_DISABLE_MATCHERS + +#define CATCH_INFO( msg ) (void)(0) +#define CATCH_WARN( msg ) (void)(0) +#define CATCH_CAPTURE( msg ) (void)(0) + +#define CATCH_TEST_CASE( ... ) INTERNAL_CATCH_TESTCASE_NO_REGISTRATION(INTERNAL_CATCH_UNIQUE_NAME( ____C_A_T_C_H____T_E_S_T____ )) +#define CATCH_TEST_CASE_METHOD( className, ... ) INTERNAL_CATCH_TESTCASE_NO_REGISTRATION(INTERNAL_CATCH_UNIQUE_NAME( ____C_A_T_C_H____T_E_S_T____ )) +#define CATCH_METHOD_AS_TEST_CASE( method, ... ) +#define CATCH_REGISTER_TEST_CASE( Function, ... ) (void)(0) +#define CATCH_SECTION( ... ) +#define CATCH_FAIL( ... ) (void)(0) +#define CATCH_FAIL_CHECK( ... ) (void)(0) +#define CATCH_SUCCEED( ... ) (void)(0) + +#define CATCH_ANON_TEST_CASE() INTERNAL_CATCH_TESTCASE_NO_REGISTRATION(INTERNAL_CATCH_UNIQUE_NAME( ____C_A_T_C_H____T_E_S_T____ )) + +// "BDD-style" convenience wrappers +#define CATCH_SCENARIO( ... ) INTERNAL_CATCH_TESTCASE_NO_REGISTRATION(INTERNAL_CATCH_UNIQUE_NAME( ____C_A_T_C_H____T_E_S_T____ )) +#define CATCH_SCENARIO_METHOD( className, ... ) INTERNAL_CATCH_TESTCASE_METHOD_NO_REGISTRATION(INTERNAL_CATCH_UNIQUE_NAME( ____C_A_T_C_H____T_E_S_T____ ), className ) +#define CATCH_GIVEN( desc ) +#define CATCH_WHEN( desc ) +#define CATCH_AND_WHEN( desc ) +#define CATCH_THEN( desc ) +#define CATCH_AND_THEN( desc ) + +// If CATCH_CONFIG_PREFIX_ALL is not defined then the CATCH_ prefix is not required +#else + +#define REQUIRE( ... ) (void)(0) +#define REQUIRE_FALSE( ... ) (void)(0) + +#define REQUIRE_THROWS( ... ) (void)(0) +#define REQUIRE_THROWS_AS( expr, exceptionType ) (void)(0) +#define REQUIRE_THROWS_WITH( expr, matcher ) (void)(0) +#if !defined(CATCH_CONFIG_DISABLE_MATCHERS) +#define REQUIRE_THROWS_MATCHES( expr, exceptionType, matcher ) (void)(0) +#endif // CATCH_CONFIG_DISABLE_MATCHERS +#define REQUIRE_NOTHROW( ... ) (void)(0) + +#define CHECK( ... ) (void)(0) +#define CHECK_FALSE( ... ) (void)(0) +#define CHECKED_IF( ... ) if (__VA_ARGS__) +#define CHECKED_ELSE( ... ) if (!(__VA_ARGS__)) +#define CHECK_NOFAIL( ... ) (void)(0) + +#define CHECK_THROWS( ... ) (void)(0) +#define CHECK_THROWS_AS( expr, exceptionType ) (void)(0) +#define CHECK_THROWS_WITH( expr, matcher ) (void)(0) +#if !defined(CATCH_CONFIG_DISABLE_MATCHERS) +#define CHECK_THROWS_MATCHES( expr, exceptionType, matcher ) (void)(0) +#endif // CATCH_CONFIG_DISABLE_MATCHERS +#define CHECK_NOTHROW( ... ) (void)(0) + +#if !defined(CATCH_CONFIG_DISABLE_MATCHERS) +#define CHECK_THAT( arg, matcher ) (void)(0) + +#define REQUIRE_THAT( arg, matcher ) (void)(0) +#endif // CATCH_CONFIG_DISABLE_MATCHERS + +#define INFO( msg ) (void)(0) +#define WARN( msg ) (void)(0) +#define CAPTURE( msg ) (void)(0) + +#define TEST_CASE( ... ) INTERNAL_CATCH_TESTCASE_NO_REGISTRATION(INTERNAL_CATCH_UNIQUE_NAME( ____C_A_T_C_H____T_E_S_T____ )) +#define TEST_CASE_METHOD( className, ... ) INTERNAL_CATCH_TESTCASE_NO_REGISTRATION(INTERNAL_CATCH_UNIQUE_NAME( ____C_A_T_C_H____T_E_S_T____ )) +#define METHOD_AS_TEST_CASE( method, ... ) +#define REGISTER_TEST_CASE( Function, ... ) (void)(0) +#define SECTION( ... ) +#define FAIL( ... ) (void)(0) +#define FAIL_CHECK( ... ) (void)(0) +#define SUCCEED( ... ) (void)(0) +#define ANON_TEST_CASE() INTERNAL_CATCH_TESTCASE_NO_REGISTRATION(INTERNAL_CATCH_UNIQUE_NAME( ____C_A_T_C_H____T_E_S_T____ )) + +#endif + +#define CATCH_TRANSLATE_EXCEPTION( signature ) INTERNAL_CATCH_TRANSLATE_EXCEPTION_NO_REG( INTERNAL_CATCH_UNIQUE_NAME( catch_internal_ExceptionTranslator ), signature ) + +// "BDD-style" convenience wrappers +#define SCENARIO( ... ) INTERNAL_CATCH_TESTCASE_NO_REGISTRATION(INTERNAL_CATCH_UNIQUE_NAME( ____C_A_T_C_H____T_E_S_T____ ) ) +#define SCENARIO_METHOD( className, ... ) INTERNAL_CATCH_TESTCASE_METHOD_NO_REGISTRATION(INTERNAL_CATCH_UNIQUE_NAME( ____C_A_T_C_H____T_E_S_T____ ), className ) + +#define GIVEN( desc ) +#define WHEN( desc ) +#define AND_WHEN( desc ) +#define THEN( desc ) +#define AND_THEN( desc ) + +using Catch::Detail::Approx; + +#endif + +// start catch_reenable_warnings.h -#define TWOBLUECUBES_CATCH_REENABLE_WARNINGS_H_INCLUDED #ifdef __clang__ # ifdef __ICC // icpc defines the __clang__ macro @@ -11601,5 +12006,7 @@ using Catch::Detail::Approx; # pragma GCC diagnostic pop #endif +// end catch_reenable_warnings.h +// end catch.hpp #endif // TWOBLUECUBES_SINGLE_INCLUDE_CATCH_HPP_INCLUDED diff --git a/test_package/conanfile.py b/test_package/conanfile.py index a864b9da..207c480e 100644 --- a/test_package/conanfile.py +++ b/test_package/conanfile.py @@ -10,7 +10,7 @@ class CatchConanTest(ConanFile): settings = "os", "compiler", "arch", "build_type" username = getenv("CONAN_USERNAME", "philsquared") channel = getenv("CONAN_CHANNEL", "testing") - requires = "Catch/1.11.0@%s/%s" % (username, channel) + requires = "Catch/2.0.1@%s/%s" % (username, channel) def build(self): cmake = CMake(self) diff --git a/third_party/clara.hpp b/third_party/clara.hpp new file mode 100644 index 00000000..aa429e7a --- /dev/null +++ b/third_party/clara.hpp @@ -0,0 +1,1231 @@ +// v1.0-develop.2 +// See https://github.com/philsquared/Clara + +#ifndef CLARA_HPP_INCLUDED +#define CLARA_HPP_INCLUDED + +#ifndef CLARA_CONFIG_CONSOLE_WIDTH +#define CLARA_CONFIG_CONSOLE_WIDTH 80 +#endif + +#ifndef CLARA_TEXTFLOW_CONFIG_CONSOLE_WIDTH +#define CLARA_TEXTFLOW_CONFIG_CONSOLE_WIDTH CLARA_CONFIG_CONSOLE_WIDTH +#endif + +// ----------- #included from clara_textflow.hpp ----------- + +// TextFlowCpp +// +// A single-header library for wrapping and laying out basic text, by Phil Nash +// +// This work is licensed under the BSD 2-Clause license. +// See the accompanying LICENSE file, or the one at https://opensource.org/licenses/BSD-2-Clause +// +// This project is hosted at https://github.com/philsquared/textflowcpp + +#ifndef CLARA_TEXTFLOW_HPP_INCLUDED +#define CLARA_TEXTFLOW_HPP_INCLUDED + +#include +#include +#include +#include + +#ifndef CLARA_TEXTFLOW_CONFIG_CONSOLE_WIDTH +#define CLARA_TEXTFLOW_CONFIG_CONSOLE_WIDTH 80 +#endif + + +namespace clara { namespace TextFlow { + + inline auto isWhitespace( char c ) -> bool { + static std::string chars = " \t\n\r"; + return chars.find( c ) != std::string::npos; + } + inline auto isBreakableBefore( char c ) -> bool { + static std::string chars = "[({<|"; + return chars.find( c ) != std::string::npos; + } + inline auto isBreakableAfter( char c ) -> bool { + static std::string chars = "])}>.,:;*+-=&/\\"; + return chars.find( c ) != std::string::npos; + } + + class Columns; + + class Column { + std::vector m_strings; + size_t m_width = CLARA_TEXTFLOW_CONFIG_CONSOLE_WIDTH; + size_t m_indent = 0; + size_t m_initialIndent = std::string::npos; + + public: + class iterator { + friend Column; + + Column const& m_column; + size_t m_stringIndex = 0; + size_t m_pos = 0; + + size_t m_len = 0; + size_t m_end = 0; + bool m_suffix = false; + + iterator( Column const& column, size_t stringIndex ) + : m_column( column ), + m_stringIndex( stringIndex ) + {} + + auto line() const -> std::string const& { return m_column.m_strings[m_stringIndex]; } + + auto isBoundary( size_t at ) const -> bool { + assert( at > 0 ); + assert( at <= line().size() ); + + return at == line().size() || + ( isWhitespace( line()[at] ) && !isWhitespace( line()[at-1] ) ) || + isBreakableBefore( line()[at] ) || + isBreakableAfter( line()[at-1] ); + } + + void calcLength() { + assert( m_stringIndex < m_column.m_strings.size() ); + + m_suffix = false; + auto width = m_column.m_width-indent(); + m_end = m_pos; + while( m_end < line().size() && line()[m_end] != '\n' ) + ++m_end; + + if( m_end < m_pos + width ) { + m_len = m_end - m_pos; + } + else { + size_t len = width; + while (len > 0 && !isBoundary(m_pos + len)) + --len; + while (len > 0 && isWhitespace( line()[m_pos + len - 1] )) + --len; + + if (len > 0) { + m_len = len; + } else { + m_suffix = true; + m_len = width - 1; + } + } + } + + auto indent() const -> size_t { + auto initial = m_pos == 0 && m_stringIndex == 0 ? m_column.m_initialIndent : std::string::npos; + return initial == std::string::npos ? m_column.m_indent : initial; + } + + auto addIndentAndSuffix(std::string const &plain) const -> std::string { + return std::string( indent(), ' ' ) + (m_suffix ? plain + "-" : plain); + } + + public: + explicit iterator( Column const& column ) : m_column( column ) { + assert( m_column.m_width > m_column.m_indent ); + assert( m_column.m_initialIndent == std::string::npos || m_column.m_width > m_column.m_initialIndent ); + calcLength(); + if( m_len == 0 ) + m_stringIndex++; // Empty string + } + + auto operator *() const -> std::string { + assert( m_stringIndex < m_column.m_strings.size() ); + assert( m_pos <= m_end ); + if( m_pos + m_column.m_width < m_end ) + return addIndentAndSuffix(line().substr(m_pos, m_len)); + else + return addIndentAndSuffix(line().substr(m_pos, m_end - m_pos)); + } + + auto operator ++() -> iterator& { + m_pos += m_len; + if( m_pos < line().size() && line()[m_pos] == '\n' ) + m_pos += 1; + else + while( m_pos < line().size() && isWhitespace( line()[m_pos] ) ) + ++m_pos; + + if( m_pos == line().size() ) { + m_pos = 0; + ++m_stringIndex; + } + if( m_stringIndex < m_column.m_strings.size() ) + calcLength(); + return *this; + } + auto operator ++(int) -> iterator { + iterator prev( *this ); + operator++(); + return prev; + } + + auto operator ==( iterator const& other ) const -> bool { + return + m_pos == other.m_pos && + m_stringIndex == other.m_stringIndex && + &m_column == &other.m_column; + } + auto operator !=( iterator const& other ) const -> bool { + return !operator==( other ); + } + }; + using const_iterator = iterator; + + explicit Column( std::string const& text ) { m_strings.push_back( text ); } + + auto width( size_t newWidth ) -> Column& { + assert( newWidth > 0 ); + m_width = newWidth; + return *this; + } + auto indent( size_t newIndent ) -> Column& { + m_indent = newIndent; + return *this; + } + auto initialIndent( size_t newIndent ) -> Column& { + m_initialIndent = newIndent; + return *this; + } + + auto width() const -> size_t { return m_width; } + auto begin() const -> iterator { return iterator( *this ); } + auto end() const -> iterator { return { *this, m_strings.size() }; } + + inline friend std::ostream& operator << ( std::ostream& os, Column const& col ) { + bool first = true; + for( auto line : col ) { + if( first ) + first = false; + else + os << "\n"; + os << line; + } + return os; + } + + auto operator + ( Column const& other ) -> Columns; + + auto toString() const -> std::string { + std::ostringstream oss; + oss << *this; + return oss.str(); + } + }; + + class Spacer : public Column { + + public: + explicit Spacer( size_t spaceWidth ) : Column( "" ) { + width( spaceWidth ); + } + }; + + class Columns { + std::vector m_columns; + + public: + + class iterator { + friend Columns; + struct EndTag {}; + + std::vector const& m_columns; + std::vector m_iterators; + size_t m_activeIterators; + + iterator( Columns const& columns, EndTag ) + : m_columns( columns.m_columns ), + m_activeIterators( 0 ) + { + m_iterators.reserve( m_columns.size() ); + + for( auto const& col : m_columns ) + m_iterators.push_back( col.end() ); + } + + public: + explicit iterator( Columns const& columns ) + : m_columns( columns.m_columns ), + m_activeIterators( m_columns.size() ) + { + m_iterators.reserve( m_columns.size() ); + + for( auto const& col : m_columns ) + m_iterators.push_back( col.begin() ); + } + + auto operator ==( iterator const& other ) const -> bool { + return m_iterators == other.m_iterators; + } + auto operator !=( iterator const& other ) const -> bool { + return m_iterators != other.m_iterators; + } + auto operator *() const -> std::string { + std::string row, padding; + + for( size_t i = 0; i < m_columns.size(); ++i ) { + auto width = m_columns[i].width(); + if( m_iterators[i] != m_columns[i].end() ) { + std::string col = *m_iterators[i]; + row += padding + col; + if( col.size() < width ) + padding = std::string( width - col.size(), ' ' ); + else + padding = ""; + } + else { + padding += std::string( width, ' ' ); + } + } + return row; + } + auto operator ++() -> iterator& { + for( size_t i = 0; i < m_columns.size(); ++i ) { + if (m_iterators[i] != m_columns[i].end()) + ++m_iterators[i]; + } + return *this; + } + auto operator ++(int) -> iterator { + iterator prev( *this ); + operator++(); + return prev; + } + }; + using const_iterator = iterator; + + auto begin() const -> iterator { return iterator( *this ); } + auto end() const -> iterator { return { *this, iterator::EndTag() }; } + + auto operator += ( Column const& col ) -> Columns& { + m_columns.push_back( col ); + return *this; + } + auto operator + ( Column const& col ) -> Columns { + Columns combined = *this; + combined += col; + return combined; + } + + inline friend std::ostream& operator << ( std::ostream& os, Columns const& cols ) { + + bool first = true; + for( auto line : cols ) { + if( first ) + first = false; + else + os << "\n"; + os << line; + } + return os; + } + + auto toString() const -> std::string { + std::ostringstream oss; + oss << *this; + return oss.str(); + } + }; + + inline auto Column::operator + ( Column const& other ) -> Columns { + Columns cols; + cols += *this; + cols += other; + return cols; + } +}} // namespace clara::TextFlow + +#endif // CLARA_TEXTFLOW_HPP_INCLUDED + +// ----------- end of #include from clara_textflow.hpp ----------- +// ........... back in clara.hpp + + +#include +#include +#include + +#if !defined(CLARA_PLATFORM_WINDOWS) && ( defined(WIN32) || defined(__WIN32__) || defined(_WIN32) || defined(_MSC_VER) ) +#define CLARA_PLATFORM_WINDOWS +#endif + +namespace clara { +namespace detail { + + // Traits for extracting arg and return type of lambdas (for single argument lambdas) + template + struct UnaryLambdaTraits : UnaryLambdaTraits {}; + + template + struct UnaryLambdaTraits { + static const bool isValid = false; + }; + + template + struct UnaryLambdaTraits { + static const bool isValid = true; + using ArgType = typename std::remove_const::type>::type;; + using ReturnType = ReturnT; + }; + + class TokenStream; + + // Transport for raw args (copied from main args, or supplied via init list for testing) + class Args { + friend TokenStream; + std::string m_exeName; + std::vector m_args; + + public: + Args( int argc, char *argv[] ) { + m_exeName = argv[0]; + for( int i = 1; i < argc; ++i ) + m_args.push_back( argv[i] ); + } + + Args( std::initializer_list args ) + : m_exeName( *args.begin() ), + m_args( args.begin()+1, args.end() ) + {} + + auto exeName() const -> std::string { + return m_exeName; + } + }; + + // Wraps a token coming from a token stream. These may not directly correspond to strings as a single string + // may encode an option + its argument if the : or = form is used + enum class TokenType { + Option, Argument + }; + struct Token { + TokenType type; + std::string token; + }; + + inline auto isOptPrefix( char c ) -> bool { + return c == '-' +#ifdef CLARA_PLATFORM_WINDOWS + || c == '/' +#endif + ; + } + + // Abstracts iterators into args as a stream of tokens, with option arguments uniformly handled + class TokenStream { + using Iterator = std::vector::const_iterator; + Iterator it; + Iterator itEnd; + std::vector m_tokenBuffer; + + void loadBuffer() { + m_tokenBuffer.resize( 0 ); + + // Skip any empty strings + while( it != itEnd && it->empty() ) + ++it; + + if( it != itEnd ) { + auto const &next = *it; + if( isOptPrefix( next[0] ) ) { + auto delimiterPos = next.find_first_of( " :=" ); + if( delimiterPos != std::string::npos ) { + m_tokenBuffer.push_back( { TokenType::Option, next.substr( 0, delimiterPos ) } ); + m_tokenBuffer.push_back( { TokenType::Argument, next.substr( delimiterPos + 1 ) } ); + } else { + if( next[1] != '-' && next.size() > 2 ) { + std::string opt = "- "; + for( size_t i = 1; i < next.size(); ++i ) { + opt[1] = next[i]; + m_tokenBuffer.push_back( { TokenType::Option, opt } ); + } + } else { + m_tokenBuffer.push_back( { TokenType::Option, next } ); + } + } + } else { + m_tokenBuffer.push_back( { TokenType::Argument, next } ); + } + } + } + + public: + explicit TokenStream( Args const &args ) : TokenStream( args.m_args.begin(), args.m_args.end() ) {} + + TokenStream( Iterator it, Iterator itEnd ) : it( it ), itEnd( itEnd ) { + loadBuffer(); + } + + explicit operator bool() const { + return !m_tokenBuffer.empty() || it != itEnd; + } + + auto count() const -> size_t { return m_tokenBuffer.size() + (itEnd - it); } + + auto operator*() const -> Token { + assert( !m_tokenBuffer.empty() ); + return m_tokenBuffer.front(); + } + + auto operator->() const -> Token const * { + assert( !m_tokenBuffer.empty() ); + return &m_tokenBuffer.front(); + } + + auto operator++() -> TokenStream & { + if( m_tokenBuffer.size() >= 2 ) { + m_tokenBuffer.erase( m_tokenBuffer.begin() ); + } else { + if( it != itEnd ) + ++it; + loadBuffer(); + } + return *this; + } + }; + + + class ResultBase { + public: + enum Type { + Ok, LogicError, RuntimeError + }; + + protected: + ResultBase( Type type ) : m_type( type ) {} + virtual ~ResultBase() = default; + + virtual void enforceOk() const = 0; + + Type m_type; + }; + + template + class ResultValueBase : public ResultBase { + public: + auto value() const -> T const & { + enforceOk(); + return m_value; + } + + protected: + ResultValueBase( Type type ) : ResultBase( type ) {} + + ResultValueBase( ResultValueBase const &other ) : ResultBase( other ) { + if( m_type == ResultBase::Ok ) + new( &m_value ) T( other.m_value ); + } + + ResultValueBase( Type, T const &value ) : ResultBase( Ok ) { + new( &m_value ) T( value ); + } + + auto operator=( ResultValueBase const &other ) -> ResultValueBase & { + if( m_type == ResultBase::Ok ) + m_value.~T(); + ResultBase::operator=(other); + if( m_type == ResultBase::Ok ) + new( &m_value ) T( other.m_value ); + return *this; + } + + ~ResultValueBase() { + if( m_type == Ok ) + m_value.~T(); + } + + union { + T m_value; + }; + }; + + template<> + class ResultValueBase : public ResultBase { + protected: + using ResultBase::ResultBase; + }; + + template + class BasicResult : public ResultValueBase { + public: + template + explicit BasicResult( BasicResult const &other ) + : ResultValueBase( other.type() ), + m_errorMessage( other.errorMessage() ) + { + assert( type() != ResultBase::Ok ); + } + + template + static auto ok( U const &value ) -> BasicResult { return { ResultBase::Ok, value }; } + static auto ok() -> BasicResult { return { ResultBase::Ok }; } + static auto logicError( std::string const &message ) -> BasicResult { return { ResultBase::LogicError, message }; } + static auto runtimeError( std::string const &message ) -> BasicResult { return { ResultBase::RuntimeError, message }; } + + explicit operator bool() const { return m_type == ResultBase::Ok; } + auto type() const -> ResultBase::Type { return m_type; } + auto errorMessage() const -> std::string { return m_errorMessage; } + + protected: + virtual void enforceOk() const { + // !TBD: If no exceptions, std::terminate here or something + switch( m_type ) { + case ResultBase::LogicError: + throw std::logic_error( m_errorMessage ); + case ResultBase::RuntimeError: + throw std::runtime_error( m_errorMessage ); + case ResultBase::Ok: + break; + } + } + + std::string m_errorMessage; // Only populated if resultType is an error + + BasicResult( ResultBase::Type type, std::string const &message ) + : ResultValueBase(type), + m_errorMessage(message) + { + assert( m_type != ResultBase::Ok ); + } + + using ResultValueBase::ResultValueBase; + using ResultBase::m_type; + }; + + enum class ParseResultType { + Matched, NoMatch, ShortCircuitAll, ShortCircuitSame + }; + + class ParseState { + public: + + ParseState( ParseResultType type, TokenStream const &remainingTokens ) + : m_type(type), + m_remainingTokens( remainingTokens ) + {} + + auto type() const -> ParseResultType { return m_type; } + auto remainingTokens() const -> TokenStream { return m_remainingTokens; } + + private: + ParseResultType m_type; + TokenStream m_remainingTokens; + }; + + using Result = BasicResult; + using ParserResult = BasicResult; + using InternalParseResult = BasicResult; + + struct HelpColumns { + std::string left; + std::string right; + }; + + template + inline auto convertInto( std::string const &source, T& target ) -> ParserResult { + std::stringstream ss; + ss << source; + ss >> target; + if( ss.fail() ) + return ParserResult::runtimeError( "Unable to convert '" + source + "' to destination type" ); + else + return ParserResult::ok( ParseResultType::Matched ); + } + inline auto convertInto( std::string const &source, std::string& target ) -> ParserResult { + target = source; + return ParserResult::ok( ParseResultType::Matched ); + } + inline auto convertInto( std::string const &source, bool &target ) -> ParserResult { + std::string srcLC = source; + std::transform( srcLC.begin(), srcLC.end(), srcLC.begin(), []( char c ) { return static_cast( ::tolower(c) ); } ); + if (srcLC == "y" || srcLC == "1" || srcLC == "true" || srcLC == "yes" || srcLC == "on") + target = true; + else if (srcLC == "n" || srcLC == "0" || srcLC == "false" || srcLC == "no" || srcLC == "off") + target = false; + else + return ParserResult::runtimeError( "Expected a boolean value but did not recognise: '" + source + "'" ); + return ParserResult::ok( ParseResultType::Matched ); + } + + struct BoundRefBase { + BoundRefBase() = default; + BoundRefBase( BoundRefBase const & ) = delete; + BoundRefBase( BoundRefBase && ) = delete; + BoundRefBase &operator=( BoundRefBase const & ) = delete; + BoundRefBase &operator=( BoundRefBase && ) = delete; + + virtual ~BoundRefBase() = default; + + virtual auto isFlag() const -> bool = 0; + virtual auto isContainer() const -> bool { return false; } + virtual auto setValue( std::string const &arg ) -> ParserResult = 0; + virtual auto setFlag( bool flag ) -> ParserResult = 0; + }; + + struct BoundValueRefBase : BoundRefBase { + auto isFlag() const -> bool override { return false; } + + auto setFlag( bool ) -> ParserResult override { + return ParserResult::logicError( "Flags can only be set on boolean fields" ); + } + }; + + struct BoundFlagRefBase : BoundRefBase { + auto isFlag() const -> bool override { return true; } + + auto setValue( std::string const &arg ) -> ParserResult override { + bool flag; + auto result = convertInto( arg, flag ); + if( result ) + setFlag( flag ); + return result; + } + }; + + template + struct BoundRef : BoundValueRefBase { + T &m_ref; + + explicit BoundRef( T &ref ) : m_ref( ref ) {} + + auto setValue( std::string const &arg ) -> ParserResult override { + return convertInto( arg, m_ref ); + } + }; + + template + struct BoundRef> : BoundValueRefBase { + std::vector &m_ref; + + explicit BoundRef( std::vector &ref ) : m_ref( ref ) {} + + auto isContainer() const -> bool override { return true; } + + auto setValue( std::string const &arg ) -> ParserResult override { + T temp; + auto result = convertInto( arg, temp ); + if( result ) + m_ref.push_back( temp ); + return result; + } + }; + + struct BoundFlagRef : BoundFlagRefBase { + bool &m_ref; + + explicit BoundFlagRef( bool &ref ) : m_ref( ref ) {} + + auto setFlag( bool flag ) -> ParserResult override { + m_ref = flag; + return ParserResult::ok( ParseResultType::Matched ); + } + }; + + template + struct LambdaInvoker { + static_assert( std::is_same::value, "Lambda must return void or clara::ParserResult" ); + + template + static auto invoke( L const &lambda, ArgType const &arg ) -> ParserResult { + return lambda( arg ); + } + }; + + template<> + struct LambdaInvoker { + template + static auto invoke( L const &lambda, ArgType const &arg ) -> ParserResult { + lambda( arg ); + return ParserResult::ok( ParseResultType::Matched ); + } + }; + + template + inline auto invokeLambda( L const &lambda, std::string const &arg ) -> ParserResult { + ArgType temp; + auto result = convertInto( arg, temp ); + return !result + ? result + : LambdaInvoker::ReturnType>::invoke( lambda, temp ); + }; + + + template + struct BoundLambda : BoundValueRefBase { + L m_lambda; + + static_assert( UnaryLambdaTraits::isValid, "Supplied lambda must take exactly one argument" ); + explicit BoundLambda( L const &lambda ) : m_lambda( lambda ) {} + + auto setValue( std::string const &arg ) -> ParserResult override { + return invokeLambda::ArgType>( m_lambda, arg ); + } + }; + + template + struct BoundFlagLambda : BoundFlagRefBase { + L m_lambda; + + static_assert( UnaryLambdaTraits::isValid, "Supplied lambda must take exactly one argument" ); + static_assert( std::is_same::ArgType, bool>::value, "flags must be boolean" ); + + explicit BoundFlagLambda( L const &lambda ) : m_lambda( lambda ) {} + + auto setFlag( bool flag ) -> ParserResult override { + return LambdaInvoker::ReturnType>::invoke( m_lambda, flag ); + } + }; + + enum class Optionality { Optional, Required }; + + struct Parser; + + class ParserBase { + public: + virtual ~ParserBase() = default; + virtual auto validate() const -> Result { return Result::ok(); } + virtual auto parse( std::string const& exeName, TokenStream const &tokens) const -> InternalParseResult = 0; + virtual auto cardinality() const -> size_t { return 1; } + + auto parse( Args const &args ) const -> InternalParseResult { + return parse( args.exeName(), TokenStream( args ) ); + } + }; + + template + class ComposableParserImpl : public ParserBase { + public: + template + auto operator|( T const &other ) const -> Parser; + }; + + // Common code and state for Args and Opts + template + class ParserRefImpl : public ComposableParserImpl { + protected: + Optionality m_optionality = Optionality::Optional; + std::shared_ptr m_ref; + std::string m_hint; + std::string m_description; + + explicit ParserRefImpl( std::shared_ptr const &ref ) : m_ref( ref ) {} + + public: + template + ParserRefImpl( T &ref, std::string const &hint ) + : m_ref( std::make_shared>( ref ) ), + m_hint( hint ) + {} + + template + ParserRefImpl( LambdaT const &ref, std::string const &hint ) + : m_ref( std::make_shared>( ref ) ), + m_hint(hint) + {} + + auto operator()( std::string const &description ) -> DerivedT & { + m_description = description; + return static_cast( *this ); + } + + auto optional() -> DerivedT & { + m_optionality = Optionality::Optional; + return static_cast( *this ); + }; + + auto required() -> DerivedT & { + m_optionality = Optionality::Required; + return static_cast( *this ); + }; + + auto isOptional() const -> bool { + return m_optionality == Optionality::Optional; + } + + auto cardinality() const -> size_t override { + if( m_ref->isContainer() ) + return 0; + else + return 1; + } + + auto hint() const -> std::string { return m_hint; } + }; + + class ExeName : public ComposableParserImpl { + std::shared_ptr m_name; + std::shared_ptr m_ref; + + template + static auto makeRef(LambdaT const &lambda) -> std::shared_ptr { + return std::make_shared>( lambda) ; + } + + public: + ExeName() : m_name( std::make_shared( "" ) ) {} + + explicit ExeName( std::string &ref ) : ExeName() { + m_ref = std::make_shared>( ref ); + } + + template + explicit ExeName( LambdaT const& lambda ) : ExeName() { + m_ref = std::make_shared>( lambda ); + } + + // The exe name is not parsed out of the normal tokens, but is handled specially + auto parse( std::string const&, TokenStream const &tokens ) const -> InternalParseResult override { + return InternalParseResult::ok( ParseState( ParseResultType::NoMatch, tokens ) ); + } + + auto name() const -> std::string { return *m_name; } + auto set( std::string const& newName ) -> ParserResult { + + auto lastSlash = newName.find_last_of( "\\/" ); + auto filename = ( lastSlash == std::string::npos ) + ? newName + : newName.substr( lastSlash+1 ); + + *m_name = filename; + if( m_ref ) + return m_ref->setValue( filename ); + else + return ParserResult::ok( ParseResultType::Matched ); + } + }; + + class Arg : public ParserRefImpl { + public: + using ParserRefImpl::ParserRefImpl; + + auto parse( std::string const &, TokenStream const &tokens ) const -> InternalParseResult override { + auto validationResult = validate(); + if( !validationResult ) + return InternalParseResult( validationResult ); + + auto remainingTokens = tokens; + auto const &token = *remainingTokens; + if( token.type != TokenType::Argument ) + return InternalParseResult::ok( ParseState( ParseResultType::NoMatch, remainingTokens ) ); + + auto result = m_ref->setValue( remainingTokens->token ); + if( !result ) + return InternalParseResult( result ); + else + return InternalParseResult::ok( ParseState( ParseResultType::Matched, ++remainingTokens ) ); + } + }; + + inline auto normaliseOpt( std::string const &optName ) -> std::string { +#ifdef CLARA_PLATFORM_WINDOWS + if( optName[0] == '/' ) + return "-" + optName.substr( 1 ); + else +#endif + return optName; + } + + class Opt : public ParserRefImpl { + protected: + std::vector m_optNames; + + public: + template + explicit Opt( LambdaT const &ref ) : ParserRefImpl( std::make_shared>( ref ) ) {} + + explicit Opt( bool &ref ) : ParserRefImpl( std::make_shared( ref ) ) {} + + template + Opt( LambdaT const &ref, std::string const &hint ) : ParserRefImpl( ref, hint ) {} + + template + Opt( T &ref, std::string const &hint ) : ParserRefImpl( ref, hint ) {} + + auto operator[]( std::string const &optName ) -> Opt & { + m_optNames.push_back( optName ); + return *this; + } + + auto getHelpColumns() const -> std::vector { + std::ostringstream oss; + bool first = true; + for( auto const &opt : m_optNames ) { + if (first) + first = false; + else + oss << ", "; + oss << opt; + } + if( !m_hint.empty() ) + oss << " <" << m_hint << ">"; + return { { oss.str(), m_description } }; + } + + auto isMatch( std::string const &optToken ) const -> bool { + auto normalisedToken = normaliseOpt( optToken ); + for( auto const &name : m_optNames ) { + if( normaliseOpt( name ) == normalisedToken ) + return true; + } + return false; + } + + using ParserBase::parse; + + auto parse( std::string const&, TokenStream const &tokens ) const -> InternalParseResult override { + auto validationResult = validate(); + if( !validationResult ) + return InternalParseResult( validationResult ); + + auto remainingTokens = tokens; + if( remainingTokens && remainingTokens->type == TokenType::Option ) { + auto const &token = *remainingTokens; + if( isMatch(token.token ) ) { + if( m_ref->isFlag() ) { + auto result = m_ref->setFlag( true ); + if( !result ) + return InternalParseResult( result ); + if( result.value() == ParseResultType::ShortCircuitAll ) + return InternalParseResult::ok( ParseState( result.value(), remainingTokens ) ); + } else { + ++remainingTokens; + if( !remainingTokens ) + return InternalParseResult::runtimeError( "Expected argument following " + token.token ); + auto const &argToken = *remainingTokens; + if( argToken.type != TokenType::Argument ) + return InternalParseResult::runtimeError( "Expected argument following " + token.token ); + auto result = m_ref->setValue( argToken.token ); + if( !result ) + return InternalParseResult( result ); + if( result.value() == ParseResultType::ShortCircuitAll ) + return InternalParseResult::ok( ParseState( result.value(), remainingTokens ) ); + } + return InternalParseResult::ok( ParseState( ParseResultType::Matched, ++remainingTokens ) ); + } + } + return InternalParseResult::ok( ParseState( ParseResultType::NoMatch, remainingTokens ) ); + } + + auto validate() const -> Result override { + if( m_optNames.empty() ) + return Result::logicError( "No options supplied to Opt" ); + for( auto const &name : m_optNames ) { + if( name.empty() ) + return Result::logicError( "Option name cannot be empty" ); +#ifdef CLARA_PLATFORM_WINDOWS + if( name[0] != '-' && name[0] != '/' ) + return Result::logicError( "Option name must begin with '-' or '/'" ); +#else + if( name[0] != '-' ) + return Result::logicError( "Option name must begin with '-'" ); +#endif + } + return ParserRefImpl::validate(); + } + }; + + struct Help : Opt { + Help( bool &showHelpFlag ) + : Opt([&]( bool flag ) { + showHelpFlag = flag; + return ParserResult::ok( ParseResultType::ShortCircuitAll ); + }) + { + static_cast( *this ) + ("display usage information") + ["-?"]["-h"]["--help"] + .optional(); + } + }; + + + struct Parser : ParserBase { + + mutable ExeName m_exeName; + std::vector m_options; + std::vector m_args; + + auto operator|=( ExeName const &exeName ) -> Parser & { + m_exeName = exeName; + return *this; + } + + auto operator|=( Arg const &arg ) -> Parser & { + m_args.push_back(arg); + return *this; + } + + auto operator|=( Opt const &opt ) -> Parser & { + m_options.push_back(opt); + return *this; + } + + auto operator|=( Parser const &other ) -> Parser & { + m_options.insert(m_options.end(), other.m_options.begin(), other.m_options.end()); + m_args.insert(m_args.end(), other.m_args.begin(), other.m_args.end()); + return *this; + } + + template + auto operator|( T const &other ) const -> Parser { + return Parser( *this ) |= other; + } + + auto getHelpColumns() const -> std::vector { + std::vector cols; + for (auto const &o : m_options) { + auto childCols = o.getHelpColumns(); + cols.insert( cols.end(), childCols.begin(), childCols.end() ); + } + return cols; + } + + void writeToStream( std::ostream &os ) const { + if (!m_exeName.name().empty()) { + os << "usage:\n" << " " << m_exeName.name() << " "; + bool required = true, first = true; + for( auto const &arg : m_args ) { + if (first) + first = false; + else + os << " "; + if( arg.isOptional() && required ) { + os << "["; + required = false; + } + os << "<" << arg.hint() << ">"; + if( arg.cardinality() == 0 ) + os << " ... "; + } + if( !required ) + os << "]"; + if( !m_options.empty() ) + os << " options"; + os << "\n\nwhere options are:" << std::endl; + } + + auto rows = getHelpColumns(); + size_t consoleWidth = CLARA_CONFIG_CONSOLE_WIDTH; + size_t optWidth = 0; + for( auto const &cols : rows ) + optWidth = (std::max)(optWidth, cols.left.size() + 2); + + for( auto const &cols : rows ) { + auto row = + TextFlow::Column( cols.left ).width( optWidth ).indent( 2 ) + + TextFlow::Spacer(4) + + TextFlow::Column( cols.right ).width( consoleWidth - 7 - optWidth ); + os << row << std::endl; + } + } + + friend auto operator<<( std::ostream &os, Parser const &parser ) -> std::ostream& { + parser.writeToStream( os ); + return os; + } + + auto validate() const -> Result override { + for( auto const &opt : m_options ) { + auto result = opt.validate(); + if( !result ) + return result; + } + for( auto const &arg : m_args ) { + auto result = arg.validate(); + if( !result ) + return result; + } + return Result::ok(); + } + + using ParserBase::parse; + + auto parse( std::string const& exeName, TokenStream const &tokens ) const -> InternalParseResult override { + + struct ParserInfo { + ParserBase const* parser = nullptr; + size_t count = 0; + }; + const size_t totalParsers = m_options.size() + m_args.size(); + assert( totalParsers < 512 ); + // ParserInfo parseInfos[totalParsers]; // <-- this is what we really want to do + ParserInfo parseInfos[512]; + + { + size_t i = 0; + for (auto const &opt : m_options) parseInfos[i++].parser = &opt; + for (auto const &arg : m_args) parseInfos[i++].parser = &arg; + } + + m_exeName.set( exeName ); + + auto result = InternalParseResult::ok( ParseState( ParseResultType::NoMatch, tokens ) ); + while( result.value().remainingTokens() ) { + bool tokenParsed = false; + + for( size_t i = 0; i < totalParsers; ++i ) { + auto& parseInfo = parseInfos[i]; + if( parseInfo.parser->cardinality() == 0 || parseInfo.count < parseInfo.parser->cardinality() ) { + result = parseInfo.parser->parse(exeName, result.value().remainingTokens()); + if (!result) + return result; + if (result.value().type() != ParseResultType::NoMatch) { + tokenParsed = true; + ++parseInfo.count; + break; + } + } + } + + if( result.value().type() == ParseResultType::ShortCircuitAll ) + return result; + if( !tokenParsed ) + return InternalParseResult::runtimeError( "Unrecognised token: " + result.value().remainingTokens()->token ); + } + // !TBD Check missing required options + return result; + } + }; + + template + template + auto ComposableParserImpl::operator|( T const &other ) const -> Parser { + return Parser() | static_cast( *this ) | other; + } +} // namespace detail + + +// A Combined parser +using detail::Parser; + +// A parser for options +using detail::Opt; + +// A parser for arguments +using detail::Arg; + +// Wrapper for argc, argv from main() +using detail::Args; + +// Specifies the name of the executable +using detail::ExeName; + +// Convenience wrapper for option parser that specifies the help option +using detail::Help; + +// enum of result types from a parse +using detail::ParseResultType; + +// Result type for parser operation +using detail::ParserResult; + + +} // namespace clara + +#endif // CLARA_HPP_INCLUDED