pax_global_header00006660000000000000000000000064150462212650014515gustar00rootroot0000000000000052 comment=e5c033e317a01b2703d13aab42288d09b2efdafc plog-1.1.11/000077500000000000000000000000001504622126500125375ustar00rootroot00000000000000plog-1.1.11/.appveyor.yml000066400000000000000000000022271504622126500152100ustar00rootroot00000000000000configuration: - Release environment: matrix: - generator: "Visual Studio 14 2015" - generator: "Visual Studio 14 2015 Win64" - generator: "Visual Studio 10 2010" - generator: "Visual Studio 10 2010 Win64" - generator: "MinGW Makefiles" dialect: mingw - generator: "MinGW Makefiles" dialect: mingw-w64 matrix: fast_finish: true shallow_clone: true skip_commits: files: - .circleci/ - .github/ - .editorconfig - .gitignore - .cirrus.yml - LICENSE - plog.nuspec - plog.targets - README.md before_build: # Workaround for CMake not wanting sh.exe on PATH for MinGW - set PATH=%PATH:C:\Program Files\Git\usr\bin;=% - if "%dialect%"=="mingw" set PATH=c:\MinGW\bin;%PATH% - if "%dialect%"=="mingw-w64" set PATH=c:\msys64\mingw64\bin;%PATH% - cmake -H. -Bbuild -G"%generator%" -DCMAKE_BUILD_TYPE=%configuration% -DPLOG_BUILD_TESTS=1 build_script: - if "%generator:~0,6%"=="Visual" set CMAKE_BUILD_FLAGS=--config %configuration% -- /m /v:m - if "%generator:~0,5%"=="MinGW" set CMAKE_BUILD_FLAGS=-- -j - cmake --build build %CMAKE_BUILD_FLAGS% test_script: - cd build && ctest -V plog-1.1.11/.circleci/000077500000000000000000000000001504622126500143725ustar00rootroot00000000000000plog-1.1.11/.circleci/config.yml000066400000000000000000000014041504622126500163610ustar00rootroot00000000000000version: 2.0 jobs: rtems: docker: - image: sergiusthebest/rtems-arm-rasberrypi-ci:latest steps: - checkout - run: cmake -H. -Bbuild -DCMAKE_TOOLCHAIN_FILE=$CMAKE_TOOLCHAIN_FILE && cd build && make -j platformio: docker: - image: ghcr.io/sergiusthebest/platformio-ci:latest steps: - checkout - run: pio run -d samples/Arduino android-ndk: docker: - image: sergiusthebest/android-ndk-ci:latest steps: - checkout - run: cmake -H. -Bbuild -DANDROID_ABI=arm64-v8a -DANDROID_PLATFORM=android-30 -DCMAKE_TOOLCHAIN_FILE=$ANDROID_NDK_HOME/build/cmake/android.toolchain.cmake && cd build && make -j workflows: version: 2 workflow: jobs: - rtems - platformio - android-ndk plog-1.1.11/.cirrus.yml000066400000000000000000000006571504622126500146570ustar00rootroot00000000000000task: name: FreeBSD freebsd_instance: matrix: - image_family: freebsd-13-5 - image_family: freebsd-14-3 - image_family: freebsd-15-0-snap matrix: - BUILD_TYPE: Release install_script: pkg install -y cmake compile_script: cmake -H. -Bbuild -DCMAKE_BUILD_TYPE=$BUILD_TYPE -DPLOG_BUILD_TESTS=1 && cmake --build build -- -j4 test_script: cd build && ctest -V plog-1.1.11/.editorconfig000066400000000000000000000004721504622126500152170ustar00rootroot00000000000000# EditorConfig helps developers define and maintain consistent # coding styles between different editors and IDEs # editorconfig.org root = true [*] trim_trailing_whitespace = true insert_final_newline = true indent_style = space indent_size = 4 [*.md] trim_trailing_whitespace = false [*.yml] indent_size = 2 plog-1.1.11/.github/000077500000000000000000000000001504622126500140775ustar00rootroot00000000000000plog-1.1.11/.github/workflows/000077500000000000000000000000001504622126500161345ustar00rootroot00000000000000plog-1.1.11/.github/workflows/build.yml000066400000000000000000000022751504622126500177640ustar00rootroot00000000000000name: Reusable build workflow (hack to be able to skip container image) on: workflow_call: inputs: image: description: Image to use for the job. If image is 'null' it will execute directly on the runner. type: string required: false default: null os: description: OS to use for the job. type: string required: false default: ubuntu--latest cxx: description: CXX to use for the job. type: string required: false default: null generator: description: Cmake generator to use for the job. type: string required: false default: null jobs: build: runs-on: ${{ inputs.os }} container: image: ${{ inputs.image }} options: --user root env: CXX: ${{ inputs.cxx }} steps: - name: Checkout uses: actions/checkout@v1 # https://github.com/actions/checkout/issues/1590 with: fetch-depth: 0 - name: Build run: | cmake -Bbuild -DCMAKE_BUILD_TYPE=Release -DPLOG_BUILD_TESTS=ON ${{ inputs.generator }} . cmake --build build --parallel - name: Test run: cd build && ctest -V plog-1.1.11/.github/workflows/ci.yml000066400000000000000000000035301504622126500172530ustar00rootroot00000000000000name: CI on: push: paths-ignore: - .circleci/ - .appveyor.yml - .cirrus.yml - .editorconfig - .gitignore - LICENSE - plog.nuspec - plog.targets - README.md pull_request: paths-ignore: - .circleci/ - .appveyor.yml - .cirrus.yml - .editorconfig - .gitignore - LICENSE - plog.nuspec - plog.targets - README.md permissions: contents: read jobs: build: name: ${{ matrix.cxx }}, ${{ matrix.os }} strategy: fail-fast: true matrix: include: [ # linux: gcc { os: ubuntu-24.04, cxx: g++-14 }, # (default on Noble 24.04) { os: ubuntu-22.04, cxx: g++-11 }, # (default on Jammy 22.04) { os: ubuntu-22.04, cxx: g++-9 }, # (default on Focal 20.04) { os: ubuntu-latest, cxx: g++-7, image: conanio/gcc7 }, # (default on Focal 20.04) { os: ubuntu-latest, cxx: g++-5, image: conanio/gcc5 }, # (default on Xenial 16.04) { os: ubuntu-latest, cxx: g++-4.8, image: conanio/gcc48 }, # (default on Trusty 14.04) # linux: clang { os: ubuntu-24.04, cxx: clang++-18 }, { os: ubuntu-latest, cxx: clang++-6.0, image: conanio/clang60 }, # windows: msvc # unfortunately, Visual Studio 2019 is not available on github-hosted runners anymore { os: windows-2022, cxx: vs2022-x86, generator: '-G "Visual Studio 17 2022" -A Win32' }, { os: windows-2022, cxx: vs2022-x64, generator: '-G "Visual Studio 17 2022" -A x64' }, # macos: clang { os: macos-13, cxx: 'clang++' } ] uses: ./.github/workflows/build.yml with: image: ${{ matrix.image }} os: ${{ matrix.os }} cxx: ${{ matrix.cxx }} generator: ${{ matrix.generator }} plog-1.1.11/.github/workflows/nuget.yml000066400000000000000000000010521504622126500177770ustar00rootroot00000000000000name: nuget on: push: tags: - '[0-9]*' jobs: build: runs-on: ubuntu-latest steps: - name: Checkout uses: actions/checkout@v3 with: fetch-depth: 0 - name: Setup NuGet uses: nuget/setup-nuget@v1 with: nuget-version: 'latest' - name: Pack NuGet run: nuget pack plog.nuspec -p version=${{github.ref_name}} - name: Publish run: nuget push *.nupkg -Source "https://nuget.pkg.github.com/SergiusTheBest/index.json" -SkipDuplicate -ApiKey "${{secrets.GITHUB_TOKEN}}" plog-1.1.11/.gitignore000066400000000000000000000000161504622126500145240ustar00rootroot00000000000000build .vscode plog-1.1.11/CMakeLists.txt000066400000000000000000000045641504622126500153100ustar00rootroot00000000000000if(${CMAKE_VERSION} VERSION_LESS 3.27.0) cmake_minimum_required(VERSION 3.0) else() cmake_minimum_required(VERSION 3.27) endif() project(plog VERSION 1.1.11 LANGUAGES CXX) # check if building as a stand-alone project if(CMAKE_PROJECT_NAME STREQUAL PROJECT_NAME) set(IS_TOPLEVEL_PROJECT TRUE) else() set(IS_TOPLEVEL_PROJECT FALSE) endif() # options option(PLOG_BUILD_SAMPLES "Build ${PROJECT_NAME} samples" ${IS_TOPLEVEL_PROJECT}) option(PLOG_INSTALL "Generate ${PROJECT_NAME} install target" ${IS_TOPLEVEL_PROJECT}) option(PLOG_BUILD_TESTS "Build tests" OFF) # make sure install paths work on all platforms include(GNUInstallDirs) add_library(${PROJECT_NAME} INTERFACE) add_library(${PROJECT_NAME}::${PROJECT_NAME} ALIAS ${PROJECT_NAME}) target_include_directories(${PROJECT_NAME} INTERFACE $ $ ) if(ANDROID) target_link_libraries(${PROJECT_NAME} INTERFACE log) endif() if(PLOG_BUILD_SAMPLES) # add a pseudo-project to make plog headers visible in IDE file(GLOB_RECURSE ${PROJECT_NAME}_HEADERS ${CMAKE_CURRENT_SOURCE_DIR}/include/*.h) add_custom_target(${PROJECT_NAME}-headers SOURCES ${${PROJECT_NAME}_HEADERS}) set_target_properties(${PROJECT_NAME}-headers PROPERTIES FOLDER Include) # add samples add_subdirectory(samples) endif() if(PLOG_BUILD_TESTS) enable_testing() add_subdirectory(test) endif() if(PLOG_INSTALL) install( TARGETS ${PROJECT_NAME} EXPORT ${PROJECT_NAME}Config ) install( EXPORT ${PROJECT_NAME}Config DESTINATION ${CMAKE_INSTALL_LIBDIR}/cmake/${PROJECT_NAME} NAMESPACE ${PROJECT_NAME}:: ) install( DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR}/include/${PROJECT_NAME} DESTINATION ${CMAKE_INSTALL_INCLUDEDIR} ) install( FILES ${CMAKE_CURRENT_SOURCE_DIR}/README.md ${CMAKE_CURRENT_SOURCE_DIR}/LICENSE DESTINATION ${CMAKE_INSTALL_DOCDIR} ) include(CMakePackageConfigHelpers) write_basic_package_version_file( ${CMAKE_CURRENT_BINARY_DIR}/${PROJECT_NAME}ConfigVersion.cmake COMPATIBILITY AnyNewerVersion ) install( FILES ${CMAKE_CURRENT_BINARY_DIR}/${PROJECT_NAME}ConfigVersion.cmake DESTINATION ${CMAKE_INSTALL_LIBDIR}/cmake/${PROJECT_NAME} ) endif() plog-1.1.11/LICENSE000066400000000000000000000020571504622126500135500ustar00rootroot00000000000000MIT License Copyright (c) 2022 Sergey Podobry Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. plog-1.1.11/README.md000066400000000000000000001443531504622126500140300ustar00rootroot00000000000000# Plog - portable, simple and extensible C++ logging library Pretty powerful logging library in about 1000 lines of code [![CI](https://github.com/SergiusTheBest/plog/actions/workflows/ci.yml/badge.svg)](https://github.com/SergiusTheBest/plog/actions/workflows/ci.yml) [![Build status](https://ci.appveyor.com/api/projects/status/rna5gwhqjb13wovr/branch/master?svg=true)](https://ci.appveyor.com/project/SergiusTheBest/plog/branch/master) [![CircleCI](https://circleci.com/gh/SergiusTheBest/plog.svg?style=svg)](https://circleci.com/gh/SergiusTheBest/plog) [![Build Status](https://api.cirrus-ci.com/github/SergiusTheBest/plog.svg)](https://cirrus-ci.com/github/SergiusTheBest/plog) ![image](doc/color-console.png) - [Introduction](#introduction) - [Hello log!](#hello-log) - [Features](#features) - [Integration](#integration) - [Copy the source](#copy-the-source) - [Git submodule](#git-submodule) - [CMake integration](#cmake-integration) - [`add_subdirectory`](#add_subdirectory) - [`FetchContent`](#fetchcontent) - [Package managers](#package-managers) - [Usage](#usage) - [Step 1: Adding includes](#step-1-adding-includes) - [Step 2: Initialization](#step-2-initialization) - [RollingFileInitializer](#rollingfileinitializer) - [ConsoleInitializer](#consoleinitializer) - [Manual initialization (Init.h)](#manual-initialization-inith) - [Step 3: Logging](#step-3-logging) - [Basic logging macros](#basic-logging-macros) - [Conditional logging macros](#conditional-logging-macros) - [Logger severity checker](#logger-severity-checker) - [Advanced usage](#advanced-usage) - [Changing severity at runtime](#changing-severity-at-runtime) - [Custom initialization](#custom-initialization) - [Multiple appenders](#multiple-appenders) - [Multiple loggers](#multiple-loggers) - [Share log instances across modules (exe, dll, so, dylib)](#share-log-instances-across-modules-exe-dll-so-dylib) - [Chained loggers](#chained-loggers) - [Architecture](#architecture) - [Overview](#overview) - [Logger](#logger) - [Record](#record) - [Formatter](#formatter) - [TxtFormatter](#txtformatter) - [TxtFormatterUtcTime](#txtformatterutctime) - [CsvFormatter](#csvformatter) - [CsvFormatterUtcTime](#csvformatterutctime) - [FuncMessageFormatter](#funcmessageformatter) - [MessageOnlyFormatter](#messageonlyformatter) - [Converter](#converter) - [UTF8Converter](#utf8converter) - [NativeEOLConverter](#nativeeolconverter) - [Appender](#appender) - [RollingFileAppender](#rollingfileappender) - [ConsoleAppender](#consoleappender) - [ColorConsoleAppender](#colorconsoleappender) - [AndroidAppender](#androidappender) - [EventLogAppender](#eventlogappender) - [DebugOutputAppender](#debugoutputappender) - [ArduinoAppender](#arduinoappender) - [DynamicAppender](#dynamicappender) - [Miscellaneous notes](#miscellaneous-notes) - [Lazy stream evaluation](#lazy-stream-evaluation) - [Stream improvements over std::ostream](#stream-improvements-over-stdostream) - [Automatic 'this' pointer capture](#automatic-this-pointer-capture) - [Headers to include](#headers-to-include) - [Unicode](#unicode) - [Wide string support](#wide-string-support) - [Performance](#performance) - [Printf style formatting](#printf-style-formatting) - [LOG_XXX macro name clashes](#log_xxx-macro-name-clashes) - [Disable logging to reduce binary size](#disable-logging-to-reduce-binary-size) - [PLOG_MESSAGE_PREFIX](#plog_message_prefix) - [Extending](#extending) - [Custom data type](#custom-data-type) - [Custom appender](#custom-appender) - [Custom formatter](#custom-formatter) - [Custom converter](#custom-converter) - [Samples](#samples) - [License](#license) - [Version history](#version-history) # Introduction ## Hello log! Plog is a C++ logging library that is designed to be as simple, small and flexible as possible. It is created as an alternative to existing large libraries and provides some unique features as [CSV log format]((#csvformatter)) and [wide string support](#wide-string-support). Here is a minimal hello log sample: ```cpp #include // Step1: include the headers #include "plog/Initializers/RollingFileInitializer.h" int main() { plog::init(plog::debug, "Hello.txt"); // Step2: initialize the logger // Step3: write log messages using a special macro // There are several log macros, use the macro you liked the most PLOGD << "Hello log!"; // short macro PLOG_DEBUG << "Hello log!"; // long macro PLOG(plog::debug) << "Hello log!"; // function-style macro // Also you can use LOG_XXX macro but it may clash with other logging libraries LOGD << "Hello log!"; // short macro LOG_DEBUG << "Hello log!"; // long macro LOG(plog::debug) << "Hello log!"; // function-style macro return 0; } ``` And its output: ``` 2015-05-18 23:12:43.921 DEBUG [21428] [main@13] Hello log! 2015-05-18 23:12:43.968 DEBUG [21428] [main@14] Hello log! 2015-05-18 23:12:43.968 DEBUG [21428] [main@15] Hello log! ``` ## Features - Very small (slightly more than 1000 LOC) - Easy to use - Headers only - No 3rd-party dependencies - Cross-platform: Windows, Linux, FreeBSD, macOS, Android, RTEMS, FreeRTOS (gcc, clang, msvc, mingw, mingw-w64, icc, c++builder) - Thread and type safe - Formatters: [TXT](#txtformatter), [CSV](#csvformatter), [FuncMessage](#funcmessageformatter), [MessageOnly](#messageonlyformatter) - Appenders: [RollingFile](#rollingfileappender), [Console](#consoleappender), [ColorConsole](#colorconsoleappender), [Android](#androidappender), [EventLog](#eventlogappender), [DebugOutput](#debugoutputappender), [DynamicAppender](#dynamicappender) - [Automatic 'this' pointer capture](#automatic-this-pointer-capture) (supported only on msvc) - [Lazy stream evaluation](#lazy-stream-evaluation) - [Unicode aware](#unicode), files are stored in UTF-8, supports [Utf8Everywhere](http://utf8everywhere.org) - Doesn't require C++11 - [Extendable](#extending) - No `windows.h` dependency - Can use UTC or local time - Can print buffers in HEX or ASCII - Can print `std` containers - Uses modern CMake # Integration Plog is a header-only C++ library, making it extremely easy to integrate into any project. You do not need to build or link any binaries — just add the headers to your include path. Here are several recommended ways to add Plog to your project: ## Copy the source Simply copy the `plog` directory into your source tree. For example: ``` . <-- root of your solution ├── README.md └── src ├── 3rd-party <-- directory for all 3rd-party dependencies │   └── plog <-- plog is copied there │   ├── include <-- add this to your include search path │   │   └── plog │   ├── LICENSE │   └── README.md ├── proj1 └── proj2 ``` Then, add `src/3rd-party/plog/include` to your project's include directories. ## Git submodule Add Plog as a git submodule to keep it up to date and track its version: ```bash git submodule add https://github.com/SergiusTheBest/plog.git src/3rd-party/plog git commit -m "Add plog as a submodule" ``` This approach allows you to easily update Plog and manage its version. Remember to add `src/3rd-party/plog/include` to your include path. ## CMake integration ### `add_subdirectory` If you use CMake, you can add Plog directly to your build: ```cmake add_subdirectory(3rd-party/plog) # Adds plog to your CMake project add_executable(myproj main.cpp) target_link_libraries(myproj plog::plog) # Links and sets include path ``` ### `FetchContent` Alternatively, use CMake's FetchContent to automatically download Plog at configure time: ```cmake include(FetchContent) FetchContent_Declare( plog GIT_REPOSITORY https://github.com/SergiusTheBest/plog GIT_TAG 1.1.10 GIT_SHALLOW true ) FetchContent_MakeAvailable(plog) # Downloads and adds plog to your CMake project add_executable(myproj main.cpp) target_link_libraries(myproj plog::plog) # Links and sets include path ``` ## Package managers Plog is also available via popular C++ package managers: - **[vcpkg](https://github.com/microsoft/vcpkg)** ``` vcpkg install plog ``` - **[Conan](https://conan.io/)** ``` conan install plog ``` - **[NuGet](https://www.nuget.org/packages/plog/)** ``` nuget install plog ``` Refer to each package manager's documentation for the latest installation instructions and version details. # Usage To start using plog you need to make 3 simple steps. ## Step 1: Adding includes At first your project needs to know about plog. For that you have to: 1. Add `plog/include` to the project include paths 2. Add `#include ` into your cpp/h files (if you have precompiled headers it is a good place to add this include there) ## Step 2: Initialization To use plog, you must initialize the logger by including the appropriate header and calling the corresponding `plog::init` overload: ```cpp Logger& init(Severity maxSeverity, ... ``` `maxSeverity` is the logger severity upper limit. Log messages with a severity value higher (less severe) than the limit are dropped. Plog defines the following severity levels: ```cpp enum Severity { none = 0, fatal = 1, error = 2, warning = 3, info = 4, debug = 5, verbose = 6 }; ``` > **Note** Messages with severity level `none` will always be printed. Plog provides several convenient initializer functions to simplify logger setup for common use cases. These initializers configure the logger with typical appenders and formatters, so you can get started quickly without manually specifying all template parameters. ### RollingFileInitializer Use this when you want to log to a file with automatic rolling (rotation) based on size and count. Add `#include ` and call `init`: ```cpp Logger& init(Severity maxSeverity, const util::nchar* fileName, size_t maxFileSize = 0, int maxFiles = 0); ``` - The log format is determined by the file extension: - `.csv` → [CSV format](#csvformatter) - anything else → [TXT format](#txtformatter) - You can override the format by specifying a formatter as a template parameter, e.g. `plog::init(...)`. - Rolling is controlled by `maxFileSize` (bytes) and `maxFiles` (number of files to keep). If either is zero, rolling is disabled. Example: ```cpp #include #include plog::init(plog::warning, "c:\\logs\\log.csv", 1000000, 5); ``` Here the logger is initialized to write all messages with up to warning severity to a file in csv format. Maximum log file size is set to 1'000'000 bytes and 5 log files are kept. ### ConsoleInitializer Use this to log to the console (stdout or stderr) with color output. Add `#include ` and call `init`: ```cpp Logger& init(Severity maxSeverity, OutputStream outputStream) ``` - By default it uses [TXT format](#txtformatter) but it can be overriden by specifying a formatter as a template parameter, e.g. `plog::init(...)`. - `outputStream` chooses the output stream: `plog::streamStdOut` or `plog::streamStdErr`. Example: ```cpp #include #include plog::init(plog::error, plog::streamStdErr); // logs error and above to stderr ``` ### Manual initialization (Init.h) For advanced or custom setups add `#include ` and call `init`: ```cpp Logger& init(Severity maxSeverity = none, IAppender* appender = NULL); ``` You must construct and manage the appender yourself. Example: ```cpp #include #include static plog::ConsoleAppender appender; plog::init(plog::info, &appender); // logs info and above to the specified appender ``` > **Note** See [Custom initialization](#custom-initialization) for advanced usage. ## Step 3: Logging Logging is performed with the help of special macros. A log message is constructed using stream output operators `<<`. Thus it is type-safe and extendable in contrast to a format string output. ### Basic logging macros This is the most used type of logging macros. They do unconditional logging. #### Long macros: ```cpp PLOG_VERBOSE << "verbose"; PLOG_DEBUG << "debug"; PLOG_INFO << "info"; PLOG_WARNING << "warning"; PLOG_ERROR << "error"; PLOG_FATAL << "fatal"; PLOG_NONE << "none"; ``` #### Short macros: ```cpp PLOGV << "verbose"; PLOGD << "debug"; PLOGI << "info"; PLOGW << "warning"; PLOGE << "error"; PLOGF << "fatal"; PLOGN << "none"; ``` #### Function-style macros: ```cpp PLOG(severity) << "msg"; ``` ### Conditional logging macros These macros are used to do conditional logging. They accept a condition as a parameter and perform logging if the condition is true. #### Long macros: ```cpp PLOG_VERBOSE_IF(cond) << "verbose"; PLOG_DEBUG_IF(cond) << "debug"; PLOG_INFO_IF(cond) << "info"; PLOG_WARNING_IF(cond) << "warning"; PLOG_ERROR_IF(cond) << "error"; PLOG_FATAL_IF(cond) << "fatal"; PLOG_NONE_IF(cond) << "none"; ``` #### Short macros: ```cpp PLOGV_IF(cond) << "verbose"; PLOGD_IF(cond) << "debug"; PLOGI_IF(cond) << "info"; PLOGW_IF(cond) << "warning"; PLOGE_IF(cond) << "error"; PLOGF_IF(cond) << "fatal"; PLOGN_IF(cond) << "none"; ``` #### Function-style macros: ```cpp PLOG_IF(severity, cond) << "msg"; ``` ### Logger severity checker In some cases there is a need to perform a group of actions depending on the current logger severity level. There is a special macro for that. It helps to minimize performance penalty when the logger is inactive. ```cpp IF_PLOG(severity) ``` Sample: ```cpp IF_PLOG(plog::debug) // we want to execute the following statements only at debug severity (and higher) { for (int i = 0; i < vec.size(); ++i) { PLOGD << "vec[" << i << "]: " << vec[i]; } } ``` # Advanced usage ## Changing severity at runtime It is possible to set the maximum severity not only at the logger initialization time but at any time later. There are special accessor methods: ```cpp Severity Logger::getMaxSeverity() const; Logger::setMaxSeverity(Severity severity); ``` To get the logger use `plog::get` function: ```cpp Logger* get(); ``` Sample: ```cpp plog::get()->setMaxSeverity(plog::debug); ``` ## Custom initialization Non-typical log cases require the use of custom initialization. It is done by the following `plog::init` function: ```cpp Logger& init(Severity maxSeverity = none, IAppender* appender = NULL); ``` You have to construct an [Appender](#appender) parameterized with a [Formatter](#formatter) and pass it to the `plog::init` function. > **Note** The appender lifetime should be static! Sample: ```cpp static plog::ConsoleAppender consoleAppender; plog::init(plog::debug, &consoleAppender); ``` ## Multiple appenders It is possible to have multiple [Appenders](#appender) within a single [Logger](#logger). In such case log message will be written to all of them. Use the following method to accomplish that: ```cpp Logger& Logger::addAppender(IAppender* appender); ``` Sample: ```cpp static plog::RollingFileAppender fileAppender("MultiAppender.csv", 8000, 3); // Create the 1st appender. static plog::ConsoleAppender consoleAppender; // Create the 2nd appender. plog::init(plog::debug, &fileAppender).addAppender(&consoleAppender); // Initialize the logger with the both appenders. ``` Here the logger is initialized in the way when log messages are written to both a file and a console. *Refer to [MultiAppender](samples/MultiAppender) for a complete sample.* ## Multiple loggers Multiple [Loggers](#logger) can be used simultaneously each with their own separate configuration. The [Loggers](#logger) differ by their instanceId (that is implemented as a template parameter). The default instanceId is zero. Initialization is done by the appropriate template `plog::init` functions: ```cpp Logger& init(...); ``` To get a logger use `plog::get` function (returns `NULL` if the logger is not initialized): ```cpp Logger* get(); ``` All logging macros have their special versions that accept an instanceId parameter. These kind of macros have an underscore at the end: ```cpp PLOGD_(instanceId) << "debug"; PLOGD_IF_(instanceId, condition) << "conditional debug"; IF_PLOG_(instanceId, severity) ``` Sample: ```cpp enum // Define log instanceIds. Default is 0 and is omitted from this enum. { SecondLog = 1 }; int main() { plog::init(plog::debug, "MultiInstance-default.txt"); // Initialize the default logger instance. plog::init(plog::debug, "MultiInstance-second.txt"); // Initialize the 2nd logger instance. // Write some messages to the default log. PLOGD << "Hello default log!"; // Write some messages to the 2nd log. PLOGD_(SecondLog) << "Hello second log!"; return 0; } ``` *Refer to [MultiInstance](samples/MultiInstance) for a complete sample.* ## Share log instances across modules (exe, dll, so, dylib) For applications that consist of several binary modules, plog instances can be local (each module has its own instance) or shared (all modules use the same instance). In case of shared you have to initialize plog only in one module, other modules will reuse that instance. Sharing behavior is controlled by the following macros and is OS-dependent: |Macro|OS|Behavior| |--|--|--| |PLOG_GLOBAL|Linux/Unix|Shared| |PLOG_LOCAL|Linux/Unix|Local| |PLOG_EXPORT|Linux/Unix|n/a| |PLOG_IMPORT|Linux/Unix|n/a| ||Linux/Unix|According to compiler settings| |PLOG_GLOBAL|Windows|n/a| |PLOG_LOCAL|Windows|Local| |PLOG_EXPORT|Windows|Shared (exports)| |PLOG_IMPORT|Windows|Shared (imports)| ||Windows|Local| For sharing on Windows one module should use `PLOG_EXPORT` and others should use `PLOG_IMPORT`. Also be careful on Linux/Unix: if you don't specify sharing behavior it will be determined by compiler settings (`-fvisibility`). *Refer to [Shared](samples/Shared) for a complete sample.* ## Chained loggers A [Logger](#logger) can work as an [Appender](#appender) for another [Logger](#logger). So you can chain several loggers together. This is useful for streaming log messages from a shared library to the main application binary. *Important: don't forget to specify `PLOG_LOCAL` sharing mode on Linux/Unix systems for this sample.* Sample: ```cpp // shared library // Function that initializes the logger in the shared library. extern "C" void EXPORT initialize(plog::Severity severity, plog::IAppender* appender) { plog::init(severity, appender); // Initialize the shared library logger. } // Function that produces a log message. extern "C" void EXPORT foo() { PLOGI << "Hello from shared lib!"; } ``` ```cpp // main app // Functions imported from the shared library. extern "C" void initialize(plog::Severity severity, plog::IAppender* appender); extern "C" void foo(); int main() { plog::init(plog::debug, "ChainedApp.txt"); // Initialize the main logger. PLOGD << "Hello from app!"; // Write a log message. initialize(plog::debug, plog::get()); // Initialize the logger in the shared library. Note that it has its own severity. foo(); // Call a function from the shared library that produces a log message. return 0; } ``` *Refer to [Chained](samples/Chained) for a complete sample.* # Architecture ## Overview Plog is designed to be small but flexible, so it prefers templates to interface inheritance. All main entities are shown on the following UML diagram: ```mermaid classDiagram class Logger~instanceId~ { <> +addAppender() +getMaxSeverity() +setMaxSeverity() +checkSeverity() -maxSeverity -appenders } class IAppender { <> +write() } Logger --|> IAppender Logger "1" o-- "*" IAppender IAppender <|-- RollingFileAppender~Formatter, Converter~ IAppender <|-- ConsoleAppender~Formatter~ IAppender <|-- AndroidAppender~Formatter~ IAppender <|-- EventLogAppender~Formatter~ IAppender <|-- DebugOutputAppender~Formatter~ IAppender <|-- DynamicAppender ConsoleAppender <|-- ColorConsoleAppender~Formatter~ DynamicAppender "1" o-- "*" IAppender ``` ```mermaid classDiagram class Severity { <> none, fatal, error, warning, info, debug, verbose } class Record { +operator<<() +printf() -time -severity -tid -object -line -message -func -file -instanceId } ``` ```mermaid classDiagram class CsvFormatter { +header()$ +format()$ } class TxtFormatter { +header()$ +format()$ } class FuncMessageFormatter { +header()$ +format()$ } class MessageOnlyFormatter { +header()$ +format()$ } ``` ```mermaid classDiagram class UTF8Converter { +header()$ +convert()$ } class NativeEOLConverter~NextConverter~{ +header()$ +convert()$ } ``` There are 5 functional parts: - [Logger](#logger) - the main object, implemented as singleton - [Record](#record) - keeps log data: time, message, etc - [Appender](#appender) - represents a log data destination: file, console, etc - [Formatter](#formatter) - formats log data into a string - [Converter](#converter) - converts formatter output into a raw buffer The log data flow is shown below: ```mermaid flowchart LR; ST((start)) --> P[PLOG macro] --> R[Record] --> L[Logger] --> A[Appender] A -->|record| F[Formatter] -->|text| C[Converter] -->|binary| A A --> FIN(((finish))) ``` ## Logger [Logger](#logger) is a center object of the whole logging system. It is a singleton and thus it forms a known single entry point for configuration and processing log data. [Logger](#logger) can act as [Appender](#appender) for another [Logger](#logger) because it implements `IAppender` interface. Also there can be several independent loggers that are parameterized by an integer instanceId number. The default instanceId is 0. ```cpp template class Logger : public util::Singleton >, public IAppender { public: Logger(Severity maxSeverity = none); Logger& addAppender(IAppender* appender); Severity getMaxSeverity() const; void setMaxSeverity(Severity severity); bool checkSeverity(Severity severity) const; virtual void write(const Record& record); void operator+=(const Record& record); }; ``` ## Record [Record](#record) stores all log data. It includes: - time - severity - thread id - 'this' pointer (if a log message is written from within an object) - source line - source file name - function name - message - instance id > **Note** Source file name isn't captured by default. To enable it define PLOG_CAPTURE_FILE. Also [Record](#record) has a number of overloaded stream output operators to construct a message. ```cpp class Record { public: Record(Severity severity, const char* func, size_t line, const char* file, const void* object, int instanceId); ////////////////////////////////////////////////////////////////////////// // Stream output operators Record& operator<<(char data); Record& operator<<(wchar_t data); template Record& operator<<(const T& data); ////////////////////////////////////////////////////////////////////////// // Getters virtual const util::Time& getTime() const; virtual Severity getSeverity() const; virtual unsigned int getTid() const; virtual const void* getObject() const; virtual size_t getLine() const; virtual const util::nchar* getMessage() const; virtual const char* getFunc() const; virtual const char* getFile() const; virtual int getInstanceId() const; }; ``` *See [Stream improvements over std::ostream](#stream-improvements-over-stdostream).* *Refer to [Demo](samples/Demo) sample to see what can be written to the log stream.* ## Formatter [Formatter](#formatter) is responsible for formatting log data from [Record](#record) into various string representations (binary forms can be used too). There is no base class for formatters, they are implemented as classes with static functions `format` and `header`: ```cpp class Formatter { public: static util::nstring header(); static util::nstring format(const Record& record); }; ``` *See [How to implement a custom formatter](#custom-formatter).* ### TxtFormatter This is a classic log format available in almost any log library. It is good for console output and it is easy to read without any tools. ``` 2014-11-11 00:29:06.245 FATAL [4460] [main@22] fatal 2014-11-11 00:29:06.261 ERROR [4460] [main@23] error 2014-11-11 00:29:06.261 INFO [4460] [main@24] info 2014-11-11 00:29:06.261 WARN [4460] [main@25] warning 2014-11-11 00:29:06.261 DEBUG [4460] [main@26] debug 2014-11-11 00:29:06.261 INFO [4460] [main@32] This is a message with "quotes"! 2014-11-11 00:29:06.261 DEBUG [4460] [Object::Object@8] 2014-11-11 00:29:06.261 DEBUG [4460] [Object::~Object@13] ``` ### TxtFormatterUtcTime This is a variant of [TxtFormatter](#txtformatter) that uses UTC time instead of local time. ### CsvFormatter This is the most powerful log format. It can be easily read without any tools (but slighlty harder than [TXT format](#txtformatter)) and can be heavily analyzed if it is opened with a CSV-aware tool (like Excel). One rows can be highlighted according to their cell values, another rows can be hidden, columns can be manipulated and you can even run SQL queries on log data! This is a recommended format if logs are big and require heavy analysis. Also 'this' pointer is shown so object instances can be told apart. ``` Date;Time;Severity;TID;This;Function;Message 2014/11/14;15:22:25.033;FATAL;4188;00000000;main@22;"fatal" 2014/11/14;15:22:25.033;ERROR;4188;00000000;main@23;"error" 2014/11/14;15:22:25.033;INFO;4188;00000000;main@24;"info" 2014/11/14;15:22:25.033;WARN;4188;00000000;main@25;"warning" 2014/11/14;15:22:25.048;DEBUG;4188;00000000;main@26;"debug" 2014/11/14;15:22:25.048;INFO;4188;00000000;main@32;"This is a message with ""quotes""!" 2014/11/14;15:22:25.048;DEBUG;4188;002EF4E3;Object::Object@8; 2014/11/14;15:22:25.048;DEBUG;4188;002EF4E3;Object::~Object@13; ``` > **Note** Message size is limited to 32000 chars. ### CsvFormatterUtcTime This is a variant of [CsvFormatter](#csvformatter) that uses UTC time instead of local time. ### FuncMessageFormatter This format is designed to be used with appenders that provide their own timestamps (like [AndroidAppender](#androidappender) or linux syslog facility). ``` main@22: fatal main@23: error main@24: info main@25: warning main@26: debug main@32: This is a message with "quotes"! Object::Object@8: Object::~Object@13: ``` ### MessageOnlyFormatter Use this formatter when you're interested only in a log message. ``` fatal error info warning debug This is a message with "quotes"! ``` ## Converter [Converter](#converter) is responsible for conversion of [Formatter](#formatter) output data to a raw buffer (represented as `std::string`). It is used by [RollingFileAppender](#rollingfileappender) to perform a conversion before writing to a file. There is no base class for converters, they are implemented as classes with static functions `convert` and `header`: ```cpp class Converter { public: static std::string header(const util::nstring& str); static std::string convert(const util::nstring& str); }; ``` *See [How to implement a custom converter](#custom-converter).* ### UTF8Converter [UTF8Converter](#utf8converter) is a default converter in plog. It converts string data to UTF-8 with BOM. ### NativeEOLConverter This converter converts `` line endings to `` on Windows and does nothing on everything else. As a template parameter it accepts another converter that is called next (by default [UTF8Converter](#utf8converter)). Sample: ```cpp plog::RollingFileAppender > fileAppender("NativeEOL.log"); ``` *Refer to [NativeEOL](samples/NativeEOL) for a complete sample.* ## Appender [Appender](#appender) uses [Formatter](#formatter) and [Converter](#converter) to get a desired representation of log data and outputs (appends) it to a file/console/etc. All appenders must implement `IAppender` interface (the only interface in plog): ```cpp class IAppender { public: virtual ~IAppender(); virtual void write(const Record& record) = 0; }; ``` *See [How to implement a custom appender](#custom-appender).* ### RollingFileAppender This appender outputs log data to a file with rolling behavior. As template parameters it accepts both [Formatter](#formatter) and [Converter](#converter). ```cpp RollingFileAppender::RollingFileAppender(const util::nchar* fileName, size_t maxFileSize = 0, int maxFiles = 0); ``` - `fileName` - a log file name - `maxFileSize` - the maximum log file size in bytes - `maxFiles` - a number of log files to keep If `maxFileSize` or `maxFiles` is 0 then rolling behavior is turned off. The sample file names produced by this appender: - mylog.log <== current log file (size < maxFileSize) - mylog.1.log <== previous log file (size >= maxFileSize) - mylog.2.log <== previous log file (size >= maxFileSize) A file name can be changed at an arbitrary moment by calling `setFileName` as well as `maxFiles` and `maxFileSize` can be changed by calling `setMaxFiles` and `setMaxFileSize`. > **Note** The lowest `maxFileSize` is 1000 bytes. > **Note** A log file is created on the first log message. ### ConsoleAppender This appender outputs log data to `stdout` or `stderr`. As a template parameter it accepts [Formatter](#formatter). ```cpp ConsoleAppender::ConsoleAppender(OutputStream outStream = streamStdOut); ``` ### ColorConsoleAppender This appender outputs log data to `stdout` or `stderr` using colors that depend on a log message severity level. As a template parameter it accepts [Formatter](#formatter). ```cpp ColorConsoleAppender::ColorConsoleAppender(OutputStream outStream = streamStdOut); ``` ### AndroidAppender [AndroidAppender](#androidappender) uses Android logging system to output log data. It can be viewed with [logcat](http://developer.android.com/tools/help/logcat.html) or in a log window of Android IDEs. As a template parameter this appender accepts [Formatter](#formatter) (usually [FuncMessageFormatter](#funcmessageformatter)). ```cpp AndroidAppender::AndroidAppender(const char* tag); ``` ### EventLogAppender This appender outputs log data to the windows event log. It can be viewed with the windows event log viewer. As a template parameter it accepts [Formatter](#formatter). The constructor parameter is the event source name - typically it is the name of the application or a subcomponent of the application. It must be unique for the whole system. ```cpp EventLogAppender::EventLogAppender(const wchar_t* sourceName); ``` [EventLogAppender](#eventlogappender) must be registered in the windows registry before use (before calling the constructor). There is a helper class for that: ```cpp bool EventLogAppenderRegistry::add(const wchar_t* sourceName, const wchar_t* logName = L"Application"); bool EventLogAppenderRegistry::exists(const wchar_t* sourceName, const wchar_t* logName = L"Application"); void EventLogAppenderRegistry::remove(const wchar_t* sourceName, const wchar_t* logName = L"Application"); ``` Registry operations are system-wide and require administrator rights. Also they are persistent so can be performed only once (when the application is installed/uninstalled). ### DebugOutputAppender [DebugOutputAppender](#debugoutputappender) sends log data to the debugger (works only on Windows). As a template parameter this appender accepts [Formatter](#formatter). ```cpp DebugOutputAppender::DebugOutputAppender(); ``` ### ArduinoAppender This appender outputs log data to an Arduino device, typically over a serial connection. As a template parameter, it accepts a [Formatter](#formatter). ```cpp ArduinoAppender::ArduinoAppender(Stream& stream); ``` - `stream` - the Arduino `Stream` object (such as `Serial`) used for output. This appender is useful for embedded systems or IoT projects where you want to monitor logs directly from an Arduino board. *Refer to [Arduino sample](samples/Arduino) for a complete sample.* ### DynamicAppender [DynamicAppender](#dynamicappender) is a wrapper that can add/remove appenders dynamically (at any point of time) in a thread-safe manner. ```cpp DynamicAppender& DynamicAppender::addAppender(IAppender* appender); DynamicAppender& DynamicAppender::removeAppender(IAppender* appender); ``` *Refer to [DynamicAppender sample](samples/DynamicAppender) for a complete sample.* # Miscellaneous notes ## Lazy stream evaluation Log messages are constructed using lazy stream evaluation. It means that if a log message will be dropped (because of its severity) then stream output operators are not executed. Thus performance penalty of unprinted log messages is negligible. ```cpp PLOGD << /* the following statements will be executed only when the logger severity is debug or higher */ ... ``` ## Stream improvements over std::ostream Stream output in plog has several improvements over the standard `std::ostream`: - handles wide chars/strings: `wchar_t`, `wchar_t*`, `std::wstring` - handles `NULL` values for C-strings: `char*` and `wchar_t*` - implicitly casts objects to: `std::string` and `std::wstring` (if they have an appropriate cast operator) - supports `QString` and `QStringRef` (you need to include Qt headers before plog) - supports `std::filesystem::path` - supports managed C++ `System::String^` ## Automatic 'this' pointer capture 'This' pointer is captured automatically to log data and can be printed by [CsvFormatter](#csvformatter). Unfortunately this feature is supported only on msvc 2010 and higher. It's disabled by default (due to some compatibility issues with `__if_exists` C++ extension), to enable it define `PLOG_ENABLE_GET_THIS`. ## Headers to include The core plog functionality is provided by inclusion of `plog/Log.h` file. Extra components require inclusion of corresponding extra headers after `plog/Log.h`. Core components are: - [TxtFormatter](#txtformatter)/[TxtFormatterUtcTime](#txtformatterutctime) - [CsvFormatter](#csvformatter)/[CsvFormatterUtcTime](#csvformatterutctime) - [UTF8Converter](#utf8converter) - [NativeEOLConverter](#nativeeolconverter) - [RollingFileAppender](#rollingfileappender) ## Unicode Plog is unicode aware and wide string friendly. All messages are converted to a system native char type: - Windows - `wchar_t` - by default - `char` - if compiling with `/utf-8` switch or set `PLOG_CHAR_IS_UTF8` to 1 - all other systems - `char` Also `char` is treated as: - Windows - active code page - be default - UTF-8 - if compiling with `/utf-8` switch or set `PLOG_CHAR_IS_UTF8` to 1 - all other systems - UTF-8 Internally plog uses `nstring`, `nstringstream` and `nchar` ('n' for native) that are defined as: ```cpp #if PLOG_CHAR_IS_UTF8 typedef std::string nstring; typedef std::ostringstream nostringstream; typedef std::istringstream nistringstream; typedef std::ostream nostream; typedef char nchar; #else typedef std::wstring nstring; typedef std::wostringstream nostringstream; typedef std::wistringstream nistringstream; typedef std::wostream nostream; typedef wchar_t nchar; #endif ``` By default all log files are stored in UTF-8 with BOM thanks to [UTF8Converter](#utf8converter). ## Wide string support Whether `wchar_t`, `wchar_t*`, `std::wstring` can be streamed to log messages or not is controlled by the `PLOG_ENABLE_WCHAR_INPUT` macro. Set it to a non-zero value to enable wide string support. By default wide string support is enabled for Windows and disabled for all non-Windows systems. > **Note** Wide string support requires linking to `iconv` on macOS. ## Performance Plog is not using any asynchronous techniques so it may slow down your application on large volumes of log messages. Producing a single log message takes the following amount of time: |CPU|OS|Time per a log call, microsec| |----|----|:----:| |AMD Phenom II 1055T @3.5GHz|Windows 2008 R2|12| |AMD Phenom II 1055T @3.5GHz|Linux Mint 17.1|8| |Intel Core i3-3120M @2.5GHz|Windows 2012 R2|25| |Intel Core i5-2500K @4.2GHz|Windows 2008 R2|8| |Intel Atom N270 @1.6GHz|Windows 2003|68| Assume 20 microsec per a log call then 500 log calls per a second will slow down an application by 1%. It is acceptable for most use cases. *Refer to [Performance](samples/Performance) for a complete sample.* ## Printf style formatting Plog supports printf style formatting: ```cpp PLOGI.printf("%d %s", 42, "test"); PLOGI.printf(L"%d %S", 42, "test"); // wchar_t version ``` ## LOG_XXX macro name clashes `LOG_XXX` macro names may be in conflict with other libraries (for example [syslog](https://linux.die.net/man/3/syslog)). In such cases you can disable the `LOG_XXX` macro by defining `PLOG_OMIT_LOG_DEFINES` and use `PLOG_XXX`. *Define `PLOG_OMIT_LOG_DEFINES` before `#include ` or in the project settings!* ## Disable logging to reduce binary size Logging code makes binary files larger. If you use it for debugging you can remove all logging code from release builds by defining the macro `PLOG_DISABLE_LOGGING`. ## PLOG_MESSAGE_PREFIX You can customize the prefix that appears before every log message by defining the `PLOG_MESSAGE_PREFIX` macro before including plog headers. This is useful for distinguishing log output from different modules or for adding custom tags to every log line. Example: ```cpp #define PLOG_MESSAGE_PREFIX "[MyApp] " #include #include int main() { plog::init(plog::debug, "log.txt"); PLOGD << "This is a debug message."; return 0; } ``` This will produce log lines prefixed with `[MyApp] `. > **Note**: The macro must be defined before including any plog headers to take effect. # Extending Plog can be easily extended to support new: - [custom data type](#custom-data-type) - [custom appender](#custom-appender) - [custom formatter](#custom-formatter) - [custom converter](#custom-converter) ## Custom data type To output a custom data type to a log message implement the following function: ```cpp namespace plog { Record& operator<<(Record& record, const MyType& t); } ``` *Refer to [CustomType](samples/CustomType) for a complete sample.* ## Custom appender A custom appender must implement the `IAppender` interface. Also it may accept [Formatter](#formatter) and [Converter](#converter) as template parameters however this is optional. ```cpp namespace plog { template class MyAppender : public IAppender { public: virtual void write(const Record& record); }; } ``` *Refer to [CustomAppender](samples/CustomAppender) for a complete sample.* ## Custom formatter A formatter that is compatible with existing appenders must be a class with 2 static methods: - `header` - returns a header for a new log - `format` - formats [Record](#record) to a string ```cpp namespace plog { class MyFormatter { public: static util::nstring header(); static util::nstring format(const Record& record); }; } ``` *Refer to [CustomFormatter](samples/CustomFormatter) for a complete sample.* ## Custom converter A converter must be a class with 2 static methods: - `header` - converts a header for a new log - `convert` - converts log messages ```cpp namespace plog { class MyConverter { public: static std::string header(const util::nstring& str); static std::string convert(const util::nstring& str); }; } ``` *Refer to [CustomConverter](samples/CustomConverter) for a complete sample.* # Samples There are a number of samples that demonstrate various aspects of using plog. They can be found in the [samples](samples) folder: |Sample|Description| |------|-----------| |[Android](samples/Android)|Shows how to use [AndroidAppender](#androidappender).| |[Arduino](samples/Arduino)|Arduino sample - shows how to use [ArduinoAppender](#arduinoappender)| |[AscDump](samples/AscDump)|Shows how to use `plog::ascdump` to dump binary buffers into ASCII.| |[Chained](samples/Chained)|Shows how to chain a logger in a shared library with the main logger (route messages).| |[ColorConsole](samples/ColorConsole)|Shows how to use [ColorConsoleAppender](#colorconsoleappender).| |[CustomAppender](samples/CustomAppender)|Shows how to implement a custom appender that stores log messages in memory.| |[CustomConverter](samples/CustomConverter)|Shows how to implement a custom converter that encrypts log messages.| |[CustomFormatter](samples/CustomFormatter)|Shows how to implement a custom formatter.| |[CustomType](samples/CustomType)|Shows how to print a custom type to the log stream.| |[CXX11](samples/CXX11)|Demonstrates log stream abilities for C++11 features.| |[CXX17](samples/CXX17)|Demonstrates log stream abilities for C++17 features.| |[DebugOutput](samples/DebugOutput)|Shows how to use [DebugOutputAppender](#debugoutputappender) to write to the windows debug output.| |[Demo](samples/Demo)|Demonstrates log stream abilities, prints various types of messages.| |[DisableLogging](samples/DisableLogging)|Shows how to disable logging (so it will be stripped from the binary).| |[DynamicAppender](samples/DynamicAppender)|Shows how to add/remove appenders dynamically).| |[EventLog](samples/EventLog)|Shows how to use [EventLogAppender](#eventlogappender) to write to the windows event log.| |[Facilities](samples/Facilities)|Shows how to use logging per facilities via multiple logger instances (useful for big projects).| |[Hello](samples/Hello)|A minimal introduction sample, shows the basic 3 steps to start using plog.| |[HexDump](samples/HexDump)|Shows how to use `plog::hexdump` to dump binary buffers into hex.| |[Library](samples/Library)|Shows plog usage in static libraries.| |[MessagePrefix](samples/MessagePrefix)|Demonstrates usage of the `PLOG_MESSAGE_PREFIX` macro to add a custom prefix to every log message.| |[MultiAppender](samples/MultiAppender)|Shows how to use multiple appenders with the same logger.| |[MultiInstance](samples/MultiInstance)|Shows how to use multiple logger instances, each instance has its own independent configuration.| |[NotShared](samples/NotShared)|Shows how to make logger instances local across binary modules (this is the default behavior on Windows but not on other platforms, so be careful).| |[ObjectiveC](samples/ObjectiveC)|Shows that plog can be used in ObjectiveC++.| |[Path](samples/Path)|A test sample to check that `std::filesystem::path` can be logged.| |[Performance](samples/Performance)|Measures time per a log call.| |[PrintVar](samples/PrintVar)|Shows how to use `PLOG_PRINT_VAR` to print variables.| |[SetFileName](samples/SetFileName)|Shows how to change a log file name at arbitrary moment.| |[Shared](samples/Shared)|Shows how to share logger instances across binary modules (this is the default behavior on everything except Windows, so be careful)| |[SkipNativeEOL](samples/SkipNativeEOL)|Shows how to skip [NativeEOLConverter](#nativeeolconverter).| |[UtcTime](samples/UtcTime)|Shows how to use UTC time instead of local time.| |[Utf8Everywhere](samples/Utf8Everywhere)|Demonstrates how to use http://utf8everywhere.org on Windows.| # License This version of plog is licensed under the [MIT license](https://choosealicense.com/licenses/mit). You can freely use it in your commercial or opensource software. # Version history ## Version 1.1.11 (11 Aug 2025) - New: Add support for FreeRTOS (#298) - New: Add PLOG_MESSAGE_PREFIX (#288) - Enh: Add more documentation (#287) - Fix: Minimum required cmake version as of cmake 3.31 and 4.0 (#296, #268, #300) - Fix: `std::filesystem::path` issue on MSVC, add more tests (#273) - Fix: ASAN warning in HexDump sample (#305, #303) ## Version 1.1.10 (20 Aug 2023) - New: Add support for UTF-8 char encoding on Windows (#76, #69, #238, #239)\ *This allows to use [Utf8Everywhere](http://utf8everywhere.org) approach* - New: Add ArduinoAppender - New: Publish on [PlatformIO Registry](https://registry.platformio.org) for embedded development (#244) - New: Add support for `char8_t` strings - New: Add tests - Enh: Add rudimentary support of VS2005 (#232) - Enh: Implementation of `vasprintf` emulation (#243) - Fix: Parsing of templated classes (#251) - Fix: Compiling with MSVC using C++20 (#236) - Fix: No newline error with '-Wnewline-eof' build flag (#263) ## Version 1.1.9 (16 Dec 2022) - New: Add ability to truncate log file using `>` in shell (#155) - New: Add override specifier (to be able to build with `-Wsuggest-override`) (#231) - New: Add nuget specs (#86) - New: Add ability to add/remove appenders (#226) - Fix: Printing `boost::filesystem::path` (#227) - Fix: Building on C++ Builder 10.4 (#225) - Fix: `PLOG_LOCAL` mode if symbol visibility set to default (#219) ## Version 1.1.8 (10 Jun 2022) - Fix: 'operator <<' is ambiguous for string_view on Windows (#217) - Fix: CMake + vcpkg: find_package (#211) ## Version 1.1.7 (09 Jun 2022) - New: Add hex dumper (#111) - New: Add ASCII dumper (#213) - New: Add support for printing std containers (#207) - New: Add console initializer - New: Add PrintVar helper - New: Add CMake find_package support (#171) - Enh: Change license to MIT (#212) - Fix: Specify calling convention for std stream manipulators (#210) - Fix: Compilation on VS2010 (#207) - Fix: Use add_custom_target for pseudo-project with headers (#216) ## Version 1.1.6 (06 Feb 2022) - New: Ability to disable logging to reduce binary size (#130) - New: Ability to change `maxFiles`/`maxFileSize` after initialization - New: Logging `std::filesystem::path` without explicit conversion to `std::string` (#168, #185, #183) - New: Allow to choose `stdout`/`stderr` for console appender (#162, #117) - New: Ability to change log file name at runtime (#62) - New: Ability to control sharing across modules (#96, #152, #20) - New: Building on platforms without thread support (#161, #113) - Enh: Change color functions from private to protected (#163) - Enh: Do not include `plog/Init.h` in `plog/Log.h` (#127, #89) - Fix: WideCharToMultiByte bug (#202) - Fix: Building with Qt6 (#190) - Fix: Compiling on GCC 4.4-4.7 (#176) - Fix: Suppress UBSan false positive (#90) - Fix: Don't share handle/fd to child process (#170) - Fix: MSVC analyzer warnings (#148) - Fix: File size truncation > 2GB on Windows (#160) - Fix: [RTEMS](https://www.rtems.org) build on newer toolchain (#158, #159) ## Version 1.1.5 (21 Oct 2019) - New: Use `NativeEOLConverter` by default (#145) - New: Add logger `instanceId` into `Record` (#141) - New: Add support for the printf style formatting (#139) - New: Make `severityFromString` case-insensitive - New: Define macro names with "PLOG" instead of "LOG" in order to avoid conflicts with "LOG" names defined in other packages or in system headers (#25, #129) - New: Add option for building samples (ON per default) (#125, #126) - New: Add CMake installer (#121, #122) - New: Add support for `QStringRef` - New: Modernize CMake (#106) - New: Allow rollLogFiles to be called manually (#100, #103) - New: Add ability to use UTC time (#101) - Fix: Disable `PLOG_GET_THIS()` by default (#120, #132) - Fix: Change `RegSetValueExW` prototype to match windows native declaration (void* -> BYTE*) - Fix: Move `System::String^` handler to a free function (#131) - Fix: Making sure we can build standalone under Windows (#123) - Fix: Parse error by ReSharper (#116) - Fix: Parse error by Clang Code Model in Qt Creator (#114) - Fix: Printing CustomType at begin of the stream (#94) - Fix: Make `RollingFileAppender` work with maxFiles set to 1 (#70) - Fix: Clang-tidy nullable issue ## Version 1.1.4 (26 Mar 2018) - New: Add `-Wundef` support - New: Add [RTEMS](https://www.rtems.org) support (#87) - New: Add Intel C++ Compiler support (#84) - New: Add FreeBSD support (#83) - New: Add `-Wnon-virtual-dtor` support (#79) - New: Support `ostream` operator<< on Windows as well as `wostream` (#66) - Fix: Fix compilation for Android (#68) - Fix: Fix compiling with CMake 2.8 ## Version 1.1.3 (09 Aug 2017) - New: Introduce `LOG_ENABLE_WCHAR_INPUT` macro to control wide string support - New: Add support for managed C++ `System::String^` (#63) - New: Add missing macros for logging with severity NONE (#61) - Fix: Unable to build [NativeEOLConverter](#nativeeolconverter)/[UTF8Converter](#utf8converter) using Visual Studio (#59) - Fix: Use `WriteConsoleW` instead of global `setlocale` for writing unicode into Windows console (#58) - Fix: Mention about linking to `iconv` on macOS (#55) - Fix: `IF_LOG` macro didn't work for curly braces blocks ## Version 1.1.2 (02 May 2017) - New: Add [NativeEOLConverter](#nativeeolconverter) - New: Add [MessageOnlyFormatter](#messageonlyformatter) - New: Slightly increase log performance on Windows (about 9%). ## Version 1.1.1 (17 Apr 2017) - New: Ability to check whether event log registry entry exists (#36) - Fix: Update includes (#47) - Fix: Get rid of `windows.h` dependency (#45, #13) - Fix: Signed unsigned assignment warning (#40) - Fix: Build warning on macOS 10.12 Sierra (#39) ## Version 1.1.0 (20 Nov 2016) - Fix: Introduce binary compatible interface to `Record` (WARNING: this is not compatible with 1.0.x version in [Chained mode](#chained-loggers), so don't mix 1.1.x and 1.0.x) (#34) ## Version 1.0.2 (19 Nov 2016) - New: Default instanceId can be set via `LOG_DEFAULT_INSTANCE` (#11) - New: Support for `QString` (#30) - New: Support for C++Builder - New: `severityFromString` function (#15) - New: Capture source file name (disabled by default) (#21) - New: Add [DebugOutputAppender](#debugoutputappender) (#33) - New: Add [EventLogAppender](#eventlogappender) (#32) - Fix: Crash on processing Obj-C function name (#12) - Fix: Compatibility with [MinGW](http://www.mingw.org/) (#17) - Fix: `IF_LOG_` macro in if/else leads to miss else branch (#27) - Fix: Thread safety for [ConsoleAppender](#consoleappender)/[ColorConsoleAppender](#colorconsoleappender) (#18, #29) - Fix: Support for stream manipulators like `std::endl` (#31) - Fix: Compatibility with old Visual Studio versions ## Version 1.0.1 (01 Nov 2015) - New: Add [ColorConsoleAppender](#colorconsoleappender) - Fix: Compatibility with [Mingw-w64](http://mingw-w64.org/) (#6) - Fix: Log file not created if file name contains Unicode characters in Windows (#7) - Fix: Flush stdout (#4) - Fix: IntelliSense error: expected an identifier (#3) ## Version 1.0.0 (19 May 2015) - Initial public release plog-1.1.11/doc/000077500000000000000000000000001504622126500133045ustar00rootroot00000000000000plog-1.1.11/doc/color-console.png000066400000000000000000001063161504622126500165770ustar00rootroot00000000000000PNG  IHDRNioRzTXtRaw profile type exifxmPQ ;Z8]]CI'J̜lRV0bD&gC]T5.Id|+c>p,+#yFa_ /?FhGiz!AoAUٮ_;_i~!N5KhrAu% nN'>YDfiCCPICC profilex}=H@_SKT ␡: *U(BP+`r& IZpc⬫ ~8)HK -b<8ǻ{wЬ2T2ɄͭWB$)u_<ܟ/_0gnXӛy8r8fƹ3#F&=O!K]t1+*q4j/d]sV}OpA[Y:$%H U#Da/K!W AwfqrM 'm]հcngJkM`FGuGS`I ّ4bx?o@[{@JX=3~4Xr72 xiTXtXML:com.adobe.xmp NbKGD? pHYs  tIME j2 IDATxwxTB) :Jf*Ti*EैTA:" !\ZHA銀 BH[0%3.<2^{w:GxwCH;:(cZc;ьjbS7Bf#)_E{,֎|`jlnP+gAm7ft1q7džc;ьnb1߅&’O/q/}Uxz9x[ٷ0ACtW@c:Nz ۆ|i  87κ11u\^g0Dw#sOsg1DGЗgg%jۻ9}>'&[L6hoC=iF_ko5"M5n,#a ׶Bs1HDB5oٿaq1熩ӻ\ɝ~$qlo]"ގf\qqpb/~&5ac1@+T7l<-' [ + mq?+D1Ojhg0NE'ꕄczScТPfhƉ~D֣Ӳ:%}f=- @-n+D1_ zSw]v/BBX\֦yBmCTE?>z?^u](>+ֈA*ʡ݅^SE\W]  -㉢=Uݛ * *ZO>)Z+-lCmaA,<36Ae\HY&h,V41{ʦscN2yvuL e&}D'N50?Յ6Ws?4 İgrN!DOVgQDiowXPcBa10ߨb& juUXhߩaCJFLU(Qbn?/N3: B2<_! X%N|׉aP/U=׏#\T%ֺĸ m.6o5Ѣ\|gonV'zS[V']q8y *gI^$'Ż1O;;$kzx|-a Q yn_~{9 X/`[ShXخ*f~R'v#Ż_&8"oĿ-#1U/HSUl۹HTDws$Z1l8-^㟵rϝwa#,)Ѣ_o͞}ql _ae78^94laFX+f]L}_P[l?&X1DGߚiBzʹVDc sRJi1Jԡa>mEtuV6"qJs}O,G]Ph:[ė$ ۅrW 8N#Z80hG!&R| I4͏-㜍z􂵎:*Cb7id_s,\Y זBn\s 1<]S..EU5ٺd>\߁ж'O(>j[OPtqRRzCi GCU%ǎ]-)THq?:ޙ96PֹDz(9F;R}mSJ38۵Ywrw"ˡqYwT"{d7?aDr wgEȞ3Il?F;b>Cbjxl N_Hj،/?ԾKk]75; !wQ ra\uP>G_ Ӑt&dgwX9'yׂ;wiR7 QRgEϣ,v=3# ~ٷ8WCuf}(;?onzx뮑mK)2 2hZ0!3W1ۈumEBH%={.x~t2oz|!:4&D7ͽKa+=i K^3NVnx=L'@4$@ ؏!)lυyxn13g1|Y׌p{W7\7zݴ'=}Ij.Zj 7Ɋʯ# ju~&ñ[XʡnTkr& 뇤n3cv}:_AtBOʞ#։0ljQ]3Μxn/)ܞsxq0Cs$LF{djHyLUTиoA).\?ѫdE Ip΀Qgԏr27ρ;$ &9'Œ|CGCRdn ."fd&BZ+ڿ0c?H$Ó`J|^B{C|)A0uֈ@SIz{2\LLRf+Xٷ@=cIBBD%cE+SVG +ށY|7.A"jm8P3^.GHlaq3} R,P#~Y[,O|\?"_6'VɄW6yR+}?ԠLƆnzxQMHp{ ͈Rۇ;͙|'pƿ#((Y4NeazrB?woE=A[E^$OMH9uK[]~nCagNRE\'eeR{ޜN_W iHz k@%?Jew3@QT_9|Ӏ%7:0Z7:P~U*LAvw?J>$揷MsWi&a$8¯ŧ^QYIV/}^q7~((^"9ԝOC,Nڌ_\oWf5-e)s+.ކj9]]$)ϙ} _`uJiΑEߧ/"y?^1_+N|?!㻫Vfb0/OΈ[VB7 W`2>7jFBHhђq,?]?CKڱ6yFHhԈ1ojORrc!Ah:i߰ ڇc8eb9Rޭ1Ĝ@UNwgwOn]7%z q#~$/AJ2\&:9KkgDk!Qgu pwOQwq#SO#A-W#:Dp+ȷRa}ČH=/$$;A3vSHCdr"~$}ݻTV1*l uZt# Re SjլKH[M~͝H^}hBOasΙ~ ƒeM)/`QcTEAx@B<ܺ ioBJJn'<+#=3X}$4jBBfe*2'6dBBuQ8~נhq_LrwsC쾌o:Ъ1?-o7ЍW`"PϬE'N0c#M;^P ORjPCU^߻^&5\h:?C.ZI%-Ah@}#B]E7cpgŜ0 R@ BZe Mɿ P] ~76ȿ3~]O{iekiavj\\!̟LdL 6y }/"WBXv.~Kq}k/xhGċnKgs_P%-2څ*8?lNsQxQ-ŀcq(4n<?#8 UMn|FvuWvf،As54]gcjUv --{" Abߓ ?xA&FRqwPũ*ޟ|wo$yu?Jz~ ǐᡨOL}-Nν>XGuy  k|N/"ˢ{fOuɿ.iD|EyR7#?73zQΆI 뭯2WkQssxg*>;~!uW:W` ןY_AN*WC渮2OOǔ8](7Cqzc [" 'Shm}?1.=s!plwbw;pogPDil!c[ۨBsmS99>K|j$ d]М>_<v96ÛNx6񭦒^q"GmB;nbhuͿ\_Q_vދ^=FB8u>JUK Lg??k!ƻ%?

bPS/T&QKzu E} V|w}g4 ^Z )!L>)OsKލLJ.&Š^TV%scZA݄SE\/}O퉭TPQhPwaTW~QP[4W1EyqbY(he`x ,h Y`S3e} yu'|vs?jй` tF-}zFnZ+S}Eash(D{?,g>v4?8u_"uU+$-BUȟh[N%=}y!ƊγKchcW+cƈ,l~@/U=Mͱ3hq^+mC\lo !e]]Tlo$߅M>)~#WQ8AMㄦ_AcG/,@/N`/^Kww6l_nDm1lo<?81>#NDt>ĩ?VuEٿxlZi rcg~u0K"qj0Qt;*^z[|D>T՚'k6XNb<^ ;{~CXMOD.TU4{oSecs KX# 3~]L}_PǸY; +D{Ú(6 ,|su.?[+?j~qI~Mj2 4v~F$yy-c?^&q% G7qH3>GGC鷼M?}YݚLvd n’\W }ʢnTkr&&U| Ìcn:moap:Y sA?Z? Gהh qqΞ.r1qz W /Ù36[)ETEٲ99?kz}3Ϳ!H" \5h{[x L=~ӐQhd "dy'R\'!1p 0l8+fA|, GXK;މ,sLKmQH]S$;Q.O7:;x1T*U>9N rj/E鏈';d4eGQo܇H p`PGq(\Mk.~t;maQ?=,/Da?rcɕ& IDATfM6A)3L ϑ8paQdLbǺ{CYE?ѫdE Ip΀@SIz{2ӯHybeD_ :|g:f4i.\O#hqfH !vWΟ̍mP fu#'ڼroflm,sUCX JFTyi7Ӣ1 /Jq-Op VR.^>!懰+wߨ ?Mn\w;gS6Ⱥz1-~` ׁ4 [I.JQwZd" MsWi&qClz!V|a 雦[,Ikr_OĿrNHmGM{/~`g垃Foo)9Gگ}r+SeXߟU21 w<~:TܲQ:,P.ád}C ~15"qs`Pnwlg&Ў}ӾFHxˁ/{:Y#.S_-TsE %ݒS ]6Lɝi@g,Iop?QAp{ LW4[Pʻ5) .þdNϿCI'#]L?[ĞCJ~`,Z4)]Enj:S޺7oKbE%K i$?:gr{0eW ` p {AK_]Lu]F<:M+g!4فi}Hz  p}~'$`GGrZWF| V"UXd+@i}*ҍlWG0:s2K8ۄ̝;/$$sҝ=}5|4mOCPY7'x[*]`Pn̍lG͊^^ M&9?$7~XѭV{w}I`jլKH[sɳ#)?ׇ_p~%8U+m,i_."?> ?(c`2t%y),oU*@r9a Z}))6';5 jUFq_K(п`pm`}$4jBBf$~kɸ{ڐ(/+î^ݫW@ei~נd@j.JkUC6w ggm[:$R( ^(-oK̸m |a LЧ68UrG4чmwIuZPyjThdí_CO6♼U۠sRFQa ϤY)~5Ea*דEvlV uv~=k2U$_[ڿzQg>sd ?(*~5m/!E^tľa0ڳz}Âyɐz+I Pz#@``n܄')5Sa}PI֏aFe6%(kjo4-`hY*U6SwZ]Q e_uJ xz0?CvN.{f!Ah@)@P @KP]~OAPf|1v VŠ<)}H7bNoDDB=:N@@;cre!$jU |0ւ A=_5;j2 MΨn2%Фf.> R 5j 77A׆AsT6mM[P&*T,cG DAxF@[v(쿗825R-!t~RJ!}%mKB/AZ\.B;Y\yBckg 1ŏ7jڙ#'#^mLaQu̷[WL!UvpR?t5duܽ:)̔Ls 6%?8ZV?(Z& Q(PKikZ>⭶sdTGik?*HwǏQƼm?葹bk"-ǴHTcXйU1Ə#ȷ7M /?`lml[P}y' {݄7 y^X?/=44Yh2(x|{E."WBXv.~+}ҽl_w[ܞmOO Qxċ> ᥸J/CC6 -KnKgݔ?{)JAf@ŗ.|VA'y/26(:7~Y>;9?Cd/#mvr@rcDq6@!}L@hbbOɹduK* ;o}55c^}%?bA-?1ښL#[,߰lWݼ)W}g<1]}<{0qhZϞٷc1ԱfLIASYo/bzVڇ EqZvS/i> ܓѓ=Q!K/*E :ő#iN:֗sݻ#G E[f- cצY^u[0Lgm*SV*1!w2A׍o#3ix(C*5rW߿ƭ9]C*u;{(1HD9>kY+ux?,gʠnsp>?M׬"P};;K$pf ֧&- eWvn>.Ԡ!|j_j?{sLk}yڟ1Nvl$U+Jmdi_c4/̒}'KZLڱGrꝻʓMjHrARIc _}[6\FznPT~^zn|pl.GFڿ|ilJ:? ޑM]KreBVb?wίӢ҉$+_u@^ycTG%2[e0}&F̑^{, #qI?^t{]!['HzVyZ>شO֍Tԗeשzɉ[d&z\W6A*(DY~;w`_Y4. ٴl0Q&m]*˹_-씰}%CxxSL"P:|=g–D|Paz _YԦk>Y^rpm1> x}Ee￸h.Sl(*ǯL_1LB n&N6( kE_-fww6&'6^NfF)3Va{ήkhݓ_c}Jl}9wx 8r`u/ѻoqc~DTٹKt:I>7D ms_1ƯʤoWˎrpj5)k'" <>#LxN^\#ۗ <~;J[}!kweֽ2,!R])qZ48y:f=><>vlؿOmuNvnVٿ@  ,ݹGoUK;\$ݦ݇#乡_ϊe~Z/2n0m^US8u¼S$VKݞ*XD(w1{"w|w\K;~nC)\$:ډ‘|769vgǞ,E*Yο$ $<׋6}Gt׉#.+[B,/NpwX2YOih ZLr0T<ҹ ?sm:g4jq|ToWf$׸oPȋcɄ<$o35w?ۤQSGW֏gG^ ?.1>{iZIr^z wjO/#%n ~V+O1csxȿzgן5obTv<6z?j.zg3NQYwJ!#JRl nǓ@4Hl2KKP?ݑbF/zo/dVg±_T~ Wx-~)MKfLL׾s7Y4սC+sv؇h~x^7`6I'&M-{>A9E1ݨ ^?L>Q*uU[}:YqM"NIRL$tT([R6H$4}fn<'e_jI0l{,խQ#G#nuywnwҗa5do^ǃYPj {'>x[^zΛsY|1,Z} \A Ap8s ;k8rt+|wlB|)?3Rp6~782"kwk;/toŦ)8Mz2fhs!2xhbEu+ܶzP 5aĄwةnOWhR+@1<1{7ڛ>eTGy?WP~q6ؚ>24_õKWSL}Ǯ3\ۼ'u=t#a[\viWdNȾ/6!tV ]wj7V-_~' pTgwŲ} sF0 H[8_Z-qS@*o%S W`٩w I7Kt 48_p־YsUxf5 L<'ƶg X$.Y,_ EJ`gpd-&5ML$ɶ"1DB8 H$!M^o[ͦ~nsH~Ⱦwہ}:K }}0G}zo[s?G_R*oa)4{7N-9$,5|tY;vb}>s;sWV3qZ=,D{3n_1WH]#㋘8{-_`}!Ϧʩ~;8c>=HYE-j =8[ePy&;!J(o\sQ-t't;i7r=2N1W;IS1meGwY[ԕ)GiIf9 3˽R2Ե|:TH)ѽ{umuR'x(' ɯ} ^uÚ xWXNwz#Hnt/MKztjWV\y?ΐRiC~_ᗻ!4mXT;?*SYR+IC^<:Q^"뿏ʫ\?*u!5~#.oD1hݠ e˔RL~ i{pag:zT&l>_VRiQ̓//-gm3M czH"Cp#1W 5 ^nבlXoU|Pk9#].@Cg "X@{@uٛo TW lFiWU_`c!Lظ2Otj^2*ѸL!l>2~RjUki׆*&"*I>'Ge4ZݎJZsʊ Atۓ5J}M}s02@Sg{0䗰%+4i"/֡PP^ߕ ._`SnGCz3bϹ~5/*l|Q"?ͣQ >ZƑo1lCo_ bi?H'wQ.LN7k$A Rf2uwoQ/T(gr+1<8E?e0F>cܷ?w |Ǜt:CO73S/# ދFVEnI&Nd|S[>/דW^1;,I]>eKz)u(?J|~ 1L}{33&u27?Ģeauկgp$qfF[FZ䔟L#όU''|@;w/ `PxO(BDl]ռE/'\SF`g { ]EF丨_q³x?<@\`-Yu lUּwǟxߣ-=v+yW P76gWrn· "ծX*^u3x&.lmeňxRsڄԵnzn;yPp xxP(OR7X2,a'K<ֵwÎkk2{/lQс4jJd| ze\nHLPQ.^1rFVf_qT>mumyϼb]2Nްu &- $V/mlk?GЮ|/ Mʆ.LY)|17YIa^bxJNJ-K}Ȥ,*똵=`;/,e5д[]9 ͍{IJJ .hodW_ϷvGyn&w%~яyױ o7^zqoy?g+3$]uH8WTdJg< --/tWJHF i 0,W} oYy|\rA4mgaѾQ?luGJ[i} [rl{U2}Ol$PJ |yr0vcwzji ~؎/XF6DG~3.RNVWt=H[u|e\rr|%m]oݧdr|ə5d4jg*[Oo(J9lPnQZmM-k# [PGeI+$jS|ZS&׶8V/Ց n- 7M*/bCIP]F+ke85|s9+_\>~ԶrvFjj2o9`Kʸi^ g'g6 SrÞ}=+˭;+ؙrҶPq!2.w&W%95@T6}%+kMd(?7׿YgDIP9@)' )[.w^_X_zK bE1]ԗ}<+B_{U)JJH6(rWڻ^"* `PHBFڞ=cO.}=k44%YT 8 *>'c|*M740.qݢ_'":u I#"Yׯ σD]ߦ_olJ1\tit}VQIʟS \|?QQAbm叚;Xv p޵OiNe+#řcŎ,ׁ"{Bj89>=gK>-(~:9Ů{Zίg3Ş`QYQ4{woOw;N})6µ=ENOG|Ny㻹Sv'z"O>;8XJt=D"K~G,vn nUऊ!1"D~xq.b]@aH00xiۓ&3SķO޶2Fv s`Qdb4 ds])Ƅ!AbźfmR&u6hϜG(ʜ{'yOWqQ gsDO\8P"ny((.盜SPq&<$vHU{]˺)1>md\L~ص#MT/\_)bcw[H_}Ej^>".ȼ1p+r?botaJ3qC}]F,*_rcPX|ޘ&NoFZ_mw\ٹYĆ?ʬ"}a'q~spSm]ݞ*V?$y}ᬁ"{iޢ d>¦~ ;ZpfP#uw maoM~;JQBnu~%@čS9^wp) nHZqSsM4:ݮO47d ھM+ߊqPb=Syk_I ~)s=>c` #5u2eW8o@5kKCxgE?3RY;7ەInX؃¯{WGduZ{H9 @^}cuTJҝ`ژPM:W@$iz4'"fA*:eYR ߄Џa^wEK5Zjfuq6Hša˻>8swW{Fﶅ\شĩ:?ҽ6)a/SXǃ::Yׅ~zG.cbwˇ{t.ޖyt̩‘_rPnνynƭg>weZjs_^ B}\A̰L~^%[moMzcA|̚~Gbܰm ~BcԎl|w_hߠ<'i"Bxt;oJ$z S;WB-#YaxU$sQw v`Snp]~mӘ};#MbR_Q(&xC~{fv`i dՕ-F~]GJ{IF-"BJ&~FJߥY&RX,IHBx8f_&ffzP qA̘ՙ?i}R~4n._QL| d6K9YDE2 D(_C0ؙ I|;uLyܽz?8gj,o%*?.d+Ҷ/7ŃFR-wYw$bHepTD g7wߪ\wvF@b߿4اɰ/|(('(e"5e ^x}@|q˭}U%;Ⱥv4wvsØFFmTVݎuI_;*&7:K$I+sx|=J"҂C@9e}M+ƥuU9[Q˫! 7J8@uҽ#:2<`NK!QƷ[ _ agc3.!`@qCғAfxWml/_&.x|X>j#rJH p`ǏͰ&~csscʼz3B1wdS)m)fO,Q*X`.ʕ $>Ev3{Y}S78\ˁ|^W3wD:Ca̟`~<5ű|Wz΀) W!;=TMN% $iɃr[fΘwwX4(;RG^@ ovG ŵ5v6PqݞN'_6*0y^t3Ac==SȒL|^jybdc!2>1Z>rί!c2 R>"ӯLz+/̩ۊXedY5 ?Aベ:+ L־zX^%g^Lvx!K&wâ`n: Ȼ4ڜ4*"l$LKz=y+鯐u~|+޲0rZ;%v/zZZZNPSg|TA GCޞAHBBU~`STh֊z [C@H.PXeTy^FYNUzC>`9.ew}Qgdџ}7;+",7\2Fex^Ε|ʋ^CMlG  JS[˟gߺpb#*#fk>u,)\:_ɵ FhwJ9c`ٵއP8 .!餩:؎urE./<Y{wџ}3y:q& {/K12vD?<{T@\/gg~ʘ {ÚzK'\Huȿcꔂ|(/㊇B9L(%Htb5Kw.uK*|U3s{=͹)'㻷VaD,(:ǦMOe@ eO`T6 85#9gkoJ ^fr6:@As]R]$Q u!<(o%{{ x}&Cy֕gs3l3#uxG;cٟ֯݁3QmuJ`UO4aΡݐ#2+Rnb2=G!ܖt!u#uvU1 JGZ9̏Rv;X&K,_q[헀4aK3?0<+pB-zxbѩo6 zY ,MU5i^F>J7, ҺRǡ60-.&i-z.g>pOFA0g%0\o7[,{& {2di&տNt z ܩeƌevfg!cHi_U咺"9 aKC|lOn:[GAMb`OKwR6šiuHuLMeKQY} 7\mݥ}Juq~f]ؗ[`ۧp͆!DVMxŌkj?+T /?P#SCRC_! A ȩ+?c2-QwMH(6XQϑr?`VVcgi(@F9pj1v kc&^ܫ8*û0օė^f!8[i}e jn橡lG.Ѳm`GƜSaMGl/na!W*-Y&F6ωWe`fF_H~畳$qcȨ ci'VG %foߐs|_3վr;^.blvF-XXAʭ\Cx02>('˛`Axn<EժB~{{;Ს#a1 o僨ڻUMAczꅅC4ȏr҆]kr B)=0$w%qL0t0&ǽ>⮗u" ;sչ@a$c/.J&ZH&B7y,ޝGOkn>DxxٰjPΆp- |6 `nqd?AM {B9po6`Z5^JRd T_] f,3vfw>VD(ȊѮsÙr\;*}ۮVoGO,!w { ;gy(<ГgBl8Mij LǍiCL *Ln_O:)o5R"cMwVʝZy/|+ yoiji P&3?iʄԝ;vPP2-\yc# }'9ně}*X !<-;+u?Ԇ+v~DS(Ӟq2Y%5}n~=vVb͇X6?MfgB.]\f'@} 7䰸޽rز1ULsڿnx@p8*b2vcwCx_[vLO*O"KW\0'"S 5_m 'rb䍻5,o/=6=otẻ |Cö9OIkco-=8kh^JOsX?LHVt_ElY(?Ƴ??<1s}翫?NBi,RrRNzg {q Wv2I=HOHٞ~<?NJ% Foo'(l.V0o80]N ocRF9}? 'N lDg1V7'2̷hbU>!^4AUV8pW~{3`6j6jy- \EVY4򜽯Z?O sq>_oPh63QT nlAb[$$J_@ GRڇŵMf-&#T.GpS['TMy~huf3i?'_?AOA߷l҃A䁈2YPء"OħNg2bI; X *8&}0=Ck"\kAR?o*UElwӞ=ߓ~Ov`WVVTqMIQUb >ԏ83:{*쎌JT8L>x}Uه["Yl2,Ue ZzWD]JjDy@z+|EdT$VO_˼hDAM*[]h{@^ok\wmVٍg&**YRӼg{BtxZZ4VHӍk=-PF`6h6ꆕ4v,]!1P2Di~z'ˈkˢYX7wV7wI_yD''%sFw£{@/]UIDAT)ݓ~F s)ƙ$EAOkR#PtNp62[ G[ t2tDIR4.QX*p~4nX0DUP n]l/+)(ftF+t_we_:A8 tL1 < ܃<}9pHDpԡ2dWoKFRm6Lv#0hh1͉~|-$I9luʄIlXF0`^Bgpz;CHRe"$뾽Ȭ7>NA0P]om)t7U<%$Ӱ8.`mX. B zz?Z_f | B3K`{`Ҵ֓~<{t6{KT6 E&"xYA'ؼp|?lfYc$7C9:- icf} 5 #])BM.D#uO%^͓|Op_O#?uSy9_,ԩ0׭t؃αXl&lJ4[l1}Wn*_5hܠ|B#_V)TUHB7X;X dcԕ~:x0 I Jb |_Bu޸|]p|Fy;f-nW~**Xd2=YBf63XXKC*e aݘ_m#_VC`8A7{ҏ7sdIB[T?g]%܄X,y12XHEѝ-nDoB~\/tːC]enޔ_dPF~?sȓ|SP[܌0sn $rh\YNPj̰"oB"4޵@Jm?}x}6U6HgEiFGX,)Y;sBͺ Ry۝3FCiW($h45u3 '#T6SxNM0d;GJ4 4cT/ye zごt,)l<,e,&t;k#mѿ6み8y6'S%עYaG1H=WD`Hv~ R\(݀-XX_0KY\ Qeh#kѹ\>9|`~&K˔V=͘EUHv{t.yRm`=3"W5k&~[8]Bb]DH J;LgklSU_ %+|R$Bu6W۔Kk\n^#+SUV|!:IC ~,>m,D 7҆68kU:ߪPe"?L0HLg2Kq K):ܡة*Lʯht2&6ٷ~?34Ȑ]L&Ld.Ng,2%׿@\`#!@[Qٮ*$[C}TߠlfCPfjIa$\әm׿e:&>6u:bbIelW:MNoY,"d?et*܆u_DI&`[lw'/ֆ_ƥ֠Q G闪?liEokHvOD!~MU٧ f;&*!)C|?[׉R.jn|;dj|h/+-Bo]/Kj-r%&)*{UUtƽUHl#L_EfH v jLn@$>RU~+F;. 3-0EV8*-?\U戦_ ? _Xyl2|+X~q2+3^m]u~q"dPU|/܉PWUR[1Qgs7ѭwm~re??bg2ۡ ~~qRSIf"7C??>u~>?Ï g,'??_1ä!K ZtܹvCHr)}.nd/"=?'2~? B'U蓛C_sf̷s?)~&΁vw ~$%?ov`'򺇻[~N9$eֿ !.r%bzfw#e~ 1 n˞)򋎐zx1]mDRê+9lF8yB²vOYas`E$tRgOva,\R>oo'QY=-xN|ndbW~KE)*_O~ۓE~p?\8Qtḻ#%["Eo&1FL[Gbh|I3zOˏ8M2,l;˳f<đ]UX͇mkyb=2^nAei3P|O.O w.,9reTt:CSᥙT-z:*Jj )Cq`ʿ$d|T#Gj<ԗPzIkmUEUon81VP ԗHq۽~vqvv˷gDEҟHMi3_Rv#6iϓRh8Y*pIL$fH^~=B.ybiy1^jK%j&97ۡ} := 䐲?ه['ÛyH"%7o0#铻/ ΏHtR]/!VP)߰i.}~Ǘs*/Mo^^[#_tO͡ˈЪiі+s {v'NCIRϸ]bȞE8l꯻{S|j28ES[=z~uH]o ^J7k~&.&f6u'ǩ~b--ߓOv-]d'E&˧E aeIyE9m0F]P(F+/l(D;QhPE. p%9!/w巧|82i4yi#8|A@J~[Rҩ~km1AɄnP] f4ʶ[k^m !T\eL?F͠thbkTn|J>.NJ"z#(9h.vt$v@0W|ݴǡ}]dU痓uf3oO5?qzXbO#a4\l?WJ97/!cn]8ON{x Πd@:EwOǟ!Ŀ{~qBuMT nبf8Aɗ^v SqmP{+"D*|_ojj֡5XP.$ C=|t,7r*k 8 P @NG 3e"6Qk/?9hSqE,1{JwǿBi7e6.c7^ԯ>YJȏ{&(BjTpU0"UuUy1Nϗg3ҏw]Qooۺ.Gx8_Wp_>|.!8"b$)@KB6 QEEk#+o?fi(3<{_cЉg 7yFW7brW{Z8.Qb޻nc{<@/KCݼ^?ﶜz'q]<ѫ_%qLI= =G% l<ȲqquM\[8 @םKP>"B._ô|O䷅OH`^őim![L:p5o3{7S#A#&.Ӈlc$;kѮ^sW~(a*96oϯq&?1IsFJNOZoNw-uMzh|wQEx˿zf>ecz4q8z=. xyץ]k2R~b1QF`Mn uŔ9&:w\,=1Sϱ_)'timoO^N]u[ǿt;7ƿO~8uze-QR`C XC1z}ooLi@"! wb_}RvC-?\K t!lݕW_k- jsysƀQ% ъ$aJ`pԥ_F; z`}xz*ꏌ*uPN$DtúϨEȐ61P!F9&:@'K}.!bhwB"xbnT{`=S1q$EsholvPt".ȤW@U[Jm$j^5gлnS(7?G+(9[paG~,>x\j?oVzI~Q?"Qm# 7s'VwmJ' v/Ï;s)*k@(O_GHߛYwRk ??k Utp?^@%~79Y #include namespace plog { template class PLOG_LINKAGE_HIDDEN AndroidAppender : public IAppender { public: AndroidAppender(const char* tag) : m_tag(tag) { } virtual void write(const Record& record) PLOG_OVERRIDE { std::string str = Formatter::format(record); __android_log_print(toPriority(record.getSeverity()), m_tag, "%s", str.c_str()); } private: static android_LogPriority toPriority(Severity severity) { switch (severity) { case fatal: return ANDROID_LOG_FATAL; case error: return ANDROID_LOG_ERROR; case warning: return ANDROID_LOG_WARN; case info: return ANDROID_LOG_INFO; case debug: return ANDROID_LOG_DEBUG; case verbose: return ANDROID_LOG_VERBOSE; default: return ANDROID_LOG_UNKNOWN; } } private: const char* const m_tag; }; } plog-1.1.11/include/plog/Appenders/ArduinoAppender.h000066400000000000000000000007361504622126500223030ustar00rootroot00000000000000#pragma once #include #include namespace plog { template class PLOG_LINKAGE_HIDDEN ArduinoAppender : public IAppender { public: ArduinoAppender(Stream &stream) : m_stream(stream) { } virtual void write(const Record &record) PLOG_OVERRIDE { m_stream.print(Formatter::format(record).c_str()); } private: Stream &m_stream; }; } plog-1.1.11/include/plog/Appenders/ColorConsoleAppender.h000066400000000000000000000064471504622126500233100ustar00rootroot00000000000000#pragma once #include #include namespace plog { template class PLOG_LINKAGE_HIDDEN ColorConsoleAppender : public ConsoleAppender { public: #ifdef _WIN32 # ifdef _MSC_VER # pragma warning(suppress: 26812) // Prefer 'enum class' over 'enum' # endif ColorConsoleAppender(OutputStream outStream = streamStdOut) : ConsoleAppender(outStream) , m_originalAttr() { if (this->m_isatty) { CONSOLE_SCREEN_BUFFER_INFO csbiInfo; GetConsoleScreenBufferInfo(this->m_outputHandle, &csbiInfo); m_originalAttr = csbiInfo.wAttributes; } } #else ColorConsoleAppender(OutputStream outStream = streamStdOut) : ConsoleAppender(outStream) {} #endif virtual void write(const Record& record) PLOG_OVERRIDE { util::nstring str = Formatter::format(record); util::MutexLock lock(this->m_mutex); setColor(record.getSeverity()); this->writestr(str); resetColor(); } protected: void setColor(Severity severity) { if (this->m_isatty) { switch (severity) { #ifdef _WIN32 case fatal: SetConsoleTextAttribute(this->m_outputHandle, foreground::kRed | foreground::kGreen | foreground::kBlue | foreground::kIntensity | background::kRed); // white on red background break; case error: SetConsoleTextAttribute(this->m_outputHandle, static_cast(foreground::kRed | foreground::kIntensity | (m_originalAttr & 0xf0))); // red break; case warning: SetConsoleTextAttribute(this->m_outputHandle, static_cast(foreground::kRed | foreground::kGreen | foreground::kIntensity | (m_originalAttr & 0xf0))); // yellow break; case debug: case verbose: SetConsoleTextAttribute(this->m_outputHandle, static_cast(foreground::kGreen | foreground::kBlue | foreground::kIntensity | (m_originalAttr & 0xf0))); // cyan break; #else case fatal: this->m_outputStream << "\x1B[97m\x1B[41m"; // white on red background break; case error: this->m_outputStream << "\x1B[91m"; // red break; case warning: this->m_outputStream << "\x1B[93m"; // yellow break; case debug: case verbose: this->m_outputStream << "\x1B[96m"; // cyan break; #endif default: break; } } } void resetColor() { if (this->m_isatty) { #ifdef _WIN32 SetConsoleTextAttribute(this->m_outputHandle, m_originalAttr); #else this->m_outputStream << "\x1B[0m\x1B[0K"; #endif } } private: #ifdef _WIN32 WORD m_originalAttr; #endif }; } plog-1.1.11/include/plog/Appenders/ConsoleAppender.h000066400000000000000000000043301504622126500222760ustar00rootroot00000000000000#pragma once #include #include #include #include namespace plog { enum OutputStream { streamStdOut, streamStdErr }; template class PLOG_LINKAGE_HIDDEN ConsoleAppender : public IAppender { public: #ifdef _WIN32 # ifdef _MSC_VER # pragma warning(suppress: 26812) // Prefer 'enum class' over 'enum' # endif ConsoleAppender(OutputStream outStream = streamStdOut) : m_isatty(!!_isatty(_fileno(outStream == streamStdOut ? stdout : stderr))) , m_outputStream(outStream == streamStdOut ? std::cout : std::cerr) , m_outputHandle() { if (m_isatty) { m_outputHandle = GetStdHandle(outStream == streamStdOut ? stdHandle::kOutput : stdHandle::kErrorOutput); } } #else ConsoleAppender(OutputStream outStream = streamStdOut) : m_isatty(!!isatty(fileno(outStream == streamStdOut ? stdout : stderr))) , m_outputStream(outStream == streamStdOut ? std::cout : std::cerr) {} #endif virtual void write(const Record& record) PLOG_OVERRIDE { util::nstring str = Formatter::format(record); util::MutexLock lock(m_mutex); writestr(str); } protected: void writestr(const util::nstring& str) { #ifdef _WIN32 if (m_isatty) { const std::wstring& wstr = util::toWide(str); WriteConsoleW(m_outputHandle, wstr.c_str(), static_cast(wstr.size()), NULL, NULL); } else { # if PLOG_CHAR_IS_UTF8 m_outputStream << str << std::flush; # else m_outputStream << util::toNarrow(str, codePage::kActive) << std::flush; # endif } #else m_outputStream << str << std::flush; #endif } private: #ifdef __BORLANDC__ static int _isatty(int fd) { return ::isatty(fd); } #endif protected: util::Mutex m_mutex; const bool m_isatty; std::ostream& m_outputStream; #ifdef _WIN32 HANDLE m_outputHandle; #endif }; } plog-1.1.11/include/plog/Appenders/DebugOutputAppender.h000066400000000000000000000006001504622126500231370ustar00rootroot00000000000000#pragma once #include #include namespace plog { template class PLOG_LINKAGE_HIDDEN DebugOutputAppender : public IAppender { public: virtual void write(const Record& record) PLOG_OVERRIDE { OutputDebugStringW(util::toWide(Formatter::format(record)).c_str()); } }; } plog-1.1.11/include/plog/Appenders/DynamicAppender.h000066400000000000000000000017451504622126500222670ustar00rootroot00000000000000#pragma once #include #include namespace plog { class PLOG_LINKAGE_HIDDEN DynamicAppender : public IAppender { public: DynamicAppender& addAppender(IAppender* appender) { assert(appender != this); util::MutexLock lock(m_mutex); m_appenders.insert(appender); return *this; } DynamicAppender& removeAppender(IAppender* appender) { util::MutexLock lock(m_mutex); m_appenders.erase(appender); return *this; } virtual void write(const Record& record) PLOG_OVERRIDE { util::MutexLock lock(m_mutex); for (std::set::iterator it = m_appenders.begin(); it != m_appenders.end(); ++it) { (*it)->write(record); } } private: mutable util::Mutex m_mutex; std::set m_appenders; }; } plog-1.1.11/include/plog/Appenders/EventLogAppender.h000066400000000000000000000100751504622126500224220ustar00rootroot00000000000000#pragma once #include #include namespace plog { template class PLOG_LINKAGE_HIDDEN EventLogAppender : public IAppender { public: EventLogAppender(const util::nchar* sourceName) : m_eventSource(RegisterEventSourceW(NULL, util::toWide(sourceName).c_str())) { } ~EventLogAppender() { DeregisterEventSource(m_eventSource); } virtual void write(const Record& record) PLOG_OVERRIDE { util::nstring str = Formatter::format(record); write(record.getSeverity(), util::toWide(str).c_str()); } private: void write(Severity severity, const wchar_t* str) { const wchar_t* logMessagePtr[] = { str }; ReportEventW(m_eventSource, logSeverityToType(severity), static_cast(severity), 0, NULL, 1, 0, logMessagePtr, NULL); } static WORD logSeverityToType(plog::Severity severity) { switch (severity) { case plog::fatal: case plog::error: return eventLog::kErrorType; case plog::warning: return eventLog::kWarningType; case plog::info: case plog::debug: case plog::verbose: default: return eventLog::kInformationType; } } private: HANDLE m_eventSource; }; class EventLogAppenderRegistry { public: static bool add(const util::nchar* sourceName, const util::nchar* logName = PLOG_NSTR("Application")) { std::wstring logKeyName; std::wstring sourceKeyName; getKeyNames(sourceName, logName, sourceKeyName, logKeyName); HKEY sourceKey; if (0 != RegCreateKeyExW(hkey::kLocalMachine, sourceKeyName.c_str(), 0, NULL, 0, regSam::kSetValue, NULL, &sourceKey, NULL)) { return false; } const DWORD kTypesSupported = eventLog::kErrorType | eventLog::kWarningType | eventLog::kInformationType; RegSetValueExW(sourceKey, L"TypesSupported", 0, regType::kDword, reinterpret_cast(&kTypesSupported), sizeof(kTypesSupported)); const wchar_t kEventMessageFile[] = L"%windir%\\Microsoft.NET\\Framework\\v4.0.30319\\EventLogMessages.dll;%windir%\\Microsoft.NET\\Framework\\v2.0.50727\\EventLogMessages.dll"; RegSetValueExW(sourceKey, L"EventMessageFile", 0, regType::kExpandSz, reinterpret_cast(kEventMessageFile), sizeof(kEventMessageFile) - sizeof(*kEventMessageFile)); RegCloseKey(sourceKey); return true; } static bool exists(const util::nchar* sourceName, const util::nchar* logName = PLOG_NSTR("Application")) { std::wstring logKeyName; std::wstring sourceKeyName; getKeyNames(sourceName, logName, sourceKeyName, logKeyName); HKEY sourceKey; if (0 != RegOpenKeyExW(hkey::kLocalMachine, sourceKeyName.c_str(), 0, regSam::kQueryValue, &sourceKey)) { return false; } RegCloseKey(sourceKey); return true; } static void remove(const util::nchar* sourceName, const util::nchar* logName = PLOG_NSTR("Application")) { std::wstring logKeyName; std::wstring sourceKeyName; getKeyNames(sourceName, logName, sourceKeyName, logKeyName); RegDeleteKeyW(hkey::kLocalMachine, sourceKeyName.c_str()); RegDeleteKeyW(hkey::kLocalMachine, logKeyName.c_str()); } private: static void getKeyNames(const util::nchar* sourceName, const util::nchar* logName, std::wstring& sourceKeyName, std::wstring& logKeyName) { const std::wstring kPrefix = L"SYSTEM\\CurrentControlSet\\Services\\EventLog\\"; logKeyName = kPrefix + util::toWide(logName); sourceKeyName = logKeyName + L"\\" + util::toWide(sourceName); } }; } plog-1.1.11/include/plog/Appenders/IAppender.h000066400000000000000000000003631504622126500210660ustar00rootroot00000000000000#pragma once #include #include namespace plog { class PLOG_LINKAGE IAppender { public: virtual ~IAppender() { } virtual void write(const Record& record) = 0; }; } plog-1.1.11/include/plog/Appenders/RollingFileAppender.h000066400000000000000000000100301504622126500230740ustar00rootroot00000000000000#pragma once #include #include #include #include #include namespace plog { template > class PLOG_LINKAGE_HIDDEN RollingFileAppender : public IAppender { public: RollingFileAppender(const util::nchar* fileName, size_t maxFileSize = 0, int maxFiles = 0) : m_fileSize() , m_maxFileSize() , m_maxFiles(maxFiles) , m_firstWrite(true) { setFileName(fileName); setMaxFileSize(maxFileSize); } #if defined(_WIN32) && !PLOG_CHAR_IS_UTF8 RollingFileAppender(const char* fileName, size_t maxFileSize = 0, int maxFiles = 0) : m_fileSize() , m_maxFileSize() , m_maxFiles(maxFiles) , m_firstWrite(true) { setFileName(fileName); setMaxFileSize(maxFileSize); } #endif virtual void write(const Record& record) PLOG_OVERRIDE { util::MutexLock lock(m_mutex); if (m_firstWrite) { openLogFile(); m_firstWrite = false; } else if (m_maxFiles > 0 && m_fileSize > m_maxFileSize && static_cast(-1) != m_fileSize) { rollLogFiles(); } size_t bytesWritten = m_file.write(Converter::convert(Formatter::format(record))); if (static_cast(-1) != bytesWritten) { m_fileSize += bytesWritten; } } void setFileName(const util::nchar* fileName) { util::MutexLock lock(m_mutex); util::splitFileName(fileName, m_fileNameNoExt, m_fileExt); m_file.close(); m_firstWrite = true; } #if defined(_WIN32) && !PLOG_CHAR_IS_UTF8 void setFileName(const char* fileName) { setFileName(util::toWide(fileName).c_str()); } #endif void setMaxFiles(int maxFiles) { m_maxFiles = maxFiles; } void setMaxFileSize(size_t maxFileSize) { m_maxFileSize = (std::max)(maxFileSize, static_cast(1000)); // set a lower limit for the maxFileSize } void rollLogFiles() { m_file.close(); util::nstring lastFileName = buildFileName(m_maxFiles - 1); util::File::unlink(lastFileName); for (int fileNumber = m_maxFiles - 2; fileNumber >= 0; --fileNumber) { util::nstring currentFileName = buildFileName(fileNumber); util::nstring nextFileName = buildFileName(fileNumber + 1); util::File::rename(currentFileName, nextFileName); } openLogFile(); m_firstWrite = false; } private: void openLogFile() { m_fileSize = m_file.open(buildFileName()); if (0 == m_fileSize) { size_t bytesWritten = m_file.write(Converter::header(Formatter::header())); if (static_cast(-1) != bytesWritten) { m_fileSize += bytesWritten; } } } util::nstring buildFileName(int fileNumber = 0) { util::nostringstream ss; ss << m_fileNameNoExt; if (fileNumber > 0) { ss << '.' << fileNumber; } if (!m_fileExt.empty()) { ss << '.' << m_fileExt; } return ss.str(); } private: util::Mutex m_mutex; util::File m_file; size_t m_fileSize; size_t m_maxFileSize; int m_maxFiles; util::nstring m_fileExt; util::nstring m_fileNameNoExt; bool m_firstWrite; }; } plog-1.1.11/include/plog/Converters/000077500000000000000000000000001504622126500172555ustar00rootroot00000000000000plog-1.1.11/include/plog/Converters/NativeEOLConverter.h000066400000000000000000000020701504622126500231030ustar00rootroot00000000000000#pragma once #include #include namespace plog { template class NativeEOLConverter : public NextConverter { #ifdef _WIN32 public: static std::string header(const util::nstring& str) { return NextConverter::header(fixLineEndings(str)); } static std::string convert(const util::nstring& str) { return NextConverter::convert(fixLineEndings(str)); } private: static util::nstring fixLineEndings(const util::nstring& str) { util::nstring output; output.reserve(str.length() * 2); // the worst case requires 2x chars for (size_t i = 0; i < str.size(); ++i) { util::nchar ch = str[i]; if (ch == PLOG_NSTR('\n')) { output.push_back(PLOG_NSTR('\r')); } output.push_back(ch); } return output; } #endif }; } plog-1.1.11/include/plog/Converters/UTF8Converter.h000066400000000000000000000010751504622126500220470ustar00rootroot00000000000000#pragma once #include namespace plog { class UTF8Converter { public: static std::string header(const util::nstring& str) { const char kBOM[] = "\xEF\xBB\xBF"; return std::string(kBOM) + convert(str); } #if PLOG_CHAR_IS_UTF8 static const std::string& convert(const util::nstring& str) { return str; } #else static std::string convert(const util::nstring& str) { return util::toNarrow(str, codePage::kUTF8); } #endif }; } plog-1.1.11/include/plog/Formatters/000077500000000000000000000000001504622126500172515ustar00rootroot00000000000000plog-1.1.11/include/plog/Formatters/CsvFormatter.h000066400000000000000000000042071504622126500220440ustar00rootroot00000000000000#pragma once #include #include #include namespace plog { template class CsvFormatterImpl { public: static util::nstring header() { return PLOG_NSTR("Date;Time;Severity;TID;This;Function;Message\n"); } static util::nstring format(const Record& record) { tm t; useUtcTime ? util::gmtime_s(&t, &record.getTime().time) : util::localtime_s(&t, &record.getTime().time); util::nostringstream ss; ss << t.tm_year + 1900 << PLOG_NSTR("/") << std::setfill(PLOG_NSTR('0')) << std::setw(2) << t.tm_mon + 1 << PLOG_NSTR("/") << std::setfill(PLOG_NSTR('0')) << std::setw(2) << t.tm_mday << PLOG_NSTR(";"); ss << std::setfill(PLOG_NSTR('0')) << std::setw(2) << t.tm_hour << PLOG_NSTR(":") << std::setfill(PLOG_NSTR('0')) << std::setw(2) << t.tm_min << PLOG_NSTR(":") << std::setfill(PLOG_NSTR('0')) << std::setw(2) << t.tm_sec << PLOG_NSTR(".") << std::setfill(PLOG_NSTR('0')) << std::setw(3) << static_cast (record.getTime().millitm) << PLOG_NSTR(";"); ss << severityToString(record.getSeverity()) << PLOG_NSTR(";"); ss << record.getTid() << PLOG_NSTR(";"); ss << record.getObject() << PLOG_NSTR(";"); ss << record.getFunc() << PLOG_NSTR("@") << record.getLine() << PLOG_NSTR(";"); util::nstring message = record.getMessage(); if (message.size() > kMaxMessageSize) { message.resize(kMaxMessageSize); message.append(PLOG_NSTR("...")); } util::nistringstream split(message); util::nstring token; while (!split.eof()) { std::getline(split, token, PLOG_NSTR('"')); ss << PLOG_NSTR("\"") << token << PLOG_NSTR("\""); } ss << PLOG_NSTR("\n"); return ss.str(); } static const size_t kMaxMessageSize = 32000; }; class CsvFormatter : public CsvFormatterImpl {}; class CsvFormatterUtcTime : public CsvFormatterImpl {}; } plog-1.1.11/include/plog/Formatters/FuncMessageFormatter.h000066400000000000000000000010001504622126500234750ustar00rootroot00000000000000#pragma once #include #include namespace plog { class FuncMessageFormatter { public: static util::nstring header() { return util::nstring(); } static util::nstring format(const Record& record) { util::nostringstream ss; ss << record.getFunc() << PLOG_NSTR("@") << record.getLine() << PLOG_NSTR(": ") << record.getMessage() << PLOG_NSTR("\n"); return ss.str(); } }; } plog-1.1.11/include/plog/Formatters/MessageOnlyFormatter.h000066400000000000000000000006631504622126500235410ustar00rootroot00000000000000#pragma once #include #include namespace plog { class MessageOnlyFormatter { public: static util::nstring header() { return util::nstring(); } static util::nstring format(const Record& record) { util::nostringstream ss; ss << record.getMessage() << PLOG_NSTR("\n"); return ss.str(); } }; } plog-1.1.11/include/plog/Formatters/TxtFormatter.h000066400000000000000000000031371504622126500220710ustar00rootroot00000000000000#pragma once #include #include #include namespace plog { template class TxtFormatterImpl { public: static util::nstring header() { return util::nstring(); } static util::nstring format(const Record& record) { tm t; useUtcTime ? util::gmtime_s(&t, &record.getTime().time) : util::localtime_s(&t, &record.getTime().time); util::nostringstream ss; ss << t.tm_year + 1900 << "-" << std::setfill(PLOG_NSTR('0')) << std::setw(2) << t.tm_mon + 1 << PLOG_NSTR("-") << std::setfill(PLOG_NSTR('0')) << std::setw(2) << t.tm_mday << PLOG_NSTR(" "); ss << std::setfill(PLOG_NSTR('0')) << std::setw(2) << t.tm_hour << PLOG_NSTR(":") << std::setfill(PLOG_NSTR('0')) << std::setw(2) << t.tm_min << PLOG_NSTR(":") << std::setfill(PLOG_NSTR('0')) << std::setw(2) << t.tm_sec << PLOG_NSTR(".") << std::setfill(PLOG_NSTR('0')) << std::setw(3) << static_cast (record.getTime().millitm) << PLOG_NSTR(" "); ss << std::setfill(PLOG_NSTR(' ')) << std::setw(5) << std::left << severityToString(record.getSeverity()) << PLOG_NSTR(" "); ss << PLOG_NSTR("[") << record.getTid() << PLOG_NSTR("] "); ss << PLOG_NSTR("[") << record.getFunc() << PLOG_NSTR("@") << record.getLine() << PLOG_NSTR("] "); ss << record.getMessage() << PLOG_NSTR("\n"); return ss.str(); } }; class TxtFormatter : public TxtFormatterImpl {}; class TxtFormatterUtcTime : public TxtFormatterImpl {}; } plog-1.1.11/include/plog/Helpers/000077500000000000000000000000001504622126500165255ustar00rootroot00000000000000plog-1.1.11/include/plog/Helpers/AscDump.h000066400000000000000000000021121504622126500202260ustar00rootroot00000000000000#pragma once #include #include namespace plog { class AscDump { public: AscDump(const void* ptr, size_t size) : m_ptr(static_cast(ptr)) , m_size(size) { } friend util::nostringstream& operator<<(util::nostringstream& stream, const AscDump& ascDump); private: const char* m_ptr; size_t m_size; }; inline util::nostringstream& operator<<(util::nostringstream& stream, const AscDump& ascDump) { for (size_t i = 0; i < ascDump.m_size; ++i) { stream << (std::isprint(ascDump.m_ptr[i]) ? ascDump.m_ptr[i] : '.'); } return stream; } inline AscDump ascdump(const void* ptr, size_t size) { return AscDump(ptr, size); } template inline AscDump ascdump(const Container& container) { return AscDump(container.data(), container.size() * sizeof(*container.data())); } template inline AscDump ascdump(const T (&arr)[N]) { return AscDump(arr, N * sizeof(*arr)); } } plog-1.1.11/include/plog/Helpers/HexDump.h000066400000000000000000000042101504622126500202450ustar00rootroot00000000000000#pragma once #include #include namespace plog { class HexDump { public: HexDump(const void* ptr, size_t size) : m_ptr(static_cast(ptr)) , m_size(size) , m_group(8) , m_digitSeparator(" ") , m_groupSeparator(" ") { } HexDump& group(size_t group) { m_group = group; return *this; } HexDump& separator(const char* digitSeparator) { m_digitSeparator = digitSeparator; return *this; } HexDump& separator(const char* digitSeparator, const char* groupSeparator) { m_digitSeparator = digitSeparator; m_groupSeparator = groupSeparator; return *this; } friend util::nostringstream& operator<<(util::nostringstream& stream, const HexDump&); private: const unsigned char* m_ptr; size_t m_size; size_t m_group; const char* m_digitSeparator; const char* m_groupSeparator; }; inline util::nostringstream& operator<<(util::nostringstream& stream, const HexDump& hexDump) { stream << std::hex << std::setfill(PLOG_NSTR('0')); for (size_t i = 0; i < hexDump.m_size;) { stream << std::setw(2) << static_cast(hexDump.m_ptr[i]); if (++i < hexDump.m_size) { if (hexDump.m_group > 0 && i % hexDump.m_group == 0) { stream << hexDump.m_groupSeparator; } else { stream << hexDump.m_digitSeparator; } } } return stream; } inline HexDump hexdump(const void* ptr, size_t size) { return HexDump(ptr, size); } template inline HexDump hexdump(const Container& container) { return HexDump(container.data(), container.size() * sizeof(*container.data())); } template inline HexDump hexdump(const T (&arr)[N]) { return HexDump(arr, N * sizeof(*arr)); } } plog-1.1.11/include/plog/Helpers/PrintVar.h000066400000000000000000000032121504622126500204410ustar00rootroot00000000000000#pragma once #define PLOG_IMPL_PRINT_VAR_1(a1) #a1 ": " << a1 #define PLOG_IMPL_PRINT_VAR_2(a1, a2) PLOG_IMPL_PRINT_VAR_1(a1) PLOG_IMPL_PRINT_VAR_TAIL(a2) #define PLOG_IMPL_PRINT_VAR_3(a1, a2, a3) PLOG_IMPL_PRINT_VAR_2(a1, a2) PLOG_IMPL_PRINT_VAR_TAIL(a3) #define PLOG_IMPL_PRINT_VAR_4(a1, a2, a3, a4) PLOG_IMPL_PRINT_VAR_3(a1, a2, a3) PLOG_IMPL_PRINT_VAR_TAIL(a4) #define PLOG_IMPL_PRINT_VAR_5(a1, a2, a3, a4, a5) PLOG_IMPL_PRINT_VAR_4(a1, a2, a3, a4) PLOG_IMPL_PRINT_VAR_TAIL(a5) #define PLOG_IMPL_PRINT_VAR_6(a1, a2, a3, a4, a5, a6) PLOG_IMPL_PRINT_VAR_5(a1, a2, a3, a4, a5) PLOG_IMPL_PRINT_VAR_TAIL(a6) #define PLOG_IMPL_PRINT_VAR_7(a1, a2, a3, a4, a5, a6, a7) PLOG_IMPL_PRINT_VAR_6(a1, a2, a3, a4, a5, a6) PLOG_IMPL_PRINT_VAR_TAIL(a7) #define PLOG_IMPL_PRINT_VAR_8(a1, a2, a3, a4, a5, a6, a7, a8) PLOG_IMPL_PRINT_VAR_7(a1, a2, a3, a4, a5, a6, a7) PLOG_IMPL_PRINT_VAR_TAIL(a8) #define PLOG_IMPL_PRINT_VAR_9(a1, a2, a3, a4, a5, a6, a7, a8, a9) PLOG_IMPL_PRINT_VAR_8(a1, a2, a3, a4, a5, a6, a7, a8) PLOG_IMPL_PRINT_VAR_TAIL(a9) #define PLOG_IMPL_PRINT_VAR_TAIL(a) << ", " PLOG_IMPL_PRINT_VAR_1(a) #define PLOG_IMPL_PRINT_VAR_EXPAND(x) x #ifdef __GNUC__ #pragma GCC system_header // disable warning: variadic macros are a C99 feature #endif #define PLOG_IMPL_PRINT_VAR_GET_MACRO(x1, x2, x3, x4, x5, x6, x7, x8, x9, NAME, ...) NAME #define PLOG_PRINT_VAR(...) PLOG_IMPL_PRINT_VAR_EXPAND(PLOG_IMPL_PRINT_VAR_GET_MACRO(__VA_ARGS__,\ PLOG_IMPL_PRINT_VAR_9, PLOG_IMPL_PRINT_VAR_8, PLOG_IMPL_PRINT_VAR_7, PLOG_IMPL_PRINT_VAR_6, PLOG_IMPL_PRINT_VAR_5,\ PLOG_IMPL_PRINT_VAR_4, PLOG_IMPL_PRINT_VAR_3, PLOG_IMPL_PRINT_VAR_2, PLOG_IMPL_PRINT_VAR_1, UNUSED)(__VA_ARGS__)) plog-1.1.11/include/plog/Init.h000066400000000000000000000010121504622126500161710ustar00rootroot00000000000000#pragma once #include namespace plog { template PLOG_LINKAGE_HIDDEN inline Logger& init(Severity maxSeverity = none, IAppender* appender = NULL) { static Logger logger(maxSeverity); return appender ? logger.addAppender(appender) : logger; } inline Logger& init(Severity maxSeverity = none, IAppender* appender = NULL) { return init(maxSeverity, appender); } } plog-1.1.11/include/plog/Initializers/000077500000000000000000000000001504622126500175715ustar00rootroot00000000000000plog-1.1.11/include/plog/Initializers/ConsoleInitializer.h000066400000000000000000000013561504622126500235550ustar00rootroot00000000000000#pragma once #include #include namespace plog { ////////////////////////////////////////////////////////////////////////// // ColorConsoleAppender with any Formatter template PLOG_LINKAGE_HIDDEN inline Logger& init(Severity maxSeverity, OutputStream outputStream) { static ColorConsoleAppender appender(outputStream); return init(maxSeverity, &appender); } template inline Logger& init(Severity maxSeverity, OutputStream outputStream) { return init(maxSeverity, outputStream); } } plog-1.1.11/include/plog/Initializers/RollingFileInitializer.h000066400000000000000000000063711504622126500243630ustar00rootroot00000000000000#pragma once #include #include #include #include #include namespace plog { ////////////////////////////////////////////////////////////////////////// // RollingFileAppender with any Formatter template PLOG_LINKAGE_HIDDEN inline Logger& init(Severity maxSeverity, const util::nchar* fileName, size_t maxFileSize = 0, int maxFiles = 0) { static RollingFileAppender rollingFileAppender(fileName, maxFileSize, maxFiles); return init(maxSeverity, &rollingFileAppender); } template inline Logger& init(Severity maxSeverity, const util::nchar* fileName, size_t maxFileSize = 0, int maxFiles = 0) { return init(maxSeverity, fileName, maxFileSize, maxFiles); } ////////////////////////////////////////////////////////////////////////// // RollingFileAppender with TXT/CSV chosen by file extension namespace { inline bool isCsv(const util::nchar* fileName) { const util::nchar* dot = util::findExtensionDot(fileName); #if PLOG_CHAR_IS_UTF8 return dot && 0 == std::strcmp(dot, ".csv"); #else return dot && 0 == std::wcscmp(dot, L".csv"); #endif } } template inline Logger& init(Severity maxSeverity, const util::nchar* fileName, size_t maxFileSize = 0, int maxFiles = 0) { return isCsv(fileName) ? init(maxSeverity, fileName, maxFileSize, maxFiles) : init(maxSeverity, fileName, maxFileSize, maxFiles); } inline Logger& init(Severity maxSeverity, const util::nchar* fileName, size_t maxFileSize = 0, int maxFiles = 0) { return init(maxSeverity, fileName, maxFileSize, maxFiles); } ////////////////////////////////////////////////////////////////////////// // CHAR variants for Windows #if defined(_WIN32) && !PLOG_CHAR_IS_UTF8 template inline Logger& init(Severity maxSeverity, const char* fileName, size_t maxFileSize = 0, int maxFiles = 0) { return init(maxSeverity, util::toWide(fileName).c_str(), maxFileSize, maxFiles); } template inline Logger& init(Severity maxSeverity, const char* fileName, size_t maxFileSize = 0, int maxFiles = 0) { return init(maxSeverity, fileName, maxFileSize, maxFiles); } template inline Logger& init(Severity maxSeverity, const char* fileName, size_t maxFileSize = 0, int maxFiles = 0) { return init(maxSeverity, util::toWide(fileName).c_str(), maxFileSize, maxFiles); } inline Logger& init(Severity maxSeverity, const char* fileName, size_t maxFileSize = 0, int maxFiles = 0) { return init(maxSeverity, fileName, maxFileSize, maxFiles); } #endif } plog-1.1.11/include/plog/Log.h000066400000000000000000000260211504622126500160160ustar00rootroot00000000000000////////////////////////////////////////////////////////////////////////// // Plog - portable and simple log for C++ // Documentation and sources: https://github.com/SergiusTheBest/plog // License: MIT, https://choosealicense.com/licenses/mit #pragma once #include ////////////////////////////////////////////////////////////////////////// // Helper macros that get context info #if defined(PLOG_ENABLE_GET_THIS) && defined(_MSC_VER) && _MSC_VER >= 1600 && !defined(__INTELLISENSE__) && !defined(__INTEL_COMPILER) && !defined(__llvm__) && !defined(__RESHARPER__) // >= Visual Studio 2010, skip IntelliSense, Intel Compiler, Clang Code Model and ReSharper # define PLOG_GET_THIS() __if_exists(this) { this } __if_not_exists(this) { 0 } #else # define PLOG_GET_THIS() reinterpret_cast(0) #endif #ifdef _MSC_VER # define PLOG_GET_FUNC() __FUNCTION__ #elif defined(__BORLANDC__) # define PLOG_GET_FUNC() __FUNC__ #else # define PLOG_GET_FUNC() __PRETTY_FUNCTION__ #endif #ifdef PLOG_CAPTURE_FILE # define PLOG_GET_FILE() __FILE__ #else # define PLOG_GET_FILE() "" #endif ////////////////////////////////////////////////////////////////////////// // Log severity level checker #ifdef PLOG_DISABLE_LOGGING # ifdef _MSC_VER # define IF_PLOG_(instanceId, severity) __pragma(warning(push)) __pragma(warning(disable:4127)) if (true) {;} else __pragma(warning(pop)) // conditional expression is constant # else # define IF_PLOG_(instanceId, severity) if (true) {;} else # endif #else # define IF_PLOG_(instanceId, severity) if (!plog::get() || !plog::get()->checkSeverity(severity)) {;} else #endif #define IF_PLOG(severity) IF_PLOG_(PLOG_DEFAULT_INSTANCE_ID, severity) ////////////////////////////////////////////////////////////////////////// // Main logging macros #ifdef PLOG_MESSAGE_PREFIX # define PLOG_PRINT_MESSAGE_PREFIX() << PLOG_MESSAGE_PREFIX #else # define PLOG_PRINT_MESSAGE_PREFIX() #endif #define PLOG_(instanceId, severity) IF_PLOG_(instanceId, severity) (*plog::get()) += \ plog::Record(severity, PLOG_GET_FUNC(), __LINE__, PLOG_GET_FILE(), PLOG_GET_THIS(), instanceId).ref() PLOG_PRINT_MESSAGE_PREFIX() #define PLOG(severity) PLOG_(PLOG_DEFAULT_INSTANCE_ID, severity) #define PLOG_VERBOSE PLOG(plog::verbose) #define PLOG_DEBUG PLOG(plog::debug) #define PLOG_INFO PLOG(plog::info) #define PLOG_WARNING PLOG(plog::warning) #define PLOG_ERROR PLOG(plog::error) #define PLOG_FATAL PLOG(plog::fatal) #define PLOG_NONE PLOG(plog::none) #define PLOG_VERBOSE_(instanceId) PLOG_(instanceId, plog::verbose) #define PLOG_DEBUG_(instanceId) PLOG_(instanceId, plog::debug) #define PLOG_INFO_(instanceId) PLOG_(instanceId, plog::info) #define PLOG_WARNING_(instanceId) PLOG_(instanceId, plog::warning) #define PLOG_ERROR_(instanceId) PLOG_(instanceId, plog::error) #define PLOG_FATAL_(instanceId) PLOG_(instanceId, plog::fatal) #define PLOG_NONE_(instanceId) PLOG_(instanceId, plog::none) #define PLOGV PLOG_VERBOSE #define PLOGD PLOG_DEBUG #define PLOGI PLOG_INFO #define PLOGW PLOG_WARNING #define PLOGE PLOG_ERROR #define PLOGF PLOG_FATAL #define PLOGN PLOG_NONE #define PLOGV_(instanceId) PLOG_VERBOSE_(instanceId) #define PLOGD_(instanceId) PLOG_DEBUG_(instanceId) #define PLOGI_(instanceId) PLOG_INFO_(instanceId) #define PLOGW_(instanceId) PLOG_WARNING_(instanceId) #define PLOGE_(instanceId) PLOG_ERROR_(instanceId) #define PLOGF_(instanceId) PLOG_FATAL_(instanceId) #define PLOGN_(instanceId) PLOG_NONE_(instanceId) ////////////////////////////////////////////////////////////////////////// // Conditional logging macros #define PLOG_IF_(instanceId, severity, condition) if (!(condition)) {;} else PLOG_(instanceId, severity) #define PLOG_IF(severity, condition) PLOG_IF_(PLOG_DEFAULT_INSTANCE_ID, severity, condition) #define PLOG_VERBOSE_IF(condition) PLOG_IF(plog::verbose, condition) #define PLOG_DEBUG_IF(condition) PLOG_IF(plog::debug, condition) #define PLOG_INFO_IF(condition) PLOG_IF(plog::info, condition) #define PLOG_WARNING_IF(condition) PLOG_IF(plog::warning, condition) #define PLOG_ERROR_IF(condition) PLOG_IF(plog::error, condition) #define PLOG_FATAL_IF(condition) PLOG_IF(plog::fatal, condition) #define PLOG_NONE_IF(condition) PLOG_IF(plog::none, condition) #define PLOG_VERBOSE_IF_(instanceId, condition) PLOG_IF_(instanceId, plog::verbose, condition) #define PLOG_DEBUG_IF_(instanceId, condition) PLOG_IF_(instanceId, plog::debug, condition) #define PLOG_INFO_IF_(instanceId, condition) PLOG_IF_(instanceId, plog::info, condition) #define PLOG_WARNING_IF_(instanceId, condition) PLOG_IF_(instanceId, plog::warning, condition) #define PLOG_ERROR_IF_(instanceId, condition) PLOG_IF_(instanceId, plog::error, condition) #define PLOG_FATAL_IF_(instanceId, condition) PLOG_IF_(instanceId, plog::fatal, condition) #define PLOG_NONE_IF_(instanceId, condition) PLOG_IF_(instanceId, plog::none, condition) #define PLOGV_IF(condition) PLOG_VERBOSE_IF(condition) #define PLOGD_IF(condition) PLOG_DEBUG_IF(condition) #define PLOGI_IF(condition) PLOG_INFO_IF(condition) #define PLOGW_IF(condition) PLOG_WARNING_IF(condition) #define PLOGE_IF(condition) PLOG_ERROR_IF(condition) #define PLOGF_IF(condition) PLOG_FATAL_IF(condition) #define PLOGN_IF(condition) PLOG_NONE_IF(condition) #define PLOGV_IF_(instanceId, condition) PLOG_VERBOSE_IF_(instanceId, condition) #define PLOGD_IF_(instanceId, condition) PLOG_DEBUG_IF_(instanceId, condition) #define PLOGI_IF_(instanceId, condition) PLOG_INFO_IF_(instanceId, condition) #define PLOGW_IF_(instanceId, condition) PLOG_WARNING_IF_(instanceId, condition) #define PLOGE_IF_(instanceId, condition) PLOG_ERROR_IF_(instanceId, condition) #define PLOGF_IF_(instanceId, condition) PLOG_FATAL_IF_(instanceId, condition) #define PLOGN_IF_(instanceId, condition) PLOG_NONE_IF_(instanceId, condition) // Old macro names for downward compatibility. To bypass including these macro names, add // #define PLOG_OMIT_LOG_DEFINES before #include #ifndef PLOG_OMIT_LOG_DEFINES ////////////////////////////////////////////////////////////////////////// // Main logging macros - can be changed later to point at macros for a different logging package #define LOG_(instanceId, severity) PLOG_(instanceId, severity) #define LOG(severity) PLOG(severity) #define LOG_VERBOSE PLOG_VERBOSE #define LOG_DEBUG PLOG_DEBUG #define LOG_INFO PLOG_INFO #define LOG_WARNING PLOG_WARNING #define LOG_ERROR PLOG_ERROR #define LOG_FATAL PLOG_FATAL #define LOG_NONE PLOG_NONE #define LOG_VERBOSE_(instanceId) PLOG_VERBOSE_(instanceId) #define LOG_DEBUG_(instanceId) PLOG_DEBUG_(instanceId) #define LOG_INFO_(instanceId) PLOG_INFO_(instanceId) #define LOG_WARNING_(instanceId) PLOG_WARNING_(instanceId) #define LOG_ERROR_(instanceId) PLOG_ERROR_(instanceId) #define LOG_FATAL_(instanceId) PLOG_FATAL_(instanceId) #define LOG_NONE_(instanceId) PLOG_NONE_(instanceId) #define LOGV PLOGV #define LOGD PLOGD #define LOGI PLOGI #define LOGW PLOGW #define LOGE PLOGE #define LOGF PLOGF #define LOGN PLOGN #define LOGV_(instanceId) PLOGV_(instanceId) #define LOGD_(instanceId) PLOGD_(instanceId) #define LOGI_(instanceId) PLOGI_(instanceId) #define LOGW_(instanceId) PLOGW_(instanceId) #define LOGE_(instanceId) PLOGE_(instanceId) #define LOGF_(instanceId) PLOGF_(instanceId) #define LOGN_(instanceId) PLOGN_(instanceId) ////////////////////////////////////////////////////////////////////////// // Conditional logging macros #define LOG_IF_(instanceId, severity, condition) PLOG_IF_(instanceId, severity, condition) #define LOG_IF(severity, condition) PLOG_IF(severity, condition) #define LOG_VERBOSE_IF(condition) PLOG_VERBOSE_IF(condition) #define LOG_DEBUG_IF(condition) PLOG_DEBUG_IF(condition) #define LOG_INFO_IF(condition) PLOG_INFO_IF(condition) #define LOG_WARNING_IF(condition) PLOG_WARNING_IF(condition) #define LOG_ERROR_IF(condition) PLOG_ERROR_IF(condition) #define LOG_FATAL_IF(condition) PLOG_FATAL_IF(condition) #define LOG_NONE_IF(condition) PLOG_NONE_IF(condition) #define LOG_VERBOSE_IF_(instanceId, condition) PLOG_VERBOSE_IF_(instanceId, condition) #define LOG_DEBUG_IF_(instanceId, condition) PLOG_DEBUG_IF_(instanceId, condition) #define LOG_INFO_IF_(instanceId, condition) PLOG_INFO_IF_(instanceId, condition) #define LOG_WARNING_IF_(instanceId, condition) PLOG_WARNING_IF_(instanceId, condition) #define LOG_ERROR_IF_(instanceId, condition) PLOG_ERROR_IF_(instanceId, condition) #define LOG_FATAL_IF_(instanceId, condition) PLOG_FATAL_IF_(instanceId, condition) #define LOG_NONE_IF_(instanceId, condition) PLOG_NONE_IF_(instanceId, condition) #define LOGV_IF(condition) PLOGV_IF(condition) #define LOGD_IF(condition) PLOGD_IF(condition) #define LOGI_IF(condition) PLOGI_IF(condition) #define LOGW_IF(condition) PLOGW_IF(condition) #define LOGE_IF(condition) PLOGE_IF(condition) #define LOGF_IF(condition) PLOGF_IF(condition) #define LOGN_IF(condition) PLOGN_IF(condition) #define LOGV_IF_(instanceId, condition) PLOGV_IF_(instanceId, condition) #define LOGD_IF_(instanceId, condition) PLOGD_IF_(instanceId, condition) #define LOGI_IF_(instanceId, condition) PLOGI_IF_(instanceId, condition) #define LOGW_IF_(instanceId, condition) PLOGW_IF_(instanceId, condition) #define LOGE_IF_(instanceId, condition) PLOGE_IF_(instanceId, condition) #define LOGF_IF_(instanceId, condition) PLOGF_IF_(instanceId, condition) #define LOGN_IF_(instanceId, condition) PLOGN_IF_(instanceId, condition) #endif plog-1.1.11/include/plog/Logger.h000066400000000000000000000037611504622126500165220ustar00rootroot00000000000000#pragma once #include #include #include #ifdef PLOG_DEFAULT_INSTANCE // for backward compatibility # define PLOG_DEFAULT_INSTANCE_ID PLOG_DEFAULT_INSTANCE #endif #ifndef PLOG_DEFAULT_INSTANCE_ID # define PLOG_DEFAULT_INSTANCE_ID 0 #endif namespace plog { template class PLOG_LINKAGE Logger : public util::Singleton >, public IAppender { public: Logger(Severity maxSeverity = none) : m_maxSeverity(maxSeverity) { } Logger& addAppender(IAppender* appender) { assert(appender != this); m_appenders.push_back(appender); return *this; } Severity getMaxSeverity() const { return m_maxSeverity; } void setMaxSeverity(Severity severity) { m_maxSeverity = severity; } bool checkSeverity(Severity severity) const { return severity <= m_maxSeverity; } virtual void write(const Record& record) PLOG_OVERRIDE { if (checkSeverity(record.getSeverity())) { *this += record; } } void operator+=(const Record& record) { for (std::vector::iterator it = m_appenders.begin(); it != m_appenders.end(); ++it) { (*it)->write(record); } } private: Severity m_maxSeverity; #ifdef _MSC_VER # pragma warning(push) # pragma warning(disable:4251) // needs to have dll-interface to be used by clients of class #endif std::vector m_appenders; #ifdef _MSC_VER # pragma warning(pop) #endif }; template inline Logger* get() { return Logger::getInstance(); } inline Logger* get() { return Logger::getInstance(); } } plog-1.1.11/include/plog/Record.h000066400000000000000000000337501504622126500165220ustar00rootroot00000000000000#pragma once #include #include #include #ifdef __cplusplus_cli #include // For PtrToStringChars #endif namespace plog { namespace detail { #if !defined(_MSC_VER) || _MSC_VER > 1400 // MSVC 2005 doesn't understand `enableIf`, so drop all `meta` namespace meta { template inline T& declval() { #ifdef __INTEL_COMPILER # pragma warning(suppress: 327) // NULL reference is not allowed #endif return *reinterpret_cast(0); } template struct enableIf {}; template struct enableIf { typedef T type; }; struct No { char a[1]; }; struct Yes { char a[2]; }; template struct isConvertible { // `+ sizeof(U*)` is required for GCC 4.5-4.7 template static typename enableIf(meta::declval())) + sizeof(U*)), Yes>::type test(int); template static No test(...); enum { value = sizeof(test(0)) == sizeof(Yes) }; }; template struct isConvertibleToString : isConvertible {}; #if PLOG_ENABLE_WCHAR_INPUT template struct isConvertibleToWString : isConvertible {}; #endif template struct isContainer { template static typename meta::enableIf().begin()) + sizeof(meta::declval().end() #else typename U::const_iterator #endif )), Yes>::type test(int); template static No test(...); enum { value = sizeof(test(0)) == sizeof(Yes) }; }; // Detects `std::filesystem::path` and `boost::filesystem::path`. They look like containers // but we don't want to treat them as containers, so we use this detector to filter them out. template struct isFilesystemPath { template static typename meta::enableIf().preferred_separator)), Yes>::type test(int); template static No test(...); enum { value = sizeof(test(0)) == sizeof(Yes) }; }; } #endif ////////////////////////////////////////////////////////////////////////// // Stream output operators as free functions #if PLOG_ENABLE_WCHAR_INPUT inline void operator<<(util::nostringstream& stream, const wchar_t* data) { data = data ? data : L"(null)"; # ifdef _WIN32 # if PLOG_CHAR_IS_UTF8 std::operator<<(stream, util::toNarrow(data, codePage::kUTF8)); # else std::operator<<(stream, data); # endif # else std::operator<<(stream, util::toNarrow(data)); # endif } inline void operator<<(util::nostringstream& stream, wchar_t* data) { plog::detail::operator<<(stream, const_cast(data)); } inline void operator<<(util::nostringstream& stream, const std::wstring& data) { plog::detail::operator<<(stream, data.c_str()); } #endif inline void operator<<(util::nostringstream& stream, const char* data) { data = data ? data : "(null)"; #if defined(_WIN32) && defined(__BORLANDC__) # if PLOG_CHAR_IS_UTF8 stream << data; # else stream << util::toWide(data); # endif #elif defined(_WIN32) # if PLOG_CHAR_IS_UTF8 std::operator<<(stream, data); # else std::operator<<(stream, util::toWide(data)); # endif #else std::operator<<(stream, data); #endif } inline void operator<<(util::nostringstream& stream, char* data) { plog::detail::operator<<(stream, const_cast(data)); } inline void operator<<(util::nostringstream& stream, const std::string& data) { plog::detail::operator<<(stream, data.c_str()); } #ifdef __cpp_char8_t inline void operator<<(util::nostringstream& stream, const char8_t* data) { # if PLOG_CHAR_IS_UTF8 plog::detail::operator<<(stream, reinterpret_cast(data)); # else plog::detail::operator<<(stream, util::toWide(reinterpret_cast(data), codePage::kUTF8)); # endif } #endif //__cpp_char8_t // Print `std::pair` template inline void operator<<(util::nostringstream& stream, const std::pair& data) { stream << data.first; stream << ":"; stream << data.second; } #if defined(__clang__) || !defined(__GNUC__) || (__GNUC__ * 100 + __GNUC_MINOR__) >= 405 // skip for GCC < 4.5 due to https://gcc.gnu.org/bugzilla/show_bug.cgi?id=38600 #if !defined(_MSC_VER) || _MSC_VER > 1400 // MSVC 2005 doesn't understand `enableIf`, so drop all `meta` // Print data that can be casted to `std::string` template inline typename meta::enableIf::value, void>::type operator<<(util::nostringstream& stream, const T& data) { plog::detail::operator<<(stream, static_cast(data)); } #if PLOG_ENABLE_WCHAR_INPUT // Print data that can be casted to `std::wstring` template inline typename meta::enableIf::value, void>::type operator<<(util::nostringstream& stream, const T& data) { plog::detail::operator<<(stream, static_cast(data)); } #endif // Print std containers template inline typename meta::enableIf::value && !meta::isConvertibleToString::value && #if PLOG_ENABLE_WCHAR_INPUT !meta::isConvertibleToWString::value && #endif !meta::isFilesystemPath::value, void>::type operator<<(util::nostringstream& stream, const T& data) { stream << "["; for (typename T::const_iterator it = data.begin(); it != data.end();) { stream << *it; if (++it == data.end()) { break; } stream << ", "; } stream << "]"; } #endif #endif #ifdef __cplusplus_cli // Print managed C++ `System::String^` inline void operator<<(util::nostringstream& stream, System::String^ data) { cli::pin_ptr ptr = PtrToStringChars(data); plog::detail::operator<<(stream, static_cast(ptr)); } #endif #if PLOG_ENABLE_WCHAR_INPUT && (!defined(_MSC_VER) || _MSC_VER > 1400) // MSVC 2005 doesn't understand `enableIf`, so drop all `meta` namespace meta { template struct valueType { enum { value = Value }; }; template inline No operator<<(Stream&, const T&); template struct isStreamable : valueType(), meta::declval())) != sizeof(No)> {}; template struct isStreamable : valueType {}; template struct isStreamable : valueType {}; template struct isStreamable : valueType {}; // meta doesn't work well for deleted functions and C++20 has `operator<<(std::ostream&, const wchar_t*) = delete` so explicitly define it template<> struct isStreamable : valueType {}; # ifdef __cpp_char8_t // meta doesn't work well for deleted functions and C++20 has `operator<<(std::ostream&, const char8_t*) = delete` so explicitly define it template struct isStreamable : valueType {}; template struct isStreamable : valueType {}; # endif //__cpp_char8_t } # if PLOG_CHAR_IS_UTF8 // Print types that can be streamed into `std::owstringstream` but not into `std::ostringstream` when we use UTF-8 on Windows template inline typename meta::enableIf::value && !meta::isStreamable::value && !meta::isConvertibleToWString::value, void>::type operator<<(std::ostringstream& stream, const T& data) { std::wostringstream ss; ss << data; stream << ss.str(); } # else // Print types that can be streamed into `std::ostringstream` but not into `std::owstringstream` when we use `wchar_t` on Windows template inline typename meta::enableIf::value && !meta::isStreamable::value && !meta::isConvertibleToString::value, void>::type operator<<(std::wostringstream& stream, const T& data) { std::ostringstream ss; ss << data; stream << ss.str(); } # endif #endif } class Record { public: Record(Severity severity, const char* func, size_t line, const char* file, const void* object, int instanceId) : m_severity(severity), m_tid(util::gettid()), m_object(object), m_line(line), m_func(func), m_file(file), m_instanceId(instanceId) { util::ftime(&m_time); } Record& ref() { return *this; } ////////////////////////////////////////////////////////////////////////// // Stream output operators Record& operator<<(char data) { char str[] = { data, 0 }; return *this << str; } #if PLOG_ENABLE_WCHAR_INPUT Record& operator<<(wchar_t data) { wchar_t str[] = { data, 0 }; return *this << str; } #endif Record& operator<<(util::nostream& (PLOG_CDECL *data)(util::nostream&)) { m_message << data; return *this; } #ifdef QT_VERSION Record& operator<<(const QString& data) { # if PLOG_CHAR_IS_UTF8 return *this << data.toStdString(); # else return *this << data.toStdWString(); # endif } # if QT_VERSION < 0x060000 Record& operator<<(const QStringRef& data) { return *this << data.toString(); } # endif # ifdef QSTRINGVIEW_H Record& operator<<(QStringView data) { return *this << data.toString(); } # endif #endif template Record& operator<<(const T& data) { using namespace plog::detail; m_message << data; return *this; } #ifndef __cplusplus_cli Record& printf(const char* format, ...) { using namespace util; char* str = NULL; va_list ap; va_start(ap, format); int len = vasprintf(&str, format, ap); static_cast(len); va_end(ap); *this << str; free(str); return *this; } #ifdef _WIN32 Record& printf(const wchar_t* format, ...) { using namespace util; wchar_t* str = NULL; va_list ap; va_start(ap, format); int len = vaswprintf(&str, format, ap); static_cast(len); va_end(ap); *this << str; free(str); return *this; } #endif #endif //__cplusplus_cli ////////////////////////////////////////////////////////////////////////// // Getters virtual const util::Time& getTime() const { return m_time; } virtual Severity getSeverity() const { return m_severity; } virtual unsigned int getTid() const { return m_tid; } virtual const void* getObject() const { return m_object; } virtual size_t getLine() const { return m_line; } virtual const util::nchar* getMessage() const { m_messageStr = m_message.str(); return m_messageStr.c_str(); } virtual const char* getFunc() const { m_funcStr = util::processFuncName(m_func); return m_funcStr.c_str(); } virtual const char* getFile() const { return m_file; } virtual ~Record() // virtual destructor to satisfy -Wnon-virtual-dtor warning { } virtual int getInstanceId() const { return m_instanceId; } private: util::Time m_time; const Severity m_severity; const unsigned int m_tid; const void* const m_object; const size_t m_line; util::nostringstream m_message; const char* const m_func; const char* const m_file; const int m_instanceId; mutable std::string m_funcStr; mutable util::nstring m_messageStr; }; } plog-1.1.11/include/plog/Severity.h000066400000000000000000000022551504622126500171120ustar00rootroot00000000000000#pragma once #include namespace plog { enum Severity { none = 0, fatal = 1, error = 2, warning = 3, info = 4, debug = 5, verbose = 6 }; #ifdef _MSC_VER # pragma warning(suppress: 26812) // Prefer 'enum class' over 'enum' #endif inline const char* severityToString(Severity severity) { switch (severity) { case fatal: return "FATAL"; case error: return "ERROR"; case warning: return "WARN"; case info: return "INFO"; case debug: return "DEBUG"; case verbose: return "VERB"; default: return "NONE"; } } inline Severity severityFromString(const char* str) { switch (std::toupper(str[0])) { case 'F': return fatal; case 'E': return error; case 'W': return warning; case 'I': return info; case 'D': return debug; case 'V': return verbose; default: return none; } } } plog-1.1.11/include/plog/Util.h000066400000000000000000000421021504622126500162100ustar00rootroot00000000000000#pragma once #include #include #include #include #include #include #include #ifndef PLOG_ENABLE_WCHAR_INPUT # ifdef _WIN32 # define PLOG_ENABLE_WCHAR_INPUT 1 # else # define PLOG_ENABLE_WCHAR_INPUT 0 # endif #endif ////////////////////////////////////////////////////////////////////////// // PLOG_CHAR_IS_UTF8 specifies character encoding of `char` type. On *nix // systems it's set to UTF-8 while on Windows in can be ANSI or UTF-8. It // automatically detects `/utf-8` command line option in MSVC. Also it can // be set manually if required. // This option allows to support http://utf8everywhere.org approach. #ifndef PLOG_CHAR_IS_UTF8 # if defined(_WIN32) && !defined(_UTF8) # define PLOG_CHAR_IS_UTF8 0 # else # define PLOG_CHAR_IS_UTF8 1 # endif #endif #ifdef _WIN32 # if defined(PLOG_EXPORT) # define PLOG_LINKAGE __declspec(dllexport) # elif defined(PLOG_IMPORT) # define PLOG_LINKAGE __declspec(dllimport) # endif # if defined(PLOG_GLOBAL) # error "PLOG_GLOBAL isn't supported on Windows" # endif #else # if defined(PLOG_GLOBAL) # define PLOG_LINKAGE __attribute__ ((visibility ("default"))) # elif defined(PLOG_LOCAL) # define PLOG_LINKAGE __attribute__ ((visibility ("hidden"))) # define PLOG_LINKAGE_HIDDEN PLOG_LINKAGE # endif # if defined(PLOG_EXPORT) || defined(PLOG_IMPORT) # error "PLOG_EXPORT/PLOG_IMPORT is supported only on Windows" # endif #endif #ifndef PLOG_LINKAGE # define PLOG_LINKAGE #endif #ifndef PLOG_LINKAGE_HIDDEN # define PLOG_LINKAGE_HIDDEN #endif #ifdef _WIN32 # include # include # include # include # include #else # include # include # if defined(__linux__) || defined(__FreeBSD__) # include # elif defined(__rtems__) # include # endif # if defined(_POSIX_THREADS) # include # endif # if PLOG_ENABLE_WCHAR_INPUT # include # endif #endif #ifdef __FREERTOS__ // There is no standard way to know if the code is compiled for FreeRTOS. We expect __FREERTOS__ macro to be defined in such case. # include # include # include #endif #if PLOG_CHAR_IS_UTF8 # define PLOG_NSTR(x) x #else # define _PLOG_NSTR(x) L##x # define PLOG_NSTR(x) _PLOG_NSTR(x) #endif #ifdef _WIN32 # define PLOG_CDECL __cdecl #else # define PLOG_CDECL #endif #if __cplusplus >= 201103L || defined(_MSC_VER) && _MSC_VER >= 1700 # define PLOG_OVERRIDE override #else # define PLOG_OVERRIDE #endif namespace plog { namespace util { #if PLOG_CHAR_IS_UTF8 typedef std::string nstring; typedef std::ostringstream nostringstream; typedef std::istringstream nistringstream; typedef std::ostream nostream; typedef char nchar; #else typedef std::wstring nstring; typedef std::wostringstream nostringstream; typedef std::wistringstream nistringstream; typedef std::wostream nostream; typedef wchar_t nchar; #endif inline void localtime_s(struct tm* t, const time_t* time) { #if defined(_WIN32) && defined(__BORLANDC__) ::localtime_s(time, t); #elif defined(_WIN32) && defined(__MINGW32__) && !defined(__MINGW64_VERSION_MAJOR) *t = *::localtime(time); #elif defined(_WIN32) ::localtime_s(t, time); #else ::localtime_r(time, t); #endif } inline void gmtime_s(struct tm* t, const time_t* time) { #if defined(_WIN32) && defined(__BORLANDC__) ::gmtime_s(time, t); #elif defined(_WIN32) && defined(__MINGW32__) && !defined(__MINGW64_VERSION_MAJOR) *t = *::gmtime(time); #elif defined(_WIN32) ::gmtime_s(t, time); #else ::gmtime_r(time, t); #endif } #ifdef _WIN32 typedef timeb Time; inline void ftime(Time* t) { ::ftime(t); } #else struct Time { time_t time; unsigned short millitm; }; inline void ftime(Time* t) { timeval tv; ::gettimeofday(&tv, NULL); t->time = tv.tv_sec; t->millitm = static_cast(tv.tv_usec / 1000); } #endif inline unsigned int gettid() { #if defined(__FREERTOS__) && defined(INCLUDE_xTaskGetCurrentTaskHandle) return static_cast(reinterpret_cast(xTaskGetCurrentTaskHandle())); #elif defined(_WIN32) return GetCurrentThreadId(); #elif defined(__linux__) return static_cast(::syscall(__NR_gettid)); #elif defined(__FreeBSD__) long tid; syscall(SYS_thr_self, &tid); return static_cast(tid); #elif defined(__rtems__) return rtems_task_self(); #elif defined(__APPLE__) uint64_t tid64; pthread_threadid_np(NULL, &tid64); return static_cast(tid64); #else return 0; #endif } #ifndef _GNU_SOURCE inline int vasprintf(char** strp, const char* format, va_list ap) { va_list ap_copy; #if defined(_MSC_VER) && _MSC_VER <= 1600 ap_copy = ap; // there is no va_copy on Visual Studio 2010 #else va_copy(ap_copy, ap); #endif #ifndef __STDC_SECURE_LIB__ int charCount = vsnprintf(NULL, 0, format, ap_copy); #else int charCount = _vscprintf(format, ap_copy); #endif va_end(ap_copy); if (charCount < 0) { return -1; } size_t bufferCharCount = static_cast(charCount) + 1; char* str = static_cast(malloc(bufferCharCount)); if (!str) { return -1; } #ifndef __STDC_SECURE_LIB__ int retval = vsnprintf(str, bufferCharCount, format, ap); #else int retval = vsnprintf_s(str, bufferCharCount, static_cast(-1), format, ap); #endif if (retval < 0) { free(str); return -1; } *strp = str; return retval; } #endif #ifdef _WIN32 inline int vaswprintf(wchar_t** strp, const wchar_t* format, va_list ap) { #if defined(__BORLANDC__) int charCount = 0x1000; // there is no _vscwprintf on Borland/Embarcadero #else int charCount = _vscwprintf(format, ap); if (charCount < 0) { return -1; } #endif size_t bufferCharCount = static_cast(charCount) + 1; wchar_t* str = static_cast(malloc(bufferCharCount * sizeof(wchar_t))); if (!str) { return -1; } #if defined(__BORLANDC__) int retval = vsnwprintf_s(str, bufferCharCount, format, ap); #elif defined(__MINGW32__) && !defined(__MINGW64_VERSION_MAJOR) int retval = _vsnwprintf(str, bufferCharCount, format, ap); #else int retval = _vsnwprintf_s(str, bufferCharCount, charCount, format, ap); #endif if (retval < 0) { free(str); return -1; } *strp = str; return retval; } #endif #ifdef _WIN32 inline std::wstring toWide(const char* str, UINT cp = codePage::kChar) { size_t len = ::strlen(str); std::wstring wstr(len, 0); if (!wstr.empty()) { int wlen = MultiByteToWideChar(cp, 0, str, static_cast(len), &wstr[0], static_cast(wstr.size())); wstr.resize(wlen); } return wstr; } inline std::wstring toWide(const std::string& str, UINT cp = codePage::kChar) { return toWide(str.c_str(), cp); } inline const std::wstring& toWide(const std::wstring& str) // do nothing for already wide string { return str; } inline std::string toNarrow(const std::wstring& wstr, long page) { int len = WideCharToMultiByte(page, 0, wstr.c_str(), static_cast(wstr.size()), 0, 0, 0, 0); std::string str(len, 0); if (!str.empty()) { WideCharToMultiByte(page, 0, wstr.c_str(), static_cast(wstr.size()), &str[0], len, 0, 0); } return str; } #elif PLOG_ENABLE_WCHAR_INPUT inline std::string toNarrow(const wchar_t* wstr) { size_t wlen = ::wcslen(wstr); std::string str(wlen * sizeof(wchar_t), 0); if (!str.empty()) { const char* in = reinterpret_cast(&wstr[0]); char* out = &str[0]; size_t inBytes = wlen * sizeof(wchar_t); size_t outBytes = str.size(); iconv_t cd = ::iconv_open("UTF-8", "WCHAR_T"); ::iconv(cd, const_cast(&in), &inBytes, &out, &outBytes); ::iconv_close(cd); str.resize(str.size() - outBytes); } return str; } #endif inline std::string processFuncName(const char* func) { #if (defined(_WIN32) && !defined(__MINGW32__)) || defined(__OBJC__) return std::string(func); #else const char* funcBegin = func; const char* funcEnd = ::strchr(funcBegin, '('); int foundTemplate = 0; if (!funcEnd) { return std::string(func); } for (const char* i = funcEnd - 1; i >= funcBegin; --i) // search backwards for the first space char { if (*i == '>') { foundTemplate++; } else if (*i == '<') { foundTemplate--; } else if (*i == ' ' && foundTemplate == 0) { funcBegin = i + 1; break; } } return std::string(funcBegin, funcEnd); #endif } inline const nchar* findExtensionDot(const nchar* fileName) { #if PLOG_CHAR_IS_UTF8 return std::strrchr(fileName, '.'); #else return std::wcsrchr(fileName, L'.'); #endif } inline void splitFileName(const nchar* fileName, nstring& fileNameNoExt, nstring& fileExt) { const nchar* dot = findExtensionDot(fileName); if (dot) { fileNameNoExt.assign(fileName, dot); fileExt.assign(dot + 1); } else { fileNameNoExt.assign(fileName); fileExt.clear(); } } class PLOG_LINKAGE NonCopyable { protected: NonCopyable() { } private: NonCopyable(const NonCopyable&); NonCopyable& operator=(const NonCopyable&); }; class PLOG_LINKAGE_HIDDEN File : NonCopyable { public: File() : m_file(-1) { } ~File() { close(); } size_t open(const nstring& fileName) { #if defined(_WIN32) && (defined(__BORLANDC__) || defined(__MINGW32__)) m_file = ::_wsopen(toWide(fileName).c_str(), _O_CREAT | _O_WRONLY | _O_BINARY | _O_NOINHERIT, SH_DENYWR, _S_IREAD | _S_IWRITE); #elif defined(_WIN32) ::_wsopen_s(&m_file, toWide(fileName).c_str(), _O_CREAT | _O_WRONLY | _O_BINARY | _O_NOINHERIT, _SH_DENYWR, _S_IREAD | _S_IWRITE); #elif defined(O_CLOEXEC) m_file = ::open(fileName.c_str(), O_CREAT | O_APPEND | O_WRONLY | O_CLOEXEC, S_IRUSR | S_IWUSR | S_IRGRP | S_IROTH); #else m_file = ::open(fileName.c_str(), O_CREAT | O_APPEND | O_WRONLY, S_IRUSR | S_IWUSR | S_IRGRP | S_IROTH); #endif return seek(0, SEEK_END); } size_t write(const void* buf, size_t count) { return m_file != -1 ? static_cast( #ifdef _WIN32 ::_write(m_file, buf, static_cast(count)) #else ::write(m_file, buf, count) #endif ) : static_cast(-1); } template size_t write(const std::basic_string& str) { return write(str.data(), str.size() * sizeof(CharType)); } size_t seek(size_t offset, int whence) { return m_file != -1 ? static_cast( #if defined(_WIN32) && (defined(__BORLANDC__) || defined(__MINGW32__)) ::_lseek(m_file, static_cast(offset), whence) #elif defined(_WIN32) ::_lseeki64(m_file, static_cast(offset), whence) #else ::lseek(m_file, static_cast(offset), whence) #endif ) : static_cast(-1); } void close() { if (m_file != -1) { #ifdef _WIN32 ::_close(m_file); #else ::close(m_file); #endif m_file = -1; } } static int unlink(const nstring& fileName) { #ifdef _WIN32 return ::_wunlink(toWide(fileName).c_str()); #else return ::unlink(fileName.c_str()); #endif } static int rename(const nstring& oldFilename, const nstring& newFilename) { #ifdef _WIN32 return MoveFileW(toWide(oldFilename).c_str(), toWide(newFilename).c_str()); #else return ::rename(oldFilename.c_str(), newFilename.c_str()); #endif } private: int m_file; }; class PLOG_LINKAGE_HIDDEN Mutex : NonCopyable { public: Mutex() { #ifdef __FREERTOS__ m_sync = xSemaphoreCreateBinary(); xSemaphoreGive(m_sync); #elif defined(_WIN32) InitializeCriticalSection(&m_sync); #elif defined(__rtems__) rtems_semaphore_create(0, 1, RTEMS_PRIORITY | RTEMS_BINARY_SEMAPHORE | RTEMS_INHERIT_PRIORITY, 1, &m_sync); #elif defined(_POSIX_THREADS) ::pthread_mutex_init(&m_sync, 0); #endif } ~Mutex() { #ifdef __FREERTOS__ vSemaphoreDelete(m_sync); #elif defined(_WIN32) DeleteCriticalSection(&m_sync); #elif defined(__rtems__) rtems_semaphore_delete(m_sync); #elif defined(_POSIX_THREADS) ::pthread_mutex_destroy(&m_sync); #endif } friend class MutexLock; private: void lock() { #ifdef __FREERTOS__ xSemaphoreTake(m_sync, portMAX_DELAY); #elif defined(_WIN32) EnterCriticalSection(&m_sync); #elif defined(__rtems__) rtems_semaphore_obtain(m_sync, RTEMS_WAIT, RTEMS_NO_TIMEOUT); #elif defined(_POSIX_THREADS) ::pthread_mutex_lock(&m_sync); #endif } void unlock() { #ifdef __FREERTOS__ xSemaphoreGive(m_sync); #elif defined(_WIN32) LeaveCriticalSection(&m_sync); #elif defined(__rtems__) rtems_semaphore_release(m_sync); #elif defined(_POSIX_THREADS) ::pthread_mutex_unlock(&m_sync); #endif } private: #ifdef __FREERTOS__ SemaphoreHandle_t m_sync; #elif defined(_WIN32) CRITICAL_SECTION m_sync; #elif defined(__rtems__) rtems_id m_sync; #elif defined(_POSIX_THREADS) pthread_mutex_t m_sync; #endif }; class PLOG_LINKAGE_HIDDEN MutexLock : NonCopyable { public: MutexLock(Mutex& mutex) : m_mutex(mutex) { m_mutex.lock(); } ~MutexLock() { m_mutex.unlock(); } private: Mutex& m_mutex; }; template #ifdef _WIN32 class Singleton : NonCopyable #else class PLOG_LINKAGE Singleton : NonCopyable #endif { public: #if (defined(__clang__) || defined(__GNUC__) && __GNUC__ >= 8) && !defined(__BORLANDC__) // This constructor is called before the `T` object is fully constructed, and // pointers are not dereferenced anyway, so UBSan shouldn't check vptrs. __attribute__((no_sanitize("vptr"))) #endif Singleton() { assert(!m_instance); m_instance = static_cast(this); } ~Singleton() { assert(m_instance); m_instance = 0; } static T* getInstance() { return m_instance; } private: static T* m_instance; }; template T* Singleton::m_instance = NULL; } } plog-1.1.11/include/plog/WinApi.h000066400000000000000000000141261504622126500164670ustar00rootroot00000000000000#pragma once #ifdef _WIN32 // These windows structs must be in a global namespace struct HKEY__; struct _SECURITY_ATTRIBUTES; struct _CONSOLE_SCREEN_BUFFER_INFO; struct _RTL_CRITICAL_SECTION; namespace plog { typedef unsigned long DWORD; typedef unsigned short WORD; typedef unsigned char BYTE; typedef unsigned int UINT; typedef int BOOL; typedef long LSTATUS; typedef char* LPSTR; typedef wchar_t* LPWSTR; typedef const char* LPCSTR; typedef const wchar_t* LPCWSTR; typedef void* HANDLE; typedef HKEY__* HKEY; typedef size_t ULONG_PTR; struct CRITICAL_SECTION { void* DebugInfo; long LockCount; long RecursionCount; HANDLE OwningThread; HANDLE LockSemaphore; ULONG_PTR SpinCount; }; struct COORD { short X; short Y; }; struct SMALL_RECT { short Left; short Top; short Right; short Bottom; }; struct CONSOLE_SCREEN_BUFFER_INFO { COORD dwSize; COORD dwCursorPosition; WORD wAttributes; SMALL_RECT srWindow; COORD dwMaximumWindowSize; }; namespace codePage { const UINT kActive = 0; const UINT kUTF8 = 65001; #if PLOG_CHAR_IS_UTF8 const UINT kChar = kUTF8; #else const UINT kChar = kActive; #endif } namespace eventLog { const WORD kErrorType = 0x0001; const WORD kWarningType = 0x0002; const WORD kInformationType = 0x0004; } namespace hkey { const HKEY kLocalMachine = reinterpret_cast(static_cast(0x80000002)); } namespace regSam { const DWORD kQueryValue = 0x0001; const DWORD kSetValue = 0x0002; } namespace regType { const DWORD kExpandSz = 2; const DWORD kDword = 4; } namespace stdHandle { const DWORD kOutput = static_cast(-11); const DWORD kErrorOutput = static_cast(-12); } namespace foreground { const WORD kBlue = 0x0001; const WORD kGreen = 0x0002; const WORD kRed = 0x0004; const WORD kIntensity = 0x0008; } namespace background { const WORD kBlue = 0x0010; const WORD kGreen = 0x0020; const WORD kRed = 0x0040; const WORD kIntensity = 0x0080; } extern "C" { __declspec(dllimport) int __stdcall MultiByteToWideChar(UINT CodePage, DWORD dwFlags, LPCSTR lpMultiByteStr, int cbMultiByte, LPWSTR lpWideCharStr, int cchWideChar); __declspec(dllimport) int __stdcall WideCharToMultiByte(UINT CodePage, DWORD dwFlags, LPCWSTR lpWideCharStr, int cchWideChar, LPSTR lpMultiByteStr, int cbMultiByte, const char* lpDefaultChar, BOOL* lpUsedDefaultChar); __declspec(dllimport) DWORD __stdcall GetCurrentThreadId(); __declspec(dllimport) BOOL __stdcall MoveFileW(LPCWSTR lpExistingFileName, LPCWSTR lpNewFileName); __declspec(dllimport) void __stdcall InitializeCriticalSection(_RTL_CRITICAL_SECTION* lpCriticalSection); __declspec(dllimport) void __stdcall EnterCriticalSection(_RTL_CRITICAL_SECTION* lpCriticalSection); __declspec(dllimport) void __stdcall LeaveCriticalSection(_RTL_CRITICAL_SECTION* lpCriticalSection); __declspec(dllimport) void __stdcall DeleteCriticalSection(_RTL_CRITICAL_SECTION* lpCriticalSection); __declspec(dllimport) HANDLE __stdcall RegisterEventSourceW(LPCWSTR lpUNCServerName, LPCWSTR lpSourceName); __declspec(dllimport) BOOL __stdcall DeregisterEventSource(HANDLE hEventLog); __declspec(dllimport) BOOL __stdcall ReportEventW(HANDLE hEventLog, WORD wType, WORD wCategory, DWORD dwEventID, void* lpUserSid, WORD wNumStrings, DWORD dwDataSize, LPCWSTR* lpStrings, void* lpRawData); __declspec(dllimport) LSTATUS __stdcall RegCreateKeyExW(HKEY hKey, LPCWSTR lpSubKey, DWORD Reserved, LPWSTR lpClass, DWORD dwOptions, DWORD samDesired, _SECURITY_ATTRIBUTES* lpSecurityAttributes, HKEY* phkResult, DWORD* lpdwDisposition); __declspec(dllimport) LSTATUS __stdcall RegSetValueExW(HKEY hKey, LPCWSTR lpValueName, DWORD Reserved, DWORD dwType, const BYTE* lpData, DWORD cbData); __declspec(dllimport) LSTATUS __stdcall RegCloseKey(HKEY hKey); __declspec(dllimport) LSTATUS __stdcall RegOpenKeyExW(HKEY hKey, LPCWSTR lpSubKey, DWORD ulOptions, DWORD samDesired, HKEY* phkResult); __declspec(dllimport) LSTATUS __stdcall RegDeleteKeyW(HKEY hKey, LPCWSTR lpSubKey); __declspec(dllimport) HANDLE __stdcall GetStdHandle(DWORD nStdHandle); __declspec(dllimport) BOOL __stdcall WriteConsoleW(HANDLE hConsoleOutput, const void* lpBuffer, DWORD nNumberOfCharsToWrite, DWORD* lpNumberOfCharsWritten, void* lpReserved); __declspec(dllimport) BOOL __stdcall GetConsoleScreenBufferInfo(HANDLE hConsoleOutput, _CONSOLE_SCREEN_BUFFER_INFO* lpConsoleScreenBufferInfo); __declspec(dllimport) BOOL __stdcall SetConsoleTextAttribute(HANDLE hConsoleOutput, WORD wAttributes); __declspec(dllimport) void __stdcall OutputDebugStringW(LPCWSTR lpOutputString); } inline void InitializeCriticalSection(CRITICAL_SECTION* criticalSection) { plog::InitializeCriticalSection(reinterpret_cast<_RTL_CRITICAL_SECTION*>(criticalSection)); } inline void EnterCriticalSection(CRITICAL_SECTION* criticalSection) { plog::EnterCriticalSection(reinterpret_cast<_RTL_CRITICAL_SECTION*>(criticalSection)); } inline void LeaveCriticalSection(CRITICAL_SECTION* criticalSection) { plog::LeaveCriticalSection(reinterpret_cast<_RTL_CRITICAL_SECTION*>(criticalSection)); } inline void DeleteCriticalSection(CRITICAL_SECTION* criticalSection) { plog::DeleteCriticalSection(reinterpret_cast<_RTL_CRITICAL_SECTION*>(criticalSection)); } inline BOOL GetConsoleScreenBufferInfo(HANDLE consoleOutput, CONSOLE_SCREEN_BUFFER_INFO* consoleScreenBufferInfo) { return plog::GetConsoleScreenBufferInfo(consoleOutput, reinterpret_cast<_CONSOLE_SCREEN_BUFFER_INFO*>(consoleScreenBufferInfo)); } } #endif // _WIN32 plog-1.1.11/library.json000066400000000000000000000025531504622126500151030ustar00rootroot00000000000000{ "$schema": "https://raw.githubusercontent.com/platformio/platformio-core/develop/platformio/assets/schema/library.json", "name": "plog", "version": "1.1.11", "description": "Portable, simple and extensible C++ logging library", "keywords": [ "plog", "log", "logger", "logging" ], "repository": { "type": "git", "url": "https://github.com/SergiusTheBest/plog.git" }, "authors": { "name": "Sergey Podobry", "email": "sergey.podobry@gmail.com", "maintainer": true }, "license": "MIT", "frameworks": "*", "platforms": "*", "headers": [ "plog/Log.h", "plog/Init.h", "plog/Appenders/ArduinoAppender.h", "plog/Formatters/CsvFormatter.h", "plog/Formatters/FuncMessageFormatter.h", "plog/Formatters/MessageOnlyFormatter.h", "plog/Formatters/TxtFormatter.h" ], "examples": [ { "name": "Arduino", "base": "samples/Arduino", "files": [ "platformio.ini", "src/main.cpp" ] } ], "export": { "include": [ "doc", "include", "samples/Arduino", "LICENSE", "README.md" ] }, "build": { "includeDir": "include" } } plog-1.1.11/plog.nuspec000066400000000000000000000016331504622126500147220ustar00rootroot00000000000000 plog $version$ Pretty powerful logging library in about 1000 lines of code SergiusTheBest https://github.com/SergiusTheBest/plog MIT false native log logging c++ cpp plog-1.1.11/plog.targets000066400000000000000000000006061504622126500150750ustar00rootroot00000000000000 $(MSBuildThisFileDirectory)../../lib/native/include;%(AdditionalIncludeDirectories) plog-1.1.11/samples/000077500000000000000000000000001504622126500142035ustar00rootroot00000000000000plog-1.1.11/samples/Android/000077500000000000000000000000001504622126500155635ustar00rootroot00000000000000plog-1.1.11/samples/Android/AndroidJNI/000077500000000000000000000000001504622126500175045ustar00rootroot00000000000000plog-1.1.11/samples/Android/AndroidJNI/Main.cpp000066400000000000000000000011521504622126500210730ustar00rootroot00000000000000// // AndroidJNI - shows how to use the Android appender in JNI. // #include #include #include #include #include extern "C" jint JNI_Onload(JavaVM*, void*) { static plog::AndroidAppender appender("MyApp"); // Create an appender and set a log tag. plog::init(plog::debug, &appender); // Initialize the logger with the appender. return JNI_VERSION_1_6; } extern "C" void Java_com_github_sergius_myapp_Sample_foo(JNIEnv*, jobject) { PLOGD << "Hello Android!"; } plog-1.1.11/samples/Android/AndroidNative/000077500000000000000000000000001504622126500203125ustar00rootroot00000000000000plog-1.1.11/samples/Android/AndroidNative/Main.cpp000066400000000000000000000011671504622126500217070ustar00rootroot00000000000000// // AndroidNative - shows how to use an Android appender. // #include #include #include #include int main() { static plog::AndroidAppender androidAppender("app"); plog::init(plog::verbose, &androidAppender); PLOG_VERBOSE << "This is a VERBOSE message"; PLOG_DEBUG << "This is a DEBUG message"; PLOG_INFO << "This is an INFO message"; PLOG_WARNING << "This is a WARNING message"; PLOG_ERROR << "This is an ERROR message"; PLOG_FATAL << "This is a FATAL message"; return 0; } plog-1.1.11/samples/Android/CMakeLists.txt000066400000000000000000000007631504622126500203310ustar00rootroot00000000000000if(ANDROID) add_library(AndroidJNI SHARED AndroidJNI/Main.cpp) target_link_libraries(AndroidJNI plog) add_executable(AndroidNative AndroidNative/Main.cpp) target_link_libraries(AndroidNative plog) else() add_custom_target(AndroidJNI SOURCES AndroidJNI/Main.cpp) add_custom_target(AndroidNative SOURCES AndroidNative/Main.cpp) endif() set_target_properties(AndroidJNI PROPERTIES FOLDER Samples/Android) set_target_properties(AndroidNative PROPERTIES FOLDER Samples/Android) plog-1.1.11/samples/Arduino/000077500000000000000000000000001504622126500156045ustar00rootroot00000000000000plog-1.1.11/samples/Arduino/platformio.ini000066400000000000000000000011611504622126500204600ustar00rootroot00000000000000; PlatformIO Project Configuration File ; ; Build options: build flags, source filter ; Upload options: custom upload port, speed and extra flags ; Library options: dependencies, extra library storages ; Advanced options: extra scripting ; ; Please visit documentation for the other options and examples ; https://docs.platformio.org/page/projectconf.html [platformio] include_dir = ../../include [env:esp8266] platform = espressif8266 board = nodemcuv2 framework = arduino [env:esp32] platform = espressif32 board = esp32dev framework = arduino [env:pico] platform = raspberrypi board = pico framework = arduino plog-1.1.11/samples/Arduino/src/000077500000000000000000000000001504622126500163735ustar00rootroot00000000000000plog-1.1.11/samples/Arduino/src/main.cpp000066400000000000000000000010071504622126500200210ustar00rootroot00000000000000#include #include #include #include #include static plog::ArduinoAppender arduinoAppender(Serial); void setup() { plog::init(plog::verbose, &arduinoAppender); PLOG_VERBOSE << "verbose"; PLOG_DEBUG << "debug"; PLOG_INFO << "info"; PLOG_WARNING << "warning"; PLOG_ERROR << "error"; PLOG_FATAL << "fatal"; } void loop() { delay(1000); // Wait for a second } plog-1.1.11/samples/AscDump/000077500000000000000000000000001504622126500155375ustar00rootroot00000000000000plog-1.1.11/samples/AscDump/CMakeLists.txt000066400000000000000000000001761504622126500203030ustar00rootroot00000000000000add_executable(AscDump Main.cpp) target_link_libraries(AscDump plog) set_target_properties(AscDump PROPERTIES FOLDER Samples) plog-1.1.11/samples/AscDump/Main.cpp000066400000000000000000000013271504622126500171320ustar00rootroot00000000000000// // AscDump - shows how to use plog::ascdump to dump binary buffers into ASCII. // #include #include #include #include #include int main() { plog::init(plog::verbose, plog::streamStdOut); std::vector v; v.push_back(0x6548); v.push_back(0x6c6c); v.push_back(0x216f); v.push_back(0); v.push_back(-1); PLOGI << "v: " << plog::ascdump(v); unsigned char arr[] = {0x48, 0x65, 0x6c, 0x6c, 0x6f, 0x21, 0xff}; PLOGI << "arr: " << plog::ascdump(arr); void* p = malloc(100); PLOGI << "p: " << plog::ascdump(p, 100); return 0; } plog-1.1.11/samples/CMakeLists.txt000066400000000000000000000050171504622126500167460ustar00rootroot00000000000000if(${CMAKE_VERSION} VERSION_LESS 3.27.0) cmake_minimum_required(VERSION 3.0) else() cmake_minimum_required(VERSION 3.27) endif() if(POLICY CMP0063) #Honor visibility properties for all target types cmake_policy(SET CMP0063 NEW) endif() set_property(GLOBAL PROPERTY USE_FOLDERS ON) macro(checkObjCXX) file(WRITE "${CMAKE_BINARY_DIR}/CMakeFiles/dummy.mm" "int main(){return 0;}\n") execute_process( WORKING_DIRECTORY ${CMAKE_BINARY_DIR}/CMakeFiles COMMAND ${CMAKE_CXX_COMPILER} dummy.mm RESULT_VARIABLE result ERROR_QUIET OUTPUT_QUIET ) file(REMOVE "${CMAKE_BINARY_DIR}/CMakeFiles/dummy.mm") if("${result}" STREQUAL "0") set(CMAKE_OBJCXX_AVAILABLE 1) message("-- ObjectiveC++ support is detected") endif() endmacro() project(Samples) if(CMAKE_CXX_COMPILER_ID MATCHES "Clang") set(CMAKE_COMPILER_IS_CLANGXX 1) endif() if(MSVC) set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} /W4 /WX") string(REPLACE "/RTC1" "" CMAKE_CXX_FLAGS_DEBUG "${CMAKE_CXX_FLAGS_DEBUG}") elseif(CMAKE_COMPILER_IS_GNUCXX OR CMAKE_COMPILER_IS_CLANGXX) set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -Wall -Wextra -Wconversion -Wnon-virtual-dtor -Wundef -pedantic -Werror") set(CMAKE_CXX_VISIBILITY_PRESET hidden) set(CMAKE_VISIBILITY_INLINES_HIDDEN 1) checkObjCXX() if(CMAKE_COMPILER_IS_GNUCXX AND CMAKE_CXX_COMPILER_VERSION VERSION_GREATER 6.0) set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -Wsuggest-override") endif() if(CMAKE_COMPILER_IS_CLANGXX) set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -Wnewline-eof") endif() endif() add_subdirectory(Android) add_subdirectory(AscDump) add_subdirectory(Chained) add_subdirectory(ColorConsole) add_subdirectory(CustomAppender) add_subdirectory(CustomConverter) add_subdirectory(CustomFormatter) add_subdirectory(CustomType) add_subdirectory(CXX11) add_subdirectory(CXX17) add_subdirectory(DebugOutput) add_subdirectory(Demo) add_subdirectory(DisableLogging) add_subdirectory(DynamicAppender) add_subdirectory(EventLog) add_subdirectory(Facilities) add_subdirectory(FreeRTOS) add_subdirectory(Hello) add_subdirectory(HexDump) add_subdirectory(Library) add_subdirectory(MessagePrefix) add_subdirectory(MultiAppender) add_subdirectory(MultiInstance) add_subdirectory(NotShared) add_subdirectory(ObjectiveC) add_subdirectory(Path) add_subdirectory(Performance) add_subdirectory(PrintVar) add_subdirectory(SetFileName) add_subdirectory(Shared) add_subdirectory(SkipNativeEOL) add_subdirectory(UtcTime) add_subdirectory(Utf8Everywhere) plog-1.1.11/samples/CXX11/000077500000000000000000000000001504622126500150075ustar00rootroot00000000000000plog-1.1.11/samples/CXX11/CMakeLists.txt000066400000000000000000000007201504622126500175460ustar00rootroot00000000000000if(NOT CMAKE_VERSION VERSION_LESS 3.3.0) if(${CMAKE_VERSION} VERSION_LESS 3.27.0) cmake_minimum_required(VERSION 3.3) else() cmake_minimum_required(VERSION 3.27) endif() if(cxx_std_11 IN_LIST CMAKE_CXX_COMPILE_FEATURES) add_executable(CXX11 Main.cpp) target_link_libraries(CXX11 plog::plog) set_target_properties(CXX11 PROPERTIES FOLDER Samples CXX_STANDARD 11 CXX_STANDARD_REQUIRED ON) endif() endif() plog-1.1.11/samples/CXX11/Main.cpp000066400000000000000000000014511504622126500164000ustar00rootroot00000000000000// // CXX11 - demonstrates log stream abilities for C++11 features. // #include #include #include #include #include #include int main() { plog::init(plog::debug, plog::streamStdOut); // Initialize logging std::unordered_map unorderedMap; unorderedMap["red"] = 1; unorderedMap["green"] = 2; unorderedMap["blue"] = 4; PLOG_INFO << unorderedMap; std::unordered_set unorderedSet; unorderedSet.insert("red"); unorderedSet.insert("green"); unorderedSet.insert("blue"); PLOG_INFO << unorderedSet; std::array array = {{1, 2, 3, 4}}; PLOG_INFO << array; return 0; } plog-1.1.11/samples/CXX17/000077500000000000000000000000001504622126500150155ustar00rootroot00000000000000plog-1.1.11/samples/CXX17/CMakeLists.txt000066400000000000000000000016101504622126500175530ustar00rootroot00000000000000if(NOT CMAKE_VERSION VERSION_LESS 3.3.0) if(${CMAKE_VERSION} VERSION_LESS 3.27.0) cmake_minimum_required(VERSION 3.3) else() cmake_minimum_required(VERSION 3.27) endif() # Unfortunately cxx_std_17 in CMAKE_CXX_COMPILE_FEATURES is not reliable, so check include files include(CheckIncludeFileCXX) CHECK_INCLUDE_FILE_CXX("string_view" HAS_STRING_VIEW) CHECK_INCLUDE_FILE_CXX("filesystem" HAS_FILESYSTEM) if(HAS_STRING_VIEW AND HAS_FILESYSTEM) add_executable(CXX17 Main.cpp) target_link_libraries(CXX17 plog::plog) set_target_properties(CXX17 PROPERTIES FOLDER Samples CXX_STANDARD 17 CXX_STANDARD_REQUIRED ON) if (CMAKE_COMPILER_IS_GNUCC AND CMAKE_CXX_COMPILER_VERSION VERSION_GREATER 7.0 AND CMAKE_CXX_COMPILER_VERSION VERSION_LESS 9.0) target_link_libraries(CXX17 stdc++fs) endif() endif() endif() plog-1.1.11/samples/CXX17/Main.cpp000066400000000000000000000012451504622126500164070ustar00rootroot00000000000000// // CXX17 - demonstrates log stream abilities for C++17 features. // #include #include #include #include #include int main() { plog::init(plog::debug, plog::streamStdOut); // Initialize logging std::string_view strView = "string view"; PLOG_INFO << strView; #if PLOG_ENABLE_WCHAR_INPUT std::wstring_view wstrView = L"wstring view"; PLOG_INFO << wstrView; // on Linux is printed as a container, will be fixed in future #endif PLOG_INFO << "Current path: " << std::filesystem::current_path(); return 0; } plog-1.1.11/samples/Chained/000077500000000000000000000000001504622126500155365ustar00rootroot00000000000000plog-1.1.11/samples/Chained/CMakeLists.txt000066400000000000000000000013701504622126500202770ustar00rootroot00000000000000# some systems have no shared libraries support, so check it if(NOT DEFINED TARGET_SUPPORTS_SHARED_LIBS OR TARGET_SUPPORTS_SHARED_LIBS) add_executable(ChainedApp ChainedApp/Main.cpp) target_link_libraries(ChainedApp ChainedLib plog) set_target_properties(ChainedApp PROPERTIES FOLDER Samples/Chained) # set PLOG to local, so instances will not be shared across modules target_compile_definitions(ChainedApp PRIVATE PLOG_LOCAL) add_library(ChainedLib SHARED ChainedLib/Main.cpp) target_link_libraries(ChainedLib plog) set_target_properties(ChainedLib PROPERTIES FOLDER Samples/Chained) # set PLOG to local, so instances will not be shared across modules target_compile_definitions(ChainedLib PRIVATE PLOG_LOCAL) endif() plog-1.1.11/samples/Chained/ChainedApp/000077500000000000000000000000001504622126500175325ustar00rootroot00000000000000plog-1.1.11/samples/Chained/ChainedApp/Main.cpp000066400000000000000000000013051504622126500211210ustar00rootroot00000000000000// // Chained - shows how to chain a logger (route messages) in a shared library with the main logger. // #include #include // Functions imported form the shared library. extern "C" void initialize(plog::Severity severity, plog::IAppender* appender); extern "C" void foo(); int main() { plog::init(plog::debug, "ChainedApp.txt"); // Initialize the main logger. PLOGD << "Hello from app!"; // Write a log message. initialize(plog::debug, plog::get()); // Initialize the logger in the shared library. Note that it has its own severity. foo(); // Call a function from the shared library that produces a log message. return 0; } plog-1.1.11/samples/Chained/ChainedLib/000077500000000000000000000000001504622126500175205ustar00rootroot00000000000000plog-1.1.11/samples/Chained/ChainedLib/Main.cpp000066400000000000000000000012551504622126500211130ustar00rootroot00000000000000// // Chained - shows how to chain a logger (route messages) in a shared library with the main logger. // #include #include // Helper macro to mark functions exported from the library. #ifdef _WIN32 # define EXPORT __declspec(dllexport) #else # define EXPORT __attribute__ ((visibility ("default"))) #endif // Function that initializes the logger in the shared library. extern "C" void EXPORT initialize(plog::Severity severity, plog::IAppender* appender) { plog::init(severity, appender); // Initialize the shared library logger. } // Function that produces a log message. extern "C" void EXPORT foo() { PLOGI << "Hello from shared lib!"; } plog-1.1.11/samples/ColorConsole/000077500000000000000000000000001504622126500166045ustar00rootroot00000000000000plog-1.1.11/samples/ColorConsole/CMakeLists.txt000066400000000000000000000002141504622126500213410ustar00rootroot00000000000000add_executable(ColorConsole Main.cpp) target_link_libraries(ColorConsole plog) set_target_properties(ColorConsole PROPERTIES FOLDER Samples)plog-1.1.11/samples/ColorConsole/Main.cpp000066400000000000000000000012721504622126500201760ustar00rootroot00000000000000// // ColorConsole - shows how to use a color console appender. // #include #include #include #include int main() { static plog::ColorConsoleAppender consoleAppender; plog::init(plog::verbose, &consoleAppender); // Log severity levels are printed in different colors. PLOG_VERBOSE << "This is a VERBOSE message"; PLOG_DEBUG << "This is a DEBUG message"; PLOG_INFO << "This is an INFO message"; PLOG_WARNING << "This is a WARNING message"; PLOG_ERROR << "This is an ERROR message"; PLOG_FATAL << "This is a FATAL message"; return 0; } plog-1.1.11/samples/CustomAppender/000077500000000000000000000000001504622126500171345ustar00rootroot00000000000000plog-1.1.11/samples/CustomAppender/CMakeLists.txt000066400000000000000000000002221504622126500216700ustar00rootroot00000000000000add_executable(CustomAppender Main.cpp) target_link_libraries(CustomAppender plog) set_target_properties(CustomAppender PROPERTIES FOLDER Samples)plog-1.1.11/samples/CustomAppender/Main.cpp000066400000000000000000000023701504622126500205260ustar00rootroot00000000000000// // CustomAppender - shows how to implement a custom appender that stores log messages in memory. // #include #include #include #include namespace plog { template // Typically a formatter is passed as a template parameter. class MyAppender : public IAppender // All appenders MUST inherit IAppender interface. { public: virtual void write(const Record& record) PLOG_OVERRIDE // This is a method from IAppender that MUST be implemented. { util::nstring str = Formatter::format(record); // Use the formatter to get a string from a record. m_messageList.push_back(str); // Store a log message in a list. } std::list& getMessageList() { return m_messageList; } private: std::list m_messageList; }; } int main() { static plog::MyAppender myAppender; // Create our custom appender. plog::init(plog::debug, &myAppender); // Initialize the logger with our appender. PLOGD << "A debug message!"; myAppender.getMessageList(); // This returns a list of stored log messages. return 0; } plog-1.1.11/samples/CustomConverter/000077500000000000000000000000001504622126500173455ustar00rootroot00000000000000plog-1.1.11/samples/CustomConverter/CMakeLists.txt000066400000000000000000000002251504622126500221040ustar00rootroot00000000000000add_executable(CustomConverter Main.cpp) target_link_libraries(CustomConverter plog) set_target_properties(CustomConverter PROPERTIES FOLDER Samples)plog-1.1.11/samples/CustomConverter/Main.cpp000066400000000000000000000027011504622126500207350ustar00rootroot00000000000000// // CustomConverter - shows how to implement a custom converter that encrypts log messages. // #include #include #include #include namespace plog { class MyConverter { public: static std::string header(const util::nstring& str) { return convert(str); // We have no special header for a file, so just call convert. } static std::string convert(const util::nstring& str) { const std::string& in = UTF8Converter::convert(str); // Convert to UTF8 first as it is more compact. std::string out; out.resize(in.size()); // This is an encryption key. const char kKey[] = "\x56\x5a\x43\x4d\x5f\x81\x4c\x4e\x19\x29\x2e\x13\x7c\x31\x14\x17\x5d\x63\x32\x39"; // Simple XOR encryption. for (size_t i = 0; i < out.size(); ++i) { out[i] = in[i] ^ kKey[i % (sizeof(kKey) - 1)]; } return out; } }; } int main() { static plog::RollingFileAppender appender("CustomConverter.txt"); // Create an appender and pass our converter as a template parameter. plog::init(plog::debug, &appender); // Initialize the logger with the appender. PLOGD << "A debug message!"; PLOGD << "Another one debug message!"; return 0; } plog-1.1.11/samples/CustomFormatter/000077500000000000000000000000001504622126500173415ustar00rootroot00000000000000plog-1.1.11/samples/CustomFormatter/CMakeLists.txt000066400000000000000000000002251504622126500221000ustar00rootroot00000000000000add_executable(CustomFormatter Main.cpp) target_link_libraries(CustomFormatter plog) set_target_properties(CustomFormatter PROPERTIES FOLDER Samples)plog-1.1.11/samples/CustomFormatter/Main.cpp000066400000000000000000000015761504622126500207420ustar00rootroot00000000000000// // CustomFormatter - shows how to implement a custom formatter. // #include #include namespace plog { class MyFormatter { public: static util::nstring header() // This method returns a header for a new file. In our case it is empty. { return util::nstring(); } static util::nstring format(const Record& record) // This method returns a string from a record. { util::nostringstream ss; ss << record.getMessage() << "\n"; // Produce a simple string with a log message. return ss.str(); } }; } int main() { plog::init(plog::debug, "CustomFormatter.txt"); // Initialize the logger and pass our formatter as a template parameter to init function. PLOGD << "A debug message!"; return 0; } plog-1.1.11/samples/CustomType/000077500000000000000000000000001504622126500163175ustar00rootroot00000000000000plog-1.1.11/samples/CustomType/CMakeLists.txt000066400000000000000000000002061504622126500210550ustar00rootroot00000000000000add_executable(CustomType Main.cpp) target_link_libraries(CustomType plog) set_target_properties(CustomType PROPERTIES FOLDER Samples)plog-1.1.11/samples/CustomType/Main.cpp000066400000000000000000000014731504622126500177140ustar00rootroot00000000000000// // CustomType - shows how to print a custom type to the log stream. // #include #include struct Point // This is our custom type that we want to print to the log stream. { int x; int y; }; namespace plog { Record& operator<<(Record& record, const Point& pt) // Implement a stream operator for our type. { return record << "(" << pt.x << ";" << pt.y << ")"; } } int main() { plog::init(plog::debug, "CustomType.txt"); // Initialize the logger. Point pt1 = { 0, 0 }; Point pt2 = { 10, -5 }; PLOGI << "We've got a line with coords: " << pt1 << pt2; // Print our type to the log stream. PLOGI << pt1 << pt2 << " - test for a custom type at begin of the stream"; // Print our type to the log stream. return 0; } plog-1.1.11/samples/DebugOutput/000077500000000000000000000000001504622126500164525ustar00rootroot00000000000000plog-1.1.11/samples/DebugOutput/CMakeLists.txt000066400000000000000000000002471504622126500212150ustar00rootroot00000000000000if(WIN32) add_executable(DebugOutput Main.cpp) target_link_libraries(DebugOutput plog) set_target_properties(DebugOutput PROPERTIES FOLDER Samples) endif()plog-1.1.11/samples/DebugOutput/Main.cpp000066400000000000000000000010441504622126500200410ustar00rootroot00000000000000// // DebugOutput - shows how to use DebugOutputAppender to write to the windows debug output. // #include #include #include #include int main() { static plog::DebugOutputAppender debugOutputAppender; plog::init(plog::verbose, &debugOutputAppender); PLOGD << "Hello log!"; // short macro PLOG_DEBUG << "Hello log!"; // long macro PLOG(plog::debug) << "Hello log!"; // function-style macro return 0; } plog-1.1.11/samples/Demo/000077500000000000000000000000001504622126500150675ustar00rootroot00000000000000plog-1.1.11/samples/Demo/CMakeLists.txt000066400000000000000000000017661504622126500176410ustar00rootroot00000000000000add_executable(Demo Main.cpp MyClass.h MyClass.cpp Customer.h) target_link_libraries(Demo plog) set_target_properties(Demo PROPERTIES FOLDER Samples) if(MSVC AND NOT (MSVC_VERSION LESS 1700)) # Visual Studio 2012 and higher add_executable(DemoManaged Main.cpp MyClass.h MyClass.cpp Customer.h) target_link_libraries(DemoManaged plog) set_property(TARGET DemoManaged PROPERTY COMPILE_FLAGS "/EHa") set_property(TARGET DemoManaged PROPERTY COMMON_LANGUAGE_RUNTIME "") set_target_properties(DemoManaged PROPERTIES FOLDER Samples CXX_STANDARD 17 CXX_STANDARD_REQUIRED OFF) # the latest standard supported by CLR is C++17 endif() if(NOT WIN32) add_executable(DemoWchar Main.cpp MyClass.h MyClass.cpp Customer.h) target_link_libraries(DemoWchar plog) set_target_properties(DemoWchar PROPERTIES COMPILE_FLAGS "-DPLOG_ENABLE_WCHAR_INPUT=1") set_target_properties(DemoWchar PROPERTIES FOLDER Samples) if(APPLE) target_link_libraries(DemoWchar -liconv) endif() endif() plog-1.1.11/samples/Demo/Customer.h000066400000000000000000000004151504622126500170410ustar00rootroot00000000000000#pragma once #include #include struct Customer { int id; std::string name; }; inline std::ostream& operator<<(std::ostream& os, const Customer& obj) { os << "Customer (id: " << obj.id << ", name: " << obj.name << ")"; return os; } plog-1.1.11/samples/Demo/Main.cpp000066400000000000000000000140051504622126500164570ustar00rootroot00000000000000// // Demo - demonstrates log stream abilities, prints various types of messages. // #include #include #include #include #include #include #include #include #include #include "MyClass.h" #include "Customer.h" #ifdef __cplusplus_cli #pragma managed(push, off) void unmanagedFunc() { PLOGI << "Inside unmanaged function (char)"; PLOGI << L"Inside unmanaged function (wchar_t)"; } #pragma managed(pop) #endif int main() { plog::init(plog::debug, "Demo.csv", 5000, 3); // Initialize logging to the file. plog::ColorConsoleAppender consoleAppender; plog::get()->addAppender(&consoleAppender); // Also add logging to the console. // Log macro types. PLOGD << "Hello log!"; // short macro PLOG_DEBUG << "Hello log!"; // long macro PLOG(plog::debug) << "Hello log!"; // function-style macro // Log severity levels. PLOG_VERBOSE << "This is a VERBOSE message"; PLOG_DEBUG << "This is a DEBUG message"; PLOG_INFO << "This is an INFO message"; PLOG_WARNING << "This is a WARNING message"; PLOG_ERROR << "This is an ERROR message"; PLOG_FATAL << "This is a FATAL message"; PLOG_NONE << "This is a NONE message"; // Integers demo. PLOG_INFO << "This is a bool: " << std::boolalpha << true; PLOG_INFO << "This is a char: " << 'x'; PLOG_INFO << "This is an unsigned char: " << (unsigned char)40; PLOG_INFO << "This is a short: " << (short)-1000; PLOG_INFO << "This is an unsigned short: " << (unsigned short)1000; PLOG_INFO << "This is an int: " << (int)-1000000; PLOG_INFO << "This is an unsigned int: " << (unsigned int)1000000; PLOG_INFO << "This is a long(hex): " << std::hex << (long)100000000; PLOG_INFO << "This is an unsigned long: " << (unsigned long)100000000; PLOG_INFO << "This is a float: " << 1.2345f; PLOG_INFO << "This is a double: " << std::setprecision(15) << 1.234512345; #ifndef __cplusplus_cli PLOG_INFO.printf("This is a format %s %d", "message", 42); #ifdef _WIN32 PLOG_INFO.printf(L"This is a wide format %s %d", L"message", 42); #endif #endif //__cplusplus_cli // Managed string. #ifdef __cplusplus_cli System::String^ managedStr = "This is a managed string"; PLOG_INFO << managedStr; unmanagedFunc(); #endif // Null strings are safe. PLOG_DEBUG << static_cast(NULL); PLOG_DEBUG << static_cast(NULL); #if PLOG_ENABLE_WCHAR_INPUT PLOG_DEBUG << static_cast(NULL); PLOG_DEBUG << static_cast(NULL); #endif // Plog handles unicode and std::string/wstring. #ifndef _WIN32 // On Windows the following code produces a warning C4566 if the codepage is not Cyrillic. PLOG_DEBUG << "test - тест"; PLOG_DEBUG << std::string("test - тест"); #endif #if PLOG_ENABLE_WCHAR_INPUT PLOG_DEBUG << L"test - тест"; PLOG_DEBUG << std::wstring(L"test - тест"); PLOG_DEBUG << L'ж'; #endif #ifdef __cpp_char8_t PLOG_DEBUG << u8"Chinese: 中文"; PLOG_DEBUG << const_cast(u8"Cyrillic: тест"); #endif // Multiline. PLOG_INFO << "This\nis\na" << std::endl << "multiline\nmessage!"; // Quotes. PLOG_INFO << "This is a message with \"quotes\"!"; // Conditional logging. int var = 0; PLOG_DEBUG_IF(var != 0) << "You shouldn't see this message"; PLOG_DEBUG_IF(var == 0) << "This is a conditional log message"; // Executed only on log level >= debug. IF_PLOG(plog::debug) var = 5; // one line IF_PLOG(plog::debug) // block { var++; } // Log macros don't break then-else clause without braces. if (var == 0) PLOGI << "then clause (condition is false, so it is skipped)"; else PLOGI << "else clase (should be visible)"; // Log in a class (capture this pointer, c++ function names). MyClass obj; obj.method(); obj.inlineMethod(); MyClass::staticMethod(); // Log in a template class. MyTemplateClass(1, 2).inlineMethod(); // Implicit cast to string. PLOG_INFO << obj; // ostream operator<< (on Windows wostream operator<< has priority but not required) Customer customer = { 10, "John" }; PLOG_INFO << customer; // Std containers can be printed std::vector vectorOfInts; vectorOfInts.push_back(1); vectorOfInts.push_back(2); vectorOfInts.push_back(3); PLOG_INFO << "std::vector: " << vectorOfInts; std::deque dequeOfStrings; dequeOfStrings.push_back("one"); dequeOfStrings.push_back("two"); dequeOfStrings.push_back("three"); PLOG_INFO << "std::deque: " << dequeOfStrings; std::list listOfCharPointers; listOfCharPointers.push_back("one"); listOfCharPointers.push_back("two"); listOfCharPointers.push_back(NULL); PLOG_INFO << "std::list: " << listOfCharPointers; std::set setOfInts; setOfInts.insert(10); setOfInts.insert(20); setOfInts.insert(30); PLOG_INFO << "std::set: " << setOfInts; std::map mapStringToInt; mapStringToInt["red"] = 1; mapStringToInt["green"] = 2; mapStringToInt["blue"] = 4; PLOG_INFO << "std::map: " << mapStringToInt; std::multimap multimapIntToString; multimapIntToString.insert(std::make_pair(1, "one")); multimapIntToString.insert(std::make_pair(1, "uno")); multimapIntToString.insert(std::make_pair(2, "two")); multimapIntToString.insert(std::make_pair(2, "due")); PLOG_INFO << "std::multimap: " << multimapIntToString; std::vector > vectorOfVectorsOfInts(3); vectorOfVectorsOfInts[0].push_back(1); vectorOfVectorsOfInts[0].push_back(2); vectorOfVectorsOfInts[1].push_back(-1); vectorOfVectorsOfInts[1].push_back(-2); PLOG_INFO << "std::vector >: " << vectorOfVectorsOfInts; return 0; } plog-1.1.11/samples/Demo/MyClass.cpp000066400000000000000000000004321504622126500171450ustar00rootroot00000000000000#include "MyClass.h" MyClass::MyClass() { PLOGD; } MyClass::~MyClass() { PLOGD; } void MyClass::method() { PLOGD; } void MyClass::staticMethod() { PLOGD; } MyClass::operator std::string() const { return std::string("This is an implicit cast to string."); } plog-1.1.11/samples/Demo/MyClass.h000066400000000000000000000007371504622126500166220ustar00rootroot00000000000000#pragma once #include class MyClass { public: MyClass(); ~MyClass(); void method(); void inlineMethod() { PLOGD; } static void staticMethod(); operator std::string() const; }; template class MyTemplateClass { public: MyTemplateClass(T t1, U t2) : m_t1(t1) , m_t2(t2) { } inline void inlineMethod() { PLOGD; } private: T m_t1; U m_t2; }; plog-1.1.11/samples/DisableLogging/000077500000000000000000000000001504622126500170555ustar00rootroot00000000000000plog-1.1.11/samples/DisableLogging/CMakeLists.txt000066400000000000000000000002231504622126500216120ustar00rootroot00000000000000add_executable(DisableLogging Main.cpp) target_link_libraries(DisableLogging plog) set_target_properties(DisableLogging PROPERTIES FOLDER Samples) plog-1.1.11/samples/DisableLogging/Main.cpp000066400000000000000000000014471504622126500204530ustar00rootroot00000000000000// // DisableLogging - shows how to disable logging (so it will be stripped from the binary). // #define PLOG_DISABLE_LOGGING #include #include #include #include int main() { #ifndef PLOG_DISABLE_LOGGING static plog::ColorConsoleAppender consoleAppender; plog::init(plog::verbose, &consoleAppender); #endif // The following code will be stripped from the binary by optimizer. PLOG_VERBOSE << "This is a VERBOSE message"; PLOG_DEBUG << "This is a DEBUG message"; PLOG_INFO << "This is an INFO message"; PLOG_WARNING << "This is a WARNING message"; PLOG_ERROR << "This is an ERROR message"; PLOG_FATAL << "This is a FATAL message"; return 0; } plog-1.1.11/samples/DynamicAppender/000077500000000000000000000000001504622126500172465ustar00rootroot00000000000000plog-1.1.11/samples/DynamicAppender/CMakeLists.txt000066400000000000000000000002261504622126500220060ustar00rootroot00000000000000add_executable(DynamicAppender Main.cpp) target_link_libraries(DynamicAppender plog) set_target_properties(DynamicAppender PROPERTIES FOLDER Samples) plog-1.1.11/samples/DynamicAppender/Main.cpp000066400000000000000000000014231504622126500206360ustar00rootroot00000000000000// // DynamicAppender - shows how to add/remove appenders dynamically. // #include #include #include #include #include int main() { static plog::DynamicAppender dynamicAppender; plog::init(plog::verbose, &dynamicAppender); PLOGW << "This message goes nowhere as no real appenders exist"; { plog::ColorConsoleAppender consoleAppender; dynamicAppender.addAppender(&consoleAppender); PLOGI << "Message from a dynamically added appender"; dynamicAppender.removeAppender(&consoleAppender); } PLOGW << "This message goes nowhere as no real appenders exist"; return 0; } plog-1.1.11/samples/EventLog/000077500000000000000000000000001504622126500157265ustar00rootroot00000000000000plog-1.1.11/samples/EventLog/CMakeLists.txt000066400000000000000000000002361504622126500204670ustar00rootroot00000000000000if(WIN32) add_executable(EventLog Main.cpp) target_link_libraries(EventLog plog) set_target_properties(EventLog PROPERTIES FOLDER Samples) endif()plog-1.1.11/samples/EventLog/Main.cpp000066400000000000000000000034021504622126500173150ustar00rootroot00000000000000// // EventLog - shows how to use EventLogAppender to write to the windows event log. // #include #include #include #include #include using namespace std; int main(int argc, char* argv[]) { const wchar_t kEventSourceName[] = L"EventLogSample"; if (argc == 2) { // // Note: register/unregister operations require admin rights. // if (0 == strcmp(argv[1], "--register")) { if (!plog::EventLogAppenderRegistry::add(kEventSourceName)) { cerr << "Failed to register eventlog source." << endl; return -1; } cout << "Successfully registered eventlog source." << endl; } else if (0 == strcmp(argv[1], "--unregister")) { plog::EventLogAppenderRegistry::remove(kEventSourceName); cout << "Successfully unregistered eventlog source." << endl; } else if (0 == strcmp(argv[1], "--query")) { cout << "Eventlog source exists: " << plog::EventLogAppenderRegistry::exists(kEventSourceName) << endl; } return 0; } // // Note: eventlog source must be registered prior to creating its appender. // static plog::EventLogAppender eventLogAppender(kEventSourceName); plog::init(plog::verbose, &eventLogAppender); PLOG_VERBOSE << "This is a VERBOSE message"; PLOG_DEBUG << "This is a DEBUG message"; PLOG_INFO << "This is an INFO message"; PLOG_WARNING << "This is a WARNING message"; PLOG_ERROR << "This is an ERROR message"; PLOG_FATAL << "This is a FATAL message"; return 0; } plog-1.1.11/samples/Facilities/000077500000000000000000000000001504622126500162575ustar00rootroot00000000000000plog-1.1.11/samples/Facilities/CMakeLists.txt000066400000000000000000000002061504622126500210150ustar00rootroot00000000000000add_executable(Facilities Main.cpp) target_link_libraries(Facilities plog) set_target_properties(Facilities PROPERTIES FOLDER Samples)plog-1.1.11/samples/Facilities/Main.cpp000066400000000000000000000020651504622126500176520ustar00rootroot00000000000000// // Facilities - shows how to use logging per facilities via multiple logger instances (useful for big projects). // #include #include enum Facility // Define log facilities. { Default, // The default is 0. Auth, FileIO, Sink = -1 // This is a log sink. Messages from other facilities go there. }; int main() { plog::init(plog::debug, "Facility.csv"); // Initialize the sink logger. // Initialize all other loggers and set the sink logger as an appender. Each of the loggers can have their own severity level. plog::init(plog::debug, plog::get()); plog::init(plog::warning, plog::get()); plog::init(plog::info, plog::get()); PLOGD_(Default) << "This is a message from the Default facility"; PLOGD << "This is a message from the Default facility too because Default = 0"; PLOGW_(Auth) << "This is a message from the Auth facility"; PLOGI_(FileIO) << "This is a message from the FileIO facility"; return 0; } plog-1.1.11/samples/FreeRTOS/000077500000000000000000000000001504622126500155745ustar00rootroot00000000000000plog-1.1.11/samples/FreeRTOS/CMakeLists.txt000066400000000000000000000024061504622126500203360ustar00rootroot00000000000000if(CMAKE_SYSTEM_NAME STREQUAL "Linux") if(POLICY CMP0135) # Controls timestamps of the extracted contents (DOWNLOAD_EXTRACT_TIMESTAMP) cmake_policy(SET CMP0135 NEW) endif() # This sample requires GCC 6.0 or later if(NOT CMAKE_COMPILER_IS_GNUCXX OR CMAKE_CXX_COMPILER_VERSION VERSION_LESS 6.0) return() endif() include(FetchContent) # Select FreeRTOS port set(FREERTOS_PORT "GCC_POSIX") # Select FreeRTOS heap set(FREERTOS_HEAP "4") # Add the freertos_config for FreeRTOS-Kernel add_subdirectory(freertos_config) # Download FreeRTOS-Kernel sources FetchContent_Declare( freertos_kernel URL https://github.com/FreeRTOS/FreeRTOS-Kernel/archive/refs/tags/V11.1.0.tar.gz URL_HASH SHA256=0e21928b3bcc4f9bcaf7333fb1c8c0299d97e2ec9e13e3faa2c5a7ac8a3bc573 ) FetchContent_MakeAvailable(freertos_kernel) # Describe our executable add_executable(FreeRTOS main.cpp) target_link_libraries(FreeRTOS freertos_kernel freertos_config plog::plog) # Important!!! There is no standard way to know if the code is compiled for FreeRTOS. So we define __FREERTOS__ macro. target_compile_definitions(FreeRTOS PUBLIC __FREERTOS__) endif() plog-1.1.11/samples/FreeRTOS/freertos_config/000077500000000000000000000000001504622126500207525ustar00rootroot00000000000000plog-1.1.11/samples/FreeRTOS/freertos_config/CMakeLists.txt000066400000000000000000000001371504622126500235130ustar00rootroot00000000000000add_library(freertos_config INTERFACE) target_include_directories(freertos_config INTERFACE .) plog-1.1.11/samples/FreeRTOS/freertos_config/FreeRTOSConfig.h000066400000000000000000001063251504622126500236510ustar00rootroot00000000000000/* * FreeRTOS Kernel * Copyright (C) 2021 Amazon.com, Inc. or its affiliates. All Rights Reserved. * * SPDX-License-Identifier: MIT * * Permission is hereby granted, free of charge, to any person obtaining a copy * of this software and associated documentation files (the "Software"), to deal * in the Software without restriction, including without limitation the rights * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell * copies of the Software, and to permit persons to whom the Software is * furnished to do so, subject to the following conditions: * * The above copyright notice and this permission notice shall be included in * all copies or substantial portions of the Software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE * SOFTWARE. * * https://www.FreeRTOS.org * https://github.com/FreeRTOS * */ /******************************************************************************* * This file provides an example FreeRTOSConfig.h header file, inclusive of an * abbreviated explanation of each configuration item. Online and reference * documentation provides more information. * https://www.freertos.org/a00110.html * * Constant values enclosed in square brackets ('[' and ']') must be completed * before this file will build. * * Use the FreeRTOSConfig.h supplied with the RTOS port in use rather than this * generic file, if one is available. ******************************************************************************/ #ifndef FREERTOS_CONFIG_H #define FREERTOS_CONFIG_H /******************************************************************************/ /* Hardware description related definitions. **********************************/ /******************************************************************************/ /* In most cases, configCPU_CLOCK_HZ must be set to the frequency of the clock * that drives the peripheral used to generate the kernels periodic tick * interrupt. The default value is set to 20MHz and matches the QEMU demo * settings. Your application will certainly need a different value so set this * correctly. This is very often, but not always, equal to the main system clock * frequency. */ #define configCPU_CLOCK_HZ ( ( unsigned long ) 20000000 ) /* configSYSTICK_CLOCK_HZ is an optional parameter for ARM Cortex-M ports only. * * By default ARM Cortex-M ports generate the RTOS tick interrupt from the * Cortex-M SysTick timer. Most Cortex-M MCUs run the SysTick timer at the same * frequency as the MCU itself - when that is the case configSYSTICK_CLOCK_HZ is * not needed and should be left undefined. If the SysTick timer is clocked at a * different frequency to the MCU core then set configCPU_CLOCK_HZ to the MCU * clock frequency, as normal, and configSYSTICK_CLOCK_HZ to the SysTick clock * frequency. Not used if left undefined. * The default value is undefined (commented out). If you need this value bring * it back and set it to a suitable value. */ /* #define configSYSTICK_CLOCK_HZ [Platform specific] */ /******************************************************************************/ /* Scheduling behaviour related definitions. **********************************/ /******************************************************************************/ /* configTICK_RATE_HZ sets frequency of the tick interrupt in Hz, normally * calculated from the configCPU_CLOCK_HZ value. */ #define configTICK_RATE_HZ 100 /* Set configUSE_PREEMPTION to 1 to use pre-emptive scheduling. Set * configUSE_PREEMPTION to 0 to use co-operative scheduling. * See https://www.freertos.org/single-core-amp-smp-rtos-scheduling.html. */ #define configUSE_PREEMPTION 1 /* Set configUSE_TIME_SLICING to 1 to have the scheduler switch between Ready * state tasks of equal priority on every tick interrupt. Set * configUSE_TIME_SLICING to 0 to prevent the scheduler switching between Ready * state tasks just because there was a tick interrupt. See * https://freertos.org/single-core-amp-smp-rtos-scheduling.html. */ #define configUSE_TIME_SLICING 0 /* Set configUSE_PORT_OPTIMISED_TASK_SELECTION to 1 to select the next task to * run using an algorithm optimised to the instruction set of the target * hardware - normally using a count leading zeros assembly instruction. Set to * 0 to select the next task to run using a generic C algorithm that works for * all FreeRTOS ports. Not all FreeRTOS ports have this option. Defaults to 0 * if left undefined. */ #define configUSE_PORT_OPTIMISED_TASK_SELECTION 0 /* Set configUSE_TICKLESS_IDLE to 1 to use the low power tickless mode. Set to * 0 to keep the tick interrupt running at all times. Not all FreeRTOS ports * support tickless mode. See * https://www.freertos.org/low-power-tickless-rtos.html Defaults to 0 if left * undefined. */ #define configUSE_TICKLESS_IDLE 0 /* configMAX_PRIORITIES Sets the number of available task priorities. Tasks can * be assigned priorities of 0 to (configMAX_PRIORITIES - 1). Zero is the * lowest priority. */ #define configMAX_PRIORITIES 5 /* configMINIMAL_STACK_SIZE defines the size of the stack used by the Idle task * (in words, not in bytes!). The kernel does not use this constant for any * other purpose. Demo applications use the constant to make the demos somewhat * portable across hardware architectures. */ #define configMINIMAL_STACK_SIZE 128 /* configMAX_TASK_NAME_LEN sets the maximum length (in characters) of a task's * human readable name. Includes the NULL terminator. */ #define configMAX_TASK_NAME_LEN 16 /* Time is measured in 'ticks' - which is the number of times the tick interrupt * has executed since the RTOS kernel was started. * The tick count is held in a variable of type TickType_t. * * configTICK_TYPE_WIDTH_IN_BITS controls the type (and therefore bit-width) of * TickType_t: * * Defining configTICK_TYPE_WIDTH_IN_BITS as TICK_TYPE_WIDTH_16_BITS causes * TickType_t to be defined (typedef'ed) as an unsigned 16-bit type. * * Defining configTICK_TYPE_WIDTH_IN_BITS as TICK_TYPE_WIDTH_32_BITS causes * TickType_t to be defined (typedef'ed) as an unsigned 32-bit type. * * Defining configTICK_TYPE_WIDTH_IN_BITS as TICK_TYPE_WIDTH_64_BITS causes * TickType_t to be defined (typedef'ed) as an unsigned 64-bit type. */ #define configTICK_TYPE_WIDTH_IN_BITS TICK_TYPE_WIDTH_64_BITS /* Set configIDLE_SHOULD_YIELD to 1 to have the Idle task yield to an * application task if there is an Idle priority (priority 0) application task * that can run. Set to 0 to have the Idle task use all of its timeslice. * Default to 1 if left undefined. */ #define configIDLE_SHOULD_YIELD 1 /* Each task has an array of task notifications. * configTASK_NOTIFICATION_ARRAY_ENTRIES sets the number of indexes in the * array. See https://www.freertos.org/RTOS-task-notifications.html Defaults to * 1 if left undefined. */ #define configTASK_NOTIFICATION_ARRAY_ENTRIES 1 /* configQUEUE_REGISTRY_SIZE sets the maximum number of queues and semaphores * that can be referenced from the queue registry. Only required when using a * kernel aware debugger. Defaults to 0 if left undefined. */ #define configQUEUE_REGISTRY_SIZE 0 /* Set configENABLE_BACKWARD_COMPATIBILITY to 1 to map function names and * datatypes from old version of FreeRTOS to their latest equivalent. Defaults * to 1 if left undefined. */ #define configENABLE_BACKWARD_COMPATIBILITY 0 /* Each task has its own array of pointers that can be used as thread local * storage. configNUM_THREAD_LOCAL_STORAGE_POINTERS set the number of indexes * in the array. See * https://www.freertos.org/thread-local-storage-pointers.html Defaults to 0 if * left undefined. */ #define configNUM_THREAD_LOCAL_STORAGE_POINTERS 0 /* When configUSE_MINI_LIST_ITEM is set to 0, MiniListItem_t and ListItem_t are * both the same. When configUSE_MINI_LIST_ITEM is set to 1, MiniListItem_t * contains 3 fewer fields than ListItem_t which saves some RAM at the cost of * violating strict aliasing rules which some compilers depend on for * optimization. Defaults to 1 if left undefined. */ #define configUSE_MINI_LIST_ITEM 1 /* Sets the type used by the parameter to xTaskCreate() that specifies the stack * size of the task being created. The same type is used to return information * about stack usage in various other API calls. Defaults to size_t if left * undefined. */ #define configSTACK_DEPTH_TYPE size_t /* configMESSAGE_BUFFER_LENGTH_TYPE sets the type used to store the length of * each message written to a FreeRTOS message buffer (the length is also written * to the message buffer. Defaults to size_t if left undefined - but that may * waste space if messages never go above a length that could be held in a * uint8_t. */ #define configMESSAGE_BUFFER_LENGTH_TYPE size_t /* If configHEAP_CLEAR_MEMORY_ON_FREE is set to 1, then blocks of memory * allocated using pvPortMalloc() will be cleared (i.e. set to zero) when freed * using vPortFree(). Defaults to 0 if left undefined. */ #define configHEAP_CLEAR_MEMORY_ON_FREE 1 /* vTaskList and vTaskGetRunTimeStats APIs take a buffer as a parameter and * assume that the length of the buffer is configSTATS_BUFFER_MAX_LENGTH. * Defaults to 0xFFFF if left undefined. New applications are recommended to use * vTaskListTasks and vTaskGetRunTimeStatistics APIs instead and supply the * length of the buffer explicitly to avoid memory corruption. */ #define configSTATS_BUFFER_MAX_LENGTH 0xFFFF /* Set configUSE_NEWLIB_REENTRANT to 1 to have a newlib reent structure * allocated for each task. Set to 0 to not support newlib reent structures. * Default to 0 if left undefined. * * Note Newlib support has been included by popular demand, but is not used or * tested by the FreeRTOS maintainers themselves. FreeRTOS is not responsible * for resulting newlib operation. User must be familiar with newlib and must * provide system-wide implementations of the necessary stubs. Note that (at the * time of writing) the current newlib design implements a system-wide malloc() * that must be provided with locks. */ #define configUSE_NEWLIB_REENTRANT 0 /******************************************************************************/ /* Software timer related definitions. ****************************************/ /******************************************************************************/ /* Set configUSE_TIMERS to 1 to include software timer functionality in the * build. Set to 0 to exclude software timer functionality from the build. The * FreeRTOS/source/timers.c source file must be included in the build if * configUSE_TIMERS is set to 1. Default to 0 if left undefined. See * https://www.freertos.org/RTOS-software-timer.html. */ #define configUSE_TIMERS 1 /* configTIMER_TASK_PRIORITY sets the priority used by the timer task. Only * used if configUSE_TIMERS is set to 1. The timer task is a standard FreeRTOS * task, so its priority is set like any other task. See * https://www.freertos.org/RTOS-software-timer-service-daemon-task.html Only * used if configUSE_TIMERS is set to 1. */ #define configTIMER_TASK_PRIORITY ( configMAX_PRIORITIES - 1 ) /* configTIMER_TASK_STACK_DEPTH sets the size of the stack allocated to the * timer task (in words, not in bytes!). The timer task is a standard FreeRTOS * task. See * https://www.freertos.org/RTOS-software-timer-service-daemon-task.html Only * used if configUSE_TIMERS is set to 1. */ #define configTIMER_TASK_STACK_DEPTH configMINIMAL_STACK_SIZE /* configTIMER_QUEUE_LENGTH sets the length of the queue (the number of discrete * items the queue can hold) used to send commands to the timer task. See * https://www.freertos.org/RTOS-software-timer-service-daemon-task.html Only * used if configUSE_TIMERS is set to 1. */ #define configTIMER_QUEUE_LENGTH 10 /******************************************************************************/ /* Event Group related definitions. *******************************************/ /******************************************************************************/ /* Set configUSE_EVENT_GROUPS to 1 to include event group functionality in the * build. Set to 0 to exclude event group functionality from the build. The * FreeRTOS/source/event_groups.c source file must be included in the build if * configUSE_EVENT_GROUPS is set to 1. Defaults to 1 if left undefined. */ #define configUSE_EVENT_GROUPS 1 /******************************************************************************/ /* Stream Buffer related definitions. *****************************************/ /******************************************************************************/ /* Set configUSE_STREAM_BUFFERS to 1 to include stream buffer functionality in * the build. Set to 0 to exclude event group functionality from the build. The * FreeRTOS/source/stream_buffer.c source file must be included in the build if * configUSE_STREAM_BUFFERS is set to 1. Defaults to 1 if left undefined. */ #define configUSE_STREAM_BUFFERS 1 /******************************************************************************/ /* Memory allocation related definitions. *************************************/ /******************************************************************************/ /* Set configSUPPORT_STATIC_ALLOCATION to 1 to include FreeRTOS API functions * that create FreeRTOS objects (tasks, queues, etc.) using statically allocated * memory in the build. Set to 0 to exclude the ability to create statically * allocated objects from the build. Defaults to 0 if left undefined. See * https://www.freertos.org/Static_Vs_Dynamic_Memory_Allocation.html. */ #define configSUPPORT_STATIC_ALLOCATION 1 /* Set configSUPPORT_DYNAMIC_ALLOCATION to 1 to include FreeRTOS API functions * that create FreeRTOS objects (tasks, queues, etc.) using dynamically * allocated memory in the build. Set to 0 to exclude the ability to create * dynamically allocated objects from the build. Defaults to 1 if left * undefined. See * https://www.freertos.org/Static_Vs_Dynamic_Memory_Allocation.html. */ #define configSUPPORT_DYNAMIC_ALLOCATION 1 /* Sets the total size of the FreeRTOS heap, in bytes, when heap_1.c, heap_2.c * or heap_4.c are included in the build. This value is defaulted to 4096 bytes * but it must be tailored to each application. Note the heap will appear in * the .bss section. See https://www.freertos.org/a00111.html. */ #define configTOTAL_HEAP_SIZE 4096 /* Set configAPPLICATION_ALLOCATED_HEAP to 1 to have the application allocate * the array used as the FreeRTOS heap. Set to 0 to have the linker allocate * the array used as the FreeRTOS heap. Defaults to 0 if left undefined. */ #define configAPPLICATION_ALLOCATED_HEAP 0 /* Set configSTACK_ALLOCATION_FROM_SEPARATE_HEAP to 1 to have task stacks * allocated from somewhere other than the FreeRTOS heap. This is useful if you * want to ensure stacks are held in fast memory. Set to 0 to have task stacks * come from the standard FreeRTOS heap. The application writer must provide * implementations for pvPortMallocStack() and vPortFreeStack() if set to 1. * Defaults to 0 if left undefined. */ #define configSTACK_ALLOCATION_FROM_SEPARATE_HEAP 0 /* Set configENABLE_HEAP_PROTECTOR to 1 to enable bounds checking and * obfuscation to internal heap block pointers in heap_4.c and heap_5.c to help * catch pointer corruptions. Defaults to 0 if left undefined. */ #define configENABLE_HEAP_PROTECTOR 0 /******************************************************************************/ /* Interrupt nesting behaviour configuration. *********************************/ /******************************************************************************/ /* configKERNEL_INTERRUPT_PRIORITY sets the priority of the tick and context * switch performing interrupts. Not supported by all FreeRTOS ports. See * https://www.freertos.org/RTOS-Cortex-M3-M4.html for information specific to * ARM Cortex-M devices. */ #define configKERNEL_INTERRUPT_PRIORITY 0 /* configMAX_SYSCALL_INTERRUPT_PRIORITY sets the interrupt priority above which * FreeRTOS API calls must not be made. Interrupts above this priority are * never disabled, so never delayed by RTOS activity. The default value is set * to the highest interrupt priority (0). Not supported by all FreeRTOS ports. * See https://www.freertos.org/RTOS-Cortex-M3-M4.html for information specific * to ARM Cortex-M devices. */ #define configMAX_SYSCALL_INTERRUPT_PRIORITY 0 /* Another name for configMAX_SYSCALL_INTERRUPT_PRIORITY - the name used depends * on the FreeRTOS port. */ #define configMAX_API_CALL_INTERRUPT_PRIORITY 0 /******************************************************************************/ /* Hook and callback function related definitions. ****************************/ /******************************************************************************/ /* Set the following configUSE_* constants to 1 to include the named hook * functionality in the build. Set to 0 to exclude the hook functionality from * the build. The application writer is responsible for providing the hook * function for any set to 1. See https://www.freertos.org/a00016.html. */ #define configUSE_IDLE_HOOK 0 #define configUSE_TICK_HOOK 0 #define configUSE_MALLOC_FAILED_HOOK 0 #define configUSE_DAEMON_TASK_STARTUP_HOOK 0 /* Set configUSE_SB_COMPLETED_CALLBACK to 1 to have send and receive completed * callbacks for each instance of a stream buffer or message buffer. When the * option is set to 1, APIs xStreamBufferCreateWithCallback() and * xStreamBufferCreateStaticWithCallback() (and likewise APIs for message * buffer) can be used to create a stream buffer or message buffer instance * with application provided callbacks. Defaults to 0 if left undefined. */ #define configUSE_SB_COMPLETED_CALLBACK 0 /* Set configCHECK_FOR_STACK_OVERFLOW to 1 or 2 for FreeRTOS to check for a * stack overflow at the time of a context switch. Set to 0 to not look for a * stack overflow. If configCHECK_FOR_STACK_OVERFLOW is 1 then the check only * looks for the stack pointer being out of bounds when a task's context is * saved to its stack - this is fast but somewhat ineffective. If * configCHECK_FOR_STACK_OVERFLOW is 2 then the check looks for a pattern * written to the end of a task's stack having been overwritten. This is * slower, but will catch most (but not all) stack overflows. The application * writer must provide the stack overflow callback when * configCHECK_FOR_STACK_OVERFLOW is set to 1. See * https://www.freertos.org/Stacks-and-stack-overflow-checking.html Defaults to * 0 if left undefined. */ #define configCHECK_FOR_STACK_OVERFLOW 0 /******************************************************************************/ /* Run time and task stats gathering related definitions. *********************/ /******************************************************************************/ /* Set configGENERATE_RUN_TIME_STATS to 1 to have FreeRTOS collect data on the * processing time used by each task. Set to 0 to not collect the data. The * application writer needs to provide a clock source if set to 1. Defaults to * 0 if left undefined. See https://www.freertos.org/rtos-run-time-stats.html. */ #define configGENERATE_RUN_TIME_STATS 0 /* Set configUSE_TRACE_FACILITY to include additional task structure members * are used by trace and visualisation functions and tools. Set to 0 to exclude * the additional information from the structures. Defaults to 0 if left * undefined. */ #define configUSE_TRACE_FACILITY 0 /* Set to 1 to include the vTaskList() and vTaskGetRunTimeStats() functions in * the build. Set to 0 to exclude these functions from the build. These two * functions introduce a dependency on string formatting functions that would * otherwise not exist - hence they are kept separate. Defaults to 0 if left * undefined. */ #define configUSE_STATS_FORMATTING_FUNCTIONS 0 /******************************************************************************/ /* Co-routine related definitions. ********************************************/ /******************************************************************************/ /* Set configUSE_CO_ROUTINES to 1 to include co-routine functionality in the * build, or 0 to omit co-routine functionality from the build. To include * co-routines, croutine.c must be included in the project. Defaults to 0 if * left undefined. */ #define configUSE_CO_ROUTINES 0 /* configMAX_CO_ROUTINE_PRIORITIES defines the number of priorities available * to the application co-routines. Any number of co-routines can share the same * priority. Defaults to 0 if left undefined. */ #define configMAX_CO_ROUTINE_PRIORITIES 1 /******************************************************************************/ /* Debugging assistance. ******************************************************/ /******************************************************************************/ /* configASSERT() has the same semantics as the standard C assert(). It can * either be defined to take an action when the assertion fails, or not defined * at all (i.e. comment out or delete the definitions) to completely remove * assertions. configASSERT() can be defined to anything you want, for example * you can call a function if an assert fails that passes the filename and line * number of the failing assert (for example, "vAssertCalled( __FILE__, __LINE__ * )" or it can simple disable interrupts and sit in a loop to halt all * execution on the failing line for viewing in a debugger. */ #define configASSERT( x ) \ if( ( x ) == 0 ) \ { \ taskDISABLE_INTERRUPTS(); \ for( ; ; ) \ ; \ } /******************************************************************************/ /* FreeRTOS MPU specific definitions. *****************************************/ /******************************************************************************/ /* If configINCLUDE_APPLICATION_DEFINED_PRIVILEGED_FUNCTIONS is set to 1 then * the application writer can provide functions that execute in privileged mode. * See: * https://www.freertos.org/a00110.html#configINCLUDE_APPLICATION_DEFINED_PRIVILEGED_FUNCTIONS * Defaults to 0 if left undefined. Only used by the FreeRTOS Cortex-M MPU * ports, not the standard ARMv7-M Cortex-M port. */ #define configINCLUDE_APPLICATION_DEFINED_PRIVILEGED_FUNCTIONS 0 /* Set configTOTAL_MPU_REGIONS to the number of MPU regions implemented on your * target hardware. Normally 8 or 16. Only used by the FreeRTOS Cortex-M MPU * ports, not the standard ARMv7-M Cortex-M port. Defaults to 8 if left * undefined. */ #define configTOTAL_MPU_REGIONS 8 /* configTEX_S_C_B_FLASH allows application writers to override the default * values for the for TEX, Shareable (S), Cacheable (C) and Bufferable (B) bits * for the MPU region covering Flash. Defaults to 0x07UL (which means TEX=000, * S=1, C=1, B=1) if left undefined. Only used by the FreeRTOS Cortex-M MPU * ports, not the standard ARMv7-M Cortex-M port. */ #define configTEX_S_C_B_FLASH 0x07UL /* configTEX_S_C_B_SRAM allows application writers to override the default * values for the for TEX, Shareable (S), Cacheable (C) and Bufferable (B) bits * for the MPU region covering RAM. Defaults to 0x07UL (which means TEX=000, * S=1, C=1, B=1) if left undefined. Only used by the FreeRTOS Cortex-M MPU * ports, not the standard ARMv7-M Cortex-M port. */ #define configTEX_S_C_B_SRAM 0x07UL /* Set configENFORCE_SYSTEM_CALLS_FROM_KERNEL_ONLY to 0 to prevent any privilege * escalations originating from outside of the kernel code itself. Set to 1 to * allow application tasks to raise privilege. Defaults to 1 if left undefined. * Only used by the FreeRTOS Cortex-M MPU ports, not the standard ARMv7-M * Cortex-M port. */ #define configENFORCE_SYSTEM_CALLS_FROM_KERNEL_ONLY 1 /* Set configALLOW_UNPRIVILEGED_CRITICAL_SECTIONS to 1 to allow unprivileged * tasks enter critical sections (effectively mask interrupts). Set to 0 to * prevent unprivileged tasks entering critical sections. Defaults to 1 if left * undefined. Only used by the FreeRTOS Cortex-M MPU ports, not the standard * ARMv7-M Cortex-M port. */ #define configALLOW_UNPRIVILEGED_CRITICAL_SECTIONS 0 /* FreeRTOS Kernel version 10.6.0 introduced a new v2 MPU wrapper, namely * mpu_wrappers_v2.c. Set configUSE_MPU_WRAPPERS_V1 to 0 to use the new v2 MPU * wrapper. Set configUSE_MPU_WRAPPERS_V1 to 1 to use the old v1 MPU wrapper * (mpu_wrappers.c). Defaults to 0 if left undefined. */ #define configUSE_MPU_WRAPPERS_V1 0 /* When using the v2 MPU wrapper, set configPROTECTED_KERNEL_OBJECT_POOL_SIZE to * the total number of kernel objects, which includes tasks, queues, semaphores, * mutexes, event groups, timers, stream buffers and message buffers, in your * application. The application will not be able to have more than * configPROTECTED_KERNEL_OBJECT_POOL_SIZE kernel objects at any point of * time. */ #define configPROTECTED_KERNEL_OBJECT_POOL_SIZE 10 /* When using the v2 MPU wrapper, set configSYSTEM_CALL_STACK_SIZE to the size * of the system call stack in words. Each task has a statically allocated * memory buffer of this size which is used as the stack to execute system * calls. For example, if configSYSTEM_CALL_STACK_SIZE is defined as 128 and * there are 10 tasks in the application, the total amount of memory used for * system call stacks is 128 * 10 = 1280 words. */ #define configSYSTEM_CALL_STACK_SIZE 128 /* When using the v2 MPU wrapper, set configENABLE_ACCESS_CONTROL_LIST to 1 to * enable Access Control List (ACL) feature. When ACL is enabled, an * unprivileged task by default does not have access to any kernel object other * than itself. The application writer needs to explicitly grant the * unprivileged task access to the kernel objects it needs using the APIs * provided for the same. Defaults to 0 if left undefined. */ #define configENABLE_ACCESS_CONTROL_LIST 1 /******************************************************************************/ /* SMP( Symmetric MultiProcessing ) Specific Configuration definitions. *******/ /******************************************************************************/ /* Set configNUMBER_OF_CORES to the number of available processor cores. * Defaults to 1 if left undefined. */ /* #define configNUMBER_OF_CORES [Num of available cores] */ /* When using SMP (i.e. configNUMBER_OF_CORES is greater than one), set * configRUN_MULTIPLE_PRIORITIES to 0 to allow multiple tasks to run * simultaneously only if they do not have equal priority, thereby maintaining * the paradigm of a lower priority task never running if a higher priority task * is able to run. If configRUN_MULTIPLE_PRIORITIES is set to 1, multiple tasks * with different priorities may run simultaneously - so a higher and lower * priority task may run on different cores at the same time. */ #define configRUN_MULTIPLE_PRIORITIES 0 /* When using SMP (i.e. configNUMBER_OF_CORES is greater than one), set * configUSE_CORE_AFFINITY to 1 to enable core affinity feature. When core * affinity feature is enabled, the vTaskCoreAffinitySet and * vTaskCoreAffinityGet APIs can be used to set and retrieve which cores a task * can run on. If configUSE_CORE_AFFINITY is set to 0 then the FreeRTOS * scheduler is free to run any task on any available core. */ #define configUSE_CORE_AFFINITY 0 /* When using SMP with core affinity feature enabled, set * configTASK_DEFAULT_CORE_AFFINITY to change the default core affinity mask for * tasks created without an affinity mask specified. Setting the define to 1 * would make such tasks run on core 0 and setting it to (1 << * portGET_CORE_ID()) would make such tasks run on the current core. This config * value is useful, if swapping tasks between cores is not supported (e.g. * Tricore) or if legacy code should be controlled. Defaults to tskNO_AFFINITY * if left undefined. */ #define configTASK_DEFAULT_CORE_AFFINITY tskNO_AFFINITY /* When using SMP (i.e. configNUMBER_OF_CORES is greater than one), if * configUSE_TASK_PREEMPTION_DISABLE is set to 1, individual tasks can be set to * either pre-emptive or co-operative mode using the vTaskPreemptionDisable and * vTaskPreemptionEnable APIs. */ #define configUSE_TASK_PREEMPTION_DISABLE 0 /* When using SMP (i.e. configNUMBER_OF_CORES is greater than one), set * configUSE_PASSIVE_IDLE_HOOK to 1 to allow the application writer to use * the passive idle task hook to add background functionality without the * overhead of a separate task. Defaults to 0 if left undefined. */ #define configUSE_PASSIVE_IDLE_HOOK 0 /* When using SMP (i.e. configNUMBER_OF_CORES is greater than one), * configTIMER_SERVICE_TASK_CORE_AFFINITY allows the application writer to set * the core affinity of the RTOS Daemon/Timer Service task. Defaults to * tskNO_AFFINITY if left undefined. */ #define configTIMER_SERVICE_TASK_CORE_AFFINITY tskNO_AFFINITY /******************************************************************************/ /* ARMv8-M secure side port related definitions. ******************************/ /******************************************************************************/ /* secureconfigMAX_SECURE_CONTEXTS define the maximum number of tasks that can * call into the secure side of an ARMv8-M chip. Not used by any other ports. */ #define secureconfigMAX_SECURE_CONTEXTS 5 /* Defines the kernel provided implementation of * vApplicationGetIdleTaskMemory() and vApplicationGetTimerTaskMemory() * to provide the memory that is used by the Idle task and Timer task * respectively. The application can provide it's own implementation of * vApplicationGetIdleTaskMemory() and vApplicationGetTimerTaskMemory() by * setting configKERNEL_PROVIDED_STATIC_MEMORY to 0 or leaving it undefined. */ #define configKERNEL_PROVIDED_STATIC_MEMORY 1 /******************************************************************************/ /* ARMv8-M port Specific Configuration definitions. ***************************/ /******************************************************************************/ /* Set configENABLE_TRUSTZONE to 1 when running FreeRTOS on the non-secure side * to enable the TrustZone support in FreeRTOS ARMv8-M ports which allows the * non-secure FreeRTOS tasks to call the (non-secure callable) functions * exported from secure side. */ #define configENABLE_TRUSTZONE 1 /* If the application writer does not want to use TrustZone, but the hardware * does not support disabling TrustZone then the entire application (including * the FreeRTOS scheduler) can run on the secure side without ever branching to * the non-secure side. To do that, in addition to setting * configENABLE_TRUSTZONE to 0, also set configRUN_FREERTOS_SECURE_ONLY to 1. */ #define configRUN_FREERTOS_SECURE_ONLY 1 /* Set configENABLE_MPU to 1 to enable the Memory Protection Unit (MPU), or 0 * to leave the Memory Protection Unit disabled. */ #define configENABLE_MPU 1 /* Set configENABLE_FPU to 1 to enable the Floating Point Unit (FPU), or 0 * to leave the Floating Point Unit disabled. */ #define configENABLE_FPU 1 /* Set configENABLE_MVE to 1 to enable the M-Profile Vector Extension (MVE) * support, or 0 to leave the MVE support disabled. This option is only * applicable to Cortex-M55 and Cortex-M85 ports as M-Profile Vector Extension * (MVE) is available only on these architectures. configENABLE_MVE must be left * undefined, or defined to 0 for the Cortex-M23,Cortex-M33 and Cortex-M35P * ports. */ #define configENABLE_MVE 1 /******************************************************************************/ /* ARMv7-M and ARMv8-M port Specific Configuration definitions. ***************/ /******************************************************************************/ /* Set configCHECK_HANDLER_INSTALLATION to 1 to enable additional asserts to * verify that the application has correctly installed FreeRTOS interrupt * handlers. * * An application can install FreeRTOS interrupt handlers in one of the * following ways: * 1. Direct Routing - Install the functions vPortSVCHandler and * xPortPendSVHandler for SVC call and PendSV interrupts respectively. * 2. Indirect Routing - Install separate handlers for SVC call and PendSV * interrupts and route program control from those * handlers to vPortSVCHandler and xPortPendSVHandler functions. The * applications that use Indirect Routing must set * configCHECK_HANDLER_INSTALLATION to 0. * * Defaults to 1 if left undefined. */ #define configCHECK_HANDLER_INSTALLATION 1 /******************************************************************************/ /* Definitions that include or exclude functionality. *************************/ /******************************************************************************/ /* Set the following configUSE_* constants to 1 to include the named feature in * the build, or 0 to exclude the named feature from the build. */ #define configUSE_TASK_NOTIFICATIONS 1 #define configUSE_MUTEXES 1 #define configUSE_RECURSIVE_MUTEXES 1 #define configUSE_COUNTING_SEMAPHORES 1 #define configUSE_QUEUE_SETS 0 #define configUSE_APPLICATION_TASK_TAG 0 /* USE_POSIX_ERRNO enables the task global FreeRTOS_errno variable which will * contain the most recent error for that task. */ #define configUSE_POSIX_ERRNO 0 /* Set the following INCLUDE_* constants to 1 to include the named API function, * or 0 to exclude the named API function. Most linkers will remove unused * functions even when the constant is 1. */ #define INCLUDE_vTaskPrioritySet 1 #define INCLUDE_uxTaskPriorityGet 1 #define INCLUDE_vTaskDelete 1 #define INCLUDE_vTaskSuspend 1 #define INCLUDE_vTaskDelayUntil 1 #define INCLUDE_vTaskDelay 1 #define INCLUDE_xTaskGetSchedulerState 1 #define INCLUDE_xTaskGetCurrentTaskHandle 1 #define INCLUDE_uxTaskGetStackHighWaterMark 0 #define INCLUDE_xTaskGetIdleTaskHandle 0 #define INCLUDE_eTaskGetState 0 #define INCLUDE_xTimerPendFunctionCall 0 #define INCLUDE_xTaskAbortDelay 0 #define INCLUDE_xTaskGetHandle 0 #define INCLUDE_xTaskResumeFromISR 1 #endif /* FREERTOS_CONFIG_H */ plog-1.1.11/samples/FreeRTOS/main.cpp000066400000000000000000000017451504622126500172330ustar00rootroot00000000000000// FreeRTOS includes #include #include // PLOG includes #include #include #include #include static void exampleTask(void* /*parameter*/) { for (;;) { PLOGI << "tick"; vTaskDelay(1000 / portTICK_PERIOD_MS); } } int main() { static plog::ColorConsoleAppender appender; plog::init(plog::verbose, &appender); PLOG_VERBOSE << "verbose"; PLOG_DEBUG << "debug"; PLOG_INFO << "info"; PLOG_WARNING << "warning"; PLOG_ERROR << "error"; PLOG_FATAL << "fatal"; xTaskCreate(exampleTask, "example1", configMINIMAL_STACK_SIZE, NULL, configMAX_PRIORITIES - 1U, NULL); xTaskCreate(exampleTask, "example2", configMINIMAL_STACK_SIZE, NULL, configMAX_PRIORITIES - 1U, NULL); // Start the scheduler vTaskStartScheduler(); // Should not reach here for (;;) { } return 0; } plog-1.1.11/samples/Hello/000077500000000000000000000000001504622126500152465ustar00rootroot00000000000000plog-1.1.11/samples/Hello/CMakeLists.txt000066400000000000000000000001671504622126500200120ustar00rootroot00000000000000add_executable(Hello Main.cpp) target_link_libraries(Hello plog) set_target_properties(Hello PROPERTIES FOLDER Samples)plog-1.1.11/samples/Hello/Main.cpp000066400000000000000000000011031504622126500166310ustar00rootroot00000000000000// // Hello - a minimal introduction sample, shows the basic 3 steps to start using plog. // #include // Step1: include the header. #include int main() { plog::init(plog::debug, "Hello.txt"); // Step2: initialize the logger. // Step3: write log messages using a special macro. There are several log macros, use the macro you liked the most. PLOGD << "Hello log!"; // short macro PLOG_DEBUG << "Hello log!"; // long macro PLOG(plog::debug) << "Hello log!"; // function-style macro return 0; } plog-1.1.11/samples/HexDump/000077500000000000000000000000001504622126500155555ustar00rootroot00000000000000plog-1.1.11/samples/HexDump/CMakeLists.txt000066400000000000000000000001761504622126500203210ustar00rootroot00000000000000add_executable(HexDump Main.cpp) target_link_libraries(HexDump plog) set_target_properties(HexDump PROPERTIES FOLDER Samples) plog-1.1.11/samples/HexDump/Main.cpp000066400000000000000000000021701504622126500171450ustar00rootroot00000000000000// // HexDump - shows how to use plog::hexdump to dump binary buffers into hex. // #include #include #include #include #include #include #include int main() { static plog::ColorConsoleAppender consoleAppender; plog::init(plog::verbose, &consoleAppender); std::vector v; v.push_back(10); v.push_back(20); v.push_back(30); v.push_back(40); v.push_back(1000000); v.push_back(2000000); v.push_back(3000000); PLOGI << "v: " << plog::hexdump(v); PLOGI << "v: " << plog::hexdump(v).group(0); PLOGI << "v: " << plog::hexdump(v).separator("", ""); PLOGI << "v: " << plog::hexdump(v).group(4).separator(" ", "|"); PLOGI << "v: " << plog::hexdump(v).separator("", " "); std::string s("Hello!"); PLOGI << "s: " << plog::hexdump(s); int arr[] = {255, 511, 65535}; PLOGI << "arr: " << plog::hexdump(arr); void* p = malloc(100); PLOGI << "p: " << plog::hexdump(p, 100); free(p); return 0; } plog-1.1.11/samples/Library/000077500000000000000000000000001504622126500156075ustar00rootroot00000000000000plog-1.1.11/samples/Library/CMakeLists.txt000066400000000000000000000005021504622126500203440ustar00rootroot00000000000000add_executable(LibraryApp LibraryApp/Main.cpp) target_link_libraries(LibraryApp LibraryLib plog) set_target_properties(LibraryApp PROPERTIES FOLDER Samples/Library) add_library(LibraryLib STATIC LibraryLib/Lib.cpp) target_link_libraries(LibraryLib plog) set_target_properties(LibraryLib PROPERTIES FOLDER Samples/Library)plog-1.1.11/samples/Library/LibraryApp/000077500000000000000000000000001504622126500176545ustar00rootroot00000000000000plog-1.1.11/samples/Library/LibraryApp/Main.cpp000066400000000000000000000007611504622126500212500ustar00rootroot00000000000000// // Library - shows plog usage in static libraries. // #include #include void foo(); // Function from the static library. int main() { plog::init(plog::debug, "LibraryApp.txt"); // Initialize the logger. The static library will use it. // Note that the main app is not required to use plog, the static library will be linked fine in any case. foo(); PLOGD << "A message from the main application!"; return 0; } plog-1.1.11/samples/Library/LibraryLib/000077500000000000000000000000001504622126500176425ustar00rootroot00000000000000plog-1.1.11/samples/Library/LibraryLib/Lib.cpp000066400000000000000000000004411504622126500210530ustar00rootroot00000000000000// // Library - shows plog usage in static libraries. // #include void foo() { // The logger is initialized in the main app. It is safe not to do that and even not to use plog at all. The library will be linked fine. PLOGD << "A message from the static library!"; } plog-1.1.11/samples/MessagePrefix/000077500000000000000000000000001504622126500167455ustar00rootroot00000000000000plog-1.1.11/samples/MessagePrefix/CMakeLists.txt000066400000000000000000000002261504622126500215050ustar00rootroot00000000000000add_executable(MessagePrefix Main.cpp) target_link_libraries(MessagePrefix plog::plog) set_target_properties(MessagePrefix PROPERTIES FOLDER Samples) plog-1.1.11/samples/MessagePrefix/Main.cpp000066400000000000000000000016421504622126500203400ustar00rootroot00000000000000// // MessagePrefix - demonstrates usage of PLOG_MESSAGE_PREFIX // #define PLOG_MESSAGE_PREFIX "[MyApp] " // define PLOG_MESSAGE_PREFIX before including plog headers #include #include int main() { plog::init(plog::debug, "MessagePrefix.log"); PLOGD << "This is a debug message with a static prefix."; PLOGI << "This is an info message with a static prefix."; // Demonstrate redefining PLOG_MESSAGE_PREFIX to a variable #undef PLOG_MESSAGE_PREFIX const char* dynamicPrefix = "[DynamicModule] "; static_cast(dynamicPrefix); // Ensure the variable is used to avoid unused variable warning #define PLOG_MESSAGE_PREFIX dynamicPrefix PLOGW << "This is a warning message with a dynamic prefix."; dynamicPrefix = "[DynamicModuleUpdated] "; PLOGE << "This is an error message with an updated dynamic prefix."; return 0; } plog-1.1.11/samples/MultiAppender/000077500000000000000000000000001504622126500167545ustar00rootroot00000000000000plog-1.1.11/samples/MultiAppender/CMakeLists.txt000066400000000000000000000002171504622126500215140ustar00rootroot00000000000000add_executable(MultiAppender Main.cpp) target_link_libraries(MultiAppender plog) set_target_properties(MultiAppender PROPERTIES FOLDER Samples)plog-1.1.11/samples/MultiAppender/Main.cpp000066400000000000000000000015341504622126500203470ustar00rootroot00000000000000// // MultiAppender - shows how to use multiple appenders with the same logger. // #include #include #include #include #include #include int main() { static plog::RollingFileAppender fileAppender("MultiAppender.csv", 8000, 3); // Create the 1st appender. static plog::ConsoleAppender consoleAppender; // Create the 2nd appender. plog::init(plog::debug, &fileAppender).addAppender(&consoleAppender); // Initialize the logger with the both appenders. // A bunch of log lines that goes to the both appenders: to the file and to the console. for (int i = 0; i < 100; ++i) { PLOG_INFO << "i: " << i; } return 0; } plog-1.1.11/samples/MultiInstance/000077500000000000000000000000001504622126500167625ustar00rootroot00000000000000plog-1.1.11/samples/MultiInstance/CMakeLists.txt000066400000000000000000000002171504622126500215220ustar00rootroot00000000000000add_executable(MultiInstance Main.cpp) target_link_libraries(MultiInstance plog) set_target_properties(MultiInstance PROPERTIES FOLDER Samples)plog-1.1.11/samples/MultiInstance/Main.cpp000066400000000000000000000016111504622126500203510ustar00rootroot00000000000000// // MultiInstance - shows how to use multiple logger instances, each instance has its own independent configuration. // #include #include enum // Define log instances. Default is 0 and is omitted from this enum. { SecondLog = 1 }; int main() { plog::init(plog::debug, "MultiInstance-default.txt"); // Initialize the default logger instance. plog::init(plog::debug, "MultiInstance-second.txt"); // Initialize the 2nd logger instance. // Write some messages to the default log. PLOGD << "Hello default log!"; PLOG_DEBUG << "Hello default log!"; PLOG(plog::debug) << "Hello default log!"; // Write some messages to the 2nd log. PLOGD_(SecondLog) << "Hello second log!"; PLOG_DEBUG_(SecondLog) << "Hello second log!"; PLOG_(SecondLog, plog::debug) << "Hello second log!"; return 0; } plog-1.1.11/samples/NotShared/000077500000000000000000000000001504622126500160725ustar00rootroot00000000000000plog-1.1.11/samples/NotShared/CMakeLists.txt000066400000000000000000000026331504622126500206360ustar00rootroot00000000000000# some systems have no shared libraries support, so check it if(NOT DEFINED TARGET_SUPPORTS_SHARED_LIBS OR TARGET_SUPPORTS_SHARED_LIBS) # reset visibility to default to test more harsh conditions # in real code it's recommended to use hidden visibility set(CMAKE_CXX_VISIBILITY_PRESET default) set(CMAKE_VISIBILITY_INLINES_HIDDEN 0) add_executable(NotSharedApp NotSharedApp/Main.cpp) target_link_libraries(NotSharedApp PRIVATE plog::plog NotSharedLib1 NotSharedLib2) set_target_properties(NotSharedApp PROPERTIES FOLDER Samples/NotShared) # define PLOG_LOCAL to make plog instances local (not shared between shared objects) target_compile_definitions(NotSharedApp PRIVATE PLOG_LOCAL) add_library(NotSharedLib1 SHARED NotSharedLib1/Main.cpp) target_link_libraries(NotSharedLib1 PRIVATE plog::plog) set_target_properties(NotSharedLib1 PROPERTIES FOLDER Samples/NotShared) # define PLOG_LOCAL to make plog instances local (not shared between shared objects) target_compile_definitions(NotSharedLib1 PRIVATE PLOG_LOCAL) add_library(NotSharedLib2 SHARED NotSharedLib2/Main.cpp) target_link_libraries(NotSharedLib2 PRIVATE plog::plog) set_target_properties(NotSharedLib2 PROPERTIES FOLDER Samples/NotShared) # define PLOG_LOCAL to make plog instances local (not shared between shared objects) target_compile_definitions(NotSharedLib2 PRIVATE PLOG_LOCAL) endif() plog-1.1.11/samples/NotShared/NotSharedApp/000077500000000000000000000000001504622126500204225ustar00rootroot00000000000000plog-1.1.11/samples/NotShared/NotSharedApp/Main.cpp000066400000000000000000000014401504622126500220110ustar00rootroot00000000000000// // NotShared - shows how to make logger instances local across binary modules (this is the default behavior on Windows but not on other platforms, so be careful). // #include #include // Functions imported form the shared libraries. extern "C" void foo1(); extern "C" void foo2(); int main() { plog::init(plog::debug, "NotSharedApp.txt"); // Initialize the logger. It will be visible only in this module and not in other modules because PLOG_LOCAL is defined. PLOGD << "Hello from app!"; // The message will go to the logger in this module. foo1(); // Call a function from the shared library that produces a log message. foo2(); // Call a function from the shared library that produces a log message. return 0; } plog-1.1.11/samples/NotShared/NotSharedLib1/000077500000000000000000000000001504622126500204715ustar00rootroot00000000000000plog-1.1.11/samples/NotShared/NotSharedLib1/Main.cpp000066400000000000000000000014101504622126500220550ustar00rootroot00000000000000// // NotShared - shows how to make logger instances local across binary modules (this is the default behavior on Windows but not on other platforms, so be careful). // #include #include // Helper macro to mark functions exported from the library. #ifdef _WIN32 # define EXPORT __declspec(dllexport) #else # define EXPORT __attribute__ ((visibility ("default"))) #endif // Function that produces a log message. extern "C" void EXPORT foo1() { plog::init(plog::debug, "NotSharedLib1.txt"); // Initialize the logger. It will be visible only in this module and not in other modules because PLOG_LOCAL is defined. PLOGI << "Hello from shared lib #1!"; // The message will go to the logger in this module. } plog-1.1.11/samples/NotShared/NotSharedLib2/000077500000000000000000000000001504622126500204725ustar00rootroot00000000000000plog-1.1.11/samples/NotShared/NotSharedLib2/Main.cpp000066400000000000000000000014101504622126500220560ustar00rootroot00000000000000// // NotShared - shows how to make logger instances local across binary modules (this is the default behavior on Windows but not on other platforms, so be careful). // #include #include // Helper macro to mark functions exported from the library. #ifdef _WIN32 # define EXPORT __declspec(dllexport) #else # define EXPORT __attribute__ ((visibility ("default"))) #endif // Function that produces a log message. extern "C" void EXPORT foo2() { plog::init(plog::debug, "NotSharedLib2.txt"); // Initialize the logger. It will be visible only in this module and not in other modules because PLOG_LOCAL is defined. PLOGI << "Hello from shared lib #2!"; // The message will go to the logger in this module. } plog-1.1.11/samples/ObjectiveC/000077500000000000000000000000001504622126500162205ustar00rootroot00000000000000plog-1.1.11/samples/ObjectiveC/CMakeLists.txt000066400000000000000000000004201504622126500207540ustar00rootroot00000000000000if(CMAKE_OBJCXX_AVAILABLE AND NOT CMAKE_COMPILER_IS_CLANGXX) add_executable(ObjectiveC Main.mm) target_link_libraries(ObjectiveC objc plog) else() add_custom_target(ObjectiveC SOURCES Main.mm) endif() set_target_properties(ObjectiveC PROPERTIES FOLDER Samples)plog-1.1.11/samples/ObjectiveC/Main.mm000066400000000000000000000007271504622126500174450ustar00rootroot00000000000000// // ObjectiveC - shows that plog can be used in ObjectiveC++. // #include #include #include @interface Greeter : Object { int dummy; } +(void) greet; @end @implementation Greeter +(void) greet { PLOGD << "Hello ObjC++!"; } @end int main() { plog::init(plog::debug, "ObjectiveC.csv"); // Initialize the logger. PLOGD << "Hello ObjC++!"; [Greeter greet]; return 0; } plog-1.1.11/samples/Path/000077500000000000000000000000001504622126500150775ustar00rootroot00000000000000plog-1.1.11/samples/Path/CMakeLists.txt000066400000000000000000000013651504622126500176440ustar00rootroot00000000000000if (CMAKE_VERSION VERSION_GREATER 3.1.0) if ((CMAKE_COMPILER_IS_GNUCC AND CMAKE_CXX_COMPILER_VERSION VERSION_GREATER 7.0) OR (MSVC AND CMAKE_CXX_COMPILER_VERSION VERSION_GREATER 19.10)) add_executable(Path Main.cpp) target_link_libraries(Path plog) set_target_properties(Path PROPERTIES FOLDER Samples) set_target_properties(Path PROPERTIES CXX_STANDARD 17) if (MSVC) target_compile_options(Path PRIVATE "/permissive-") # enable conformance mode to be more strict endif() if (CMAKE_COMPILER_IS_GNUCC AND CMAKE_CXX_COMPILER_VERSION VERSION_GREATER 7.0 AND CMAKE_CXX_COMPILER_VERSION VERSION_LESS 9.0) target_link_libraries(Path stdc++fs) endif() endif() endif() plog-1.1.11/samples/Path/Main.cpp000066400000000000000000000007411504622126500164710ustar00rootroot00000000000000// // Path - a test sample to check that std::filesystem::path can be logged. // #include #include #if __has_include() #include namespace fs = std::filesystem; #else #include namespace fs = std::experimental::filesystem; #endif int main() { plog::init(plog::debug, "Path.txt"); PLOGI << "Current path: " << fs::current_path(); return 0; } plog-1.1.11/samples/Performance/000077500000000000000000000000001504622126500164445ustar00rootroot00000000000000plog-1.1.11/samples/Performance/CMakeLists.txt000066400000000000000000000002111504622126500211760ustar00rootroot00000000000000add_executable(Performance Main.cpp) target_link_libraries(Performance plog) set_target_properties(Performance PROPERTIES FOLDER Samples)plog-1.1.11/samples/Performance/Main.cpp000066400000000000000000000021511504622126500200330ustar00rootroot00000000000000// // Performance - measures time per a log call. // #include #include #include #include #include enum { Console = 1 }; int main() { // Initialize the logger that will be measured. plog::init(plog::debug, "Performance.txt"); // Initialize the logger for printing info messages. static plog::ConsoleAppender consoleAppender; plog::init(plog::debug, &consoleAppender); PLOGI_(Console) << "Test started"; plog::util::Time startTime; plog::util::ftime(&startTime); const int kCount = 50000; // Performance measure loop. for (int i = 0; i < kCount; ++i) { PLOGD << "Hello log!"; } plog::util::Time finishTime; plog::util::ftime(&finishTime); time_t timeDiff = (finishTime.millitm - startTime.millitm) + (finishTime.time - startTime.time) * 1000; PLOGI_(Console) << "Test finished: " << static_cast(timeDiff) * 1000 / kCount << " microsec per call"; return 0; } plog-1.1.11/samples/PrintVar/000077500000000000000000000000001504622126500157505ustar00rootroot00000000000000plog-1.1.11/samples/PrintVar/CMakeLists.txt000066400000000000000000000002011504622126500205010ustar00rootroot00000000000000add_executable(PrintVar Main.cpp) target_link_libraries(PrintVar plog) set_target_properties(PrintVar PROPERTIES FOLDER Samples) plog-1.1.11/samples/PrintVar/Main.cpp000066400000000000000000000024461504622126500173460ustar00rootroot00000000000000// // PrintVar - shows how to use PLOG_PRINT_VAR to print variables. // #include #include #include #include class C { public: C(int value) : m_value(value) { PLOGD << PLOG_PRINT_VAR(this, m_value) << " - can print 'this' (useful for distinguishing class instances)"; } ~C() { PLOGD << PLOG_PRINT_VAR(this); } private: int m_value; }; int main() { plog::init(plog::verbose, plog::streamStdOut); C c1(42); C c2(42); int x = 10; int y = 20; int z = 30; PLOGI << PLOG_PRINT_VAR(x); PLOGI << PLOG_PRINT_VAR(x, y); PLOGI << PLOG_PRINT_VAR(x, y, z); PLOGI << PLOG_PRINT_VAR(x, y, z, x); PLOGI << PLOG_PRINT_VAR(x, y, z, x, y); PLOGI << PLOG_PRINT_VAR(x, y, z, x, y, z); PLOGI << PLOG_PRINT_VAR(x, y, z, x, y, z, x); PLOGI << PLOG_PRINT_VAR(x, y, z, x, y, z, x, y); PLOGI << PLOG_PRINT_VAR(x, y, z, x, y, z, x, y, z) << " - can print up to 9 variables"; PLOGI << PLOG_PRINT_VAR(x + y, z * 2, &c1) << " - can execute expressions"; PLOGI << "can print some data before, " << PLOG_PRINT_VAR(x, y) << " - in the middle, " PLOG_PRINT_VAR(z) << " - and after"; return 0; } plog-1.1.11/samples/SetFileName/000077500000000000000000000000001504622126500163375ustar00rootroot00000000000000plog-1.1.11/samples/SetFileName/CMakeLists.txt000066400000000000000000000002121504622126500210720ustar00rootroot00000000000000add_executable(SetFileName Main.cpp) target_link_libraries(SetFileName plog) set_target_properties(SetFileName PROPERTIES FOLDER Samples) plog-1.1.11/samples/SetFileName/Main.cpp000066400000000000000000000011251504622126500177260ustar00rootroot00000000000000// // SetFileName - shows how to change a log file name at arbitrary moment. // #include #include #include #include int main() { static plog::RollingFileAppender fileAppender("SetFileNameAAA.log"); plog::init(plog::debug, &fileAppender); for (int i = 0; i < 100; ++i) { PLOG_INFO << "i: " << i; } fileAppender.setFileName("SetFileNameBBB.log"); for (int i = 0; i < 100; ++i) { PLOG_INFO << "i: " << i; } return 0; } plog-1.1.11/samples/Shared/000077500000000000000000000000001504622126500154115ustar00rootroot00000000000000plog-1.1.11/samples/Shared/CMakeLists.txt000066400000000000000000000033331504622126500201530ustar00rootroot00000000000000# some systems have no shared libraries support, so check it if(NOT DEFINED TARGET_SUPPORTS_SHARED_LIBS OR TARGET_SUPPORTS_SHARED_LIBS) add_executable(SharedApp SharedApp/Main.cpp) target_link_libraries(SharedApp PRIVATE plog::plog) set_target_properties(SharedApp PROPERTIES FOLDER Samples/Shared) # define PLOG_GLOBAL to share plog instances across modules (PLOG_EXPORT to export on Windows) if(WIN32) target_compile_definitions(SharedApp PRIVATE PLOG_EXPORT) set_target_properties(SharedApp PROPERTIES ENABLE_EXPORTS 1) else() target_compile_definitions(SharedApp PRIVATE PLOG_GLOBAL) target_link_libraries(SharedApp PRIVATE SharedLib1 SharedLib2) endif() add_library(SharedLib1 SHARED SharedLib1/Main.cpp) target_link_libraries(SharedLib1 PRIVATE plog::plog) set_target_properties(SharedLib1 PROPERTIES FOLDER Samples/Shared) # define PLOG_GLOBAL to share plog instances across modules (PLOG_IMPORT to import on Windows) if(WIN32) target_compile_definitions(SharedLib1 PRIVATE PLOG_IMPORT) target_link_libraries(SharedLib1 PRIVATE SharedApp) else() target_compile_definitions(SharedLib1 PRIVATE PLOG_GLOBAL) endif() add_library(SharedLib2 SHARED SharedLib2/Main.cpp) target_link_libraries(SharedLib2 PRIVATE plog::plog) set_target_properties(SharedLib2 PROPERTIES FOLDER Samples/Shared) # define PLOG_GLOBAL to share plog instances across modules (PLOG_IMPORT to import on Windows) if(WIN32) target_compile_definitions(SharedLib2 PRIVATE PLOG_IMPORT) target_link_libraries(SharedLib2 PRIVATE SharedApp) else() target_compile_definitions(SharedLib2 PRIVATE PLOG_GLOBAL) endif() endif() plog-1.1.11/samples/Shared/SharedApp/000077500000000000000000000000001504622126500172605ustar00rootroot00000000000000plog-1.1.11/samples/Shared/SharedApp/Main.cpp000066400000000000000000000034131504622126500206510ustar00rootroot00000000000000// // Shared - shows how to share logger instances across binary modules (this is the default behavior on everything except Windows, so be careful). // #include #include // Functions imported form the shared libraries. // // NOTE: // We use dynamic linking on Windows as DLL imports logger instances from EXE and static linking will make a dependency loop: // DLL --(import logger)--> EXE, EXE --(import foo)--> DLL. // // On everything except Winows logger instances are shared across all modules, so there is no dependency loop. #ifdef _WIN32 # include typedef void (*Foo1Fn)(); typedef void (*Foo2Fn)(); #else extern "C" void foo1(); extern "C" void foo2(); #endif int main() { plog::init(plog::debug, "Shared.txt"); // Initialize the logger. // It will be shared across modules because PLOG_GLOBAL is defined, so no need to call `plog::init` in them. // On Windows the logger will be exported because PLOG_EXPORT is defined. PLOGD << "Hello from app!"; // The message will go to the logger in this module. #ifdef _WIN32 HMODULE lib1 = LoadLibraryW(L"SharedLib1.dll"); PLOGE_IF(!lib1) << "Couldn't load SharedLib1.dll"; Foo1Fn foo1 = reinterpret_cast(GetProcAddress(lib1, "foo1")); PLOGE_IF(!foo1) << "Couldn't get foo1 from SharedLib1.dll"; HMODULE lib2 = LoadLibraryW(L"SharedLib2.dll"); PLOGE_IF(!lib2) << "Couldn't load SharedLib2.dll"; Foo1Fn foo2 = reinterpret_cast(GetProcAddress(lib2, "foo2")); PLOGE_IF(!foo2) << "Couldn't get foo2 from SharedLib2.dll"; #endif foo1(); // Call a function from the shared library that produces a log message. foo2(); // Call a function from the shared library that produces a log message. return 0; } plog-1.1.11/samples/Shared/SharedLib1/000077500000000000000000000000001504622126500173275ustar00rootroot00000000000000plog-1.1.11/samples/Shared/SharedLib1/Main.cpp000066400000000000000000000012541504622126500207210ustar00rootroot00000000000000// // Shared - shows how to share logger instances across binary modules (this is the default behavior on everything except Windows, so be careful). // #include // Helper macro to mark functions exported from the library. #ifdef _WIN32 # define EXPORT __declspec(dllexport) #else # define EXPORT __attribute__ ((visibility ("default"))) #endif // Function that produces a log message. extern "C" void EXPORT foo1() { PLOGI << "Hello from shared lib #1!"; // The message will go to the logger initialized in the main module because PLOG_GLOBAL is defined. // On Windows the logger instance is imported from the main module because PLOG_IMPORT is defined. } plog-1.1.11/samples/Shared/SharedLib2/000077500000000000000000000000001504622126500173305ustar00rootroot00000000000000plog-1.1.11/samples/Shared/SharedLib2/Main.cpp000066400000000000000000000012541504622126500207220ustar00rootroot00000000000000// // Shared - shows how to share logger instances across binary modules (this is the default behavior on everything except Windows, so be careful). // #include // Helper macro to mark functions exported from the library. #ifdef _WIN32 # define EXPORT __declspec(dllexport) #else # define EXPORT __attribute__ ((visibility ("default"))) #endif // Function that produces a log message. extern "C" void EXPORT foo2() { PLOGI << "Hello from shared lib #2!"; // The message will go to the logger initialized in the main module because PLOG_GLOBAL is defined. // On Windows the logger instance is imported from the main module because PLOG_IMPORT is defined. } plog-1.1.11/samples/SkipNativeEOL/000077500000000000000000000000001504622126500166205ustar00rootroot00000000000000plog-1.1.11/samples/SkipNativeEOL/CMakeLists.txt000066400000000000000000000002201504622126500213520ustar00rootroot00000000000000add_executable(SkipNativeEOL Main.cpp) target_link_libraries(SkipNativeEOL plog) set_target_properties(SkipNativeEOL PROPERTIES FOLDER Samples) plog-1.1.11/samples/SkipNativeEOL/Main.cpp000066400000000000000000000015201504622126500202060ustar00rootroot00000000000000// // SkipNativeEOL - shows how to skip NativeEOLConverter. // #include #include #include #include #include int main() { // NativeEOLConverter will use on Windows and on everything else as line endings. // It's used by default. // If you want to always use you can skip NativeEOLConverter and specify UTF8Converter directly. static plog::RollingFileAppender fileAppender("SkipNativeEOL.log", 8000, 3); // Create an appender without NativeEOLConverter. plog::init(plog::debug, &fileAppender); // Initialize the logger. // Write some data. for (int i = 0; i < 100; ++i) { PLOGI << "i: " << i; } return 0; } plog-1.1.11/samples/UtcTime/000077500000000000000000000000001504622126500155555ustar00rootroot00000000000000plog-1.1.11/samples/UtcTime/CMakeLists.txt000066400000000000000000000001751504622126500203200ustar00rootroot00000000000000add_executable(UtcTime Main.cpp) target_link_libraries(UtcTime plog) set_target_properties(UtcTime PROPERTIES FOLDER Samples)plog-1.1.11/samples/UtcTime/Main.cpp000066400000000000000000000016021504622126500171440ustar00rootroot00000000000000// // UtcTime - shows how to use UTC time in logs. // #include #include #include #include #include #include int main() { static plog::ColorConsoleAppender consoleAppender; // TxtFormatter in UTC static plog::RollingFileAppender fileAppender("UtcTime.csv", 10000, 2); // CsvFormatter in UTC plog::init(plog::verbose, &consoleAppender).addAppender(&fileAppender); PLOG_VERBOSE << "This is a VERBOSE message"; PLOG_DEBUG << "This is a DEBUG message"; PLOG_INFO << "This is an INFO message"; PLOG_WARNING << "This is a WARNING message"; PLOG_ERROR << "This is an ERROR message"; PLOG_FATAL << "This is a FATAL message"; return 0; } plog-1.1.11/samples/Utf8Everywhere/000077500000000000000000000000001504622126500170775ustar00rootroot00000000000000plog-1.1.11/samples/Utf8Everywhere/CMakeLists.txt000066400000000000000000000004101504622126500216320ustar00rootroot00000000000000if(MSVC AND NOT (MSVC_VERSION LESS 1900)) # Visual Studio 2015 and higher add_executable(Utf8Everywhere Main.cpp) target_link_libraries(Utf8Everywhere plog) set_target_properties(Utf8Everywhere PROPERTIES FOLDER Samples COMPILE_FLAGS "/utf-8") endif() plog-1.1.11/samples/Utf8Everywhere/Main.cpp000066400000000000000000000013331504622126500204670ustar00rootroot00000000000000// // Utf8Everywhere - demonstrates using http://utf8everywhere.org on Windows. // #include #include #include #include void 中文() { PLOGD << "Chinese: 中文"; PLOGD << L"Chinese: 中文"; } void тест() { PLOGD << "Cyrillic: тест"; PLOGD << L"Cyrillic: тест"; } int main() { plog::init(plog::debug, "Utf8Everywhere.log"); // Initialize logging to the file. plog::ColorConsoleAppender consoleAppender; plog::get()->addAppender(&consoleAppender); // Also add logging to the console. 中文(); тест(); return 0; } plog-1.1.11/test/000077500000000000000000000000001504622126500135165ustar00rootroot00000000000000plog-1.1.11/test/CMakeLists.txt000066400000000000000000000076421504622126500162670ustar00rootroot00000000000000if(${CMAKE_VERSION} VERSION_LESS 3.27.0) cmake_minimum_required(VERSION 3.1) else() cmake_minimum_required(VERSION 3.27) endif() project(PlogTest CXX) # # Specify test sources # set(SOURCES CastToString.cpp Common.h Conditional.cpp TestAppender.h Main.cpp MessagePrefix.cpp NullCharPointer.cpp Path.cpp Printf.cpp SimpleTypes.cpp StringTypes.cpp StdContainers.cpp StdManipulators.cpp StdStreamable.cpp ) # # Check wchar_t platform support # include(CheckTypeSize) CHECK_TYPE_SIZE(wchar_t PLOG_SIZEOF_WCHAR) # # Test creation functions to avoid code duplication # function(plog_add_test _target) add_executable(${_target} ${SOURCES}) target_link_libraries(${_target} plog::plog) target_compile_definitions(${_target} PRIVATE DOCTEST_CONFIG_NO_POSIX_SIGNALS DOCTEST_CONFIG_NO_MULTITHREADING) set_target_properties(${_target} PROPERTIES FOLDER Test) # Add std::filesystem support library for GCC if(CMAKE_COMPILER_IS_GNUCC AND CMAKE_CXX_COMPILER_VERSION VERSION_GREATER 7.0 AND CMAKE_CXX_COMPILER_VERSION VERSION_LESS 9.0) target_link_libraries(${_target} stdc++fs) endif() # Enable conformance mode to be more strict if(MSVC AND NOT (MSVC_VERSION LESS 1900)) # Visual Studio 2015 and higher target_compile_options(${_target} PRIVATE "/permissive-") endif() add_test(${_target} ${_target}) endfunction() function(plog_add_test_utf8 _target) if(MSVC AND NOT (MSVC_VERSION LESS 1900)) # Visual Studio 2015 and higher plog_add_test(${_target}) target_compile_options(${_target} PRIVATE "/utf-8") endif() endfunction() function(plog_add_test_wchar _target) if(NOT WIN32 AND NOT BSD AND PLOG_SIZEOF_WCHAR) # FIXME: for some reason wchar_t is not working on BSD plog_add_test(${_target}) target_compile_definitions(${_target} PRIVATE PLOG_ENABLE_WCHAR_INPUT=1) if(APPLE) target_link_libraries(${_target} -liconv) endif() endif() endfunction() # # Create a basic test with default compiler features # plog_add_test(${PROJECT_NAME}) # # Create a test for Utf8Everywhere mode with default compiler features # plog_add_test_utf8(${PROJECT_NAME}_utf8) # # Create a test for wchar input with default compiler features # plog_add_test_wchar(${PROJECT_NAME}_wchar) # # Check if running a descent version of CMake and enable it # if(CMAKE_VERSION VERSION_LESS 3.8.0) return() elseif(${CMAKE_VERSION} VERSION_LESS 3.27.0) cmake_policy(VERSION 3.8) endif() set(PLOG_CXX_STANDARDS_LIST LIST cxx_std_11 cxx_std_14 cxx_std_17 cxx_std_20 cxx_std_23) function(plog_get_latest_cxx_std _output) foreach(cxx_std IN ITEMS ${PLOG_CXX_STANDARDS_LIST}) if(${cxx_std} IN_LIST CMAKE_CXX_COMPILE_FEATURES) set(${_output} ${cxx_std} PARENT_SCOPE) endif() endforeach() endfunction() plog_get_latest_cxx_std(PLOG_LATEST_CXX_STD) function(plog_set_target_cxx_standard _target _cxx_std) if(TARGET ${_target}) target_compile_features(${_target} PRIVATE ${_cxx_std}) endif() endfunction() # # Create basic tests for different C++ Standard versions # foreach(cxx_std IN ITEMS ${PLOG_CXX_STANDARDS_LIST}) if(${cxx_std} IN_LIST CMAKE_CXX_COMPILE_FEATURES) plog_add_test(${PROJECT_NAME}_${cxx_std}) plog_set_target_cxx_standard(${PROJECT_NAME}_${cxx_std} ${cxx_std}) endif() endforeach() # # Create a test for Utf8Everywhere mode with the latest C++ Standard # if(PLOG_LATEST_CXX_STD) plog_add_test_utf8(${PROJECT_NAME}_${PLOG_LATEST_CXX_STD}_utf8) plog_set_target_cxx_standard(${PROJECT_NAME}_${PLOG_LATEST_CXX_STD}_utf8 ${PLOG_LATEST_CXX_STD}) endif() # # Create a test for wchar input with the latest C++ Standard # if(PLOG_LATEST_CXX_STD) plog_add_test_wchar(${PROJECT_NAME}_${PLOG_LATEST_CXX_STD}_wchar) plog_set_target_cxx_standard(${PROJECT_NAME}_${PLOG_LATEST_CXX_STD}_wchar ${PLOG_LATEST_CXX_STD}) endif() plog-1.1.11/test/CastToString.cpp000066400000000000000000000021121504622126500166020ustar00rootroot00000000000000#include "Common.h" class CastableToString { public: operator std::string() const { return "object"; } }; #if PLOG_ENABLE_WCHAR_INPUT class CastableToWString { public: operator std::wstring() const { return L"object"; } }; #endif SCENARIO("cast to string") { GIVEN("logger is initialised") { plog::TestAppender testAppender; plog::Logger logger(plog::verbose); logger.addAppender(&testAppender); WHEN("type is castable to std::string") { CastableToString var; PLOGI << var; THEN("the result is as expected") { CHECK_EQ(testAppender.getMessage(), PLOG_NSTR("object")); } } #if PLOG_ENABLE_WCHAR_INPUT WHEN("type is castable to std::wstring") { CastableToWString var; PLOGI << var; THEN("the result is as expected") { CHECK_EQ(testAppender.getMessage(), PLOG_NSTR("object")); } } #endif } } plog-1.1.11/test/Common.h000066400000000000000000000005721504622126500151230ustar00rootroot00000000000000#pragma once #if defined(__cpp_constexpr) && (!defined(__MINGW32__) || defined(__MINGW64_VERSION_MAJOR)) # include "doctest/2.4.11/doctest.h" // C++11 and higher #else # include "doctest/1.2.9/doctest.h" // pre C++11 #endif #include #include "TestAppender.h" #ifdef __has_include # if __has_include() # include # endif #endif plog-1.1.11/test/Conditional.cpp000066400000000000000000000043431504622126500164710ustar00rootroot00000000000000#include "Common.h" SCENARIO("conditional logging") { GIVEN("logger is initialised") { plog::TestAppender testAppender; plog::Logger logger(plog::info); logger.addAppender(&testAppender); WHEN("condition is true") { int var = 0; PLOG_INFO_IF(var == 0) << "message"; THEN("the message is printed") { CHECK_EQ(testAppender.getMessage(), PLOG_NSTR("message")); } } WHEN("condition is false") { int var = 0; PLOG_INFO_IF(var != 0) << "message"; THEN("the message is not printed") { CHECK(testAppender.getMessage().empty()); } } WHEN("log level check is true") { int var = 0; IF_PLOG(plog::info) var = 5; // one line IF_PLOG(plog::info) // block { var++; } THEN("statements were executed") { CHECK_EQ(var, 5 + 1); } } WHEN("log level check is false") { int var = 0; IF_PLOG(plog::debug) var = 5; // one line IF_PLOG(plog::debug) // block { var++; } THEN("statements were not executed") { CHECK_EQ(var, 0); } } WHEN("log macros are used in 'then-else' clauses without braces and condition is true") { int var = 0; if (var == 0) PLOGI << "then clause"; else PLOGI << "else clause"; THEN("nothing is broken, 'then' clause is executed") { CHECK_EQ(testAppender.getMessage(), PLOG_NSTR("then clause")); } } WHEN("log macros are used in 'then-else' clauses without braces and condition is false") { int var = 0; if (var != 0) PLOGI << "then clause"; else PLOGI << "else clause"; THEN("nothing is broken, 'else' clause is executed") { CHECK_EQ(testAppender.getMessage(), PLOG_NSTR("else clause")); } } } } plog-1.1.11/test/Main.cpp000066400000000000000000000000771504622126500151120ustar00rootroot00000000000000#define DOCTEST_CONFIG_IMPLEMENT_WITH_MAIN #include "Common.h" plog-1.1.11/test/MessagePrefix.cpp000066400000000000000000000036731504622126500167750ustar00rootroot00000000000000#define PLOG_MESSAGE_PREFIX "" #include "Common.h" SCENARIO("message prefix") { GIVEN("logger is initialised") { plog::TestAppender testAppender; plog::Logger logger(plog::info); logger.addAppender(&testAppender); WHEN("set PLOG_MESSAGE_PREFIX and print a message") { #undef PLOG_MESSAGE_PREFIX #define PLOG_MESSAGE_PREFIX "[test] " PLOGI << "message"; THEN("the message is printed with the prefix") { CHECK_EQ(testAppender.getMessage(), PLOG_NSTR("[test] message")); } } WHEN("set PLOG_MESSAGE_PREFIX to an empty string and print a message") { #undef PLOG_MESSAGE_PREFIX #define PLOG_MESSAGE_PREFIX "" PLOGI << "message"; THEN("the message is printed without the prefix as it's empty") { CHECK_EQ(testAppender.getMessage(), PLOG_NSTR("message")); } } } GIVEN("logger is initialised and PLOG_MESSAGE_PREFIX is set to a variable") { plog::TestAppender testAppender; plog::Logger logger(plog::info); logger.addAppender(&testAppender); #undef PLOG_MESSAGE_PREFIX #define PLOG_MESSAGE_PREFIX plogPrefix std::string plogPrefix = "[value] "; WHEN("print a message") { PLOGI << "message"; THEN("the message is printed with the prefix") { CHECK_EQ(testAppender.getMessage(), PLOG_NSTR("[value] message")); } AND_WHEN("change the variable and print a new message") { plogPrefix = "***"; PLOGI << "new message"; THEN("the new message is printed with the new prefix") { CHECK_EQ(testAppender.getMessage(), PLOG_NSTR("***new message")); } } } } } plog-1.1.11/test/NullCharPointer.cpp000066400000000000000000000044251504622126500173000ustar00rootroot00000000000000#include "Common.h" #include #include SCENARIO("handling null char pointers") { GIVEN("logger is initialised") { plog::TestAppender testAppender; plog::Logger logger(plog::verbose); logger.addAppender(&testAppender); WHEN("type is char* and its value is NULL") { char* var = NULL; PLOGI << var; THEN("the result is as expected") { CHECK_EQ(testAppender.getMessage(), PLOG_NSTR("(null)")); } } WHEN("type is std::vector and value of elements is NULL") { std::vector var; var.push_back(NULL); var.push_back(NULL); PLOGI << var; THEN("the result is as expected") { CHECK_EQ(testAppender.getMessage(), PLOG_NSTR("[(null), (null)]")); } } WHEN("type is std::pair and value of elements is NULL") { std::pair var; PLOGI << var; THEN("the result is as expected") { CHECK_EQ(testAppender.getMessage(), PLOG_NSTR("(null):(null)")); } } #if PLOG_ENABLE_WCHAR_INPUT WHEN("type is wchar_t* and its value is NULL") { wchar_t* var = NULL; PLOGI << var; THEN("the result is as expected") { CHECK_EQ(testAppender.getMessage(), PLOG_NSTR("(null)")); } } WHEN("type is std::vector and value of elements is NULL") { std::vector var; var.push_back(NULL); var.push_back(NULL); PLOGI << var; THEN("the result is as expected") { CHECK_EQ(testAppender.getMessage(), PLOG_NSTR("[(null), (null)]")); } } WHEN("type is std::pair and value of elements is NULL") { std::pair var; PLOGI << var; THEN("the result is as expected") { CHECK_EQ(testAppender.getMessage(), PLOG_NSTR("(null):(null)")); } } #endif } } plog-1.1.11/test/Path.cpp000066400000000000000000000011531504622126500151160ustar00rootroot00000000000000#include "Common.h" #ifdef __cpp_lib_filesystem # include SCENARIO("std::filesystem::path") { GIVEN("logger is initialised") { plog::TestAppender testAppender; plog::Logger logger(plog::verbose); logger.addAppender(&testAppender); WHEN("type is std::filesystem::path") { std::filesystem::path path("/usr/lib"); PLOGI << path; THEN("the result is as expected") { CHECK_EQ(testAppender.getMessage(), PLOG_NSTR("/usr/lib")); } } } } #endif plog-1.1.11/test/Printf.cpp000066400000000000000000000043311504622126500154650ustar00rootroot00000000000000#include "Common.h" #ifndef __cplusplus_cli SCENARIO("printf-style messages") { GIVEN("logger is initialised") { plog::TestAppender testAppender; plog::Logger logger(plog::verbose); logger.addAppender(&testAppender); WHEN("use empty format") { PLOGI.printf(""); THEN("the result is as expected") { CHECK_EQ(testAppender.getMessage(), PLOG_NSTR("")); } } WHEN("use text only format") { PLOGI.printf("test"); THEN("the result is as expected") { CHECK_EQ(testAppender.getMessage(), PLOG_NSTR("test")); } } WHEN("use format %d") { PLOGI.printf("test %d", 42); THEN("the result is as expected") { CHECK_EQ(testAppender.getMessage(), PLOG_NSTR("test 42")); } } WHEN("use format %s") { PLOGI.printf("hello %s", "world"); THEN("the result is as expected") { CHECK_EQ(testAppender.getMessage(), PLOG_NSTR("hello world")); } } #ifdef _WIN32 WHEN("use empty format (wide)") { PLOGI.printf(L""); THEN("the result is as expected") { CHECK_EQ(testAppender.getMessage(), PLOG_NSTR("")); } } WHEN("use text only format (wide)") { PLOGI.printf(L"test"); THEN("the result is as expected") { CHECK_EQ(testAppender.getMessage(), PLOG_NSTR("test")); } } WHEN("use format %d (wide)") { PLOGI.printf(L"test %d", 42); THEN("the result is as expected") { CHECK_EQ(testAppender.getMessage(), PLOG_NSTR("test 42")); } } WHEN("use format %s (wide)") { PLOGI.printf(L"hello %s", L"world"); THEN("the result is as expected") { CHECK_EQ(testAppender.getMessage(), PLOG_NSTR("hello world")); } } #endif } } #endif plog-1.1.11/test/SimpleTypes.cpp000066400000000000000000000114471504622126500165070ustar00rootroot00000000000000#include "Common.h" SCENARIO("simple types") { GIVEN("logger is initialised") { plog::TestAppender testAppender; plog::Logger logger(plog::verbose); logger.addAppender(&testAppender); WHEN("type is bool") { bool var = true; PLOGI << var; THEN("the result is as expected") { CHECK_EQ(testAppender.getMessage(), PLOG_NSTR("1")); } } WHEN("type is char") { char var = 'a'; PLOGI << var; THEN("the result is as expected") { CHECK_EQ(testAppender.getMessage(), PLOG_NSTR("a")); } } WHEN("type is signed char") { signed char var = 'a'; PLOGI << var; THEN("the result is as expected") { CHECK_EQ(testAppender.getMessage(), PLOG_NSTR("a")); } } WHEN("type is unsigned char") { unsigned char var = 'a'; PLOGI << var; THEN("the result is as expected") { CHECK_EQ(testAppender.getMessage(), PLOG_NSTR("a")); } } WHEN("type is short") { short var = -1000; PLOGI << var; THEN("the result is as expected") { CHECK_EQ(testAppender.getMessage(), PLOG_NSTR("-1000")); } } WHEN("type is unsigned short") { unsigned short var = 1000; PLOGI << var; THEN("the result is as expected") { CHECK_EQ(testAppender.getMessage(), PLOG_NSTR("1000")); } } WHEN("type is int") { int var = -1000; PLOGI << var; THEN("the result is as expected") { CHECK_EQ(testAppender.getMessage(), PLOG_NSTR("-1000")); } } WHEN("type is unsigned int") { unsigned int var = 1000; PLOGI << var; THEN("the result is as expected") { CHECK_EQ(testAppender.getMessage(), PLOG_NSTR("1000")); } } WHEN("type is long") { long var = -1000; PLOGI << var; THEN("the result is as expected") { CHECK_EQ(testAppender.getMessage(), PLOG_NSTR("-1000")); } } WHEN("type is unsigned long") { unsigned long var = 1000; PLOGI << var; THEN("the result is as expected") { CHECK_EQ(testAppender.getMessage(), PLOG_NSTR("1000")); } } WHEN("type is long long") { long long var = -1000; PLOGI << var; THEN("the result is as expected") { CHECK_EQ(testAppender.getMessage(), PLOG_NSTR("-1000")); } } WHEN("type is unsigned long long") { unsigned long long var = 1000; PLOGI << var; THEN("the result is as expected") { CHECK_EQ(testAppender.getMessage(), PLOG_NSTR("1000")); } } WHEN("type is float") { float var = 1.2345f; PLOGI << var; THEN("the result is as expected") { CHECK_EQ(testAppender.getMessage(), PLOG_NSTR("1.2345")); } } WHEN("type is double") { double var = 1.2345; PLOGI << var; THEN("the result is as expected") { CHECK_EQ(testAppender.getMessage(), PLOG_NSTR("1.2345")); } } WHEN("type is void pointer") { void* var = &var; PLOGI << var; THEN("the result is as expected") { CHECK(testAppender.getMessage().size() >= 4); // could be 2 hex formats for pointers: with 0x prefix and without it if (testAppender.getMessage().at(1) == PLOG_NSTR('x')) { CHECK_EQ(testAppender.getMessage().at(0), PLOG_NSTR('0')); for (size_t i = 2; i < testAppender.getMessage().size(); ++i) { CHECK(std::isxdigit(testAppender.getMessage().at(i))); } } else { for (size_t i = 0; i < testAppender.getMessage().size(); ++i) { CHECK(std::isxdigit(testAppender.getMessage().at(i))); } } } } } } plog-1.1.11/test/StdContainers.cpp000066400000000000000000000110231504622126500167770ustar00rootroot00000000000000#include "Common.h" #include #include #include #include #include #include #ifdef __cpp_lib_span # include #endif SCENARIO("std containers") { GIVEN("logger is initialised") { plog::TestAppender testAppender; plog::Logger logger(plog::verbose); logger.addAppender(&testAppender); WHEN("empty collection") { std::vector vectorOfInts; PLOGI << vectorOfInts; THEN("the result is as expected") { CHECK_EQ(testAppender.getMessage(), PLOG_NSTR("[]")); } } WHEN("std::vector") { std::vector vectorOfInts; vectorOfInts.push_back(1); vectorOfInts.push_back(2); vectorOfInts.push_back(3); PLOGI << vectorOfInts; THEN("the result is as expected") { CHECK_EQ(testAppender.getMessage(), PLOG_NSTR("[1, 2, 3]")); } } WHEN("std::deque") { std::deque dequeOfStrings; dequeOfStrings.push_back("one"); dequeOfStrings.push_back("two"); dequeOfStrings.push_back("three"); PLOGI << dequeOfStrings; THEN("the result is as expected") { CHECK_EQ(testAppender.getMessage(), PLOG_NSTR("[one, two, three]")); } } WHEN("std::list") { std::list listOfCharPointers; listOfCharPointers.push_back("one"); listOfCharPointers.push_back("two"); listOfCharPointers.push_back(NULL); PLOGI << listOfCharPointers; THEN("the result is as expected") { CHECK_EQ(testAppender.getMessage(), PLOG_NSTR("[one, two, (null)]")); } } WHEN("std::set") { std::set setOfInts; setOfInts.insert(10); setOfInts.insert(20); setOfInts.insert(30); PLOGI << setOfInts; THEN("the result is as expected") { CHECK_EQ(testAppender.getMessage(), PLOG_NSTR("[10, 20, 30]")); } } WHEN("std::map") { std::map mapStringToInt; mapStringToInt["red"] = 1; mapStringToInt["green"] = 2; mapStringToInt["blue"] = 4; PLOGI << mapStringToInt; THEN("the result is as expected") { CHECK_EQ(testAppender.getMessage(), PLOG_NSTR("[blue:4, green:2, red:1]")); } } WHEN("std::multimap") { std::multimap multimapIntToString; multimapIntToString.insert(std::make_pair(1, "one")); multimapIntToString.insert(std::make_pair(1, "uno")); multimapIntToString.insert(std::make_pair(2, "two")); multimapIntToString.insert(std::make_pair(2, "due")); PLOGI << multimapIntToString; THEN("the result is as expected") { CHECK_EQ(testAppender.getMessage(), PLOG_NSTR("[1:one, 1:uno, 2:two, 2:due]")); } } WHEN("std::vector of std::vector") { std::vector > vectorOfVectorsOfInts(3); vectorOfVectorsOfInts[0].push_back(1); vectorOfVectorsOfInts[0].push_back(2); vectorOfVectorsOfInts[1].push_back(-1); vectorOfVectorsOfInts[1].push_back(-2); PLOGI << vectorOfVectorsOfInts; THEN("the result is as expected") { CHECK_EQ(testAppender.getMessage(), PLOG_NSTR("[[1, 2], [-1, -2], []]")); } } WHEN("std::pair") { std::pair pairOfInts(5, 10); PLOGI << pairOfInts; THEN("the result is as expected") { CHECK_EQ(testAppender.getMessage(), PLOG_NSTR("5:10")); } } #if 0 // std::span is not supported yet as it has no const_iterator till c++23 #ifdef __cpp_lib_span WHEN("std::span") { int arr[] = {1, 2, 3}; std::span spanOfInts(std::begin(arr), std::end(arr)); PLOGI << spanOfInts; THEN("the result is as expected") { CHECK_EQ(testAppender.getMessage(), PLOG_NSTR("[1, 2, 3]")); } } #endif #endif } } plog-1.1.11/test/StdManipulators.cpp000066400000000000000000000022101504622126500173460ustar00rootroot00000000000000#include "Common.h" SCENARIO("std manipulators") { GIVEN("logger is initialised") { plog::TestAppender testAppender; plog::Logger logger(plog::verbose); logger.addAppender(&testAppender); WHEN("std::boolalpha") { PLOGI << std::boolalpha << true; THEN("the result is as expected") { CHECK_EQ(testAppender.getMessage(), PLOG_NSTR("true")); } } WHEN("std::hex") { PLOGI << std::hex << 65534; THEN("the result is as expected") { CHECK_EQ(testAppender.getMessage(), PLOG_NSTR("fffe")); } } WHEN("use manipulator for the 1st message") { bool var = true; PLOGI << std::boolalpha << var; AND_WHEN("log the 2nd message") { PLOGI << var; THEN("manipulators are cleared for the 2nd message") { CHECK_EQ(testAppender.getMessage(), PLOG_NSTR("1")); } } } } } plog-1.1.11/test/StdStreamable.cpp000066400000000000000000000030351504622126500167550ustar00rootroot00000000000000#include "Common.h" #include #include struct StdStreamable { int id; std::string name; }; inline std::ostream& operator<<(std::ostream& os, const StdStreamable& obj) { os << "StdStreamable (id: " << obj.id << ", name: " << obj.name << ")"; return os; } #if PLOG_ENABLE_WCHAR_INPUT struct StdWStreamable { int id; std::wstring name; }; inline std::wostream& operator<<(std::wostream& os, const StdWStreamable& obj) { os << L"StdWStreamable (id: " << obj.id << L", name: " << obj.name << L")"; return os; } #endif SCENARIO("cast to string") { GIVEN("logger is initialised") { plog::TestAppender testAppender; plog::Logger logger(plog::verbose); logger.addAppender(&testAppender); WHEN("type is streamable to std::ostream") { StdStreamable var; var.id = 1; var.name = "water"; PLOGI << var; THEN("the result is as expected") { CHECK_EQ(testAppender.getMessage(), PLOG_NSTR("StdStreamable (id: 1, name: water)")); } } #if PLOG_ENABLE_WCHAR_INPUT WHEN("type is streamable to std::wostream") { StdWStreamable var; var.id = 1; var.name = L"water"; PLOGI << var; THEN("the result is as expected") { CHECK_EQ(testAppender.getMessage(), PLOG_NSTR("StdWStreamable (id: 1, name: water)")); } } #endif } } plog-1.1.11/test/StringTypes.cpp000066400000000000000000000121041504622126500165130ustar00rootroot00000000000000#include "Common.h" #ifdef __cpp_lib_string_view # include #endif SCENARIO("string types") { GIVEN("logger is initialised") { plog::TestAppender testAppender; plog::Logger logger(plog::verbose); logger.addAppender(&testAppender); WHEN("type is char*") { char* var = const_cast("test"); PLOGI << var; THEN("the result is as expected") { CHECK_EQ(testAppender.getMessage(), PLOG_NSTR("test")); } } WHEN("type is const char*") { const char* var = "test"; PLOGI << var; THEN("the result is as expected") { CHECK_EQ(testAppender.getMessage(), PLOG_NSTR("test")); } } WHEN("type is char[]") { char var[] = "test"; PLOGI << var; THEN("the result is as expected") { CHECK_EQ(testAppender.getMessage(), PLOG_NSTR("test")); } } WHEN("type is const char[]") { const char var[] = "test"; PLOGI << var; THEN("the result is as expected") { CHECK_EQ(testAppender.getMessage(), PLOG_NSTR("test")); } } WHEN("type is std::string") { std::string var = "test"; PLOGI << var; THEN("the result is as expected") { CHECK_EQ(testAppender.getMessage(), PLOG_NSTR("test")); } } WHEN("type is const std::string") { const std::string var = "test"; PLOGI << var; THEN("the result is as expected") { CHECK_EQ(testAppender.getMessage(), PLOG_NSTR("test")); } } #ifdef __cpp_lib_string_view WHEN("type is std::string_view") { std::string_view var = "test"; PLOGI << var; THEN("the result is as expected") { CHECK_EQ(testAppender.getMessage(), PLOG_NSTR("test")); } } WHEN("type is const std::string_view") { const std::string_view var = "test"; PLOGI << var; THEN("the result is as expected") { CHECK_EQ(testAppender.getMessage(), PLOG_NSTR("test")); } } #endif #if PLOG_ENABLE_WCHAR_INPUT WHEN("type is wchar_t*") { wchar_t* var = const_cast(L"test"); PLOGI << var; THEN("the result is as expected") { CHECK_EQ(testAppender.getMessage(), PLOG_NSTR("test")); } } WHEN("type is const wchar_t*") { const wchar_t* var = L"test"; PLOGI << var; THEN("the result is as expected") { CHECK_EQ(testAppender.getMessage(), PLOG_NSTR("test")); } } WHEN("type is wchar_t[]") { wchar_t var[] = L"test"; PLOGI << var; THEN("the result is as expected") { CHECK_EQ(testAppender.getMessage(), PLOG_NSTR("test")); } } WHEN("type is const wchar_t[]") { const wchar_t var[] = L"test"; PLOGI << var; THEN("the result is as expected") { CHECK_EQ(testAppender.getMessage(), PLOG_NSTR("test")); } } WHEN("type is std::wstring") { std::wstring var = L"test"; PLOGI << var; THEN("the result is as expected") { CHECK_EQ(testAppender.getMessage(), PLOG_NSTR("test")); } } WHEN("type is const std::wstring") { const std::wstring var = L"test"; PLOGI << var; THEN("the result is as expected") { CHECK_EQ(testAppender.getMessage(), PLOG_NSTR("test")); } } # ifdef __cpp_lib_string_view WHEN("type is std::wstring_view") { std::wstring_view var = L"test"; PLOGI << var; THEN("the result is as expected") { CHECK_EQ(testAppender.getMessage(), PLOG_NSTR("test")); } } WHEN("type is const std::wstring_view") { const std::wstring_view var = L"test"; PLOGI << var; THEN("the result is as expected") { CHECK_EQ(testAppender.getMessage(), PLOG_NSTR("test")); } } # endif #endif #ifdef __cplusplus_cli WHEN("type is const System::String^") { System::String^ var = "test"; PLOGI << var; THEN("the result is as expected") { CHECK_EQ(testAppender.getMessage(), PLOG_NSTR("test")); } } #endif //TODO: add more tests } } plog-1.1.11/test/TestAppender.h000066400000000000000000000006331504622126500162670ustar00rootroot00000000000000#pragma once #include namespace plog { class TestAppender : public IAppender { public: virtual void write(const Record& record) PLOG_OVERRIDE { m_message = record.getMessage(); } const util::nstring& getMessage() const { return m_message; } private: util::nstring m_message; }; }plog-1.1.11/test/doctest/000077500000000000000000000000001504622126500151635ustar00rootroot00000000000000plog-1.1.11/test/doctest/1.2.9/000077500000000000000000000000001504622126500156325ustar00rootroot00000000000000plog-1.1.11/test/doctest/1.2.9/doctest.h000066400000000000000000007512261504622126500174650ustar00rootroot00000000000000// ====================================================================== // == DO NOT MODIFY THIS FILE BY HAND - IT IS AUTO GENERATED BY CMAKE! == // ====================================================================== // // doctest.h - the lightest feature-rich C++ single-header testing framework for unit tests and TDD // // Copyright (c) 2016-2018 Viktor Kirilov // // Distributed under the MIT Software License // See accompanying file LICENSE.txt or copy at // https://opensource.org/licenses/MIT // // The documentation can be found at the library's page: // https://github.com/onqtam/doctest/blob/master/doc/markdown/readme.md // // ================================================================================================= // ================================================================================================= // ================================================================================================= // // The library is heavily influenced by Catch - https://github.com/philsquared/Catch // which uses the Boost Software License - Version 1.0 // see here - https://github.com/philsquared/Catch/blob/master/LICENSE.txt // // The concept of subcases (sections in Catch) and expression decomposition are from there. // Some parts of the code are taken directly: // - stringification - the detection of "ostream& operator<<(ostream&, const T&)" and StringMaker<> // - the Approx() helper class for floating point comparison // - colors in the console // - breaking into a debugger // - signal / SEH handling // - timer // // The expression decomposing templates are taken from lest - https://github.com/martinmoene/lest // which uses the Boost Software License - Version 1.0 // see here - https://github.com/martinmoene/lest/blob/master/LICENSE.txt // // The type list and the foreach algorithm on it for C++98 are taken from Loki // - http://loki-lib.sourceforge.net/ // - https://en.wikipedia.org/wiki/Loki_%28C%2B%2B%29 // - https://github.com/snaewe/loki-lib // which uses the MIT Software License // // ================================================================================================= // ================================================================================================= // ================================================================================================= #ifndef DOCTEST_LIBRARY_INCLUDED #define DOCTEST_LIBRARY_INCLUDED // ================================================================================================= // == VERSION ====================================================================================== // ================================================================================================= #define DOCTEST_VERSION_MAJOR 1 #define DOCTEST_VERSION_MINOR 2 #define DOCTEST_VERSION_PATCH 9 #define DOCTEST_VERSION_STR "1.2.9" #define DOCTEST_VERSION \ (DOCTEST_VERSION_MAJOR * 10000 + DOCTEST_VERSION_MINOR * 100 + DOCTEST_VERSION_PATCH) // ================================================================================================= // == COMPILER VERSION ============================================================================= // ================================================================================================= // ideas for the version stuff are taken from here: https://github.com/cxxstuff/cxx_detect #define DOCTEST_COMPILER(MAJOR, MINOR, PATCH) ((MAJOR)*10000000 + (MINOR)*100000 + (PATCH)) #if defined(_MSC_VER) && defined(_MSC_FULL_VER) #if _MSC_VER == _MSC_FULL_VER / 10000 #define DOCTEST_MSVC DOCTEST_COMPILER(_MSC_VER / 100, _MSC_VER % 100, _MSC_FULL_VER % 10000) #else #define DOCTEST_MSVC \ DOCTEST_COMPILER(_MSC_VER / 100, (_MSC_FULL_VER / 100000) % 100, _MSC_FULL_VER % 100000) #endif #elif defined(__clang__) && defined(__clang_minor__) #define DOCTEST_CLANG DOCTEST_COMPILER(__clang_major__, __clang_minor__, __clang_patchlevel__) #elif defined(__GNUC__) && defined(__GNUC_MINOR__) && defined(__GNUC_PATCHLEVEL__) && \ !defined(__INTEL_COMPILER) #define DOCTEST_GCC DOCTEST_COMPILER(__GNUC__, __GNUC_MINOR__, __GNUC_PATCHLEVEL__) #endif #ifndef DOCTEST_MSVC #define DOCTEST_MSVC 0 #endif // DOCTEST_MSVC #ifndef DOCTEST_CLANG #define DOCTEST_CLANG 0 #endif // DOCTEST_CLANG #ifndef DOCTEST_GCC #define DOCTEST_GCC 0 #endif // DOCTEST_GCC // ================================================================================================= // == COMPILER WARNINGS HELPERS ==================================================================== // ================================================================================================= #if DOCTEST_CLANG #ifdef __has_warning #define DOCTEST_CLANG_HAS_WARNING(x) __has_warning(x) #endif // __has_warning #ifdef __has_feature #define DOCTEST_CLANG_HAS_FEATURE(x) __has_feature(x) #endif // __has_feature #define DOCTEST_PRAGMA_TO_STR(x) _Pragma(#x) #define DOCTEST_CLANG_SUPPRESS_WARNING_PUSH _Pragma("clang diagnostic push") #define DOCTEST_MSVC_SUPPRESS_WARNING_PUSH #define DOCTEST_GCC_SUPPRESS_WARNING_PUSH #define DOCTEST_CLANG_SUPPRESS_WARNING(w) DOCTEST_PRAGMA_TO_STR(clang diagnostic ignored w) #define DOCTEST_MSVC_SUPPRESS_WARNING(w) #define DOCTEST_GCC_SUPPRESS_WARNING(w) #define DOCTEST_CLANG_SUPPRESS_WARNING_POP _Pragma("clang diagnostic pop") #define DOCTEST_MSVC_SUPPRESS_WARNING_POP #define DOCTEST_GCC_SUPPRESS_WARNING_POP #define DOCTEST_CLANG_SUPPRESS_WARNING_WITH_PUSH(w) \ DOCTEST_CLANG_SUPPRESS_WARNING_PUSH DOCTEST_CLANG_SUPPRESS_WARNING(w) #define DOCTEST_MSVC_SUPPRESS_WARNING_WITH_PUSH(w) #define DOCTEST_GCC_SUPPRESS_WARNING_WITH_PUSH(w) #elif DOCTEST_GCC #define DOCTEST_PRAGMA_TO_STR(x) _Pragma(#x) #define DOCTEST_CLANG_SUPPRESS_WARNING_PUSH #define DOCTEST_MSVC_SUPPRESS_WARNING_PUSH #if DOCTEST_GCC >= DOCTEST_COMPILER(4, 7, 0) #define DOCTEST_GCC_SUPPRESS_WARNING_PUSH _Pragma("GCC diagnostic push") #else // GCC 4.7+ #define DOCTEST_GCC_SUPPRESS_WARNING_PUSH #endif // GCC 4.7+ #define DOCTEST_CLANG_SUPPRESS_WARNING(w) #define DOCTEST_MSVC_SUPPRESS_WARNING(w) #define DOCTEST_GCC_SUPPRESS_WARNING(w) DOCTEST_PRAGMA_TO_STR(GCC diagnostic ignored w) #define DOCTEST_CLANG_SUPPRESS_WARNING_POP #define DOCTEST_MSVC_SUPPRESS_WARNING_POP #if DOCTEST_GCC >= DOCTEST_COMPILER(4, 7, 0) #define DOCTEST_GCC_SUPPRESS_WARNING_POP _Pragma("GCC diagnostic pop") #else // GCC 4.7+ #define DOCTEST_GCC_SUPPRESS_WARNING_POP #endif // GCC 4.7+ #define DOCTEST_CLANG_SUPPRESS_WARNING_WITH_PUSH(w) #define DOCTEST_MSVC_SUPPRESS_WARNING_WITH_PUSH(w) #define DOCTEST_GCC_SUPPRESS_WARNING_WITH_PUSH(w) \ DOCTEST_GCC_SUPPRESS_WARNING_PUSH DOCTEST_GCC_SUPPRESS_WARNING(w) #elif DOCTEST_MSVC #define DOCTEST_PRAGMA_TO_STR(x) #define DOCTEST_CLANG_SUPPRESS_WARNING_PUSH #define DOCTEST_MSVC_SUPPRESS_WARNING_PUSH __pragma(warning(push)) #define DOCTEST_GCC_SUPPRESS_WARNING_PUSH #define DOCTEST_CLANG_SUPPRESS_WARNING(w) #define DOCTEST_MSVC_SUPPRESS_WARNING(w) __pragma(warning(disable : w)) #define DOCTEST_GCC_SUPPRESS_WARNING(w) #define DOCTEST_CLANG_SUPPRESS_WARNING_POP #define DOCTEST_MSVC_SUPPRESS_WARNING_POP __pragma(warning(pop)) #define DOCTEST_GCC_SUPPRESS_WARNING_POP #define DOCTEST_CLANG_SUPPRESS_WARNING_WITH_PUSH(w) #define DOCTEST_MSVC_SUPPRESS_WARNING_WITH_PUSH(w) \ DOCTEST_MSVC_SUPPRESS_WARNING_PUSH DOCTEST_MSVC_SUPPRESS_WARNING(w) #define DOCTEST_GCC_SUPPRESS_WARNING_WITH_PUSH(w) #endif // different compilers - warning suppression macros #ifndef DOCTEST_CLANG_HAS_WARNING #define DOCTEST_CLANG_HAS_WARNING(x) 1 #endif // DOCTEST_CLANG_HAS_WARNING #ifndef DOCTEST_CLANG_HAS_FEATURE #define DOCTEST_CLANG_HAS_FEATURE(x) 0 #endif // DOCTEST_CLANG_HAS_FEATURE // ================================================================================================= // == COMPILER WARNINGS ============================================================================ // ================================================================================================= DOCTEST_CLANG_SUPPRESS_WARNING_PUSH DOCTEST_CLANG_SUPPRESS_WARNING("-Wunknown-pragmas") DOCTEST_CLANG_SUPPRESS_WARNING("-Wnon-virtual-dtor") DOCTEST_CLANG_SUPPRESS_WARNING("-Wweak-vtables") DOCTEST_CLANG_SUPPRESS_WARNING("-Wpadded") DOCTEST_CLANG_SUPPRESS_WARNING("-Wdeprecated") DOCTEST_CLANG_SUPPRESS_WARNING("-Wmissing-prototypes") DOCTEST_CLANG_SUPPRESS_WARNING("-Wunused-local-typedef") DOCTEST_CLANG_SUPPRESS_WARNING("-Wc++11-long-long") #if DOCTEST_CLANG && DOCTEST_CLANG_HAS_WARNING("-Wzero-as-null-pointer-constant") DOCTEST_CLANG_SUPPRESS_WARNING("-Wzero-as-null-pointer-constant") #endif // clang - 0 as null DOCTEST_GCC_SUPPRESS_WARNING_PUSH DOCTEST_GCC_SUPPRESS_WARNING("-Wunknown-pragmas") DOCTEST_GCC_SUPPRESS_WARNING("-Wpragmas") DOCTEST_GCC_SUPPRESS_WARNING("-Weffc++") DOCTEST_GCC_SUPPRESS_WARNING("-Wstrict-overflow") DOCTEST_GCC_SUPPRESS_WARNING("-Wstrict-aliasing") DOCTEST_GCC_SUPPRESS_WARNING("-Wctor-dtor-privacy") DOCTEST_GCC_SUPPRESS_WARNING("-Wmissing-declarations") DOCTEST_GCC_SUPPRESS_WARNING("-Wnon-virtual-dtor") DOCTEST_GCC_SUPPRESS_WARNING("-Winline") DOCTEST_GCC_SUPPRESS_WARNING("-Wlong-long") DOCTEST_GCC_SUPPRESS_WARNING("-Wzero-as-null-pointer-constant") DOCTEST_GCC_SUPPRESS_WARNING("-Wunused-local-typedefs") DOCTEST_GCC_SUPPRESS_WARNING("-Wuseless-cast") DOCTEST_MSVC_SUPPRESS_WARNING_PUSH DOCTEST_MSVC_SUPPRESS_WARNING(4616) // invalid compiler warning DOCTEST_MSVC_SUPPRESS_WARNING(4619) // invalid compiler warning DOCTEST_MSVC_SUPPRESS_WARNING(4996) // The compiler encountered a deprecated declaration DOCTEST_MSVC_SUPPRESS_WARNING(4706) // assignment within conditional expression DOCTEST_MSVC_SUPPRESS_WARNING(4512) // 'class' : assignment operator could not be generated DOCTEST_MSVC_SUPPRESS_WARNING(4127) // conditional expression is constant DOCTEST_MSVC_SUPPRESS_WARNING(4820) // padding DOCTEST_MSVC_SUPPRESS_WARNING(4625) // copy constructor was implicitly defined as deleted DOCTEST_MSVC_SUPPRESS_WARNING(4626) // assignment operator was implicitly defined as deleted DOCTEST_MSVC_SUPPRESS_WARNING(5027) // move assignment operator was implicitly defined as deleted DOCTEST_MSVC_SUPPRESS_WARNING(5026) // move constructor was implicitly defined as deleted DOCTEST_MSVC_SUPPRESS_WARNING(4623) // default constructor was implicitly defined as deleted DOCTEST_MSVC_SUPPRESS_WARNING(4640) // construction of local static object is not thread-safe // C4548 - expression before comma has no effect; expected expression with side - effect // C4986 - exception specification does not match previous declaration // C4350 - behavior change: 'member1' called instead of 'member2' // C4668 - 'x' is not defined as a preprocessor macro, replacing with '0' for '#if/#elif' // C4365 - conversion from 'int' to 'unsigned long', signed/unsigned mismatch // C4774 - format string expected in argument 'x' is not a string literal // C4820 - padding in structs // only 4 should be disabled globally: // - C4514 # unreferenced inline function has been removed // - C4571 # SEH related // - C4710 # function not inlined // - C4711 # function 'x' selected for automatic inline expansion #define DOCTEST_MAKE_STD_HEADERS_CLEAN_FROM_WARNINGS_ON_WALL_BEGIN \ DOCTEST_MSVC_SUPPRESS_WARNING_PUSH \ DOCTEST_MSVC_SUPPRESS_WARNING(4548) \ DOCTEST_MSVC_SUPPRESS_WARNING(4986) \ DOCTEST_MSVC_SUPPRESS_WARNING(4350) \ DOCTEST_MSVC_SUPPRESS_WARNING(4668) \ DOCTEST_MSVC_SUPPRESS_WARNING(4365) \ DOCTEST_MSVC_SUPPRESS_WARNING(4774) \ DOCTEST_MSVC_SUPPRESS_WARNING(4820) \ DOCTEST_MSVC_SUPPRESS_WARNING(4625) \ DOCTEST_MSVC_SUPPRESS_WARNING(4626) \ DOCTEST_MSVC_SUPPRESS_WARNING(5027) \ DOCTEST_MSVC_SUPPRESS_WARNING(5026) \ DOCTEST_MSVC_SUPPRESS_WARNING(4623) \ DOCTEST_MSVC_SUPPRESS_WARNING(5039) #define DOCTEST_MAKE_STD_HEADERS_CLEAN_FROM_WARNINGS_ON_WALL_END DOCTEST_MSVC_SUPPRESS_WARNING_POP // ================================================================================================= // == FEATURE DETECTION ============================================================================ // ================================================================================================= #if __cplusplus >= 201103L #ifndef DOCTEST_CONFIG_WITH_DELETED_FUNCTIONS #define DOCTEST_CONFIG_WITH_DELETED_FUNCTIONS #endif // DOCTEST_CONFIG_WITH_DELETED_FUNCTIONS #ifndef DOCTEST_CONFIG_WITH_RVALUE_REFERENCES #define DOCTEST_CONFIG_WITH_RVALUE_REFERENCES #endif // DOCTEST_CONFIG_WITH_RVALUE_REFERENCES #ifndef DOCTEST_CONFIG_WITH_NULLPTR #define DOCTEST_CONFIG_WITH_NULLPTR #endif // DOCTEST_CONFIG_WITH_NULLPTR #ifndef DOCTEST_CONFIG_WITH_LONG_LONG #define DOCTEST_CONFIG_WITH_LONG_LONG #endif // DOCTEST_CONFIG_WITH_LONG_LONG #ifndef DOCTEST_CONFIG_WITH_STATIC_ASSERT #define DOCTEST_CONFIG_WITH_STATIC_ASSERT #endif // DOCTEST_CONFIG_WITH_STATIC_ASSERT #ifndef DOCTEST_CONFIG_WITH_VARIADIC_MACROS #define DOCTEST_CONFIG_WITH_VARIADIC_MACROS #endif // DOCTEST_CONFIG_WITH_VARIADIC_MACROS #endif // __cplusplus >= 201103L // MSVC C++11 feature support table: https://msdn.microsoft.com/en-us/library/hh567368.aspx // GCC C++11 feature support table: https://gcc.gnu.org/projects/cxx-status.html // MSVC version table: // MSVC++ 15.0 _MSC_VER == 1910 (Visual Studio 2017) // MSVC++ 14.0 _MSC_VER == 1900 (Visual Studio 2015) // MSVC++ 12.0 _MSC_VER == 1800 (Visual Studio 2013) // MSVC++ 11.0 _MSC_VER == 1700 (Visual Studio 2012) // MSVC++ 10.0 _MSC_VER == 1600 (Visual Studio 2010) // MSVC++ 9.0 _MSC_VER == 1500 (Visual Studio 2008) // MSVC++ 8.0 _MSC_VER == 1400 (Visual Studio 2005) // deleted functions #ifndef DOCTEST_CONFIG_WITH_DELETED_FUNCTIONS #if DOCTEST_MSVC >= DOCTEST_COMPILER(18, 0, 0) #define DOCTEST_CONFIG_WITH_DELETED_FUNCTIONS #endif // MSVC #if DOCTEST_CLANG && DOCTEST_CLANG_HAS_FEATURE(cxx_deleted_functions) #define DOCTEST_CONFIG_WITH_DELETED_FUNCTIONS #endif // clang #if DOCTEST_GCC >= DOCTEST_COMPILER(4, 5, 0) && defined(__GXX_EXPERIMENTAL_CXX0X__) #define DOCTEST_CONFIG_WITH_DELETED_FUNCTIONS #endif // GCC #endif // DOCTEST_CONFIG_WITH_DELETED_FUNCTIONS #if defined(DOCTEST_CONFIG_NO_DELETED_FUNCTIONS) && defined(DOCTEST_CONFIG_WITH_DELETED_FUNCTIONS) #undef DOCTEST_CONFIG_WITH_DELETED_FUNCTIONS #endif // DOCTEST_CONFIG_NO_DELETED_FUNCTIONS // rvalue references #ifndef DOCTEST_CONFIG_WITH_RVALUE_REFERENCES #if DOCTEST_MSVC >= DOCTEST_COMPILER(16, 0, 0) #define DOCTEST_CONFIG_WITH_RVALUE_REFERENCES #endif // MSVC #if DOCTEST_CLANG && DOCTEST_CLANG_HAS_FEATURE(cxx_rvalue_references) #define DOCTEST_CONFIG_WITH_RVALUE_REFERENCES #endif // clang #if DOCTEST_GCC >= DOCTEST_COMPILER(4, 3, 0) && defined(__GXX_EXPERIMENTAL_CXX0X__) #define DOCTEST_CONFIG_WITH_RVALUE_REFERENCES #endif // GCC #endif // DOCTEST_CONFIG_WITH_RVALUE_REFERENCES #if defined(DOCTEST_CONFIG_NO_RVALUE_REFERENCES) && defined(DOCTEST_CONFIG_WITH_RVALUE_REFERENCES) #undef DOCTEST_CONFIG_WITH_RVALUE_REFERENCES #endif // DOCTEST_CONFIG_NO_RVALUE_REFERENCES // nullptr #ifndef DOCTEST_CONFIG_WITH_NULLPTR #if DOCTEST_CLANG && DOCTEST_CLANG_HAS_FEATURE(cxx_nullptr) #define DOCTEST_CONFIG_WITH_NULLPTR #endif // clang #if DOCTEST_GCC >= DOCTEST_COMPILER(4, 6, 0) && defined(__GXX_EXPERIMENTAL_CXX0X__) #define DOCTEST_CONFIG_WITH_NULLPTR #endif // GCC #if DOCTEST_MSVC >= DOCTEST_COMPILER(16, 0, 0) #define DOCTEST_CONFIG_WITH_NULLPTR #endif // MSVC #endif // DOCTEST_CONFIG_WITH_NULLPTR #if defined(DOCTEST_CONFIG_NO_NULLPTR) && defined(DOCTEST_CONFIG_WITH_NULLPTR) #undef DOCTEST_CONFIG_WITH_NULLPTR #endif // DOCTEST_CONFIG_NO_NULLPTR // variadic macros #ifndef DOCTEST_CONFIG_WITH_VARIADIC_MACROS #if DOCTEST_MSVC >= DOCTEST_COMPILER(14, 0, 0) && !defined(__EDGE__) #define DOCTEST_CONFIG_WITH_VARIADIC_MACROS #endif // MSVC #if(DOCTEST_CLANG || DOCTEST_GCC >= DOCTEST_COMPILER(4, 1, 0)) && \ defined(__GXX_EXPERIMENTAL_CXX0X__) #define DOCTEST_CONFIG_WITH_VARIADIC_MACROS #endif // GCC and clang #endif // DOCTEST_CONFIG_WITH_VARIADIC_MACROS #if defined(DOCTEST_CONFIG_NO_VARIADIC_MACROS) && defined(DOCTEST_CONFIG_WITH_VARIADIC_MACROS) #undef DOCTEST_CONFIG_WITH_VARIADIC_MACROS #endif // DOCTEST_CONFIG_NO_VARIADIC_MACROS // long long #ifndef DOCTEST_CONFIG_WITH_LONG_LONG #if DOCTEST_MSVC >= DOCTEST_COMPILER(14, 0, 0) #define DOCTEST_CONFIG_WITH_LONG_LONG #endif // MSVC #if(DOCTEST_CLANG || DOCTEST_GCC >= DOCTEST_COMPILER(4, 5, 0)) && \ defined(__GXX_EXPERIMENTAL_CXX0X__) #define DOCTEST_CONFIG_WITH_LONG_LONG #endif // GCC and clang #endif // DOCTEST_CONFIG_WITH_LONG_LONG #if defined(DOCTEST_CONFIG_NO_LONG_LONG) && defined(DOCTEST_CONFIG_WITH_LONG_LONG) #undef DOCTEST_CONFIG_WITH_LONG_LONG #endif // DOCTEST_CONFIG_NO_LONG_LONG // static_assert #ifndef DOCTEST_CONFIG_WITH_STATIC_ASSERT #if DOCTEST_CLANG && DOCTEST_CLANG_HAS_FEATURE(cxx_static_assert) #define DOCTEST_CONFIG_WITH_STATIC_ASSERT #endif // clang #if DOCTEST_GCC >= DOCTEST_COMPILER(4, 3, 0) && defined(__GXX_EXPERIMENTAL_CXX0X__) #define DOCTEST_CONFIG_WITH_STATIC_ASSERT #endif // GCC #if DOCTEST_MSVC >= DOCTEST_COMPILER(16, 0, 0) #define DOCTEST_CONFIG_WITH_STATIC_ASSERT #endif // MSVC #endif // DOCTEST_CONFIG_WITH_STATIC_ASSERT #if defined(DOCTEST_CONFIG_NO_STATIC_ASSERT) && defined(DOCTEST_CONFIG_WITH_STATIC_ASSERT) #undef DOCTEST_CONFIG_WITH_STATIC_ASSERT #endif // DOCTEST_CONFIG_NO_STATIC_ASSERT // other stuff... #if defined(DOCTEST_CONFIG_WITH_RVALUE_REFERENCES) || defined(DOCTEST_CONFIG_WITH_LONG_LONG) || \ defined(DOCTEST_CONFIG_WITH_DELETED_FUNCTIONS) || defined(DOCTEST_CONFIG_WITH_NULLPTR) || \ defined(DOCTEST_CONFIG_WITH_VARIADIC_MACROS) || defined(DOCTEST_CONFIG_WITH_STATIC_ASSERT) #define DOCTEST_NO_CPP11_COMPAT #endif // c++11 stuff #if defined(DOCTEST_NO_CPP11_COMPAT) DOCTEST_CLANG_SUPPRESS_WARNING("-Wc++98-compat") DOCTEST_CLANG_SUPPRESS_WARNING("-Wc++98-compat-pedantic") #endif // DOCTEST_NO_CPP11_COMPAT #if DOCTEST_MSVC && !defined(DOCTEST_CONFIG_WINDOWS_SEH) #define DOCTEST_CONFIG_WINDOWS_SEH #endif // MSVC #if defined(DOCTEST_CONFIG_NO_WINDOWS_SEH) && defined(DOCTEST_CONFIG_WINDOWS_SEH) #undef DOCTEST_CONFIG_WINDOWS_SEH #endif // DOCTEST_CONFIG_NO_WINDOWS_SEH #if !defined(_WIN32) && !defined(__QNX__) && !defined(DOCTEST_CONFIG_POSIX_SIGNALS) #define DOCTEST_CONFIG_POSIX_SIGNALS #endif // _WIN32 #if defined(DOCTEST_CONFIG_NO_POSIX_SIGNALS) && defined(DOCTEST_CONFIG_POSIX_SIGNALS) #undef DOCTEST_CONFIG_POSIX_SIGNALS #endif // DOCTEST_CONFIG_NO_POSIX_SIGNALS #ifndef DOCTEST_CONFIG_NO_EXCEPTIONS #if(DOCTEST_GCC || DOCTEST_CLANG) && !defined(__EXCEPTIONS) #define DOCTEST_CONFIG_NO_EXCEPTIONS #endif // clang and gcc // in MSVC _HAS_EXCEPTIONS is defined in a header instead of as a project define // so we can't do the automatic detection for MSVC without including some header #endif // DOCTEST_CONFIG_NO_EXCEPTIONS #ifdef DOCTEST_CONFIG_NO_EXCEPTIONS_BUT_WITH_ALL_ASSERTS #ifndef DOCTEST_CONFIG_NO_EXCEPTIONS #define DOCTEST_CONFIG_NO_EXCEPTIONS #endif // DOCTEST_CONFIG_NO_EXCEPTIONS #endif // DOCTEST_CONFIG_NO_EXCEPTIONS_BUT_WITH_ALL_ASSERTS #if defined(DOCTEST_CONFIG_NO_EXCEPTIONS) && !defined(DOCTEST_CONFIG_NO_TRY_CATCH_IN_ASSERTS) #define DOCTEST_CONFIG_NO_TRY_CATCH_IN_ASSERTS #endif // DOCTEST_CONFIG_NO_EXCEPTIONS && !DOCTEST_CONFIG_NO_TRY_CATCH_IN_ASSERTS #if defined(DOCTEST_CONFIG_IMPLEMENT_WITH_MAIN) && !defined(DOCTEST_CONFIG_IMPLEMENT) #define DOCTEST_CONFIG_IMPLEMENT #endif // DOCTEST_CONFIG_IMPLEMENT_WITH_MAIN #if defined _WIN32 || defined __CYGWIN__ #if DOCTEST_MSVC #define DOCTEST_SYMBOL_EXPORT __declspec(dllexport) #define DOCTEST_SYMBOL_IMPORT __declspec(dllimport) #else // MSVC #define DOCTEST_SYMBOL_EXPORT __attribute__((dllexport)) #define DOCTEST_SYMBOL_IMPORT __attribute__((dllimport)) #endif // MSVC #else // _WIN32 #define DOCTEST_SYMBOL_EXPORT __attribute__((visibility("default"))) #define DOCTEST_SYMBOL_IMPORT #endif // _WIN32 #ifdef DOCTEST_CONFIG_IMPLEMENTATION_IN_DLL #ifdef DOCTEST_CONFIG_IMPLEMENT #define DOCTEST_INTERFACE DOCTEST_SYMBOL_EXPORT #else // DOCTEST_CONFIG_IMPLEMENT #define DOCTEST_INTERFACE DOCTEST_SYMBOL_IMPORT #endif // DOCTEST_CONFIG_IMPLEMENT #else // DOCTEST_CONFIG_IMPLEMENTATION_IN_DLL #define DOCTEST_INTERFACE #endif // DOCTEST_CONFIG_IMPLEMENTATION_IN_DLL #if DOCTEST_MSVC #define DOCTEST_NOINLINE __declspec(noinline) #define DOCTEST_UNUSED #define DOCTEST_ALIGNMENT(x) #else // MSVC #define DOCTEST_NOINLINE __attribute__((noinline)) #define DOCTEST_UNUSED __attribute__((unused)) #define DOCTEST_ALIGNMENT(x) __attribute__((aligned(x))) #endif // MSVC #ifndef DOCTEST_CONFIG_NUM_CAPTURES_ON_STACK #define DOCTEST_CONFIG_NUM_CAPTURES_ON_STACK 5 #endif // DOCTEST_CONFIG_NUM_CAPTURES_ON_STACK // ================================================================================================= // == FEATURE DETECTION END ======================================================================== // ================================================================================================= // internal macros for string concatenation and anonymous variable name generation #define DOCTEST_CAT_IMPL(s1, s2) s1##s2 #define DOCTEST_CAT(s1, s2) DOCTEST_CAT_IMPL(s1, s2) #ifdef __COUNTER__ // not standard and may be missing for some compilers #define DOCTEST_ANONYMOUS(x) DOCTEST_CAT(x, __COUNTER__) #else // __COUNTER__ #define DOCTEST_ANONYMOUS(x) DOCTEST_CAT(x, __LINE__) #endif // __COUNTER__ // macro for making a string out of an identifier #ifdef DOCTEST_CONFIG_WITH_VARIADIC_MACROS #define DOCTEST_TOSTR_IMPL(...) #__VA_ARGS__ #define DOCTEST_TOSTR(...) DOCTEST_TOSTR_IMPL(__VA_ARGS__) #else // DOCTEST_CONFIG_WITH_VARIADIC_MACROS #define DOCTEST_TOSTR_IMPL(x) #x #define DOCTEST_TOSTR(x) DOCTEST_TOSTR_IMPL(x) #endif // DOCTEST_CONFIG_WITH_VARIADIC_MACROS // counts the number of elements in a C string #define DOCTEST_COUNTOF(x) (sizeof(x) / sizeof(x[0])) #ifndef DOCTEST_CONFIG_ASSERTION_PARAMETERS_BY_VALUE #define DOCTEST_REF_WRAP(x) x& #else // DOCTEST_CONFIG_ASSERTION_PARAMETERS_BY_VALUE #define DOCTEST_REF_WRAP(x) x #endif // DOCTEST_CONFIG_ASSERTION_PARAMETERS_BY_VALUE // not using __APPLE__ because... this is how Catch does it #if defined(__MAC_OS_X_VERSION_MIN_REQUIRED) #define DOCTEST_PLATFORM_MAC #elif defined(__IPHONE_OS_VERSION_MIN_REQUIRED) #define DOCTEST_PLATFORM_IPHONE #elif defined(_WIN32) #define DOCTEST_PLATFORM_WINDOWS #else #define DOCTEST_PLATFORM_LINUX #endif #define DOCTEST_GLOBAL_NO_WARNINGS(var) \ DOCTEST_CLANG_SUPPRESS_WARNING_WITH_PUSH("-Wglobal-constructors") static int var DOCTEST_UNUSED #define DOCTEST_GLOBAL_NO_WARNINGS_END() DOCTEST_CLANG_SUPPRESS_WARNING_POP // should probably take a look at https://github.com/scottt/debugbreak #ifdef DOCTEST_PLATFORM_MAC #define DOCTEST_BREAK_INTO_DEBUGGER() __asm__("int $3\n" : :) #elif DOCTEST_MSVC #define DOCTEST_BREAK_INTO_DEBUGGER() __debugbreak() #elif defined(__MINGW32__) extern "C" __declspec(dllimport) void __stdcall DebugBreak(); #define DOCTEST_BREAK_INTO_DEBUGGER() ::DebugBreak() #else // linux #define DOCTEST_BREAK_INTO_DEBUGGER() ((void)0) #endif // linux #if DOCTEST_CLANG // to detect if libc++ is being used with clang (the _LIBCPP_VERSION identifier) #include #endif // clang #ifdef _LIBCPP_VERSION // not forward declaring ostream for libc++ because I had some problems (inline namespaces vs c++98) // so the header is used - also it is very light and doesn't drag a ton of stuff #include #else // _LIBCPP_VERSION #ifndef DOCTEST_CONFIG_USE_IOSFWD namespace std { template struct char_traits; template <> struct char_traits; template class basic_ostream; typedef basic_ostream > ostream; } // namespace std #else // DOCTEST_CONFIG_USE_IOSFWD #include #endif // DOCTEST_CONFIG_USE_IOSFWD #endif // _LIBCPP_VERSION // static assert macro - because of the c++98 support requires that the message is an // identifier (no spaces and not a C string) - example without quotes: I_am_a_message // taken from here: http://stackoverflow.com/a/1980156/3162383 #ifdef DOCTEST_CONFIG_WITH_STATIC_ASSERT #define DOCTEST_STATIC_ASSERT(expression, message) static_assert(expression, #message) #else // DOCTEST_CONFIG_WITH_STATIC_ASSERT #define DOCTEST_STATIC_ASSERT(expression, message) \ struct DOCTEST_CAT(__static_assertion_at_line_, __LINE__) \ { \ doctest::detail::static_assert_impl::StaticAssertion((expression))> \ DOCTEST_CAT(DOCTEST_CAT(DOCTEST_CAT(STATIC_ASSERTION_FAILED_AT_LINE_, __LINE__), \ _), \ message); \ }; \ typedef doctest::detail::static_assert_impl::StaticAssertionTest( \ sizeof(DOCTEST_CAT(__static_assertion_at_line_, __LINE__)))> \ DOCTEST_CAT(__static_assertion_test_at_line_, __LINE__) #endif // DOCTEST_CONFIG_WITH_STATIC_ASSERT #ifdef DOCTEST_CONFIG_WITH_NULLPTR #ifdef _LIBCPP_VERSION #include #else // _LIBCPP_VERSION namespace std { typedef decltype(nullptr) nullptr_t; } #endif // _LIBCPP_VERSION #endif // DOCTEST_CONFIG_WITH_NULLPTR #ifndef DOCTEST_CONFIG_DISABLE #ifdef DOCTEST_CONFIG_INCLUDE_TYPE_TRAITS #include #endif // DOCTEST_CONFIG_INCLUDE_TYPE_TRAITS namespace doctest { namespace detail { struct TestSuite { const char* m_test_suite; const char* m_description; bool m_skip; bool m_may_fail; bool m_should_fail; int m_expected_failures; double m_timeout; TestSuite& operator*(const char* in) { m_test_suite = in; // clear state m_description = 0; m_skip = false; m_may_fail = false; m_should_fail = false; m_expected_failures = 0; m_timeout = 0; return *this; } template TestSuite& operator*(const T& in) { in.fill(*this); return *this; } }; } // namespace detail } // namespace doctest // in a separate namespace outside of doctest because the DOCTEST_TEST_SUITE macro // introduces an anonymous namespace in which getCurrentTestSuite gets overridden namespace doctest_detail_test_suite_ns { DOCTEST_INTERFACE doctest::detail::TestSuite& getCurrentTestSuite(); } // namespace doctest_detail_test_suite_ns #endif // DOCTEST_CONFIG_DISABLE namespace doctest { // A 24 byte string class (can be as small as 17 for x64 and 13 for x86) that can hold strings with length // of up to 23 chars on the stack before going on the heap - the last byte of the buffer is used for: // - "is small" bit - the highest bit - if "0" then it is small - otherwise its "1" (128) // - if small - capacity left before going on the heap - using the lowest 5 bits // - if small - 2 bits are left unused - the second and third highest ones // - if small - acts as a null terminator if strlen() is 23 (24 including the null terminator) // and the "is small" bit remains "0" ("as well as the capacity left") so its OK // Idea taken from this lecture about the string implementation of facebook/folly - fbstring // https://www.youtube.com/watch?v=kPR8h4-qZdk // TODO: // - optimizations - like not deleting memory unnecessarily in operator= and etc. // - resize/reserve/clear // - substr // - replace // - back/front // - iterator stuff // - find & friends // - push_back/pop_back // - assign/insert/erase // - relational operators as free functions - taking const char* as one of the params class DOCTEST_INTERFACE String { static const unsigned len = 24; //!OCLINT avoid private static members static const unsigned last = len - 1; //!OCLINT avoid private static members struct view // len should be more than sizeof(view) - because of the final byte for flags { char* ptr; unsigned size; unsigned capacity; }; union { char buf[len]; view data; }; void copy(const String& other); void setOnHeap() { *reinterpret_cast(&buf[last]) = 128; } void setLast(unsigned in = last) { buf[last] = char(in); } public: String() { buf[0] = '\0'; setLast(); } String(const char* in); String(const String& other) { copy(other); } ~String() { if(!isOnStack()) delete[] data.ptr; } // GCC 4.9/5/6 report Wstrict-overflow when optimizations are ON and it got inlined in the vector class somewhere... // see commit 574ef95f0cd379118be5011704664e4b5351f1e0 and build https://travis-ci.org/onqtam/doctest/builds/230671611 DOCTEST_NOINLINE String& operator=(const String& other) { if(this != &other) { if(!isOnStack()) delete[] data.ptr; copy(other); } return *this; } String& operator+=(const String& other); String operator+(const String& other) const { return String(*this) += other; } #ifdef DOCTEST_CONFIG_WITH_RVALUE_REFERENCES String(String&& other); String& operator=(String&& other); #endif // DOCTEST_CONFIG_WITH_RVALUE_REFERENCES bool isOnStack() const { return (buf[last] & 128) == 0; } char operator[](unsigned i) const { return const_cast(this)->operator[](i); } // NOLINT char& operator[](unsigned i) { if(isOnStack()) return reinterpret_cast(buf)[i]; return data.ptr[i]; } const char* c_str() const { return const_cast(this)->c_str(); } // NOLINT char* c_str() { if(isOnStack()) return reinterpret_cast(buf); return data.ptr; } DOCTEST_GCC_SUPPRESS_WARNING_WITH_PUSH("-Wmaybe-uninitialized") unsigned size() const { if(isOnStack()) return last - (unsigned(buf[last]) & 31); // using "last" would work only if "len" is 32 return data.size; } DOCTEST_GCC_SUPPRESS_WARNING_POP unsigned capacity() const { if(isOnStack()) return len; return data.capacity; } int compare(const char* other, bool no_case = false) const; int compare(const String& other, bool no_case = false) const; }; // clang-format off inline bool operator==(const String& lhs, const String& rhs) { return lhs.compare(rhs) == 0; } inline bool operator!=(const String& lhs, const String& rhs) { return lhs.compare(rhs) != 0; } inline bool operator< (const String& lhs, const String& rhs) { return lhs.compare(rhs) < 0; } inline bool operator> (const String& lhs, const String& rhs) { return lhs.compare(rhs) > 0; } inline bool operator<=(const String& lhs, const String& rhs) { return (lhs != rhs) ? lhs.compare(rhs) < 0 : true; } inline bool operator>=(const String& lhs, const String& rhs) { return (lhs != rhs) ? lhs.compare(rhs) > 0 : true; } // clang-format on DOCTEST_INTERFACE std::ostream& operator<<(std::ostream& s, const String& in); namespace detail { #ifndef DOCTEST_CONFIG_WITH_STATIC_ASSERT namespace static_assert_impl { template struct StaticAssertion; template <> struct StaticAssertion {}; template struct StaticAssertionTest {}; } // namespace static_assert_impl #endif // DOCTEST_CONFIG_WITH_STATIC_ASSERT template struct enable_if {}; template struct enable_if { typedef TYPE type; }; template struct deferred_false // cppcheck-suppress unusedStructMember { static const bool value = false; }; // to silence the warning "-Wzero-as-null-pointer-constant" only for gcc 5 for the Approx template ctor - pragmas don't work for it... inline void* getNull() { return 0; } namespace has_insertion_operator_impl { typedef char no; typedef char yes[2]; struct any_t { template // cppcheck-suppress noExplicitConstructor any_t(const DOCTEST_REF_WRAP(T)); }; yes& testStreamable(std::ostream&); no testStreamable(no); no operator<<(const std::ostream&, const any_t&); template struct has_insertion_operator { static std::ostream& s; static const DOCTEST_REF_WRAP(T) t; static const bool value = sizeof(testStreamable(s << t)) == sizeof(yes); }; } // namespace has_insertion_operator_impl template struct has_insertion_operator : has_insertion_operator_impl::has_insertion_operator {}; DOCTEST_INTERFACE void my_memcpy(void* dest, const void* src, unsigned num); DOCTEST_INTERFACE std::ostream* createStream(); DOCTEST_INTERFACE String getStreamResult(std::ostream*); DOCTEST_INTERFACE void freeStream(std::ostream*); template struct StringMakerBase { template static String convert(const DOCTEST_REF_WRAP(T)) { return "{?}"; } }; template <> struct StringMakerBase { template static String convert(const DOCTEST_REF_WRAP(T) in) { std::ostream* s = createStream(); *s << in; String result = getStreamResult(s); freeStream(s); return result; } }; DOCTEST_INTERFACE String rawMemoryToString(const void* object, unsigned size); template String rawMemoryToString(const DOCTEST_REF_WRAP(T) object) { return rawMemoryToString(&object, sizeof(object)); } class NullType { }; template struct Typelist { typedef T Head; typedef U Tail; }; // type of recursive function template struct ForEachType; // Recursion rule template struct ForEachType, Callable> : public ForEachType { enum { value = 1 + ForEachType::value }; explicit ForEachType(Callable& callable) : ForEachType(callable) { #if DOCTEST_MSVC && DOCTEST_MSVC < DOCTEST_COMPILER(19, 10, 0) callable.operator()(); #else // MSVC callable.template operator()(); #endif // MSVC } }; // Recursion end template struct ForEachType, Callable> { public: enum { value = 0 }; explicit ForEachType(Callable& callable) { #if DOCTEST_MSVC && DOCTEST_MSVC < DOCTEST_COMPILER(19, 10, 0) callable.operator()(); #else // MSVC callable.template operator()(); #endif // MSVC } }; template const char* type_to_string() { return "<>"; } } // namespace detail template struct Types { private: typedef typename Types::Result TailResult; public: typedef detail::Typelist Result; }; template <> struct Types<> { typedef detail::NullType Result; }; template struct StringMaker : detail::StringMakerBase::value> {}; template struct StringMaker { template static String convert(U* p) { if(p) return detail::rawMemoryToString(p); return "NULL"; } }; template struct StringMaker { static String convert(R C::*p) { if(p) return detail::rawMemoryToString(p); return "NULL"; } }; template String toString(const DOCTEST_REF_WRAP(T) value) { return StringMaker::convert(value); } #ifdef DOCTEST_CONFIG_TREAT_CHAR_STAR_AS_STRING DOCTEST_INTERFACE String toString(char* in); DOCTEST_INTERFACE String toString(const char* in); #endif // DOCTEST_CONFIG_TREAT_CHAR_STAR_AS_STRING DOCTEST_INTERFACE String toString(bool in); DOCTEST_INTERFACE String toString(float in); DOCTEST_INTERFACE String toString(double in); DOCTEST_INTERFACE String toString(double long in); DOCTEST_INTERFACE String toString(char in); DOCTEST_INTERFACE String toString(char signed in); DOCTEST_INTERFACE String toString(char unsigned in); DOCTEST_INTERFACE String toString(int short in); DOCTEST_INTERFACE String toString(int short unsigned in); DOCTEST_INTERFACE String toString(int in); DOCTEST_INTERFACE String toString(int unsigned in); DOCTEST_INTERFACE String toString(int long in); DOCTEST_INTERFACE String toString(int long unsigned in); #ifdef DOCTEST_CONFIG_WITH_LONG_LONG DOCTEST_INTERFACE String toString(int long long in); DOCTEST_INTERFACE String toString(int long long unsigned in); #endif // DOCTEST_CONFIG_WITH_LONG_LONG #ifdef DOCTEST_CONFIG_WITH_NULLPTR DOCTEST_INTERFACE String toString(std::nullptr_t in); #endif // DOCTEST_CONFIG_WITH_NULLPTR class DOCTEST_INTERFACE Approx { public: explicit Approx(double value); Approx operator()(double value) const { Approx approx(value); approx.epsilon(m_epsilon); approx.scale(m_scale); return approx; } #ifdef DOCTEST_CONFIG_INCLUDE_TYPE_TRAITS template explicit Approx(const T& value, typename detail::enable_if::value>::type* = static_cast(detail::getNull())) { *this = Approx(static_cast(value)); } #endif // DOCTEST_CONFIG_INCLUDE_TYPE_TRAITS // clang-format off // overloads for double - the first one is necessary as it is in the implementation part of doctest // as for the others - keeping them for potentially faster compile times DOCTEST_INTERFACE friend bool operator==(double lhs, Approx const& rhs); 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; } 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; } #ifdef DOCTEST_CONFIG_INCLUDE_TYPE_TRAITS #define DOCTEST_APPROX_PREFIX \ template friend typename detail::enable_if::value, bool>::type DOCTEST_APPROX_PREFIX operator==(const T& lhs, const Approx& rhs) { return operator==(double(lhs), rhs); } DOCTEST_APPROX_PREFIX operator==(const Approx& lhs, const T& rhs) { return operator==(rhs, lhs); } DOCTEST_APPROX_PREFIX operator!=(const T& lhs, const Approx& rhs) { return !operator==(lhs, rhs); } DOCTEST_APPROX_PREFIX operator!=(const Approx& lhs, const T& rhs) { return !operator==(rhs, lhs); } DOCTEST_APPROX_PREFIX operator<=(const T& lhs, const Approx& rhs) { return double(lhs) < rhs.m_value || lhs == rhs; } DOCTEST_APPROX_PREFIX operator<=(const Approx& lhs, const T& rhs) { return lhs.m_value < double(rhs) || lhs == rhs; } DOCTEST_APPROX_PREFIX operator>=(const T& lhs, const Approx& rhs) { return double(lhs) > rhs.m_value || lhs == rhs; } DOCTEST_APPROX_PREFIX operator>=(const Approx& lhs, const T& rhs) { return lhs.m_value > double(rhs) || lhs == rhs; } DOCTEST_APPROX_PREFIX operator< (const T& lhs, const Approx& rhs) { return double(lhs) < rhs.m_value && lhs != rhs; } DOCTEST_APPROX_PREFIX operator< (const Approx& lhs, const T& rhs) { return lhs.m_value < double(rhs) && lhs != rhs; } DOCTEST_APPROX_PREFIX operator> (const T& lhs, const Approx& rhs) { return double(lhs) > rhs.m_value && lhs != rhs; } DOCTEST_APPROX_PREFIX operator> (const Approx& lhs, const T& rhs) { return lhs.m_value > double(rhs) && lhs != rhs; } #undef DOCTEST_APPROX_PREFIX #endif // DOCTEST_CONFIG_INCLUDE_TYPE_TRAITS // clang-format on Approx& epsilon(double newEpsilon) { m_epsilon = newEpsilon; return *this; } #ifdef DOCTEST_CONFIG_INCLUDE_TYPE_TRAITS template typename detail::enable_if::value, Approx&>::type epsilon( const T& newEpsilon) { m_epsilon = static_cast(newEpsilon); return *this; } #endif // DOCTEST_CONFIG_INCLUDE_TYPE_TRAITS Approx& scale(double newScale) { m_scale = newScale; return *this; } #ifdef DOCTEST_CONFIG_INCLUDE_TYPE_TRAITS template typename detail::enable_if::value, Approx&>::type scale( const T& newScale) { m_scale = static_cast(newScale); return *this; } #endif // DOCTEST_CONFIG_INCLUDE_TYPE_TRAITS String toString() const; private: double m_epsilon; double m_scale; double m_value; }; template <> inline String toString(const DOCTEST_REF_WRAP(Approx) value) { return value.toString(); } #if !defined(DOCTEST_CONFIG_DISABLE) namespace detail { // the function type this library works with typedef void (*funcType)(); namespace assertType { enum Enum { // macro traits is_warn = 1, is_check = 2, is_require = 4, is_throws = 8, is_throws_as = 16, is_nothrow = 32, is_fast = 64, // not checked anywhere - used just to distinguish the types is_false = 128, is_unary = 256, is_eq = 512, is_ne = 1024, is_lt = 2048, is_gt = 4096, is_ge = 8192, is_le = 16384, // macro types DT_WARN = is_warn, DT_CHECK = is_check, DT_REQUIRE = is_require, DT_WARN_FALSE = is_false | is_warn, DT_CHECK_FALSE = is_false | is_check, DT_REQUIRE_FALSE = is_false | is_require, DT_WARN_THROWS = is_throws | is_warn, DT_CHECK_THROWS = is_throws | is_check, DT_REQUIRE_THROWS = is_throws | is_require, DT_WARN_THROWS_AS = is_throws_as | is_warn, DT_CHECK_THROWS_AS = is_throws_as | is_check, DT_REQUIRE_THROWS_AS = is_throws_as | is_require, DT_WARN_NOTHROW = is_nothrow | is_warn, DT_CHECK_NOTHROW = is_nothrow | is_check, DT_REQUIRE_NOTHROW = is_nothrow | is_require, DT_WARN_EQ = is_eq | is_warn, DT_CHECK_EQ = is_eq | is_check, DT_REQUIRE_EQ = is_eq | is_require, DT_WARN_NE = is_ne | is_warn, DT_CHECK_NE = is_ne | is_check, DT_REQUIRE_NE = is_ne | is_require, DT_WARN_GT = is_gt | is_warn, DT_CHECK_GT = is_gt | is_check, DT_REQUIRE_GT = is_gt | is_require, DT_WARN_LT = is_lt | is_warn, DT_CHECK_LT = is_lt | is_check, DT_REQUIRE_LT = is_lt | is_require, DT_WARN_GE = is_ge | is_warn, DT_CHECK_GE = is_ge | is_check, DT_REQUIRE_GE = is_ge | is_require, DT_WARN_LE = is_le | is_warn, DT_CHECK_LE = is_le | is_check, DT_REQUIRE_LE = is_le | is_require, DT_WARN_UNARY = is_unary | is_warn, DT_CHECK_UNARY = is_unary | is_check, DT_REQUIRE_UNARY = is_unary | is_require, DT_WARN_UNARY_FALSE = is_false | is_unary | is_warn, DT_CHECK_UNARY_FALSE = is_false | is_unary | is_check, DT_REQUIRE_UNARY_FALSE = is_false | is_unary | is_require, DT_FAST_WARN_EQ = is_fast | is_eq | is_warn, DT_FAST_CHECK_EQ = is_fast | is_eq | is_check, DT_FAST_REQUIRE_EQ = is_fast | is_eq | is_require, DT_FAST_WARN_NE = is_fast | is_ne | is_warn, DT_FAST_CHECK_NE = is_fast | is_ne | is_check, DT_FAST_REQUIRE_NE = is_fast | is_ne | is_require, DT_FAST_WARN_GT = is_fast | is_gt | is_warn, DT_FAST_CHECK_GT = is_fast | is_gt | is_check, DT_FAST_REQUIRE_GT = is_fast | is_gt | is_require, DT_FAST_WARN_LT = is_fast | is_lt | is_warn, DT_FAST_CHECK_LT = is_fast | is_lt | is_check, DT_FAST_REQUIRE_LT = is_fast | is_lt | is_require, DT_FAST_WARN_GE = is_fast | is_ge | is_warn, DT_FAST_CHECK_GE = is_fast | is_ge | is_check, DT_FAST_REQUIRE_GE = is_fast | is_ge | is_require, DT_FAST_WARN_LE = is_fast | is_le | is_warn, DT_FAST_CHECK_LE = is_fast | is_le | is_check, DT_FAST_REQUIRE_LE = is_fast | is_le | is_require, DT_FAST_WARN_UNARY = is_fast | is_unary | is_warn, DT_FAST_CHECK_UNARY = is_fast | is_unary | is_check, DT_FAST_REQUIRE_UNARY = is_fast | is_unary | is_require, DT_FAST_WARN_UNARY_FALSE = is_fast | is_false | is_unary | is_warn, DT_FAST_CHECK_UNARY_FALSE = is_fast | is_false | is_unary | is_check, DT_FAST_REQUIRE_UNARY_FALSE = is_fast | is_false | is_unary | is_require }; } // namespace assertType DOCTEST_INTERFACE const char* assertString(assertType::Enum val); // clang-format off template struct decay_array { typedef T type; }; template struct decay_array { typedef T* type; }; template struct decay_array { typedef T* type; }; template struct not_char_pointer { enum { value = 1 }; }; template<> struct not_char_pointer { enum { value = 0 }; }; template<> struct not_char_pointer { enum { value = 0 }; }; template struct can_use_op : not_char_pointer::type> {}; // clang-format on struct TestFailureException { }; DOCTEST_INTERFACE bool checkIfShouldThrow(assertType::Enum at); DOCTEST_INTERFACE void fastAssertThrowIfFlagSet(int flags); struct TestAccessibleContextState { bool no_throw; // to skip exceptions-related assertion macros bool success; // include successful assertions in output }; struct ContextState; DOCTEST_INTERFACE TestAccessibleContextState* getTestsContextState(); struct DOCTEST_INTERFACE SubcaseSignature { const char* m_name; const char* m_file; int m_line; SubcaseSignature(const char* name, const char* file, int line) : m_name(name) , m_file(file) , m_line(line) {} bool operator<(const SubcaseSignature& other) const; }; // cppcheck-suppress copyCtorAndEqOperator struct DOCTEST_INTERFACE Subcase { SubcaseSignature m_signature; bool m_entered; Subcase(const char* name, const char* file, int line); Subcase(const Subcase& other); ~Subcase(); operator bool() const { return m_entered; } }; template String stringifyBinaryExpr(const DOCTEST_REF_WRAP(L) lhs, const char* op, const DOCTEST_REF_WRAP(R) rhs) { return toString(lhs) + op + toString(rhs); } struct DOCTEST_INTERFACE Result { bool m_passed; String m_decomposition; ~Result(); DOCTEST_NOINLINE Result(bool passed = false, const String& decomposition = String()) : m_passed(passed) , m_decomposition(decomposition) {} DOCTEST_NOINLINE Result(const Result& other) : m_passed(other.m_passed) , m_decomposition(other.m_decomposition) {} Result& operator=(const Result& other); operator bool() { return !m_passed; } // clang-format off // forbidding some expressions based on this table: http://en.cppreference.com/w/cpp/language/operator_precedence template Result& operator& (const R&) { DOCTEST_STATIC_ASSERT(deferred_false::value, Expression_Too_Complex_Please_Rewrite_As_Binary_Comparison); return *this; } template Result& operator^ (const R&) { DOCTEST_STATIC_ASSERT(deferred_false::value, Expression_Too_Complex_Please_Rewrite_As_Binary_Comparison); return *this; } template Result& operator| (const R&) { DOCTEST_STATIC_ASSERT(deferred_false::value, Expression_Too_Complex_Please_Rewrite_As_Binary_Comparison); return *this; } template Result& operator&& (const R&) { DOCTEST_STATIC_ASSERT(deferred_false::value, Expression_Too_Complex_Please_Rewrite_As_Binary_Comparison); return *this; } template Result& operator|| (const R&) { DOCTEST_STATIC_ASSERT(deferred_false::value, Expression_Too_Complex_Please_Rewrite_As_Binary_Comparison); return *this; } template Result& operator== (const R&) { DOCTEST_STATIC_ASSERT(deferred_false::value, Expression_Too_Complex_Please_Rewrite_As_Binary_Comparison); return *this; } template Result& operator!= (const R&) { DOCTEST_STATIC_ASSERT(deferred_false::value, Expression_Too_Complex_Please_Rewrite_As_Binary_Comparison); return *this; } template Result& operator< (const R&) { DOCTEST_STATIC_ASSERT(deferred_false::value, Expression_Too_Complex_Please_Rewrite_As_Binary_Comparison); return *this; } template Result& operator> (const R&) { DOCTEST_STATIC_ASSERT(deferred_false::value, Expression_Too_Complex_Please_Rewrite_As_Binary_Comparison); return *this; } template Result& operator<= (const R&) { DOCTEST_STATIC_ASSERT(deferred_false::value, Expression_Too_Complex_Please_Rewrite_As_Binary_Comparison); return *this; } template Result& operator>= (const R&) { DOCTEST_STATIC_ASSERT(deferred_false::value, Expression_Too_Complex_Please_Rewrite_As_Binary_Comparison); return *this; } template Result& operator= (const R&) { DOCTEST_STATIC_ASSERT(deferred_false::value, Expression_Too_Complex_Please_Rewrite_As_Binary_Comparison); return *this; } template Result& operator+= (const R&) { DOCTEST_STATIC_ASSERT(deferred_false::value, Expression_Too_Complex_Please_Rewrite_As_Binary_Comparison); return *this; } template Result& operator-= (const R&) { DOCTEST_STATIC_ASSERT(deferred_false::value, Expression_Too_Complex_Please_Rewrite_As_Binary_Comparison); return *this; } template Result& operator*= (const R&) { DOCTEST_STATIC_ASSERT(deferred_false::value, Expression_Too_Complex_Please_Rewrite_As_Binary_Comparison); return *this; } template Result& operator/= (const R&) { DOCTEST_STATIC_ASSERT(deferred_false::value, Expression_Too_Complex_Please_Rewrite_As_Binary_Comparison); return *this; } template Result& operator%= (const R&) { DOCTEST_STATIC_ASSERT(deferred_false::value, Expression_Too_Complex_Please_Rewrite_As_Binary_Comparison); return *this; } template Result& operator<<=(const R&) { DOCTEST_STATIC_ASSERT(deferred_false::value, Expression_Too_Complex_Please_Rewrite_As_Binary_Comparison); return *this; } template Result& operator>>=(const R&) { DOCTEST_STATIC_ASSERT(deferred_false::value, Expression_Too_Complex_Please_Rewrite_As_Binary_Comparison); return *this; } template Result& operator&= (const R&) { DOCTEST_STATIC_ASSERT(deferred_false::value, Expression_Too_Complex_Please_Rewrite_As_Binary_Comparison); return *this; } template Result& operator^= (const R&) { DOCTEST_STATIC_ASSERT(deferred_false::value, Expression_Too_Complex_Please_Rewrite_As_Binary_Comparison); return *this; } template Result& operator|= (const R&) { DOCTEST_STATIC_ASSERT(deferred_false::value, Expression_Too_Complex_Please_Rewrite_As_Binary_Comparison); return *this; } // clang-format on }; #ifndef DOCTEST_CONFIG_NO_COMPARISON_WARNING_SUPPRESSION DOCTEST_CLANG_SUPPRESS_WARNING_PUSH DOCTEST_CLANG_SUPPRESS_WARNING("-Wsign-conversion") DOCTEST_CLANG_SUPPRESS_WARNING("-Wsign-compare") //DOCTEST_CLANG_SUPPRESS_WARNING("-Wdouble-promotion") //DOCTEST_CLANG_SUPPRESS_WARNING("-Wconversion") //DOCTEST_CLANG_SUPPRESS_WARNING("-Wfloat-equal") DOCTEST_GCC_SUPPRESS_WARNING_PUSH DOCTEST_GCC_SUPPRESS_WARNING("-Wsign-conversion") DOCTEST_GCC_SUPPRESS_WARNING("-Wsign-compare") //DOCTEST_GCC_SUPPRESS_WARNING("-Wdouble-promotion") //DOCTEST_GCC_SUPPRESS_WARNING("-Wconversion") //DOCTEST_GCC_SUPPRESS_WARNING("-Wfloat-equal") DOCTEST_MSVC_SUPPRESS_WARNING_PUSH // http://stackoverflow.com/questions/39479163 what's the difference between C4018 and C4389 DOCTEST_MSVC_SUPPRESS_WARNING(4388) // signed/unsigned mismatch DOCTEST_MSVC_SUPPRESS_WARNING(4389) // 'operator' : signed/unsigned mismatch DOCTEST_MSVC_SUPPRESS_WARNING(4018) // 'expression' : signed/unsigned mismatch //DOCTEST_MSVC_SUPPRESS_WARNING(4805) // 'operation' : unsafe mix of type 'type' and type 'type' in operation #endif // DOCTEST_CONFIG_NO_COMPARISON_WARNING_SUPPRESSION // clang-format off #ifndef DOCTEST_CONFIG_TREAT_CHAR_STAR_AS_STRING #define DOCTEST_COMPARISON_RETURN_TYPE bool #else // DOCTEST_CONFIG_TREAT_CHAR_STAR_AS_STRING #define DOCTEST_COMPARISON_RETURN_TYPE typename enable_if::value || can_use_op::value, bool>::type inline bool eq(const char* lhs, const char* rhs) { return String(lhs) == String(rhs); } inline bool ne(const char* lhs, const char* rhs) { return String(lhs) != String(rhs); } inline bool lt(const char* lhs, const char* rhs) { return String(lhs) < String(rhs); } inline bool gt(const char* lhs, const char* rhs) { return String(lhs) > String(rhs); } inline bool le(const char* lhs, const char* rhs) { return String(lhs) <= String(rhs); } inline bool ge(const char* lhs, const char* rhs) { return String(lhs) >= String(rhs); } #endif // DOCTEST_CONFIG_TREAT_CHAR_STAR_AS_STRING template DOCTEST_COMPARISON_RETURN_TYPE eq(const DOCTEST_REF_WRAP(L) lhs, const DOCTEST_REF_WRAP(R) rhs) { return lhs == rhs; } template DOCTEST_COMPARISON_RETURN_TYPE ne(const DOCTEST_REF_WRAP(L) lhs, const DOCTEST_REF_WRAP(R) rhs) { return lhs != rhs; } template DOCTEST_COMPARISON_RETURN_TYPE lt(const DOCTEST_REF_WRAP(L) lhs, const DOCTEST_REF_WRAP(R) rhs) { return lhs < rhs; } template DOCTEST_COMPARISON_RETURN_TYPE gt(const DOCTEST_REF_WRAP(L) lhs, const DOCTEST_REF_WRAP(R) rhs) { return lhs > rhs; } template DOCTEST_COMPARISON_RETURN_TYPE le(const DOCTEST_REF_WRAP(L) lhs, const DOCTEST_REF_WRAP(R) rhs) { return lhs <= rhs; } template DOCTEST_COMPARISON_RETURN_TYPE ge(const DOCTEST_REF_WRAP(L) lhs, const DOCTEST_REF_WRAP(R) rhs) { return lhs >= rhs; } // clang-format on #ifndef DOCTEST_CONFIG_TREAT_CHAR_STAR_AS_STRING #define DOCTEST_CMP_EQ(l, r) l == r #define DOCTEST_CMP_NE(l, r) l != r #define DOCTEST_CMP_GT(l, r) l > r #define DOCTEST_CMP_LT(l, r) l < r #define DOCTEST_CMP_GE(l, r) l >= r #define DOCTEST_CMP_LE(l, r) l <= r #else // DOCTEST_CONFIG_TREAT_CHAR_STAR_AS_STRING #define DOCTEST_CMP_EQ(l, r) eq(l, r) #define DOCTEST_CMP_NE(l, r) ne(l, r) #define DOCTEST_CMP_GT(l, r) gt(l, r) #define DOCTEST_CMP_LT(l, r) lt(l, r) #define DOCTEST_CMP_GE(l, r) ge(l, r) #define DOCTEST_CMP_LE(l, r) le(l, r) #endif // DOCTEST_CONFIG_TREAT_CHAR_STAR_AS_STRING #define DOCTEST_DO_BINARY_EXPRESSION_COMPARISON(op, op_str, op_macro) \ template \ DOCTEST_NOINLINE Result operator op(const DOCTEST_REF_WRAP(R) rhs) { \ bool res = op_macro(lhs, rhs); \ if(m_assert_type & assertType::is_false) \ res = !res; \ if(!res || doctest::detail::getTestsContextState()->success) \ return Result(res, stringifyBinaryExpr(lhs, op_str, rhs)); \ return Result(res); \ } #define DOCTEST_FORBIT_EXPRESSION(op) \ template \ Expression_lhs& operator op(const R&) { \ DOCTEST_STATIC_ASSERT(deferred_false::value, \ Expression_Too_Complex_Please_Rewrite_As_Binary_Comparison); \ return *this; \ } template // cppcheck-suppress copyCtorAndEqOperator struct Expression_lhs { L lhs; assertType::Enum m_assert_type; explicit Expression_lhs(L in, assertType::Enum at) : lhs(in) , m_assert_type(at) {} DOCTEST_NOINLINE operator Result() { bool res = !!lhs; if(m_assert_type & assertType::is_false) //!OCLINT bitwise operator in conditional res = !res; if(!res || getTestsContextState()->success) return Result(res, toString(lhs)); return Result(res); } // clang-format off DOCTEST_DO_BINARY_EXPRESSION_COMPARISON(==, " == ", DOCTEST_CMP_EQ) //!OCLINT bitwise operator in conditional DOCTEST_DO_BINARY_EXPRESSION_COMPARISON(!=, " != ", DOCTEST_CMP_NE) //!OCLINT bitwise operator in conditional DOCTEST_DO_BINARY_EXPRESSION_COMPARISON(>, " > ", DOCTEST_CMP_GT) //!OCLINT bitwise operator in conditional DOCTEST_DO_BINARY_EXPRESSION_COMPARISON(<, " < ", DOCTEST_CMP_LT) //!OCLINT bitwise operator in conditional DOCTEST_DO_BINARY_EXPRESSION_COMPARISON(>=, " >= ", DOCTEST_CMP_GE) //!OCLINT bitwise operator in conditional DOCTEST_DO_BINARY_EXPRESSION_COMPARISON(<=, " <= ", DOCTEST_CMP_LE) //!OCLINT bitwise operator in conditional // clang-format on // forbidding some expressions based on this table: http://en.cppreference.com/w/cpp/language/operator_precedence DOCTEST_FORBIT_EXPRESSION(&) DOCTEST_FORBIT_EXPRESSION(^) DOCTEST_FORBIT_EXPRESSION(|) DOCTEST_FORBIT_EXPRESSION(&&) DOCTEST_FORBIT_EXPRESSION(||) DOCTEST_FORBIT_EXPRESSION(=) DOCTEST_FORBIT_EXPRESSION(+=) DOCTEST_FORBIT_EXPRESSION(-=) DOCTEST_FORBIT_EXPRESSION(*=) DOCTEST_FORBIT_EXPRESSION(/=) DOCTEST_FORBIT_EXPRESSION(%=) DOCTEST_FORBIT_EXPRESSION(<<=) DOCTEST_FORBIT_EXPRESSION(>>=) DOCTEST_FORBIT_EXPRESSION(&=) DOCTEST_FORBIT_EXPRESSION(^=) DOCTEST_FORBIT_EXPRESSION(|=) // these 2 are unfortunate because they should be allowed - they have higher precedence over the comparisons, but the // ExpressionDecomposer class uses the left shift operator to capture the left operand of the binary expression... DOCTEST_FORBIT_EXPRESSION(<<) DOCTEST_FORBIT_EXPRESSION(>>) }; #ifndef DOCTEST_CONFIG_NO_COMPARISON_WARNING_SUPPRESSION DOCTEST_CLANG_SUPPRESS_WARNING_POP DOCTEST_MSVC_SUPPRESS_WARNING_POP DOCTEST_GCC_SUPPRESS_WARNING_POP #endif // DOCTEST_CONFIG_NO_COMPARISON_WARNING_SUPPRESSION struct ExpressionDecomposer { assertType::Enum m_assert_type; ExpressionDecomposer(assertType::Enum at) : m_assert_type(at) {} // The right operator for capturing expressions is "<=" instead of "<<" (based on the operator precedence table) // but then there will be warnings from GCC about "-Wparentheses" and since "_Pragma()" is problematic this will stay for now... // https://github.com/philsquared/Catch/issues/870 // https://github.com/philsquared/Catch/issues/565 template Expression_lhs operator<<(const DOCTEST_REF_WRAP(L) operand) { return Expression_lhs(operand, m_assert_type); } }; struct DOCTEST_INTERFACE TestCase { // not used for determining uniqueness funcType m_test; // a function pointer to the test case String m_full_name; // contains the name (only for templated test cases!) + the template type const char* m_name; // name of the test case const char* m_type; // for templated test cases - gets appended to the real name const char* m_test_suite; // the test suite in which the test was added const char* m_description; bool m_skip; bool m_may_fail; bool m_should_fail; int m_expected_failures; double m_timeout; // fields by which uniqueness of test cases shall be determined const char* m_file; // the file in which the test was registered unsigned m_line; // the line where the test was registered int m_template_id; // an ID used to distinguish between the different versions of a templated test case TestCase(funcType test, const char* file, unsigned line, const TestSuite& test_suite, const char* type = "", int template_id = -1); // for gcc 4.7 DOCTEST_NOINLINE ~TestCase() {} TestCase& operator*(const char* in); template TestCase& operator*(const T& in) { in.fill(*this); return *this; } TestCase(const TestCase& other) { *this = other; } TestCase& operator=(const TestCase& other); bool operator<(const TestCase& other) const; }; // forward declarations of functions used by the macros DOCTEST_INTERFACE int regTest(const TestCase& tc); DOCTEST_INTERFACE int setTestSuite(const TestSuite& ts); namespace binaryAssertComparison { enum Enum { eq = 0, ne, gt, lt, ge, le }; } // namespace binaryAssertComparison // clang-format off template struct RelationalComparator { bool operator()(const DOCTEST_REF_WRAP(L), const DOCTEST_REF_WRAP(R) ) const { return false; } }; template struct RelationalComparator<0, L, R> { bool operator()(const DOCTEST_REF_WRAP(L) lhs, const DOCTEST_REF_WRAP(R) rhs) const { return eq(lhs, rhs); } }; template struct RelationalComparator<1, L, R> { bool operator()(const DOCTEST_REF_WRAP(L) lhs, const DOCTEST_REF_WRAP(R) rhs) const { return ne(lhs, rhs); } }; template struct RelationalComparator<2, L, R> { bool operator()(const DOCTEST_REF_WRAP(L) lhs, const DOCTEST_REF_WRAP(R) rhs) const { return gt(lhs, rhs); } }; template struct RelationalComparator<3, L, R> { bool operator()(const DOCTEST_REF_WRAP(L) lhs, const DOCTEST_REF_WRAP(R) rhs) const { return lt(lhs, rhs); } }; template struct RelationalComparator<4, L, R> { bool operator()(const DOCTEST_REF_WRAP(L) lhs, const DOCTEST_REF_WRAP(R) rhs) const { return ge(lhs, rhs); } }; template struct RelationalComparator<5, L, R> { bool operator()(const DOCTEST_REF_WRAP(L) lhs, const DOCTEST_REF_WRAP(R) rhs) const { return le(lhs, rhs); } }; // clang-format on struct DOCTEST_INTERFACE ResultBuilder { assertType::Enum m_assert_type; const char* m_file; int m_line; const char* m_expr; const char* m_exception_type; Result m_result; bool m_threw; bool m_threw_as; bool m_failed; String m_exception; ResultBuilder(assertType::Enum at, const char* file, int line, const char* expr, const char* exception_type = ""); ~ResultBuilder(); void setResult(const Result& res) { m_result = res; } template DOCTEST_NOINLINE void binary_assert(const DOCTEST_REF_WRAP(L) lhs, const DOCTEST_REF_WRAP(R) rhs) { m_result.m_passed = RelationalComparator()(lhs, rhs); if(!m_result.m_passed || getTestsContextState()->success) m_result.m_decomposition = stringifyBinaryExpr(lhs, ", ", rhs); } template DOCTEST_NOINLINE void unary_assert(const DOCTEST_REF_WRAP(L) val) { m_result.m_passed = !!val; if(m_assert_type & assertType::is_false) //!OCLINT bitwise operator in conditional m_result.m_passed = !m_result.m_passed; if(!m_result.m_passed || getTestsContextState()->success) m_result.m_decomposition = toString(val); } void unexpectedExceptionOccurred(); bool log(); void react() const; }; namespace assertAction { enum Enum { nothing = 0, dbgbreak = 1, shouldthrow = 2 }; } // namespace assertAction template DOCTEST_NOINLINE int fast_binary_assert(assertType::Enum at, const char* file, int line, const char* expr, const DOCTEST_REF_WRAP(L) lhs, const DOCTEST_REF_WRAP(R) rhs) { ResultBuilder rb(at, file, line, expr); rb.m_result.m_passed = RelationalComparator()(lhs, rhs); if(!rb.m_result.m_passed || getTestsContextState()->success) rb.m_result.m_decomposition = stringifyBinaryExpr(lhs, ", ", rhs); int res = 0; if(rb.log()) res |= assertAction::dbgbreak; if(rb.m_failed && checkIfShouldThrow(at)) res |= assertAction::shouldthrow; #ifdef DOCTEST_CONFIG_SUPER_FAST_ASSERTS // ######################################################################################### // IF THE DEBUGGER BREAKS HERE - GO 1 LEVEL UP IN THE CALLSTACK TO SEE THE FAILING ASSERTION // THIS IS THE EFFECT OF HAVING 'DOCTEST_CONFIG_SUPER_FAST_ASSERTS' DEFINED // ######################################################################################### if(res & assertAction::dbgbreak) DOCTEST_BREAK_INTO_DEBUGGER(); fastAssertThrowIfFlagSet(res); #endif // DOCTEST_CONFIG_SUPER_FAST_ASSERTS return res; } template DOCTEST_NOINLINE int fast_unary_assert(assertType::Enum at, const char* file, int line, const char* val_str, const DOCTEST_REF_WRAP(L) val) { ResultBuilder rb(at, file, line, val_str); rb.m_result.m_passed = !!val; if(at & assertType::is_false) //!OCLINT bitwise operator in conditional rb.m_result.m_passed = !rb.m_result.m_passed; if(!rb.m_result.m_passed || getTestsContextState()->success) rb.m_result.m_decomposition = toString(val); int res = 0; if(rb.log()) res |= assertAction::dbgbreak; if(rb.m_failed && checkIfShouldThrow(at)) res |= assertAction::shouldthrow; #ifdef DOCTEST_CONFIG_SUPER_FAST_ASSERTS // ######################################################################################### // IF THE DEBUGGER BREAKS HERE - GO 1 LEVEL UP IN THE CALLSTACK TO SEE THE FAILING ASSERTION // THIS IS THE EFFECT OF HAVING 'DOCTEST_CONFIG_SUPER_FAST_ASSERTS' DEFINED // ######################################################################################### if(res & assertAction::dbgbreak) DOCTEST_BREAK_INTO_DEBUGGER(); fastAssertThrowIfFlagSet(res); #endif // DOCTEST_CONFIG_SUPER_FAST_ASSERTS return res; } struct DOCTEST_INTERFACE IExceptionTranslator { virtual ~IExceptionTranslator() {} virtual bool translate(String&) const = 0; }; template class ExceptionTranslator : public IExceptionTranslator //!OCLINT destructor of virtual class { public: explicit ExceptionTranslator(String (*translateFunction)(T)) : m_translateFunction(translateFunction) {} bool translate(String& res) const { #ifndef DOCTEST_CONFIG_NO_EXCEPTIONS try { throw; // cppcheck-suppress catchExceptionByValue } catch(T ex) { // NOLINT res = m_translateFunction(ex); //!OCLINT parameter reassignment return true; } catch(...) {} //!OCLINT - empty catch statement #endif // DOCTEST_CONFIG_NO_EXCEPTIONS ((void)res); // to silence -Wunused-parameter return false; } protected: String (*m_translateFunction)(T); }; DOCTEST_INTERFACE void registerExceptionTranslatorImpl( const IExceptionTranslator* translateFunction); // FIX FOR VISUAL STUDIO VERSIONS PRIOR TO 2015 - they failed to compile the call to operator<< with // std::ostream passed as a reference noting that there is a use of an undefined type (which there isn't) DOCTEST_INTERFACE void writeStringToStream(std::ostream* s, const String& str); template struct StringStreamBase { template static void convert(std::ostream* s, const T& in) { writeStringToStream(s, toString(in)); } // always treat char* as a string in this context - no matter // if DOCTEST_CONFIG_TREAT_CHAR_STAR_AS_STRING is defined static void convert(std::ostream* s, const char* in) { writeStringToStream(s, String(in)); } }; template <> struct StringStreamBase { template static void convert(std::ostream* s, const T& in) { *s << in; } }; template struct StringStream : StringStreamBase::value> {}; template void toStream(std::ostream* s, const T& value) { StringStream::convert(s, value); } #ifdef DOCTEST_CONFIG_TREAT_CHAR_STAR_AS_STRING DOCTEST_INTERFACE void toStream(std::ostream* s, char* in); DOCTEST_INTERFACE void toStream(std::ostream* s, const char* in); #endif // DOCTEST_CONFIG_TREAT_CHAR_STAR_AS_STRING DOCTEST_INTERFACE void toStream(std::ostream* s, bool in); DOCTEST_INTERFACE void toStream(std::ostream* s, float in); DOCTEST_INTERFACE void toStream(std::ostream* s, double in); DOCTEST_INTERFACE void toStream(std::ostream* s, double long in); DOCTEST_INTERFACE void toStream(std::ostream* s, char in); DOCTEST_INTERFACE void toStream(std::ostream* s, char signed in); DOCTEST_INTERFACE void toStream(std::ostream* s, char unsigned in); DOCTEST_INTERFACE void toStream(std::ostream* s, int short in); DOCTEST_INTERFACE void toStream(std::ostream* s, int short unsigned in); DOCTEST_INTERFACE void toStream(std::ostream* s, int in); DOCTEST_INTERFACE void toStream(std::ostream* s, int unsigned in); DOCTEST_INTERFACE void toStream(std::ostream* s, int long in); DOCTEST_INTERFACE void toStream(std::ostream* s, int long unsigned in); #ifdef DOCTEST_CONFIG_WITH_LONG_LONG DOCTEST_INTERFACE void toStream(std::ostream* s, int long long in); DOCTEST_INTERFACE void toStream(std::ostream* s, int long long unsigned in); #endif // DOCTEST_CONFIG_WITH_LONG_LONG struct IContextScope { virtual ~IContextScope() {} virtual void build(std::ostream*) = 0; }; DOCTEST_INTERFACE void addToContexts(IContextScope* ptr); DOCTEST_INTERFACE void popFromContexts(); DOCTEST_INTERFACE void useContextIfExceptionOccurred(IContextScope* ptr); // cppcheck-suppress copyCtorAndEqOperator class ContextBuilder { friend class ContextScope; struct ICapture { virtual ~ICapture() {} virtual void toStream(std::ostream*) const = 0; }; template struct Capture : ICapture //!OCLINT destructor of virtual class { const T* capture; explicit Capture(const T* in) : capture(in) {} virtual void toStream(std::ostream* s) const { // override detail::toStream(s, *capture); } }; struct Chunk { char buf[sizeof(Capture)] DOCTEST_ALIGNMENT( 2 * sizeof(void*)); // place to construct a Capture }; struct Node { Chunk chunk; Node* next; }; Chunk stackChunks[DOCTEST_CONFIG_NUM_CAPTURES_ON_STACK]; int numCaptures; Node* head; Node* tail; DOCTEST_GCC_SUPPRESS_WARNING_WITH_PUSH("-Wcast-align") void build(std::ostream* s) const { int curr = 0; // iterate over small buffer while(curr < numCaptures && curr < DOCTEST_CONFIG_NUM_CAPTURES_ON_STACK) reinterpret_cast(stackChunks[curr++].buf)->toStream(s); // iterate over list Node* curr_elem = head; while(curr < numCaptures) { reinterpret_cast(curr_elem->chunk.buf)->toStream(s); curr_elem = curr_elem->next; ++curr; } } DOCTEST_GCC_SUPPRESS_WARNING_POP // steal the contents of the other - acting as a move constructor... DOCTEST_NOINLINE ContextBuilder(ContextBuilder& other) : numCaptures(other.numCaptures) , head(other.head) , tail(other.tail) { other.numCaptures = 0; other.head = 0; other.tail = 0; my_memcpy(stackChunks, other.stackChunks, unsigned(int(sizeof(Chunk)) * DOCTEST_CONFIG_NUM_CAPTURES_ON_STACK)); } ContextBuilder& operator=(const ContextBuilder&); // NOLINT public: // cppcheck-suppress uninitMemberVar DOCTEST_NOINLINE ContextBuilder() // NOLINT : numCaptures(0) , head(0) , tail(0) {} template DOCTEST_NOINLINE ContextBuilder& operator<<(T& in) { Capture temp(&in); // construct either on stack or on heap // copy the bytes for the whole object - including the vtable because we cant construct // the object directly in the buffer using placement new - need the header... if(numCaptures < DOCTEST_CONFIG_NUM_CAPTURES_ON_STACK) { my_memcpy(stackChunks[numCaptures].buf, &temp, sizeof(Chunk)); } else { Node* curr = new Node; curr->next = 0; if(tail) { tail->next = curr; tail = curr; } else { head = tail = curr; } my_memcpy(tail->chunk.buf, &temp, sizeof(Chunk)); } ++numCaptures; return *this; } DOCTEST_NOINLINE ~ContextBuilder() { // free the linked list - the ones on the stack are left as-is // no destructors are called at all - there is no need while(head) { Node* next = head->next; delete head; head = next; } } #ifdef DOCTEST_CONFIG_WITH_RVALUE_REFERENCES template ContextBuilder& operator<<(const T&&) { DOCTEST_STATIC_ASSERT( deferred_false::value, Cannot_pass_temporaries_or_rvalues_to_the_streaming_operator_because_it_caches_pointers_to_the_passed_objects_for_lazy_evaluation); return *this; } #endif // DOCTEST_CONFIG_WITH_RVALUE_REFERENCES }; class ContextScope : public IContextScope { ContextBuilder contextBuilder; public: DOCTEST_NOINLINE explicit ContextScope(ContextBuilder& temp) : contextBuilder(temp) { addToContexts(this); } DOCTEST_NOINLINE ~ContextScope() { useContextIfExceptionOccurred(this); popFromContexts(); } void build(std::ostream* s) { contextBuilder.build(s); } }; class DOCTEST_INTERFACE MessageBuilder { std::ostream* m_stream; const char* m_file; int m_line; assertType::Enum m_severity; public: MessageBuilder(const char* file, int line, assertType::Enum severity); ~MessageBuilder(); template MessageBuilder& operator<<(const T& in) { toStream(m_stream, in); return *this; } void log(std::ostream&); bool log(); void react(); }; } // namespace detail struct test_suite { const char* data; test_suite(const char* in) : data(in) {} void fill(detail::TestCase& state) const { state.m_test_suite = data; } void fill(detail::TestSuite& state) const { state.m_test_suite = data; } }; struct description { const char* data; description(const char* in) : data(in) {} void fill(detail::TestCase& state) const { state.m_description = data; } void fill(detail::TestSuite& state) const { state.m_description = data; } }; struct skip { bool data; skip(bool in = true) : data(in) {} void fill(detail::TestCase& state) const { state.m_skip = data; } void fill(detail::TestSuite& state) const { state.m_skip = data; } }; struct timeout { double data; timeout(double in) : data(in) {} void fill(detail::TestCase& state) const { state.m_timeout = data; } void fill(detail::TestSuite& state) const { state.m_timeout = data; } }; struct may_fail { bool data; may_fail(bool in = true) : data(in) {} void fill(detail::TestCase& state) const { state.m_may_fail = data; } void fill(detail::TestSuite& state) const { state.m_may_fail = data; } }; struct should_fail { bool data; should_fail(bool in = true) : data(in) {} void fill(detail::TestCase& state) const { state.m_should_fail = data; } void fill(detail::TestSuite& state) const { state.m_should_fail = data; } }; struct expected_failures { int data; expected_failures(int in) : data(in) {} void fill(detail::TestCase& state) const { state.m_expected_failures = data; } void fill(detail::TestSuite& state) const { state.m_expected_failures = data; } }; #endif // DOCTEST_CONFIG_DISABLE #ifndef DOCTEST_CONFIG_DISABLE template int registerExceptionTranslator(String (*translateFunction)(T)) { DOCTEST_CLANG_SUPPRESS_WARNING_WITH_PUSH("-Wexit-time-destructors") static detail::ExceptionTranslator exceptionTranslator(translateFunction); DOCTEST_CLANG_SUPPRESS_WARNING_POP detail::registerExceptionTranslatorImpl(&exceptionTranslator); return 0; } #else // DOCTEST_CONFIG_DISABLE template int registerExceptionTranslator(String (*)(T)) { return 0; } #endif // DOCTEST_CONFIG_DISABLE DOCTEST_INTERFACE bool isRunningInTest(); // cppcheck-suppress noCopyConstructor class DOCTEST_INTERFACE Context { #if !defined(DOCTEST_CONFIG_DISABLE) detail::ContextState* p; void parseArgs(int argc, const char* const* argv, bool withDefaults = false); #endif // DOCTEST_CONFIG_DISABLE public: explicit Context(int argc = 0, const char* const* argv = 0); ~Context(); void applyCommandLine(int argc, const char* const* argv); void addFilter(const char* filter, const char* value); void clearFilters(); void setOption(const char* option, int value); void setOption(const char* option, const char* value); bool shouldExit(); int run(); }; } // namespace doctest // if registering is not disabled #if !defined(DOCTEST_CONFIG_DISABLE) #ifdef DOCTEST_CONFIG_WITH_VARIADIC_MACROS #define DOCTEST_EXPAND_VA_ARGS(...) __VA_ARGS__ #else // DOCTEST_CONFIG_WITH_VARIADIC_MACROS #define DOCTEST_EXPAND_VA_ARGS #endif // DOCTEST_CONFIG_WITH_VARIADIC_MACROS #define DOCTEST_STRIP_PARENS(x) x #define DOCTEST_HANDLE_BRACED_VA_ARGS(expr) DOCTEST_STRIP_PARENS(DOCTEST_EXPAND_VA_ARGS expr) // registers the test by initializing a dummy var with a function #define DOCTEST_REGISTER_FUNCTION(f, decorators) \ DOCTEST_GLOBAL_NO_WARNINGS(DOCTEST_ANONYMOUS(_DOCTEST_ANON_VAR_)) = doctest::detail::regTest( \ doctest::detail::TestCase(f, __FILE__, __LINE__, \ doctest_detail_test_suite_ns::getCurrentTestSuite()) * \ decorators); \ DOCTEST_GLOBAL_NO_WARNINGS_END() #define DOCTEST_IMPLEMENT_FIXTURE(der, base, func, decorators) \ namespace \ { \ struct der : base \ { \ void f(); \ }; \ static void func() { \ der v; \ v.f(); \ } \ DOCTEST_REGISTER_FUNCTION(func, decorators) \ } \ inline DOCTEST_NOINLINE void der::f() #define DOCTEST_CREATE_AND_REGISTER_FUNCTION(f, decorators) \ static void f(); \ DOCTEST_REGISTER_FUNCTION(f, decorators) \ static void f() // for registering tests #define DOCTEST_TEST_CASE(decorators) \ DOCTEST_CREATE_AND_REGISTER_FUNCTION(DOCTEST_ANONYMOUS(_DOCTEST_ANON_FUNC_), decorators) // for registering tests with a fixture #define DOCTEST_TEST_CASE_FIXTURE(c, decorators) \ DOCTEST_IMPLEMENT_FIXTURE(DOCTEST_ANONYMOUS(_DOCTEST_ANON_CLASS_), c, \ DOCTEST_ANONYMOUS(_DOCTEST_ANON_FUNC_), decorators) // for converting types to strings without the header and demangling #ifdef DOCTEST_CONFIG_WITH_VARIADIC_MACROS #define DOCTEST_TYPE_TO_STRING_IMPL(...) \ template <> \ inline const char* type_to_string<__VA_ARGS__>() { \ return "<" #__VA_ARGS__ ">"; \ } #define DOCTEST_TYPE_TO_STRING(...) \ namespace doctest \ { \ namespace detail \ { \ DOCTEST_TYPE_TO_STRING_IMPL(__VA_ARGS__) \ } \ } \ typedef int DOCTEST_ANONYMOUS(_DOCTEST_ANON_FOR_SEMICOLON_) #else // DOCTEST_CONFIG_WITH_VARIADIC_MACROS #define DOCTEST_TYPE_TO_STRING_IMPL(x) \ template <> \ inline const char* type_to_string() { \ return "<" #x ">"; \ } #define DOCTEST_TYPE_TO_STRING(x) \ namespace doctest \ { \ namespace detail \ { \ DOCTEST_TYPE_TO_STRING_IMPL(x) \ } \ } \ typedef int DOCTEST_ANONYMOUS(_DOCTEST_ANON_FOR_SEMICOLON_) #endif // DOCTEST_CONFIG_WITH_VARIADIC_MACROS // for typed tests #define DOCTEST_TEST_CASE_TEMPLATE_IMPL(decorators, T, types, anon) \ template \ inline void anon(); \ struct DOCTEST_CAT(anon, FUNCTOR) \ { \ template \ void operator()() { \ doctest::detail::regTest( \ doctest::detail::TestCase(anon, __FILE__, __LINE__, \ doctest_detail_test_suite_ns::getCurrentTestSuite(), \ doctest::detail::type_to_string(), Index) * \ decorators); \ } \ }; \ inline int DOCTEST_CAT(anon, REG_FUNC)() { \ DOCTEST_CAT(anon, FUNCTOR) registrar; \ doctest::detail::ForEachType \ doIt(registrar); \ return 0; \ } \ DOCTEST_GLOBAL_NO_WARNINGS(DOCTEST_CAT(anon, DUMMY)) = DOCTEST_CAT(anon, REG_FUNC)(); \ DOCTEST_GLOBAL_NO_WARNINGS_END() \ template \ inline void anon() #ifdef DOCTEST_CONFIG_WITH_VARIADIC_MACROS #define DOCTEST_TEST_CASE_TEMPLATE(decorators, T, ...) \ DOCTEST_TEST_CASE_TEMPLATE_IMPL(decorators, T, (__VA_ARGS__), \ DOCTEST_ANONYMOUS(_DOCTEST_ANON_TMP_)) #else // DOCTEST_CONFIG_WITH_VARIADIC_MACROS #define DOCTEST_TEST_CASE_TEMPLATE(decorators, T, types) \ DOCTEST_TEST_CASE_TEMPLATE_IMPL(decorators, T, types, DOCTEST_ANONYMOUS(_DOCTEST_ANON_TMP_)) #endif // DOCTEST_CONFIG_WITH_VARIADIC_MACROS #define DOCTEST_TEST_CASE_TEMPLATE_DEFINE_IMPL(decorators, T, id, anon) \ template \ inline void anon(); \ struct DOCTEST_CAT(id, _FUNCTOR) \ { \ int m_line; \ DOCTEST_CAT(id, _FUNCTOR) \ (int line) \ : m_line(line) {} \ template \ void operator()() { \ doctest::detail::regTest( \ doctest::detail::TestCase(anon, __FILE__, __LINE__, \ doctest_detail_test_suite_ns::getCurrentTestSuite(), \ doctest::detail::type_to_string(), \ m_line * 1000 + Index) * \ decorators); \ } \ }; \ template \ inline void anon() #define DOCTEST_TEST_CASE_TEMPLATE_DEFINE(decorators, T, id) \ DOCTEST_TEST_CASE_TEMPLATE_DEFINE_IMPL(decorators, T, id, DOCTEST_ANONYMOUS(_DOCTEST_ANON_TMP_)) #define DOCTEST_TEST_CASE_TEMPLATE_INSTANTIATE_IMPL(id, types, anon) \ static int DOCTEST_CAT(anon, REG_FUNC)() { \ DOCTEST_CAT(id, _FUNCTOR) registrar(__LINE__); \ doctest::detail::ForEachType \ doIt(registrar); \ return 0; \ } \ DOCTEST_GLOBAL_NO_WARNINGS(DOCTEST_CAT(anon, DUMMY)) = DOCTEST_CAT(anon, REG_FUNC)(); \ DOCTEST_GLOBAL_NO_WARNINGS_END() typedef int DOCTEST_ANONYMOUS(_DOCTEST_ANON_FOR_SEMICOLON_) #ifdef DOCTEST_CONFIG_WITH_VARIADIC_MACROS #define DOCTEST_TEST_CASE_TEMPLATE_INSTANTIATE(id, ...) \ DOCTEST_TEST_CASE_TEMPLATE_INSTANTIATE_IMPL(id, (__VA_ARGS__), \ DOCTEST_ANONYMOUS(_DOCTEST_ANON_TMP_)) #else // DOCTEST_CONFIG_WITH_VARIADIC_MACROS #define DOCTEST_TEST_CASE_TEMPLATE_INSTANTIATE(id, types) \ DOCTEST_TEST_CASE_TEMPLATE_INSTANTIATE_IMPL(id, types, DOCTEST_ANONYMOUS(_DOCTEST_ANON_TMP_)) #endif // DOCTEST_CONFIG_WITH_VARIADIC_MACROS // for subcases #define DOCTEST_SUBCASE(name) \ if(const doctest::detail::Subcase & DOCTEST_ANONYMOUS(_DOCTEST_ANON_SUBCASE_) DOCTEST_UNUSED = \ doctest::detail::Subcase(name, __FILE__, __LINE__)) // for grouping tests in test suites by using code blocks #define DOCTEST_TEST_SUITE_IMPL(decorators, ns_name) \ namespace ns_name \ { \ namespace doctest_detail_test_suite_ns \ { \ static DOCTEST_NOINLINE doctest::detail::TestSuite& getCurrentTestSuite() { \ static doctest::detail::TestSuite data; \ static bool inited = false; \ if(!inited) { \ data* decorators; \ inited = true; \ } \ return data; \ } \ } \ } \ namespace ns_name #define DOCTEST_TEST_SUITE(decorators) \ DOCTEST_TEST_SUITE_IMPL(decorators, DOCTEST_ANONYMOUS(_DOCTEST_ANON_SUITE_)) // for starting a testsuite block #define DOCTEST_TEST_SUITE_BEGIN(decorators) \ DOCTEST_GLOBAL_NO_WARNINGS(DOCTEST_ANONYMOUS(_DOCTEST_ANON_VAR_)) = \ doctest::detail::setTestSuite(doctest::detail::TestSuite() * decorators); \ DOCTEST_GLOBAL_NO_WARNINGS_END() \ typedef int DOCTEST_ANONYMOUS(_DOCTEST_ANON_FOR_SEMICOLON_) // for ending a testsuite block #define DOCTEST_TEST_SUITE_END \ DOCTEST_GLOBAL_NO_WARNINGS(DOCTEST_ANONYMOUS(_DOCTEST_ANON_VAR_)) = \ doctest::detail::setTestSuite(doctest::detail::TestSuite() * ""); \ DOCTEST_GLOBAL_NO_WARNINGS_END() \ typedef int DOCTEST_ANONYMOUS(_DOCTEST_ANON_FOR_SEMICOLON_) // for registering exception translators #define DOCTEST_REGISTER_EXCEPTION_TRANSLATOR_IMPL(translatorName, signature) \ inline doctest::String translatorName(signature); \ DOCTEST_GLOBAL_NO_WARNINGS(DOCTEST_ANONYMOUS(_DOCTEST_ANON_TRANSLATOR_)) = \ doctest::registerExceptionTranslator(translatorName); \ DOCTEST_GLOBAL_NO_WARNINGS_END() \ doctest::String translatorName(signature) #define DOCTEST_REGISTER_EXCEPTION_TRANSLATOR(signature) \ DOCTEST_REGISTER_EXCEPTION_TRANSLATOR_IMPL(DOCTEST_ANONYMOUS(_DOCTEST_ANON_TRANSLATOR_), \ signature) // for logging #define DOCTEST_INFO(x) \ doctest::detail::ContextScope DOCTEST_ANONYMOUS(_DOCTEST_CAPTURE_)( \ doctest::detail::ContextBuilder() << x) #define DOCTEST_CAPTURE(x) DOCTEST_INFO(#x " := " << x) #define DOCTEST_ADD_AT_IMPL(type, file, line, mb, x) \ do { \ doctest::detail::MessageBuilder mb(file, line, doctest::detail::assertType::type); \ mb << x; \ if(mb.log()) \ DOCTEST_BREAK_INTO_DEBUGGER(); \ mb.react(); \ } while((void)0, 0) // clang-format off #define DOCTEST_ADD_MESSAGE_AT(file, line, x) DOCTEST_ADD_AT_IMPL(is_warn, file, line, DOCTEST_ANONYMOUS(_DOCTEST_MESSAGE_), x) #define DOCTEST_ADD_FAIL_CHECK_AT(file, line, x) DOCTEST_ADD_AT_IMPL(is_check, file, line, DOCTEST_ANONYMOUS(_DOCTEST_MESSAGE_), x) #define DOCTEST_ADD_FAIL_AT(file, line, x) DOCTEST_ADD_AT_IMPL(is_require, file, line, DOCTEST_ANONYMOUS(_DOCTEST_MESSAGE_), x) // clang-format on #define DOCTEST_MESSAGE(x) DOCTEST_ADD_MESSAGE_AT(__FILE__, __LINE__, x) #define DOCTEST_FAIL_CHECK(x) DOCTEST_ADD_FAIL_CHECK_AT(__FILE__, __LINE__, x) #define DOCTEST_FAIL(x) DOCTEST_ADD_FAIL_AT(__FILE__, __LINE__, x) #if __cplusplus >= 201402L || (DOCTEST_MSVC >= DOCTEST_COMPILER(19, 10, 0)) template constexpr T to_lvalue = x; #define DOCTEST_TO_LVALUE(...) to_lvalue #else #ifdef DOCTEST_CONFIG_WITH_VARIADIC_MACROS #define DOCTEST_TO_LVALUE(...) TO_LVALUE_CAN_BE_USED_ONLY_IN_CPP14_MODE_OR_WITH_VS_2017_OR_NEWER #else // DOCTEST_CONFIG_WITH_VARIADIC_MACROS #define DOCTEST_TO_LVALUE(x) TO_LVALUE_CAN_BE_USED_ONLY_IN_CPP14_MODE_OR_WITH_VS_2017_OR_NEWER #endif // DOCTEST_CONFIG_WITH_VARIADIC_MACROS #endif // TO_LVALUE hack for logging macros like INFO() // common code in asserts - for convenience #define DOCTEST_ASSERT_LOG_AND_REACT(rb) \ if(rb.log()) \ DOCTEST_BREAK_INTO_DEBUGGER(); \ rb.react() #ifdef DOCTEST_CONFIG_NO_TRY_CATCH_IN_ASSERTS #define DOCTEST_WRAP_IN_TRY(x) x; #else // DOCTEST_CONFIG_NO_TRY_CATCH_IN_ASSERTS #define DOCTEST_WRAP_IN_TRY(x) \ try { \ x; \ } catch(...) { _DOCTEST_RB.unexpectedExceptionOccurred(); } #endif // DOCTEST_CONFIG_NO_TRY_CATCH_IN_ASSERTS #define DOCTEST_ASSERT_IMPLEMENT_2(expr, assert_type) \ DOCTEST_CLANG_SUPPRESS_WARNING_WITH_PUSH("-Woverloaded-shift-op-parentheses") \ doctest::detail::ResultBuilder _DOCTEST_RB( \ doctest::detail::assertType::assert_type, __FILE__, __LINE__, \ DOCTEST_TOSTR(DOCTEST_HANDLE_BRACED_VA_ARGS(expr))); \ DOCTEST_WRAP_IN_TRY(_DOCTEST_RB.setResult( \ doctest::detail::ExpressionDecomposer(doctest::detail::assertType::assert_type) \ << DOCTEST_HANDLE_BRACED_VA_ARGS(expr))) \ DOCTEST_ASSERT_LOG_AND_REACT(_DOCTEST_RB) \ DOCTEST_CLANG_SUPPRESS_WARNING_POP #define DOCTEST_ASSERT_IMPLEMENT_1(expr, assert_type) \ do { \ DOCTEST_ASSERT_IMPLEMENT_2(expr, assert_type); \ } while((void)0, 0) #ifdef DOCTEST_CONFIG_WITH_VARIADIC_MACROS #define DOCTEST_WARN(...) DOCTEST_ASSERT_IMPLEMENT_1((__VA_ARGS__), DT_WARN) #define DOCTEST_CHECK(...) DOCTEST_ASSERT_IMPLEMENT_1((__VA_ARGS__), DT_CHECK) #define DOCTEST_REQUIRE(...) DOCTEST_ASSERT_IMPLEMENT_1((__VA_ARGS__), DT_REQUIRE) #define DOCTEST_WARN_FALSE(...) DOCTEST_ASSERT_IMPLEMENT_1((__VA_ARGS__), DT_WARN_FALSE) #define DOCTEST_CHECK_FALSE(...) DOCTEST_ASSERT_IMPLEMENT_1((__VA_ARGS__), DT_CHECK_FALSE) #define DOCTEST_REQUIRE_FALSE(...) DOCTEST_ASSERT_IMPLEMENT_1((__VA_ARGS__), DT_REQUIRE_FALSE) #else // DOCTEST_CONFIG_WITH_VARIADIC_MACROS #define DOCTEST_WARN(expr) DOCTEST_ASSERT_IMPLEMENT_1(expr, DT_WARN) #define DOCTEST_CHECK(expr) DOCTEST_ASSERT_IMPLEMENT_1(expr, DT_CHECK) #define DOCTEST_REQUIRE(expr) DOCTEST_ASSERT_IMPLEMENT_1(expr, DT_REQUIRE) #define DOCTEST_WARN_FALSE(expr) DOCTEST_ASSERT_IMPLEMENT_1(expr, DT_WARN_FALSE) #define DOCTEST_CHECK_FALSE(expr) DOCTEST_ASSERT_IMPLEMENT_1(expr, DT_CHECK_FALSE) #define DOCTEST_REQUIRE_FALSE(expr) DOCTEST_ASSERT_IMPLEMENT_1(expr, DT_REQUIRE_FALSE) #endif // DOCTEST_CONFIG_WITH_VARIADIC_MACROS // clang-format off #ifdef DOCTEST_CONFIG_WITH_VARIADIC_MACROS #define DOCTEST_WARN_MESSAGE(cond, msg) do { DOCTEST_INFO(msg); DOCTEST_ASSERT_IMPLEMENT_2((cond), DT_WARN); } while((void)0, 0) #define DOCTEST_CHECK_MESSAGE(cond, msg) do { DOCTEST_INFO(msg); DOCTEST_ASSERT_IMPLEMENT_2((cond), DT_CHECK); } while((void)0, 0) #define DOCTEST_REQUIRE_MESSAGE(cond, msg) do { DOCTEST_INFO(msg); DOCTEST_ASSERT_IMPLEMENT_2((cond), DT_REQUIRE); } while((void)0, 0) #define DOCTEST_WARN_FALSE_MESSAGE(cond, msg) do { DOCTEST_INFO(msg); DOCTEST_ASSERT_IMPLEMENT_2((cond), DT_WARN_FALSE); } while((void)0, 0) #define DOCTEST_CHECK_FALSE_MESSAGE(cond, msg) do { DOCTEST_INFO(msg); DOCTEST_ASSERT_IMPLEMENT_2((cond), DT_CHECK_FALSE); } while((void)0, 0) #define DOCTEST_REQUIRE_FALSE_MESSAGE(cond, msg) do { DOCTEST_INFO(msg); DOCTEST_ASSERT_IMPLEMENT_2((cond), DT_REQUIRE_FALSE); } while((void)0, 0) #else // DOCTEST_CONFIG_WITH_VARIADIC_MACROS #define DOCTEST_WARN_MESSAGE(cond, msg) do { DOCTEST_INFO(msg); DOCTEST_ASSERT_IMPLEMENT_2(cond, DT_WARN); } while((void)0, 0) #define DOCTEST_CHECK_MESSAGE(cond, msg) do { DOCTEST_INFO(msg); DOCTEST_ASSERT_IMPLEMENT_2(cond, DT_CHECK); } while((void)0, 0) #define DOCTEST_REQUIRE_MESSAGE(cond, msg) do { DOCTEST_INFO(msg); DOCTEST_ASSERT_IMPLEMENT_2(cond, DT_REQUIRE); } while((void)0, 0) #define DOCTEST_WARN_FALSE_MESSAGE(cond, msg) do { DOCTEST_INFO(msg); DOCTEST_ASSERT_IMPLEMENT_2(cond, DT_WARN_FALSE); } while((void)0, 0) #define DOCTEST_CHECK_FALSE_MESSAGE(cond, msg) do { DOCTEST_INFO(msg); DOCTEST_ASSERT_IMPLEMENT_2(cond, DT_CHECK_FALSE); } while((void)0, 0) #define DOCTEST_REQUIRE_FALSE_MESSAGE(cond, msg) do { DOCTEST_INFO(msg); DOCTEST_ASSERT_IMPLEMENT_2(cond, DT_REQUIRE_FALSE); } while((void)0, 0) #endif // DOCTEST_CONFIG_WITH_VARIADIC_MACROS // clang-format on #define DOCTEST_ASSERT_THROWS(expr, assert_type) \ do { \ if(!doctest::detail::getTestsContextState()->no_throw) { \ doctest::detail::ResultBuilder _DOCTEST_RB(doctest::detail::assertType::assert_type, \ __FILE__, __LINE__, #expr); \ try { \ expr; \ } catch(...) { _DOCTEST_RB.m_threw = true; } \ DOCTEST_ASSERT_LOG_AND_REACT(_DOCTEST_RB); \ } \ } while((void)0, 0) #define DOCTEST_ASSERT_THROWS_AS(expr, as, assert_type) \ do { \ if(!doctest::detail::getTestsContextState()->no_throw) { \ doctest::detail::ResultBuilder _DOCTEST_RB( \ doctest::detail::assertType::assert_type, __FILE__, __LINE__, #expr, \ DOCTEST_TOSTR(DOCTEST_HANDLE_BRACED_VA_ARGS(as))); \ try { \ expr; \ } catch(const DOCTEST_HANDLE_BRACED_VA_ARGS(as)&) { \ _DOCTEST_RB.m_threw = true; \ _DOCTEST_RB.m_threw_as = true; \ } catch(...) { _DOCTEST_RB.unexpectedExceptionOccurred(); } \ DOCTEST_ASSERT_LOG_AND_REACT(_DOCTEST_RB); \ } \ } while((void)0, 0) #define DOCTEST_ASSERT_NOTHROW(expr, assert_type) \ do { \ if(!doctest::detail::getTestsContextState()->no_throw) { \ doctest::detail::ResultBuilder _DOCTEST_RB(doctest::detail::assertType::assert_type, \ __FILE__, __LINE__, #expr); \ try { \ expr; \ } catch(...) { _DOCTEST_RB.unexpectedExceptionOccurred(); } \ DOCTEST_ASSERT_LOG_AND_REACT(_DOCTEST_RB); \ } \ } while((void)0, 0) #define DOCTEST_WARN_THROWS(expr) DOCTEST_ASSERT_THROWS(expr, DT_WARN_THROWS) #define DOCTEST_CHECK_THROWS(expr) DOCTEST_ASSERT_THROWS(expr, DT_CHECK_THROWS) #define DOCTEST_REQUIRE_THROWS(expr) DOCTEST_ASSERT_THROWS(expr, DT_REQUIRE_THROWS) // clang-format off #ifdef DOCTEST_CONFIG_WITH_VARIADIC_MACROS #define DOCTEST_WARN_THROWS_AS(expr, ...) DOCTEST_ASSERT_THROWS_AS(expr, (__VA_ARGS__), DT_WARN_THROWS_AS) #define DOCTEST_CHECK_THROWS_AS(expr, ...) DOCTEST_ASSERT_THROWS_AS(expr, (__VA_ARGS__), DT_CHECK_THROWS_AS) #define DOCTEST_REQUIRE_THROWS_AS(expr, ...) DOCTEST_ASSERT_THROWS_AS(expr, (__VA_ARGS__), DT_REQUIRE_THROWS_AS) #else // DOCTEST_CONFIG_WITH_VARIADIC_MACROS #define DOCTEST_WARN_THROWS_AS(expr, ex) DOCTEST_ASSERT_THROWS_AS(expr, ex, DT_WARN_THROWS_AS) #define DOCTEST_CHECK_THROWS_AS(expr, ex) DOCTEST_ASSERT_THROWS_AS(expr, ex, DT_CHECK_THROWS_AS) #define DOCTEST_REQUIRE_THROWS_AS(expr, ex) DOCTEST_ASSERT_THROWS_AS(expr, ex, DT_REQUIRE_THROWS_AS) #endif // DOCTEST_CONFIG_WITH_VARIADIC_MACROS // clang-format on #define DOCTEST_WARN_NOTHROW(expr) DOCTEST_ASSERT_NOTHROW(expr, DT_WARN_NOTHROW) #define DOCTEST_CHECK_NOTHROW(expr) DOCTEST_ASSERT_NOTHROW(expr, DT_CHECK_NOTHROW) #define DOCTEST_REQUIRE_NOTHROW(expr) DOCTEST_ASSERT_NOTHROW(expr, DT_REQUIRE_NOTHROW) // clang-format off #define DOCTEST_WARN_THROWS_MESSAGE(expr, msg) do { DOCTEST_INFO(msg); DOCTEST_WARN_THROWS(expr); } while((void)0, 0) #define DOCTEST_CHECK_THROWS_MESSAGE(expr, msg) do { DOCTEST_INFO(msg); DOCTEST_CHECK_THROWS(expr); } while((void)0, 0) #define DOCTEST_REQUIRE_THROWS_MESSAGE(expr, msg) do { DOCTEST_INFO(msg); DOCTEST_REQUIRE_THROWS(expr); } while((void)0, 0) #define DOCTEST_WARN_THROWS_AS_MESSAGE(expr, ex, msg) do { DOCTEST_INFO(msg); DOCTEST_WARN_THROWS_AS(expr, ex); } while((void)0, 0) #define DOCTEST_CHECK_THROWS_AS_MESSAGE(expr, ex, msg) do { DOCTEST_INFO(msg); DOCTEST_CHECK_THROWS_AS(expr, ex); } while((void)0, 0) #define DOCTEST_REQUIRE_THROWS_AS_MESSAGE(expr, ex, msg) do { DOCTEST_INFO(msg); DOCTEST_REQUIRE_THROWS_AS(expr, ex); } while((void)0, 0) #define DOCTEST_WARN_NOTHROW_MESSAGE(expr, msg) do { DOCTEST_INFO(msg); DOCTEST_WARN_NOTHROW(expr); } while((void)0, 0) #define DOCTEST_CHECK_NOTHROW_MESSAGE(expr, msg) do { DOCTEST_INFO(msg); DOCTEST_CHECK_NOTHROW(expr); } while((void)0, 0) #define DOCTEST_REQUIRE_NOTHROW_MESSAGE(expr, msg) do { DOCTEST_INFO(msg); DOCTEST_REQUIRE_NOTHROW(expr); } while((void)0, 0) // clang-format on #ifdef DOCTEST_CONFIG_WITH_VARIADIC_MACROS #define DOCTEST_BINARY_ASSERT(assert_type, expr, comp) \ do { \ doctest::detail::ResultBuilder _DOCTEST_RB( \ doctest::detail::assertType::assert_type, __FILE__, __LINE__, \ DOCTEST_TOSTR(DOCTEST_HANDLE_BRACED_VA_ARGS(expr))); \ DOCTEST_WRAP_IN_TRY( \ _DOCTEST_RB.binary_assert( \ DOCTEST_HANDLE_BRACED_VA_ARGS(expr))) \ DOCTEST_ASSERT_LOG_AND_REACT(_DOCTEST_RB); \ } while((void)0, 0) #else // DOCTEST_CONFIG_WITH_VARIADIC_MACROS #define DOCTEST_BINARY_ASSERT(assert_type, lhs, rhs, comp) \ do { \ doctest::detail::ResultBuilder _DOCTEST_RB(doctest::detail::assertType::assert_type, \ __FILE__, __LINE__, #lhs ", " #rhs); \ DOCTEST_WRAP_IN_TRY( \ _DOCTEST_RB.binary_assert(lhs, \ rhs)) \ DOCTEST_ASSERT_LOG_AND_REACT(_DOCTEST_RB); \ } while((void)0, 0) #endif // DOCTEST_CONFIG_WITH_VARIADIC_MACROS #define DOCTEST_UNARY_ASSERT(assert_type, expr) \ do { \ doctest::detail::ResultBuilder _DOCTEST_RB( \ doctest::detail::assertType::assert_type, __FILE__, __LINE__, \ DOCTEST_TOSTR(DOCTEST_HANDLE_BRACED_VA_ARGS(expr))); \ DOCTEST_WRAP_IN_TRY(_DOCTEST_RB.unary_assert(DOCTEST_HANDLE_BRACED_VA_ARGS(expr))) \ DOCTEST_ASSERT_LOG_AND_REACT(_DOCTEST_RB); \ } while((void)0, 0) #ifdef DOCTEST_CONFIG_WITH_VARIADIC_MACROS #define DOCTEST_WARN_EQ(...) DOCTEST_BINARY_ASSERT(DT_WARN_EQ, (__VA_ARGS__), eq) #define DOCTEST_CHECK_EQ(...) DOCTEST_BINARY_ASSERT(DT_CHECK_EQ, (__VA_ARGS__), eq) #define DOCTEST_REQUIRE_EQ(...) DOCTEST_BINARY_ASSERT(DT_REQUIRE_EQ, (__VA_ARGS__), eq) #define DOCTEST_WARN_NE(...) DOCTEST_BINARY_ASSERT(DT_WARN_NE, (__VA_ARGS__), ne) #define DOCTEST_CHECK_NE(...) DOCTEST_BINARY_ASSERT(DT_CHECK_NE, (__VA_ARGS__), ne) #define DOCTEST_REQUIRE_NE(...) DOCTEST_BINARY_ASSERT(DT_REQUIRE_NE, (__VA_ARGS__), ne) #define DOCTEST_WARN_GT(...) DOCTEST_BINARY_ASSERT(DT_WARN_GT, (__VA_ARGS__), gt) #define DOCTEST_CHECK_GT(...) DOCTEST_BINARY_ASSERT(DT_CHECK_GT, (__VA_ARGS__), gt) #define DOCTEST_REQUIRE_GT(...) DOCTEST_BINARY_ASSERT(DT_REQUIRE_GT, (__VA_ARGS__), gt) #define DOCTEST_WARN_LT(...) DOCTEST_BINARY_ASSERT(DT_WARN_LT, (__VA_ARGS__), lt) #define DOCTEST_CHECK_LT(...) DOCTEST_BINARY_ASSERT(DT_CHECK_LT, (__VA_ARGS__), lt) #define DOCTEST_REQUIRE_LT(...) DOCTEST_BINARY_ASSERT(DT_REQUIRE_LT, (__VA_ARGS__), lt) #define DOCTEST_WARN_GE(...) DOCTEST_BINARY_ASSERT(DT_WARN_GE, (__VA_ARGS__), ge) #define DOCTEST_CHECK_GE(...) DOCTEST_BINARY_ASSERT(DT_CHECK_GE, (__VA_ARGS__), ge) #define DOCTEST_REQUIRE_GE(...) DOCTEST_BINARY_ASSERT(DT_REQUIRE_GE, (__VA_ARGS__), ge) #define DOCTEST_WARN_LE(...) DOCTEST_BINARY_ASSERT(DT_WARN_LE, (__VA_ARGS__), le) #define DOCTEST_CHECK_LE(...) DOCTEST_BINARY_ASSERT(DT_CHECK_LE, (__VA_ARGS__), le) #define DOCTEST_REQUIRE_LE(...) DOCTEST_BINARY_ASSERT(DT_REQUIRE_LE, (__VA_ARGS__), le) #define DOCTEST_WARN_UNARY(...) DOCTEST_UNARY_ASSERT(DT_WARN_UNARY, (__VA_ARGS__)) #define DOCTEST_CHECK_UNARY(...) DOCTEST_UNARY_ASSERT(DT_CHECK_UNARY, (__VA_ARGS__)) #define DOCTEST_REQUIRE_UNARY(...) DOCTEST_UNARY_ASSERT(DT_REQUIRE_UNARY, (__VA_ARGS__)) #define DOCTEST_WARN_UNARY_FALSE(...) DOCTEST_UNARY_ASSERT(DT_WARN_UNARY_FALSE, (__VA_ARGS__)) #define DOCTEST_CHECK_UNARY_FALSE(...) DOCTEST_UNARY_ASSERT(DT_CHECK_UNARY_FALSE, (__VA_ARGS__)) #define DOCTEST_REQUIRE_UNARY_FALSE(...) DOCTEST_UNARY_ASSERT(DT_REQUIRE_UNARY_FALSE, (__VA_ARGS__)) #else // DOCTEST_CONFIG_WITH_VARIADIC_MACROS #define DOCTEST_WARN_EQ(lhs, rhs) DOCTEST_BINARY_ASSERT(DT_WARN_EQ, lhs, rhs, eq) #define DOCTEST_CHECK_EQ(lhs, rhs) DOCTEST_BINARY_ASSERT(DT_CHECK_EQ, lhs, rhs, eq) #define DOCTEST_REQUIRE_EQ(lhs, rhs) DOCTEST_BINARY_ASSERT(DT_REQUIRE_EQ, lhs, rhs, eq) #define DOCTEST_WARN_NE(lhs, rhs) DOCTEST_BINARY_ASSERT(DT_WARN_NE, lhs, rhs, ne) #define DOCTEST_CHECK_NE(lhs, rhs) DOCTEST_BINARY_ASSERT(DT_CHECK_NE, lhs, rhs, ne) #define DOCTEST_REQUIRE_NE(lhs, rhs) DOCTEST_BINARY_ASSERT(DT_REQUIRE_NE, lhs, rhs, ne) #define DOCTEST_WARN_GT(lhs, rhs) DOCTEST_BINARY_ASSERT(DT_WARN_GT, lhs, rhs, gt) #define DOCTEST_CHECK_GT(lhs, rhs) DOCTEST_BINARY_ASSERT(DT_CHECK_GT, lhs, rhs, gt) #define DOCTEST_REQUIRE_GT(lhs, rhs) DOCTEST_BINARY_ASSERT(DT_REQUIRE_GT, lhs, rhs, gt) #define DOCTEST_WARN_LT(lhs, rhs) DOCTEST_BINARY_ASSERT(DT_WARN_LT, lhs, rhs, lt) #define DOCTEST_CHECK_LT(lhs, rhs) DOCTEST_BINARY_ASSERT(DT_CHECK_LT, lhs, rhs, lt) #define DOCTEST_REQUIRE_LT(lhs, rhs) DOCTEST_BINARY_ASSERT(DT_REQUIRE_LT, lhs, rhs, lt) #define DOCTEST_WARN_GE(lhs, rhs) DOCTEST_BINARY_ASSERT(DT_WARN_GE, lhs, rhs, ge) #define DOCTEST_CHECK_GE(lhs, rhs) DOCTEST_BINARY_ASSERT(DT_CHECK_GE, lhs, rhs, ge) #define DOCTEST_REQUIRE_GE(lhs, rhs) DOCTEST_BINARY_ASSERT(DT_REQUIRE_GE, lhs, rhs, ge) #define DOCTEST_WARN_LE(lhs, rhs) DOCTEST_BINARY_ASSERT(DT_WARN_LE, lhs, rhs, le) #define DOCTEST_CHECK_LE(lhs, rhs) DOCTEST_BINARY_ASSERT(DT_CHECK_LE, lhs, rhs, le) #define DOCTEST_REQUIRE_LE(lhs, rhs) DOCTEST_BINARY_ASSERT(DT_REQUIRE_LE, lhs, rhs, le) #define DOCTEST_WARN_UNARY(v) DOCTEST_UNARY_ASSERT(DT_WARN_UNARY, v) #define DOCTEST_CHECK_UNARY(v) DOCTEST_UNARY_ASSERT(DT_CHECK_UNARY, v) #define DOCTEST_REQUIRE_UNARY(v) DOCTEST_UNARY_ASSERT(DT_REQUIRE_UNARY, v) #define DOCTEST_WARN_UNARY_FALSE(v) DOCTEST_UNARY_ASSERT(DT_WARN_UNARY_FALSE, v) #define DOCTEST_CHECK_UNARY_FALSE(v) DOCTEST_UNARY_ASSERT(DT_CHECK_UNARY_FALSE, v) #define DOCTEST_REQUIRE_UNARY_FALSE(v) DOCTEST_UNARY_ASSERT(DT_REQUIRE_UNARY_FALSE, v) #endif // DOCTEST_CONFIG_WITH_VARIADIC_MACROS #ifndef DOCTEST_CONFIG_SUPER_FAST_ASSERTS #ifdef DOCTEST_CONFIG_WITH_VARIADIC_MACROS #define DOCTEST_FAST_BINARY_ASSERT(assert_type, expr, comparison) \ do { \ int _DOCTEST_FAST_RES = doctest::detail::fast_binary_assert< \ doctest::detail::binaryAssertComparison::comparison>( \ doctest::detail::assertType::assert_type, __FILE__, __LINE__, \ DOCTEST_TOSTR(DOCTEST_HANDLE_BRACED_VA_ARGS(expr)), \ DOCTEST_HANDLE_BRACED_VA_ARGS(expr)); \ if(_DOCTEST_FAST_RES & doctest::detail::assertAction::dbgbreak) \ DOCTEST_BREAK_INTO_DEBUGGER(); \ doctest::detail::fastAssertThrowIfFlagSet(_DOCTEST_FAST_RES); \ } while((void)0, 0) #else // DOCTEST_CONFIG_WITH_VARIADIC_MACROS #define DOCTEST_FAST_BINARY_ASSERT(assert_type, lhs, rhs, comparison) \ do { \ int _DOCTEST_FAST_RES = doctest::detail::fast_binary_assert< \ doctest::detail::binaryAssertComparison::comparison>( \ doctest::detail::assertType::assert_type, __FILE__, __LINE__, #lhs ", " #rhs, lhs, \ rhs); \ if(_DOCTEST_FAST_RES & doctest::detail::assertAction::dbgbreak) \ DOCTEST_BREAK_INTO_DEBUGGER(); \ doctest::detail::fastAssertThrowIfFlagSet(_DOCTEST_FAST_RES); \ } while((void)0, 0) #endif // DOCTEST_CONFIG_WITH_VARIADIC_MACROS #define DOCTEST_FAST_UNARY_ASSERT(assert_type, expr) \ do { \ int _DOCTEST_FAST_RES = doctest::detail::fast_unary_assert( \ doctest::detail::assertType::assert_type, __FILE__, __LINE__, \ DOCTEST_TOSTR(DOCTEST_HANDLE_BRACED_VA_ARGS(expr)), \ DOCTEST_HANDLE_BRACED_VA_ARGS(expr)); \ if(_DOCTEST_FAST_RES & doctest::detail::assertAction::dbgbreak) \ DOCTEST_BREAK_INTO_DEBUGGER(); \ doctest::detail::fastAssertThrowIfFlagSet(_DOCTEST_FAST_RES); \ } while((void)0, 0) #else // DOCTEST_CONFIG_SUPER_FAST_ASSERTS #ifdef DOCTEST_CONFIG_WITH_VARIADIC_MACROS #define DOCTEST_FAST_BINARY_ASSERT(assert_type, expr, comparison) \ doctest::detail::fast_binary_assert( \ doctest::detail::assertType::assert_type, __FILE__, __LINE__, \ DOCTEST_TOSTR(DOCTEST_HANDLE_BRACED_VA_ARGS(expr)), \ DOCTEST_HANDLE_BRACED_VA_ARGS(expr)) #else // DOCTEST_CONFIG_WITH_VARIADIC_MACROS #define DOCTEST_FAST_BINARY_ASSERT(assert_type, lhs, rhs, comparison) \ doctest::detail::fast_binary_assert( \ doctest::detail::assertType::assert_type, __FILE__, __LINE__, #lhs ", " #rhs, lhs, \ rhs) #endif // DOCTEST_CONFIG_WITH_VARIADIC_MACROS #define DOCTEST_FAST_UNARY_ASSERT(assert_type, expr) \ doctest::detail::fast_unary_assert(doctest::detail::assertType::assert_type, __FILE__, \ __LINE__, \ DOCTEST_TOSTR(DOCTEST_HANDLE_BRACED_VA_ARGS(expr)), \ DOCTEST_HANDLE_BRACED_VA_ARGS(expr)) #endif // DOCTEST_CONFIG_SUPER_FAST_ASSERTS // clang-format off #ifdef DOCTEST_CONFIG_WITH_VARIADIC_MACROS #define DOCTEST_FAST_WARN_EQ(...) DOCTEST_FAST_BINARY_ASSERT(DT_FAST_WARN_EQ, (__VA_ARGS__), eq) #define DOCTEST_FAST_CHECK_EQ(...) DOCTEST_FAST_BINARY_ASSERT(DT_FAST_CHECK_EQ, (__VA_ARGS__), eq) #define DOCTEST_FAST_REQUIRE_EQ(...) DOCTEST_FAST_BINARY_ASSERT(DT_FAST_REQUIRE_EQ, (__VA_ARGS__), eq) #define DOCTEST_FAST_WARN_NE(...) DOCTEST_FAST_BINARY_ASSERT(DT_FAST_WARN_NE, (__VA_ARGS__), ne) #define DOCTEST_FAST_CHECK_NE(...) DOCTEST_FAST_BINARY_ASSERT(DT_FAST_CHECK_NE, (__VA_ARGS__), ne) #define DOCTEST_FAST_REQUIRE_NE(...) DOCTEST_FAST_BINARY_ASSERT(DT_FAST_REQUIRE_NE, (__VA_ARGS__), ne) #define DOCTEST_FAST_WARN_GT(...) DOCTEST_FAST_BINARY_ASSERT(DT_FAST_WARN_GT, (__VA_ARGS__), gt) #define DOCTEST_FAST_CHECK_GT(...) DOCTEST_FAST_BINARY_ASSERT(DT_FAST_CHECK_GT, (__VA_ARGS__), gt) #define DOCTEST_FAST_REQUIRE_GT(...) DOCTEST_FAST_BINARY_ASSERT(DT_FAST_REQUIRE_GT, (__VA_ARGS__), gt) #define DOCTEST_FAST_WARN_LT(...) DOCTEST_FAST_BINARY_ASSERT(DT_FAST_WARN_LT, (__VA_ARGS__), lt) #define DOCTEST_FAST_CHECK_LT(...) DOCTEST_FAST_BINARY_ASSERT(DT_FAST_CHECK_LT, (__VA_ARGS__), lt) #define DOCTEST_FAST_REQUIRE_LT(...) DOCTEST_FAST_BINARY_ASSERT(DT_FAST_REQUIRE_LT, (__VA_ARGS__), lt) #define DOCTEST_FAST_WARN_GE(...) DOCTEST_FAST_BINARY_ASSERT(DT_FAST_WARN_GE, (__VA_ARGS__), ge) #define DOCTEST_FAST_CHECK_GE(...) DOCTEST_FAST_BINARY_ASSERT(DT_FAST_CHECK_GE, (__VA_ARGS__), ge) #define DOCTEST_FAST_REQUIRE_GE(...) DOCTEST_FAST_BINARY_ASSERT(DT_FAST_REQUIRE_GE, (__VA_ARGS__), ge) #define DOCTEST_FAST_WARN_LE(...) DOCTEST_FAST_BINARY_ASSERT(DT_FAST_WARN_LE, (__VA_ARGS__), le) #define DOCTEST_FAST_CHECK_LE(...) DOCTEST_FAST_BINARY_ASSERT(DT_FAST_CHECK_LE, (__VA_ARGS__), le) #define DOCTEST_FAST_REQUIRE_LE(...) DOCTEST_FAST_BINARY_ASSERT(DT_FAST_REQUIRE_LE, (__VA_ARGS__), le) #define DOCTEST_FAST_WARN_UNARY(...) DOCTEST_FAST_UNARY_ASSERT(DT_FAST_WARN_UNARY, (__VA_ARGS__)) #define DOCTEST_FAST_CHECK_UNARY(...) DOCTEST_FAST_UNARY_ASSERT(DT_FAST_CHECK_UNARY, (__VA_ARGS__)) #define DOCTEST_FAST_REQUIRE_UNARY(...) DOCTEST_FAST_UNARY_ASSERT(DT_FAST_REQUIRE_UNARY, (__VA_ARGS__)) #define DOCTEST_FAST_WARN_UNARY_FALSE(...) DOCTEST_FAST_UNARY_ASSERT(DT_FAST_WARN_UNARY_FALSE, (__VA_ARGS__)) #define DOCTEST_FAST_CHECK_UNARY_FALSE(...) DOCTEST_FAST_UNARY_ASSERT(DT_FAST_CHECK_UNARY_FALSE, (__VA_ARGS__)) #define DOCTEST_FAST_REQUIRE_UNARY_FALSE(...) DOCTEST_FAST_UNARY_ASSERT(DT_FAST_REQUIRE_UNARY_FALSE, (__VA_ARGS__)) #else // DOCTEST_CONFIG_WITH_VARIADIC_MACROS #define DOCTEST_FAST_WARN_EQ(l, r) DOCTEST_FAST_BINARY_ASSERT(DT_FAST_WARN_EQ, l, r, eq) #define DOCTEST_FAST_CHECK_EQ(l, r) DOCTEST_FAST_BINARY_ASSERT(DT_FAST_CHECK_EQ, l, r, eq) #define DOCTEST_FAST_REQUIRE_EQ(l, r) DOCTEST_FAST_BINARY_ASSERT(DT_FAST_REQUIRE_EQ, l, r, eq) #define DOCTEST_FAST_WARN_NE(l, r) DOCTEST_FAST_BINARY_ASSERT(DT_FAST_WARN_NE, l, r, ne) #define DOCTEST_FAST_CHECK_NE(l, r) DOCTEST_FAST_BINARY_ASSERT(DT_FAST_CHECK_NE, l, r, ne) #define DOCTEST_FAST_REQUIRE_NE(l, r) DOCTEST_FAST_BINARY_ASSERT(DT_FAST_REQUIRE_NE, l, r, ne) #define DOCTEST_FAST_WARN_GT(l, r) DOCTEST_FAST_BINARY_ASSERT(DT_FAST_WARN_GT, l, r, gt) #define DOCTEST_FAST_CHECK_GT(l, r) DOCTEST_FAST_BINARY_ASSERT(DT_FAST_CHECK_GT, l, r, gt) #define DOCTEST_FAST_REQUIRE_GT(l, r) DOCTEST_FAST_BINARY_ASSERT(DT_FAST_REQUIRE_GT, l, r, gt) #define DOCTEST_FAST_WARN_LT(l, r) DOCTEST_FAST_BINARY_ASSERT(DT_FAST_WARN_LT, l, r, lt) #define DOCTEST_FAST_CHECK_LT(l, r) DOCTEST_FAST_BINARY_ASSERT(DT_FAST_CHECK_LT, l, r, lt) #define DOCTEST_FAST_REQUIRE_LT(l, r) DOCTEST_FAST_BINARY_ASSERT(DT_FAST_REQUIRE_LT, l, r, lt) #define DOCTEST_FAST_WARN_GE(l, r) DOCTEST_FAST_BINARY_ASSERT(DT_FAST_WARN_GE, l, r, ge) #define DOCTEST_FAST_CHECK_GE(l, r) DOCTEST_FAST_BINARY_ASSERT(DT_FAST_CHECK_GE, l, r, ge) #define DOCTEST_FAST_REQUIRE_GE(l, r) DOCTEST_FAST_BINARY_ASSERT(DT_FAST_REQUIRE_GE, l, r, ge) #define DOCTEST_FAST_WARN_LE(l, r) DOCTEST_FAST_BINARY_ASSERT(DT_FAST_WARN_LE, l, r, le) #define DOCTEST_FAST_CHECK_LE(l, r) DOCTEST_FAST_BINARY_ASSERT(DT_FAST_CHECK_LE, l, r, le) #define DOCTEST_FAST_REQUIRE_LE(l, r) DOCTEST_FAST_BINARY_ASSERT(DT_FAST_REQUIRE_LE, l, r, le) #define DOCTEST_FAST_WARN_UNARY(v) DOCTEST_FAST_UNARY_ASSERT(DT_FAST_WARN_UNARY, v) #define DOCTEST_FAST_CHECK_UNARY(v) DOCTEST_FAST_UNARY_ASSERT(DT_FAST_CHECK_UNARY, v) #define DOCTEST_FAST_REQUIRE_UNARY(v) DOCTEST_FAST_UNARY_ASSERT(DT_FAST_REQUIRE_UNARY, v) #define DOCTEST_FAST_WARN_UNARY_FALSE(v) DOCTEST_FAST_UNARY_ASSERT(DT_FAST_WARN_UNARY_FALSE, v) #define DOCTEST_FAST_CHECK_UNARY_FALSE(v) DOCTEST_FAST_UNARY_ASSERT(DT_FAST_CHECK_UNARY_FALSE, v) #define DOCTEST_FAST_REQUIRE_UNARY_FALSE(v) DOCTEST_FAST_UNARY_ASSERT(DT_FAST_REQUIRE_UNARY_FALSE, v) #endif // DOCTEST_CONFIG_WITH_VARIADIC_MACROS // clang-format on #ifdef DOCTEST_CONFIG_NO_EXCEPTIONS #undef DOCTEST_WARN_THROWS #undef DOCTEST_CHECK_THROWS #undef DOCTEST_REQUIRE_THROWS #undef DOCTEST_WARN_THROWS_AS #undef DOCTEST_CHECK_THROWS_AS #undef DOCTEST_REQUIRE_THROWS_AS #undef DOCTEST_WARN_NOTHROW #undef DOCTEST_CHECK_NOTHROW #undef DOCTEST_REQUIRE_NOTHROW #undef DOCTEST_WARN_THROWS_MESSAGE #undef DOCTEST_CHECK_THROWS_MESSAGE #undef DOCTEST_REQUIRE_THROWS_MESSAGE #undef DOCTEST_WARN_THROWS_AS_MESSAGE #undef DOCTEST_CHECK_THROWS_AS_MESSAGE #undef DOCTEST_REQUIRE_THROWS_AS_MESSAGE #undef DOCTEST_WARN_NOTHROW_MESSAGE #undef DOCTEST_CHECK_NOTHROW_MESSAGE #undef DOCTEST_REQUIRE_NOTHROW_MESSAGE #ifdef DOCTEST_CONFIG_NO_EXCEPTIONS_BUT_WITH_ALL_ASSERTS #define DOCTEST_WARN_THROWS(expr) ((void)0) #define DOCTEST_CHECK_THROWS(expr) ((void)0) #define DOCTEST_REQUIRE_THROWS(expr) ((void)0) #ifdef DOCTEST_CONFIG_WITH_VARIADIC_MACROS #define DOCTEST_WARN_THROWS_AS(expr, ...) ((void)0) #define DOCTEST_CHECK_THROWS_AS(expr, ...) ((void)0) #define DOCTEST_REQUIRE_THROWS_AS(expr, ...) ((void)0) #else // DOCTEST_CONFIG_WITH_VARIADIC_MACROS #define DOCTEST_WARN_THROWS_AS(expr, ex) ((void)0) #define DOCTEST_CHECK_THROWS_AS(expr, ex) ((void)0) #define DOCTEST_REQUIRE_THROWS_AS(expr, ex) ((void)0) #endif // DOCTEST_CONFIG_WITH_VARIADIC_MACROS #define DOCTEST_WARN_NOTHROW(expr) ((void)0) #define DOCTEST_CHECK_NOTHROW(expr) ((void)0) #define DOCTEST_REQUIRE_NOTHROW(expr) ((void)0) #define DOCTEST_WARN_THROWS_MESSAGE(expr, msg) ((void)0) #define DOCTEST_CHECK_THROWS_MESSAGE(expr, msg) ((void)0) #define DOCTEST_REQUIRE_THROWS_MESSAGE(expr, msg) ((void)0) #define DOCTEST_WARN_THROWS_AS_MESSAGE(expr, ex, msg) ((void)0) #define DOCTEST_CHECK_THROWS_AS_MESSAGE(expr, ex, msg) ((void)0) #define DOCTEST_REQUIRE_THROWS_AS_MESSAGE(expr, ex, msg) ((void)0) #define DOCTEST_WARN_NOTHROW_MESSAGE(expr, msg) ((void)0) #define DOCTEST_CHECK_NOTHROW_MESSAGE(expr, msg) ((void)0) #define DOCTEST_REQUIRE_NOTHROW_MESSAGE(expr, msg) ((void)0) #else // DOCTEST_CONFIG_NO_EXCEPTIONS_BUT_WITH_ALL_ASSERTS #undef DOCTEST_REQUIRE #undef DOCTEST_REQUIRE_FALSE #undef DOCTEST_REQUIRE_MESSAGE #undef DOCTEST_REQUIRE_FALSE_MESSAGE #undef DOCTEST_REQUIRE_EQ #undef DOCTEST_REQUIRE_NE #undef DOCTEST_REQUIRE_GT #undef DOCTEST_REQUIRE_LT #undef DOCTEST_REQUIRE_GE #undef DOCTEST_REQUIRE_LE #undef DOCTEST_REQUIRE_UNARY #undef DOCTEST_REQUIRE_UNARY_FALSE #undef DOCTEST_FAST_REQUIRE_EQ #undef DOCTEST_FAST_REQUIRE_NE #undef DOCTEST_FAST_REQUIRE_GT #undef DOCTEST_FAST_REQUIRE_LT #undef DOCTEST_FAST_REQUIRE_GE #undef DOCTEST_FAST_REQUIRE_LE #undef DOCTEST_FAST_REQUIRE_UNARY #undef DOCTEST_FAST_REQUIRE_UNARY_FALSE #endif // DOCTEST_CONFIG_NO_EXCEPTIONS_BUT_WITH_ALL_ASSERTS #endif // DOCTEST_CONFIG_NO_EXCEPTIONS // ================================================================================================= // == WHAT FOLLOWS IS VERSIONS OF THE MACROS THAT DO NOT DO ANY REGISTERING! == // == THIS CAN BE ENABLED BY DEFINING DOCTEST_CONFIG_DISABLE GLOBALLY! == // ================================================================================================= #else // DOCTEST_CONFIG_DISABLE #define DOCTEST_IMPLEMENT_FIXTURE(der, base, func, name) \ namespace \ { \ template \ struct der : base \ { void f(); }; \ } \ template \ inline void der::f() #define DOCTEST_CREATE_AND_REGISTER_FUNCTION(f, name) \ template \ static inline void f() // for registering tests #define DOCTEST_TEST_CASE(name) \ DOCTEST_CREATE_AND_REGISTER_FUNCTION(DOCTEST_ANONYMOUS(_DOCTEST_ANON_FUNC_), name) // for registering tests with a fixture #define DOCTEST_TEST_CASE_FIXTURE(x, name) \ DOCTEST_IMPLEMENT_FIXTURE(DOCTEST_ANONYMOUS(_DOCTEST_ANON_CLASS_), x, \ DOCTEST_ANONYMOUS(_DOCTEST_ANON_FUNC_), name) // for converting types to strings without the header and demangling #ifdef DOCTEST_CONFIG_WITH_VARIADIC_MACROS #define DOCTEST_TYPE_TO_STRING(...) typedef int DOCTEST_ANONYMOUS(_DOCTEST_ANON_FOR_SEMICOLON_) #define DOCTEST_TYPE_TO_STRING_IMPL(...) #else // DOCTEST_CONFIG_WITH_VARIADIC_MACROS #define DOCTEST_TYPE_TO_STRING(x) typedef int DOCTEST_ANONYMOUS(_DOCTEST_ANON_FOR_SEMICOLON_) #define DOCTEST_TYPE_TO_STRING_IMPL(x) #endif // DOCTEST_CONFIG_WITH_VARIADIC_MACROS // for typed tests #define DOCTEST_TEST_CASE_TEMPLATE(name, type, types) \ template \ inline void DOCTEST_ANONYMOUS(_DOCTEST_ANON_TMP_)() #define DOCTEST_TEST_CASE_TEMPLATE_DEFINE(name, type, id) \ template \ inline void DOCTEST_ANONYMOUS(_DOCTEST_ANON_TMP_)() #define DOCTEST_TEST_CASE_TEMPLATE_INSTANTIATE(id, types) \ typedef int DOCTEST_ANONYMOUS(_DOCTEST_ANON_FOR_SEMICOLON_) // for subcases #define DOCTEST_SUBCASE(name) // for a testsuite block #define DOCTEST_TEST_SUITE(name) namespace // for starting a testsuite block #define DOCTEST_TEST_SUITE_BEGIN(name) typedef int DOCTEST_ANONYMOUS(_DOCTEST_ANON_FOR_SEMICOLON_) // for ending a testsuite block #define DOCTEST_TEST_SUITE_END typedef int DOCTEST_ANONYMOUS(_DOCTEST_ANON_FOR_SEMICOLON_) #define DOCTEST_REGISTER_EXCEPTION_TRANSLATOR(signature) \ template \ static inline doctest::String DOCTEST_ANONYMOUS(_DOCTEST_ANON_TRANSLATOR_)(signature) #define DOCTEST_INFO(x) ((void)0) #define DOCTEST_CAPTURE(x) ((void)0) #define DOCTEST_ADD_MESSAGE_AT(file, line, x) ((void)0) #define DOCTEST_ADD_FAIL_CHECK_AT(file, line, x) ((void)0) #define DOCTEST_ADD_FAIL_AT(file, line, x) ((void)0) #define DOCTEST_MESSAGE(x) ((void)0) #define DOCTEST_FAIL_CHECK(x) ((void)0) #define DOCTEST_FAIL(x) ((void)0) #ifdef DOCTEST_CONFIG_WITH_VARIADIC_MACROS #define DOCTEST_WARN(...) ((void)0) #define DOCTEST_CHECK(...) ((void)0) #define DOCTEST_REQUIRE(...) ((void)0) #define DOCTEST_WARN_FALSE(...) ((void)0) #define DOCTEST_CHECK_FALSE(...) ((void)0) #define DOCTEST_REQUIRE_FALSE(...) ((void)0) #else // DOCTEST_CONFIG_WITH_VARIADIC_MACROS #define DOCTEST_WARN(expr) ((void)0) #define DOCTEST_CHECK(expr) ((void)0) #define DOCTEST_REQUIRE(expr) ((void)0) #define DOCTEST_WARN_FALSE(expr) ((void)0) #define DOCTEST_CHECK_FALSE(expr) ((void)0) #define DOCTEST_REQUIRE_FALSE(expr) ((void)0) #endif // DOCTEST_CONFIG_WITH_VARIADIC_MACROS #define DOCTEST_WARN_MESSAGE(cond, msg) ((void)0) #define DOCTEST_CHECK_MESSAGE(cond, msg) ((void)0) #define DOCTEST_REQUIRE_MESSAGE(cond, msg) ((void)0) #define DOCTEST_WARN_FALSE_MESSAGE(cond, msg) ((void)0) #define DOCTEST_CHECK_FALSE_MESSAGE(cond, msg) ((void)0) #define DOCTEST_REQUIRE_FALSE_MESSAGE(cond, msg) ((void)0) #define DOCTEST_WARN_THROWS(expr) ((void)0) #define DOCTEST_CHECK_THROWS(expr) ((void)0) #define DOCTEST_REQUIRE_THROWS(expr) ((void)0) #ifdef DOCTEST_CONFIG_WITH_VARIADIC_MACROS #define DOCTEST_WARN_THROWS_AS(expr, ...) ((void)0) #define DOCTEST_CHECK_THROWS_AS(expr, ...) ((void)0) #define DOCTEST_REQUIRE_THROWS_AS(expr, ...) ((void)0) #else // DOCTEST_CONFIG_WITH_VARIADIC_MACROS #define DOCTEST_WARN_THROWS_AS(expr, ex) ((void)0) #define DOCTEST_CHECK_THROWS_AS(expr, ex) ((void)0) #define DOCTEST_REQUIRE_THROWS_AS(expr, ex) ((void)0) #endif // DOCTEST_CONFIG_WITH_VARIADIC_MACROS #define DOCTEST_WARN_NOTHROW(expr) ((void)0) #define DOCTEST_CHECK_NOTHROW(expr) ((void)0) #define DOCTEST_REQUIRE_NOTHROW(expr) ((void)0) #define DOCTEST_WARN_THROWS_MESSAGE(expr, msg) ((void)0) #define DOCTEST_CHECK_THROWS_MESSAGE(expr, msg) ((void)0) #define DOCTEST_REQUIRE_THROWS_MESSAGE(expr, msg) ((void)0) #define DOCTEST_WARN_THROWS_AS_MESSAGE(expr, ex, msg) ((void)0) #define DOCTEST_CHECK_THROWS_AS_MESSAGE(expr, ex, msg) ((void)0) #define DOCTEST_REQUIRE_THROWS_AS_MESSAGE(expr, ex, msg) ((void)0) #define DOCTEST_WARN_NOTHROW_MESSAGE(expr, msg) ((void)0) #define DOCTEST_CHECK_NOTHROW_MESSAGE(expr, msg) ((void)0) #define DOCTEST_REQUIRE_NOTHROW_MESSAGE(expr, msg) ((void)0) #ifdef DOCTEST_CONFIG_WITH_VARIADIC_MACROS #define DOCTEST_WARN_EQ(...) ((void)0) #define DOCTEST_CHECK_EQ(...) ((void)0) #define DOCTEST_REQUIRE_EQ(...) ((void)0) #define DOCTEST_WARN_NE(...) ((void)0) #define DOCTEST_CHECK_NE(...) ((void)0) #define DOCTEST_REQUIRE_NE(...) ((void)0) #define DOCTEST_WARN_GT(...) ((void)0) #define DOCTEST_CHECK_GT(...) ((void)0) #define DOCTEST_REQUIRE_GT(...) ((void)0) #define DOCTEST_WARN_LT(...) ((void)0) #define DOCTEST_CHECK_LT(...) ((void)0) #define DOCTEST_REQUIRE_LT(...) ((void)0) #define DOCTEST_WARN_GE(...) ((void)0) #define DOCTEST_CHECK_GE(...) ((void)0) #define DOCTEST_REQUIRE_GE(...) ((void)0) #define DOCTEST_WARN_LE(...) ((void)0) #define DOCTEST_CHECK_LE(...) ((void)0) #define DOCTEST_REQUIRE_LE(...) ((void)0) #define DOCTEST_WARN_UNARY(...) ((void)0) #define DOCTEST_CHECK_UNARY(...) ((void)0) #define DOCTEST_REQUIRE_UNARY(...) ((void)0) #define DOCTEST_WARN_UNARY_FALSE(...) ((void)0) #define DOCTEST_CHECK_UNARY_FALSE(...) ((void)0) #define DOCTEST_REQUIRE_UNARY_FALSE(...) ((void)0) #define DOCTEST_FAST_WARN_EQ(...) ((void)0) #define DOCTEST_FAST_CHECK_EQ(...) ((void)0) #define DOCTEST_FAST_REQUIRE_EQ(...) ((void)0) #define DOCTEST_FAST_WARN_NE(...) ((void)0) #define DOCTEST_FAST_CHECK_NE(...) ((void)0) #define DOCTEST_FAST_REQUIRE_NE(...) ((void)0) #define DOCTEST_FAST_WARN_GT(...) ((void)0) #define DOCTEST_FAST_CHECK_GT(...) ((void)0) #define DOCTEST_FAST_REQUIRE_GT(...) ((void)0) #define DOCTEST_FAST_WARN_LT(...) ((void)0) #define DOCTEST_FAST_CHECK_LT(...) ((void)0) #define DOCTEST_FAST_REQUIRE_LT(...) ((void)0) #define DOCTEST_FAST_WARN_GE(...) ((void)0) #define DOCTEST_FAST_CHECK_GE(...) ((void)0) #define DOCTEST_FAST_REQUIRE_GE(...) ((void)0) #define DOCTEST_FAST_WARN_LE(...) ((void)0) #define DOCTEST_FAST_CHECK_LE(...) ((void)0) #define DOCTEST_FAST_REQUIRE_LE(...) ((void)0) #define DOCTEST_FAST_WARN_UNARY(...) ((void)0) #define DOCTEST_FAST_CHECK_UNARY(...) ((void)0) #define DOCTEST_FAST_REQUIRE_UNARY(...) ((void)0) #define DOCTEST_FAST_WARN_UNARY_FALSE(...) ((void)0) #define DOCTEST_FAST_CHECK_UNARY_FALSE(...) ((void)0) #define DOCTEST_FAST_REQUIRE_UNARY_FALSE(...) ((void)0) #else // DOCTEST_CONFIG_WITH_VARIADIC_MACROS #define DOCTEST_WARN_EQ(lhs, rhs) ((void)0) #define DOCTEST_CHECK_EQ(lhs, rhs) ((void)0) #define DOCTEST_REQUIRE_EQ(lhs, rhs) ((void)0) #define DOCTEST_WARN_NE(lhs, rhs) ((void)0) #define DOCTEST_CHECK_NE(lhs, rhs) ((void)0) #define DOCTEST_REQUIRE_NE(lhs, rhs) ((void)0) #define DOCTEST_WARN_GT(lhs, rhs) ((void)0) #define DOCTEST_CHECK_GT(lhs, rhs) ((void)0) #define DOCTEST_REQUIRE_GT(lhs, rhs) ((void)0) #define DOCTEST_WARN_LT(lhs, rhs) ((void)0) #define DOCTEST_CHECK_LT(lhs, rhs) ((void)0) #define DOCTEST_REQUIRE_LT(lhs, rhs) ((void)0) #define DOCTEST_WARN_GE(lhs, rhs) ((void)0) #define DOCTEST_CHECK_GE(lhs, rhs) ((void)0) #define DOCTEST_REQUIRE_GE(lhs, rhs) ((void)0) #define DOCTEST_WARN_LE(lhs, rhs) ((void)0) #define DOCTEST_CHECK_LE(lhs, rhs) ((void)0) #define DOCTEST_REQUIRE_LE(lhs, rhs) ((void)0) #define DOCTEST_WARN_UNARY(val) ((void)0) #define DOCTEST_CHECK_UNARY(val) ((void)0) #define DOCTEST_REQUIRE_UNARY(val) ((void)0) #define DOCTEST_WARN_UNARY_FALSE(val) ((void)0) #define DOCTEST_CHECK_UNARY_FALSE(val) ((void)0) #define DOCTEST_REQUIRE_UNARY_FALSE(val) ((void)0) #define DOCTEST_FAST_WARN_EQ(lhs, rhs) ((void)0) #define DOCTEST_FAST_CHECK_EQ(lhs, rhs) ((void)0) #define DOCTEST_FAST_REQUIRE_EQ(lhs, rhs) ((void)0) #define DOCTEST_FAST_WARN_NE(lhs, rhs) ((void)0) #define DOCTEST_FAST_CHECK_NE(lhs, rhs) ((void)0) #define DOCTEST_FAST_REQUIRE_NE(lhs, rhs) ((void)0) #define DOCTEST_FAST_WARN_GT(lhs, rhs) ((void)0) #define DOCTEST_FAST_CHECK_GT(lhs, rhs) ((void)0) #define DOCTEST_FAST_REQUIRE_GT(lhs, rhs) ((void)0) #define DOCTEST_FAST_WARN_LT(lhs, rhs) ((void)0) #define DOCTEST_FAST_CHECK_LT(lhs, rhs) ((void)0) #define DOCTEST_FAST_REQUIRE_LT(lhs, rhs) ((void)0) #define DOCTEST_FAST_WARN_GE(lhs, rhs) ((void)0) #define DOCTEST_FAST_CHECK_GE(lhs, rhs) ((void)0) #define DOCTEST_FAST_REQUIRE_GE(lhs, rhs) ((void)0) #define DOCTEST_FAST_WARN_LE(lhs, rhs) ((void)0) #define DOCTEST_FAST_CHECK_LE(lhs, rhs) ((void)0) #define DOCTEST_FAST_REQUIRE_LE(lhs, rhs) ((void)0) #define DOCTEST_FAST_WARN_UNARY(val) ((void)0) #define DOCTEST_FAST_CHECK_UNARY(val) ((void)0) #define DOCTEST_FAST_REQUIRE_UNARY(val) ((void)0) #define DOCTEST_FAST_WARN_UNARY_FALSE(val) ((void)0) #define DOCTEST_FAST_CHECK_UNARY_FALSE(val) ((void)0) #define DOCTEST_FAST_REQUIRE_UNARY_FALSE(val) ((void)0) #endif // DOCTEST_CONFIG_WITH_VARIADIC_MACROS #endif // DOCTEST_CONFIG_DISABLE // BDD style macros // clang-format off #define DOCTEST_SCENARIO(name) TEST_CASE(" Scenario: " name) #ifdef DOCTEST_CONFIG_WITH_VARIADIC_MACROS #define DOCTEST_SCENARIO_TEMPLATE(name, T, ...) TEST_CASE_TEMPLATE(" Scenario: " name, T, __VA_ARGS__) #else // DOCTEST_CONFIG_WITH_VARIADIC_MACROS #define DOCTEST_SCENARIO_TEMPLATE(name, T, types) TEST_CASE_TEMPLATE(" Scenario: " name, T, types) #endif // DOCTEST_CONFIG_WITH_VARIADIC_MACROS #define DOCTEST_SCENARIO_TEMPLATE_DEFINE(name, T, id) DOCTEST_TEST_CASE_TEMPLATE_DEFINE(" Scenario: " name, T, id) #define DOCTEST_GIVEN(name) SUBCASE(" Given: " name) #define DOCTEST_WHEN(name) SUBCASE(" When: " name) #define DOCTEST_AND_WHEN(name) SUBCASE("And when: " name) #define DOCTEST_THEN(name) SUBCASE(" Then: " name) #define DOCTEST_AND_THEN(name) SUBCASE(" And: " name) // clang-format on // == SHORT VERSIONS OF THE MACROS #if !defined(DOCTEST_CONFIG_NO_SHORT_MACRO_NAMES) #define TEST_CASE DOCTEST_TEST_CASE #define TEST_CASE_FIXTURE DOCTEST_TEST_CASE_FIXTURE #define TYPE_TO_STRING DOCTEST_TYPE_TO_STRING #define TEST_CASE_TEMPLATE DOCTEST_TEST_CASE_TEMPLATE #define TEST_CASE_TEMPLATE_DEFINE DOCTEST_TEST_CASE_TEMPLATE_DEFINE #define TEST_CASE_TEMPLATE_INSTANTIATE DOCTEST_TEST_CASE_TEMPLATE_INSTANTIATE #define SUBCASE DOCTEST_SUBCASE #define TEST_SUITE DOCTEST_TEST_SUITE #define TEST_SUITE_BEGIN DOCTEST_TEST_SUITE_BEGIN #define TEST_SUITE_END DOCTEST_TEST_SUITE_END #define REGISTER_EXCEPTION_TRANSLATOR DOCTEST_REGISTER_EXCEPTION_TRANSLATOR #define INFO DOCTEST_INFO #define CAPTURE DOCTEST_CAPTURE #define ADD_MESSAGE_AT DOCTEST_ADD_MESSAGE_AT #define ADD_FAIL_CHECK_AT DOCTEST_ADD_FAIL_CHECK_AT #define ADD_FAIL_AT DOCTEST_ADD_FAIL_AT #define MESSAGE DOCTEST_MESSAGE #define FAIL_CHECK DOCTEST_FAIL_CHECK #define FAIL DOCTEST_FAIL #define TO_LVALUE DOCTEST_TO_LVALUE #define WARN DOCTEST_WARN #define WARN_FALSE DOCTEST_WARN_FALSE #define WARN_THROWS DOCTEST_WARN_THROWS #define WARN_THROWS_AS DOCTEST_WARN_THROWS_AS #define WARN_NOTHROW DOCTEST_WARN_NOTHROW #define CHECK DOCTEST_CHECK #define CHECK_FALSE DOCTEST_CHECK_FALSE #define CHECK_THROWS DOCTEST_CHECK_THROWS #define CHECK_THROWS_AS DOCTEST_CHECK_THROWS_AS #define CHECK_NOTHROW DOCTEST_CHECK_NOTHROW #define REQUIRE DOCTEST_REQUIRE #define REQUIRE_FALSE DOCTEST_REQUIRE_FALSE #define REQUIRE_THROWS DOCTEST_REQUIRE_THROWS #define REQUIRE_THROWS_AS DOCTEST_REQUIRE_THROWS_AS #define REQUIRE_NOTHROW DOCTEST_REQUIRE_NOTHROW #define WARN_MESSAGE DOCTEST_WARN_MESSAGE #define WARN_FALSE_MESSAGE DOCTEST_WARN_FALSE_MESSAGE #define WARN_THROWS_MESSAGE DOCTEST_WARN_THROWS_MESSAGE #define WARN_THROWS_AS_MESSAGE DOCTEST_WARN_THROWS_AS_MESSAGE #define WARN_NOTHROW_MESSAGE DOCTEST_WARN_NOTHROW_MESSAGE #define CHECK_MESSAGE DOCTEST_CHECK_MESSAGE #define CHECK_FALSE_MESSAGE DOCTEST_CHECK_FALSE_MESSAGE #define CHECK_THROWS_MESSAGE DOCTEST_CHECK_THROWS_MESSAGE #define CHECK_THROWS_AS_MESSAGE DOCTEST_CHECK_THROWS_AS_MESSAGE #define CHECK_NOTHROW_MESSAGE DOCTEST_CHECK_NOTHROW_MESSAGE #define REQUIRE_MESSAGE DOCTEST_REQUIRE_MESSAGE #define REQUIRE_FALSE_MESSAGE DOCTEST_REQUIRE_FALSE_MESSAGE #define REQUIRE_THROWS_MESSAGE DOCTEST_REQUIRE_THROWS_MESSAGE #define REQUIRE_THROWS_AS_MESSAGE DOCTEST_REQUIRE_THROWS_AS_MESSAGE #define REQUIRE_NOTHROW_MESSAGE DOCTEST_REQUIRE_NOTHROW_MESSAGE #define SCENARIO DOCTEST_SCENARIO #define SCENARIO_TEMPLATE DOCTEST_SCENARIO_TEMPLATE #define SCENARIO_TEMPLATE_DEFINE DOCTEST_SCENARIO_TEMPLATE_DEFINE #define GIVEN DOCTEST_GIVEN #define WHEN DOCTEST_WHEN #define AND_WHEN DOCTEST_AND_WHEN #define THEN DOCTEST_THEN #define AND_THEN DOCTEST_AND_THEN #define WARN_EQ DOCTEST_WARN_EQ #define CHECK_EQ DOCTEST_CHECK_EQ #define REQUIRE_EQ DOCTEST_REQUIRE_EQ #define WARN_NE DOCTEST_WARN_NE #define CHECK_NE DOCTEST_CHECK_NE #define REQUIRE_NE DOCTEST_REQUIRE_NE #define WARN_GT DOCTEST_WARN_GT #define CHECK_GT DOCTEST_CHECK_GT #define REQUIRE_GT DOCTEST_REQUIRE_GT #define WARN_LT DOCTEST_WARN_LT #define CHECK_LT DOCTEST_CHECK_LT #define REQUIRE_LT DOCTEST_REQUIRE_LT #define WARN_GE DOCTEST_WARN_GE #define CHECK_GE DOCTEST_CHECK_GE #define REQUIRE_GE DOCTEST_REQUIRE_GE #define WARN_LE DOCTEST_WARN_LE #define CHECK_LE DOCTEST_CHECK_LE #define REQUIRE_LE DOCTEST_REQUIRE_LE #define WARN_UNARY DOCTEST_WARN_UNARY #define CHECK_UNARY DOCTEST_CHECK_UNARY #define REQUIRE_UNARY DOCTEST_REQUIRE_UNARY #define WARN_UNARY_FALSE DOCTEST_WARN_UNARY_FALSE #define CHECK_UNARY_FALSE DOCTEST_CHECK_UNARY_FALSE #define REQUIRE_UNARY_FALSE DOCTEST_REQUIRE_UNARY_FALSE #define FAST_WARN_EQ DOCTEST_FAST_WARN_EQ #define FAST_CHECK_EQ DOCTEST_FAST_CHECK_EQ #define FAST_REQUIRE_EQ DOCTEST_FAST_REQUIRE_EQ #define FAST_WARN_NE DOCTEST_FAST_WARN_NE #define FAST_CHECK_NE DOCTEST_FAST_CHECK_NE #define FAST_REQUIRE_NE DOCTEST_FAST_REQUIRE_NE #define FAST_WARN_GT DOCTEST_FAST_WARN_GT #define FAST_CHECK_GT DOCTEST_FAST_CHECK_GT #define FAST_REQUIRE_GT DOCTEST_FAST_REQUIRE_GT #define FAST_WARN_LT DOCTEST_FAST_WARN_LT #define FAST_CHECK_LT DOCTEST_FAST_CHECK_LT #define FAST_REQUIRE_LT DOCTEST_FAST_REQUIRE_LT #define FAST_WARN_GE DOCTEST_FAST_WARN_GE #define FAST_CHECK_GE DOCTEST_FAST_CHECK_GE #define FAST_REQUIRE_GE DOCTEST_FAST_REQUIRE_GE #define FAST_WARN_LE DOCTEST_FAST_WARN_LE #define FAST_CHECK_LE DOCTEST_FAST_CHECK_LE #define FAST_REQUIRE_LE DOCTEST_FAST_REQUIRE_LE #define FAST_WARN_UNARY DOCTEST_FAST_WARN_UNARY #define FAST_CHECK_UNARY DOCTEST_FAST_CHECK_UNARY #define FAST_REQUIRE_UNARY DOCTEST_FAST_REQUIRE_UNARY #define FAST_WARN_UNARY_FALSE DOCTEST_FAST_WARN_UNARY_FALSE #define FAST_CHECK_UNARY_FALSE DOCTEST_FAST_CHECK_UNARY_FALSE #define FAST_REQUIRE_UNARY_FALSE DOCTEST_FAST_REQUIRE_UNARY_FALSE #endif // DOCTEST_CONFIG_NO_SHORT_MACRO_NAMES // this is here to clear the 'current test suite' for the current translation unit - at the top DOCTEST_TEST_SUITE_END(); // add stringification for primitive/fundamental types namespace doctest { namespace detail { DOCTEST_TYPE_TO_STRING_IMPL(bool) DOCTEST_TYPE_TO_STRING_IMPL(float) DOCTEST_TYPE_TO_STRING_IMPL(double) DOCTEST_TYPE_TO_STRING_IMPL(long double) DOCTEST_TYPE_TO_STRING_IMPL(char) DOCTEST_TYPE_TO_STRING_IMPL(signed char) DOCTEST_TYPE_TO_STRING_IMPL(unsigned char) DOCTEST_TYPE_TO_STRING_IMPL(wchar_t) DOCTEST_TYPE_TO_STRING_IMPL(short int) DOCTEST_TYPE_TO_STRING_IMPL(unsigned short int) DOCTEST_TYPE_TO_STRING_IMPL(int) DOCTEST_TYPE_TO_STRING_IMPL(unsigned int) DOCTEST_TYPE_TO_STRING_IMPL(long int) DOCTEST_TYPE_TO_STRING_IMPL(unsigned long int) #ifdef DOCTEST_CONFIG_WITH_LONG_LONG DOCTEST_TYPE_TO_STRING_IMPL(long long int) DOCTEST_TYPE_TO_STRING_IMPL(unsigned long long int) #endif // DOCTEST_CONFIG_WITH_LONG_LONG } // namespace detail } // namespace doctest DOCTEST_CLANG_SUPPRESS_WARNING_POP DOCTEST_MSVC_SUPPRESS_WARNING_POP DOCTEST_GCC_SUPPRESS_WARNING_POP #endif // DOCTEST_LIBRARY_INCLUDED #ifndef DOCTEST_SINGLE_HEADER #define DOCTEST_SINGLE_HEADER #endif // DOCTEST_SINGLE_HEADER #if defined(DOCTEST_CONFIG_IMPLEMENT) || !defined(DOCTEST_SINGLE_HEADER) #ifndef DOCTEST_LIBRARY_IMPLEMENTATION #define DOCTEST_LIBRARY_IMPLEMENTATION #ifndef DOCTEST_SINGLE_HEADER #include "doctest_fwd.h" #endif // DOCTEST_SINGLE_HEADER DOCTEST_CLANG_SUPPRESS_WARNING_PUSH DOCTEST_CLANG_SUPPRESS_WARNING("-Wunknown-pragmas") DOCTEST_CLANG_SUPPRESS_WARNING("-Wpadded") DOCTEST_CLANG_SUPPRESS_WARNING("-Wglobal-constructors") DOCTEST_CLANG_SUPPRESS_WARNING("-Wexit-time-destructors") DOCTEST_CLANG_SUPPRESS_WARNING("-Wmissing-prototypes") DOCTEST_CLANG_SUPPRESS_WARNING("-Wsign-conversion") DOCTEST_CLANG_SUPPRESS_WARNING("-Wshorten-64-to-32") DOCTEST_CLANG_SUPPRESS_WARNING("-Wmissing-variable-declarations") DOCTEST_CLANG_SUPPRESS_WARNING("-Wswitch") DOCTEST_CLANG_SUPPRESS_WARNING("-Wswitch-enum") DOCTEST_CLANG_SUPPRESS_WARNING("-Wcovered-switch-default") DOCTEST_CLANG_SUPPRESS_WARNING("-Wmissing-noreturn") DOCTEST_CLANG_SUPPRESS_WARNING("-Wunused-local-typedef") DOCTEST_CLANG_SUPPRESS_WARNING("-Wdisabled-macro-expansion") DOCTEST_CLANG_SUPPRESS_WARNING("-Wmissing-braces") DOCTEST_CLANG_SUPPRESS_WARNING("-Wmissing-field-initializers") DOCTEST_CLANG_SUPPRESS_WARNING("-Wc++11-long-long") #if DOCTEST_CLANG && DOCTEST_CLANG_HAS_WARNING("-Wzero-as-null-pointer-constant") DOCTEST_CLANG_SUPPRESS_WARNING("-Wzero-as-null-pointer-constant") #endif // clang - 0 as null DOCTEST_GCC_SUPPRESS_WARNING_PUSH DOCTEST_GCC_SUPPRESS_WARNING("-Wunknown-pragmas") DOCTEST_GCC_SUPPRESS_WARNING("-Wpragmas") DOCTEST_GCC_SUPPRESS_WARNING("-Wconversion") DOCTEST_GCC_SUPPRESS_WARNING("-Weffc++") DOCTEST_GCC_SUPPRESS_WARNING("-Wsign-conversion") DOCTEST_GCC_SUPPRESS_WARNING("-Wstrict-overflow") DOCTEST_GCC_SUPPRESS_WARNING("-Wmissing-field-initializers") DOCTEST_GCC_SUPPRESS_WARNING("-Wmissing-braces") DOCTEST_GCC_SUPPRESS_WARNING("-Wmissing-declarations") DOCTEST_GCC_SUPPRESS_WARNING("-Winline") DOCTEST_GCC_SUPPRESS_WARNING("-Wswitch") DOCTEST_GCC_SUPPRESS_WARNING("-Wswitch-enum") DOCTEST_GCC_SUPPRESS_WARNING("-Wswitch-default") DOCTEST_GCC_SUPPRESS_WARNING("-Wunsafe-loop-optimizations") DOCTEST_GCC_SUPPRESS_WARNING("-Wlong-long") DOCTEST_GCC_SUPPRESS_WARNING("-Wold-style-cast") DOCTEST_GCC_SUPPRESS_WARNING("-Wzero-as-null-pointer-constant") DOCTEST_GCC_SUPPRESS_WARNING("-Wunused-local-typedefs") DOCTEST_GCC_SUPPRESS_WARNING("-Wuseless-cast") DOCTEST_MSVC_SUPPRESS_WARNING_PUSH DOCTEST_MSVC_SUPPRESS_WARNING(4616) // invalid compiler warning DOCTEST_MSVC_SUPPRESS_WARNING(4619) // invalid compiler warning DOCTEST_MSVC_SUPPRESS_WARNING(4996) // The compiler encountered a deprecated declaration DOCTEST_MSVC_SUPPRESS_WARNING(4267) // 'var' : conversion from 'x' to 'y', possible loss of data DOCTEST_MSVC_SUPPRESS_WARNING(4706) // assignment within conditional expression DOCTEST_MSVC_SUPPRESS_WARNING(4512) // 'class' : assignment operator could not be generated DOCTEST_MSVC_SUPPRESS_WARNING(4127) // conditional expression is constant DOCTEST_MSVC_SUPPRESS_WARNING(4530) // C++ exception handler used, but unwind semantics not enabled DOCTEST_MSVC_SUPPRESS_WARNING(4577) // 'noexcept' used with no exception handling mode specified DOCTEST_MSVC_SUPPRESS_WARNING(4774) // format string expected in argument is not a string literal DOCTEST_MSVC_SUPPRESS_WARNING(4365) // conversion from 'int' to 'unsigned', signed/unsigned mismatch DOCTEST_MSVC_SUPPRESS_WARNING(4820) // padding in structs DOCTEST_MSVC_SUPPRESS_WARNING(4640) // construction of local static object is not thread-safe DOCTEST_MSVC_SUPPRESS_WARNING(5039) // pointer to potentially throwing function passed to extern C DOCTEST_MSVC_SUPPRESS_WARNING(5045) // Spectre mitigation stuff #if defined(DOCTEST_NO_CPP11_COMPAT) DOCTEST_CLANG_SUPPRESS_WARNING("-Wc++98-compat") DOCTEST_CLANG_SUPPRESS_WARNING("-Wc++98-compat-pedantic") #endif // DOCTEST_NO_CPP11_COMPAT #define DOCTEST_LOG_START(s) \ do { \ if(!contextState->hasLoggedCurrentTestStart) { \ logTestStart(s, *contextState->currentTest); \ DOCTEST_PRINT_TO_OUTPUT_WINDOW_IN_IDE_BEGIN; \ logTestStart(oss, *contextState->currentTest); \ DOCTEST_PRINT_TO_OUTPUT_WINDOW_IN_IDE_END; \ contextState->hasLoggedCurrentTestStart = true; \ } \ } while(false) #define DOCTEST_PRINT_TO_OUTPUT_WINDOW_IN_IDE_BEGIN \ if(isDebuggerActive()) { \ ContextState* p_cs = contextState; \ bool with_col = p_cs->no_colors; \ p_cs->no_colors = false; \ std::ostringstream oss #define DOCTEST_PRINT_TO_OUTPUT_WINDOW_IN_IDE_END \ printToDebugConsole(oss.str().c_str()); \ p_cs->no_colors = with_col; \ } \ ((void)0) DOCTEST_MAKE_STD_HEADERS_CLEAN_FROM_WARNINGS_ON_WALL_BEGIN // required includes - will go only in one translation unit! #include #include // borland (Embarcadero) compiler requires math.h and not cmath - https://github.com/onqtam/doctest/pull/37 #ifdef __BORLANDC__ #include #endif // __BORLANDC__ #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #if !DOCTEST_MSVC #include #endif // !MSVC DOCTEST_MAKE_STD_HEADERS_CLEAN_FROM_WARNINGS_ON_WALL_END namespace doctest { namespace detail { // case insensitive strcmp int stricmp(char const* a, char const* b) { for(;; a++, b++) { const int d = tolower(*a) - tolower(*b); if(d != 0 || !*a) return d; } } void my_memcpy(void* dest, const void* src, unsigned num) { memcpy(dest, src, num); } template String fpToString(T value, int precision) { std::ostringstream oss; oss << std::setprecision(precision) << std::fixed << value; std::string d = oss.str(); 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.c_str(); } struct Endianness { enum Arch { Big, Little }; static Arch which() { union _ { int asInt; char asChar[sizeof(int)]; } u; u.asInt = 1; // NOLINT return (u.asChar[sizeof(int) - 1] == 1) ? Big : Little; // NOLINT } }; String rawMemoryToString(const void* object, unsigned 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 oss; oss << "0x" << std::setfill('0') << std::hex; for(; i != end; i += inc) oss << std::setw(2) << static_cast(bytes[i]); return oss.str().c_str(); } std::ostream* createStream() { return new std::ostringstream(); } String getStreamResult(std::ostream* s) { return static_cast(s)->str().c_str(); // NOLINT } void freeStream(std::ostream* s) { delete s; } #ifndef DOCTEST_CONFIG_DISABLE // this holds both parameters for the command line and runtime data for tests struct ContextState : TestAccessibleContextState //!OCLINT too many fields { // == parameters from the command line std::vector > filters; String order_by; // how tests should be ordered unsigned rand_seed; // the seed for rand ordering unsigned first; // the first (matching) test to be executed unsigned last; // the last (matching) test to be executed int abort_after; // stop tests after this many failed assertions int subcase_filter_levels; // apply the subcase filters for the first N levels bool case_sensitive; // if filtering should be case sensitive bool exit; // if the program should be exited after the tests are ran/whatever bool duration; // print the time duration of each test case bool no_exitcode; // if the framework should return 0 as the exitcode bool no_run; // to not run the tests at all (can be done with an "*" exclude) bool no_version; // to not print the version of the framework bool no_colors; // if output to the console should be colorized bool force_colors; // forces the use of colors even when a tty cannot be detected bool no_breaks; // to not break into the debugger bool no_skip; // don't skip test cases which are marked to be skipped bool gnu_file_line; // if line numbers should be surrounded with :x: and not (x): bool no_path_in_filenames; // if the path to files should be removed from the output bool no_line_numbers; // if source code line numbers should be omitted from the output bool no_skipped_summary; // don't print "skipped" in the summary !!! UNDOCUMENTED !!! bool help; // to print the help bool version; // to print the version bool count; // if only the count of matching tests is to be retreived bool list_test_cases; // to list all tests matching the filters bool list_test_suites; // to list all suites matching the filters // == data for the tests being ran unsigned numTestsPassingFilters; unsigned numTestSuitesPassingFilters; unsigned numFailed; const TestCase* currentTest; bool hasLoggedCurrentTestStart; int numAssertionsForCurrentTestcase; int numAssertions; int numFailedAssertionsForCurrentTestcase; int numFailedAssertions; bool hasCurrentTestFailed; std::vector contexts; // for logging with INFO() and friends std::vector exceptionalContexts; // logging from INFO() due to an exception // stuff for subcases std::set subcasesPassed; std::set subcasesEnteredLevels; std::vector subcasesStack; int subcasesCurrentLevel; bool subcasesHasSkipped; void resetRunData() { numTestsPassingFilters = 0; numTestSuitesPassingFilters = 0; numFailed = 0; numAssertions = 0; numFailedAssertions = 0; numFailedAssertionsForCurrentTestcase = 0; } // cppcheck-suppress uninitMemberVar ContextState() : filters(8) // 8 different filters total { resetRunData(); } }; ContextState* contextState = 0; #endif // DOCTEST_CONFIG_DISABLE } // namespace detail void String::copy(const String& other) { if(other.isOnStack()) { memcpy(buf, other.buf, len); } else { setOnHeap(); data.size = other.data.size; data.capacity = data.size + 1; data.ptr = new char[data.capacity]; memcpy(data.ptr, other.data.ptr, data.size + 1); } } String::String(const char* in) { unsigned in_len = strlen(in); if(in_len <= last) { memcpy(buf, in, in_len + 1); setLast(last - in_len); } else { setOnHeap(); data.size = in_len; data.capacity = data.size + 1; data.ptr = new char[data.capacity]; memcpy(data.ptr, in, in_len + 1); } } String& String::operator+=(const String& other) { const unsigned my_old_size = size(); const unsigned other_size = other.size(); const unsigned total_size = my_old_size + other_size; if(isOnStack()) { if(total_size < len) { // append to the current stack space memcpy(buf + my_old_size, other.c_str(), other_size + 1); setLast(last - total_size); } else { // alloc new chunk char* temp = new char[total_size + 1]; // copy current data to new location before writing in the union memcpy(temp, buf, my_old_size); // skip the +1 ('\0') for speed // update data in union setOnHeap(); data.size = total_size; data.capacity = data.size + 1; data.ptr = temp; // transfer the rest of the data memcpy(data.ptr + my_old_size, other.c_str(), other_size + 1); } } else { if(data.capacity > total_size) { // append to the current heap block data.size = total_size; memcpy(data.ptr + my_old_size, other.c_str(), other_size + 1); } else { // resize data.capacity *= 2; if(data.capacity <= total_size) data.capacity = total_size + 1; // alloc new chunk char* temp = new char[data.capacity]; // copy current data to new location before releasing it memcpy(temp, data.ptr, my_old_size); // skip the +1 ('\0') for speed // release old chunk delete[] data.ptr; // update the rest of the union members data.size = total_size; data.ptr = temp; // transfer the rest of the data memcpy(data.ptr + my_old_size, other.c_str(), other_size + 1); } } return *this; } #ifdef DOCTEST_CONFIG_WITH_RVALUE_REFERENCES String::String(String&& other) { memcpy(buf, other.buf, len); other.buf[0] = '\0'; other.setLast(); } String& String::operator=(String&& other) { if(this != &other) { if(!isOnStack()) delete[] data.ptr; memcpy(buf, other.buf, len); other.buf[0] = '\0'; other.setLast(); } return *this; } #endif // DOCTEST_CONFIG_WITH_RVALUE_REFERENCES int String::compare(const char* other, bool no_case) const { if(no_case) return detail::stricmp(c_str(), other); return std::strcmp(c_str(), other); } int String::compare(const String& other, bool no_case) const { return compare(other.c_str(), no_case); } std::ostream& operator<<(std::ostream& s, const String& in) { return s << in.c_str(); } Approx::Approx(double value) : m_epsilon(static_cast(std::numeric_limits::epsilon()) * 100) , m_scale(1.0) , m_value(value) {} bool operator==(double lhs, Approx const& rhs) { // Thanks to Richard Harris for his help refining this formula return std::fabs(lhs - rhs.m_value) < rhs.m_epsilon * (rhs.m_scale + std::max(std::fabs(lhs), std::fabs(rhs.m_value))); } String Approx::toString() const { return String("Approx( ") + doctest::toString(m_value) + " )"; } #ifdef DOCTEST_CONFIG_TREAT_CHAR_STAR_AS_STRING String toString(char* in) { return toString(static_cast(in)); } String toString(const char* in) { return String("\"") + (in ? in : "{null string}") + "\""; } #endif // DOCTEST_CONFIG_TREAT_CHAR_STAR_AS_STRING String toString(bool in) { return in ? "true" : "false"; } String toString(float in) { return detail::fpToString(in, 5) + "f"; } String toString(double in) { return detail::fpToString(in, 10); } String toString(double long in) { return detail::fpToString(in, 15); } String toString(char in) { char buf[64]; std::sprintf(buf, "%d", in); return buf; } String toString(char signed in) { char buf[64]; std::sprintf(buf, "%d", in); return buf; } String toString(char unsigned in) { char buf[64]; std::sprintf(buf, "%ud", in); return buf; } String toString(int short in) { char buf[64]; std::sprintf(buf, "%d", in); return buf; } String toString(int short unsigned in) { char buf[64]; std::sprintf(buf, "%u", in); return buf; } String toString(int in) { char buf[64]; std::sprintf(buf, "%d", in); return buf; } String toString(int unsigned in) { char buf[64]; std::sprintf(buf, "%u", in); return buf; } String toString(int long in) { char buf[64]; std::sprintf(buf, "%ld", in); return buf; } String toString(int long unsigned in) { char buf[64]; std::sprintf(buf, "%lu", in); return buf; } #ifdef DOCTEST_CONFIG_WITH_LONG_LONG String toString(int long long in) { char buf[64]; std::sprintf(buf, "%lld", in); return buf; } String toString(int long long unsigned in) { char buf[64]; std::sprintf(buf, "%llu", in); return buf; } #endif // DOCTEST_CONFIG_WITH_LONG_LONG #ifdef DOCTEST_CONFIG_WITH_NULLPTR String toString(std::nullptr_t) { return "nullptr"; } #endif // DOCTEST_CONFIG_WITH_NULLPTR } // namespace doctest #ifdef DOCTEST_CONFIG_DISABLE namespace doctest { bool isRunningInTest() { return false; } Context::Context(int, const char* const*) {} Context::~Context() {} void Context::applyCommandLine(int, const char* const*) {} void Context::addFilter(const char*, const char*) {} void Context::clearFilters() {} void Context::setOption(const char*, int) {} void Context::setOption(const char*, const char*) {} bool Context::shouldExit() { return false; } int Context::run() { return 0; } } // namespace doctest #else // DOCTEST_CONFIG_DISABLE #if !defined(DOCTEST_CONFIG_COLORS_NONE) #if !defined(DOCTEST_CONFIG_COLORS_WINDOWS) && !defined(DOCTEST_CONFIG_COLORS_ANSI) #ifdef DOCTEST_PLATFORM_WINDOWS #define DOCTEST_CONFIG_COLORS_WINDOWS #else // linux #define DOCTEST_CONFIG_COLORS_ANSI #endif // platform #endif // DOCTEST_CONFIG_COLORS_WINDOWS && DOCTEST_CONFIG_COLORS_ANSI #endif // DOCTEST_CONFIG_COLORS_NONE #if DOCTEST_MSVC || defined(__MINGW32__) #if DOCTEST_MSVC >= DOCTEST_COMPILER(17, 0, 0) #define DOCTEST_WINDOWS_SAL_IN_OPT _In_opt_ #else // MSVC #define DOCTEST_WINDOWS_SAL_IN_OPT #endif // MSVC extern "C" __declspec(dllimport) void __stdcall OutputDebugStringA( DOCTEST_WINDOWS_SAL_IN_OPT const char*); extern "C" __declspec(dllimport) int __stdcall IsDebuggerPresent(); #endif // MSVC || __MINGW32__ #ifdef DOCTEST_CONFIG_COLORS_ANSI #include #endif // DOCTEST_CONFIG_COLORS_ANSI #ifdef DOCTEST_PLATFORM_WINDOWS // defines for a leaner windows.h #ifndef WIN32_LEAN_AND_MEAN #define WIN32_LEAN_AND_MEAN #endif // WIN32_LEAN_AND_MEAN #ifndef VC_EXTRA_LEAN #define VC_EXTRA_LEAN #endif // VC_EXTRA_LEAN #ifndef NOMINMAX #define NOMINMAX #endif // NOMINMAX DOCTEST_MAKE_STD_HEADERS_CLEAN_FROM_WARNINGS_ON_WALL_BEGIN // not sure what AfxWin.h is for - here I do what Catch does #ifdef __AFXDLL #include #else #include #endif #include DOCTEST_MAKE_STD_HEADERS_CLEAN_FROM_WARNINGS_ON_WALL_END #else // DOCTEST_PLATFORM_WINDOWS #include #endif // DOCTEST_PLATFORM_WINDOWS namespace doctest_detail_test_suite_ns { // holds the current test suite doctest::detail::TestSuite& getCurrentTestSuite() { static doctest::detail::TestSuite data; return data; } } // namespace doctest_detail_test_suite_ns namespace doctest { namespace detail { TestCase::TestCase(funcType test, const char* file, unsigned line, const TestSuite& test_suite, const char* type, int template_id) : m_test(test) , m_name(0) , m_type(type) , m_test_suite(test_suite.m_test_suite) , m_description(test_suite.m_description) , m_skip(test_suite.m_skip) , m_may_fail(test_suite.m_may_fail) , m_should_fail(test_suite.m_should_fail) , m_expected_failures(test_suite.m_expected_failures) , m_timeout(test_suite.m_timeout) , m_file(file) , m_line(line) , m_template_id(template_id) {} TestCase& TestCase::operator*(const char* in) { m_name = in; // make a new name with an appended type for templated test case if(m_template_id != -1) { m_full_name = String(m_name) + m_type; // redirect the name to point to the newly constructed full name m_name = m_full_name.c_str(); } return *this; } TestCase& TestCase::operator=(const TestCase& other) { m_test = other.m_test; m_full_name = other.m_full_name; m_name = other.m_name; m_type = other.m_type; m_test_suite = other.m_test_suite; m_description = other.m_description; m_skip = other.m_skip; m_may_fail = other.m_may_fail; m_should_fail = other.m_should_fail; m_expected_failures = other.m_expected_failures; m_timeout = other.m_timeout; m_file = other.m_file; m_line = other.m_line; m_template_id = other.m_template_id; if(m_template_id != -1) m_name = m_full_name.c_str(); return *this; } bool TestCase::operator<(const TestCase& other) const { if(m_line != other.m_line) return m_line < other.m_line; const int file_cmp = std::strcmp(m_file, other.m_file); if(file_cmp != 0) return file_cmp < 0; return m_template_id < other.m_template_id; } const char* assertString(assertType::Enum val) { DOCTEST_MSVC_SUPPRESS_WARNING_WITH_PUSH( 4062) // enumerator 'x' in switch of enum 'y' is not handled switch(val) { //!OCLINT missing default in switch statements // clang-format off case assertType::DT_WARN : return "WARN"; case assertType::DT_CHECK : return "CHECK"; case assertType::DT_REQUIRE : return "REQUIRE"; case assertType::DT_WARN_FALSE : return "WARN_FALSE"; case assertType::DT_CHECK_FALSE : return "CHECK_FALSE"; case assertType::DT_REQUIRE_FALSE : return "REQUIRE_FALSE"; case assertType::DT_WARN_THROWS : return "WARN_THROWS"; case assertType::DT_CHECK_THROWS : return "CHECK_THROWS"; case assertType::DT_REQUIRE_THROWS : return "REQUIRE_THROWS"; case assertType::DT_WARN_THROWS_AS : return "WARN_THROWS_AS"; case assertType::DT_CHECK_THROWS_AS : return "CHECK_THROWS_AS"; case assertType::DT_REQUIRE_THROWS_AS : return "REQUIRE_THROWS_AS"; case assertType::DT_WARN_NOTHROW : return "WARN_NOTHROW"; case assertType::DT_CHECK_NOTHROW : return "CHECK_NOTHROW"; case assertType::DT_REQUIRE_NOTHROW : return "REQUIRE_NOTHROW"; case assertType::DT_WARN_EQ : return "WARN_EQ"; case assertType::DT_CHECK_EQ : return "CHECK_EQ"; case assertType::DT_REQUIRE_EQ : return "REQUIRE_EQ"; case assertType::DT_WARN_NE : return "WARN_NE"; case assertType::DT_CHECK_NE : return "CHECK_NE"; case assertType::DT_REQUIRE_NE : return "REQUIRE_NE"; case assertType::DT_WARN_GT : return "WARN_GT"; case assertType::DT_CHECK_GT : return "CHECK_GT"; case assertType::DT_REQUIRE_GT : return "REQUIRE_GT"; case assertType::DT_WARN_LT : return "WARN_LT"; case assertType::DT_CHECK_LT : return "CHECK_LT"; case assertType::DT_REQUIRE_LT : return "REQUIRE_LT"; case assertType::DT_WARN_GE : return "WARN_GE"; case assertType::DT_CHECK_GE : return "CHECK_GE"; case assertType::DT_REQUIRE_GE : return "REQUIRE_GE"; case assertType::DT_WARN_LE : return "WARN_LE"; case assertType::DT_CHECK_LE : return "CHECK_LE"; case assertType::DT_REQUIRE_LE : return "REQUIRE_LE"; case assertType::DT_WARN_UNARY : return "WARN_UNARY"; case assertType::DT_CHECK_UNARY : return "CHECK_UNARY"; case assertType::DT_REQUIRE_UNARY : return "REQUIRE_UNARY"; case assertType::DT_WARN_UNARY_FALSE : return "WARN_UNARY_FALSE"; case assertType::DT_CHECK_UNARY_FALSE : return "CHECK_UNARY_FALSE"; case assertType::DT_REQUIRE_UNARY_FALSE : return "REQUIRE_UNARY_FALSE"; case assertType::DT_FAST_WARN_EQ : return "FAST_WARN_EQ"; case assertType::DT_FAST_CHECK_EQ : return "FAST_CHECK_EQ"; case assertType::DT_FAST_REQUIRE_EQ : return "FAST_REQUIRE_EQ"; case assertType::DT_FAST_WARN_NE : return "FAST_WARN_NE"; case assertType::DT_FAST_CHECK_NE : return "FAST_CHECK_NE"; case assertType::DT_FAST_REQUIRE_NE : return "FAST_REQUIRE_NE"; case assertType::DT_FAST_WARN_GT : return "FAST_WARN_GT"; case assertType::DT_FAST_CHECK_GT : return "FAST_CHECK_GT"; case assertType::DT_FAST_REQUIRE_GT : return "FAST_REQUIRE_GT"; case assertType::DT_FAST_WARN_LT : return "FAST_WARN_LT"; case assertType::DT_FAST_CHECK_LT : return "FAST_CHECK_LT"; case assertType::DT_FAST_REQUIRE_LT : return "FAST_REQUIRE_LT"; case assertType::DT_FAST_WARN_GE : return "FAST_WARN_GE"; case assertType::DT_FAST_CHECK_GE : return "FAST_CHECK_GE"; case assertType::DT_FAST_REQUIRE_GE : return "FAST_REQUIRE_GE"; case assertType::DT_FAST_WARN_LE : return "FAST_WARN_LE"; case assertType::DT_FAST_CHECK_LE : return "FAST_CHECK_LE"; case assertType::DT_FAST_REQUIRE_LE : return "FAST_REQUIRE_LE"; case assertType::DT_FAST_WARN_UNARY : return "FAST_WARN_UNARY"; case assertType::DT_FAST_CHECK_UNARY : return "FAST_CHECK_UNARY"; case assertType::DT_FAST_REQUIRE_UNARY : return "FAST_REQUIRE_UNARY"; case assertType::DT_FAST_WARN_UNARY_FALSE : return "FAST_WARN_UNARY_FALSE"; case assertType::DT_FAST_CHECK_UNARY_FALSE : return "FAST_CHECK_UNARY_FALSE"; case assertType::DT_FAST_REQUIRE_UNARY_FALSE: return "FAST_REQUIRE_UNARY_FALSE"; // clang-format on } DOCTEST_MSVC_SUPPRESS_WARNING_POP return ""; } bool checkIfShouldThrow(assertType::Enum at) { if(at & assertType::is_require) //!OCLINT bitwise operator in conditional return true; if((at & assertType::is_check) //!OCLINT bitwise operator in conditional && contextState->abort_after > 0 && contextState->numFailedAssertions >= contextState->abort_after) return true; return false; } void throwException() { #ifndef DOCTEST_CONFIG_NO_EXCEPTIONS throw TestFailureException(); #endif // DOCTEST_CONFIG_NO_EXCEPTIONS } void fastAssertThrowIfFlagSet(int flags) { if(flags & assertAction::shouldthrow) //!OCLINT bitwise operator in conditional throwException(); } // matching of a string against a wildcard mask (case sensitivity configurable) taken from // https://www.codeproject.com/Articles/1088/Wildcard-string-compare-globbing int wildcmp(const char* str, const char* wild, bool caseSensitive) { const char* cp = 0; const char* mp = 0; while((*str) && (*wild != '*')) { if((caseSensitive ? (*wild != *str) : (tolower(*wild) != tolower(*str))) && (*wild != '?')) { return 0; } wild++; str++; } while(*str) { if(*wild == '*') { if(!*++wild) { return 1; } mp = wild; cp = str + 1; } else if((caseSensitive ? (*wild == *str) : (tolower(*wild) == tolower(*str))) || (*wild == '?')) { wild++; str++; } else { wild = mp; //!OCLINT parameter reassignment str = cp++; //!OCLINT parameter reassignment } } while(*wild == '*') { wild++; } return !*wild; } //// C string hash function (djb2) - taken from http://www.cse.yorku.ca/~oz/hash.html //unsigned hashStr(unsigned const char* str) { // unsigned long hash = 5381; // char c; // while((c = *str++)) // hash = ((hash << 5) + hash) + c; // hash * 33 + c // return hash; //} // checks if the name matches any of the filters (and can be configured what to do when empty) bool matchesAny(const char* name, const std::vector& filters, int matchEmpty, bool caseSensitive) { if(filters.empty() && matchEmpty) return true; for(unsigned i = 0; i < filters.size(); ++i) if(wildcmp(name, filters[i].c_str(), caseSensitive)) return true; return false; } #ifdef DOCTEST_PLATFORM_WINDOWS typedef unsigned long long UInt64; 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 // DOCTEST_PLATFORM_WINDOWS typedef uint64_t UInt64; UInt64 getCurrentTicks() { timeval t; gettimeofday(&t, 0); return static_cast(t.tv_sec) * 1000000 + static_cast(t.tv_usec); } #endif // DOCTEST_PLATFORM_WINDOWS class Timer { public: Timer() : m_ticks(0) {} void start() { m_ticks = getCurrentTicks(); } unsigned int getElapsedMicroseconds() const { return static_cast(getCurrentTicks() - m_ticks); } unsigned int getElapsedMilliseconds() const { return static_cast(getElapsedMicroseconds() / 1000); } double getElapsedSeconds() const { return getElapsedMicroseconds() / 1000000.0; } private: UInt64 m_ticks; }; TestAccessibleContextState* getTestsContextState() { return contextState; } // TODO: remove this from here void logTestEnd(); bool SubcaseSignature::operator<(const SubcaseSignature& other) const { if(m_line != other.m_line) return m_line < other.m_line; if(std::strcmp(m_file, other.m_file) != 0) return std::strcmp(m_file, other.m_file) < 0; return std::strcmp(m_name, other.m_name) < 0; } Subcase::Subcase(const char* name, const char* file, int line) : m_signature(name, file, line) , m_entered(false) { ContextState* s = contextState; // if we have already completed it if(s->subcasesPassed.count(m_signature) != 0) return; // check subcase filters if(s->subcasesCurrentLevel < s->subcase_filter_levels) { if(!matchesAny(m_signature.m_name, s->filters[6], 1, s->case_sensitive)) return; if(matchesAny(m_signature.m_name, s->filters[7], 0, s->case_sensitive)) return; } // if a Subcase on the same level has already been entered if(s->subcasesEnteredLevels.count(s->subcasesCurrentLevel) != 0) { s->subcasesHasSkipped = true; return; } s->subcasesStack.push_back(*this); if(s->hasLoggedCurrentTestStart) logTestEnd(); s->hasLoggedCurrentTestStart = false; s->subcasesEnteredLevels.insert(s->subcasesCurrentLevel++); m_entered = true; } Subcase::Subcase(const Subcase& other) : m_signature(other.m_signature.m_name, other.m_signature.m_file, other.m_signature.m_line) , m_entered(other.m_entered) {} Subcase::~Subcase() { if(m_entered) { ContextState* s = contextState; s->subcasesCurrentLevel--; // only mark the subcase as passed if no subcases have been skipped if(s->subcasesHasSkipped == false) s->subcasesPassed.insert(m_signature); if(!s->subcasesStack.empty()) s->subcasesStack.pop_back(); if(s->hasLoggedCurrentTestStart) logTestEnd(); s->hasLoggedCurrentTestStart = false; } } Result::~Result() {} Result& Result::operator=(const Result& other) { m_passed = other.m_passed; m_decomposition = other.m_decomposition; return *this; } // for sorting tests by file/line int fileOrderComparator(const void* a, const void* b) { const TestCase* lhs = *static_cast(a); const TestCase* rhs = *static_cast(b); #if DOCTEST_MSVC // this is needed because MSVC gives different case for drive letters // for __FILE__ when evaluated in a header and a source file const int res = stricmp(lhs->m_file, rhs->m_file); #else // MSVC const int res = std::strcmp(lhs->m_file, rhs->m_file); #endif // MSVC if(res != 0) return res; return static_cast(lhs->m_line - rhs->m_line); } // for sorting tests by suite/file/line int suiteOrderComparator(const void* a, const void* b) { const TestCase* lhs = *static_cast(a); const TestCase* rhs = *static_cast(b); const int res = std::strcmp(lhs->m_test_suite, rhs->m_test_suite); if(res != 0) return res; return fileOrderComparator(a, b); } // for sorting tests by name/suite/file/line int nameOrderComparator(const void* a, const void* b) { const TestCase* lhs = *static_cast(a); const TestCase* rhs = *static_cast(b); const int res_name = std::strcmp(lhs->m_name, rhs->m_name); if(res_name != 0) return res_name; return suiteOrderComparator(a, b); } // sets the current test suite int setTestSuite(const TestSuite& ts) { doctest_detail_test_suite_ns::getCurrentTestSuite() = ts; return 0; } // all the registered tests std::set& getRegisteredTests() { static std::set data; return data; } // used by the macros for registering tests int regTest(const TestCase& tc) { getRegisteredTests().insert(tc); return 0; } namespace Color { enum Code { None = 0, White, Red, Green, Blue, Cyan, Yellow, Grey, Bright = 0x10, BrightRed = Bright | Red, BrightGreen = Bright | Green, LightGrey = Bright | Grey, BrightWhite = Bright | White }; #ifdef DOCTEST_CONFIG_COLORS_WINDOWS HANDLE g_stdoutHandle; WORD g_originalForegroundAttributes; WORD g_originalBackgroundAttributes; bool g_attrsInitted = false; #endif // DOCTEST_CONFIG_COLORS_WINDOWS void init() { #ifdef DOCTEST_CONFIG_COLORS_WINDOWS if(!g_attrsInitted) { g_stdoutHandle = GetStdHandle(STD_OUTPUT_HANDLE); g_attrsInitted = true; CONSOLE_SCREEN_BUFFER_INFO csbiInfo; GetConsoleScreenBufferInfo(g_stdoutHandle, &csbiInfo); g_originalForegroundAttributes = csbiInfo.wAttributes & ~(BACKGROUND_GREEN | BACKGROUND_RED | BACKGROUND_BLUE | BACKGROUND_INTENSITY); g_originalBackgroundAttributes = csbiInfo.wAttributes & ~(FOREGROUND_GREEN | FOREGROUND_RED | FOREGROUND_BLUE | FOREGROUND_INTENSITY); } #endif // DOCTEST_CONFIG_COLORS_WINDOWS } std::ostream& operator<<(std::ostream& s, Color::Code #ifndef DOCTEST_CONFIG_COLORS_NONE code #endif // DOCTEST_CONFIG_COLORS_NONE ) { const ContextState* p = contextState; if(p->no_colors) return s; #ifdef DOCTEST_CONFIG_COLORS_ANSI if(isatty(STDOUT_FILENO) == false && p->force_colors == false) return s; const char* col = ""; // clang-format off switch(code) { //!OCLINT missing break in switch statement / unnecessary default statement in covered switch statement case Color::Red: col = "[0;31m"; break; case Color::Green: col = "[0;32m"; break; case Color::Blue: col = "[0;34m"; break; case Color::Cyan: col = "[0;36m"; break; case Color::Yellow: col = "[0;33m"; break; case Color::Grey: col = "[1;30m"; break; case Color::LightGrey: col = "[0;37m"; break; case Color::BrightRed: col = "[1;31m"; break; case Color::BrightGreen: col = "[1;32m"; break; case Color::BrightWhite: col = "[1;37m"; break; case Color::Bright: // invalid case Color::None: case Color::White: default: col = "[0m"; } // clang-format on s << "\033" << col; #endif // DOCTEST_CONFIG_COLORS_ANSI #ifdef DOCTEST_CONFIG_COLORS_WINDOWS if(isatty(fileno(stdout)) == false && p->force_colors == false) return s; #define DOCTEST_SET_ATTR(x) \ SetConsoleTextAttribute(g_stdoutHandle, x | g_originalBackgroundAttributes) // clang-format off switch (code) { case Color::White: DOCTEST_SET_ATTR(FOREGROUND_GREEN | FOREGROUND_RED | FOREGROUND_BLUE); break; case Color::Red: DOCTEST_SET_ATTR(FOREGROUND_RED); break; case Color::Green: DOCTEST_SET_ATTR(FOREGROUND_GREEN); break; case Color::Blue: DOCTEST_SET_ATTR(FOREGROUND_BLUE); break; case Color::Cyan: DOCTEST_SET_ATTR(FOREGROUND_BLUE | FOREGROUND_GREEN); break; case Color::Yellow: DOCTEST_SET_ATTR(FOREGROUND_RED | FOREGROUND_GREEN); break; case Color::Grey: DOCTEST_SET_ATTR(0); break; case Color::LightGrey: DOCTEST_SET_ATTR(FOREGROUND_INTENSITY); break; case Color::BrightRed: DOCTEST_SET_ATTR(FOREGROUND_INTENSITY | FOREGROUND_RED); break; case Color::BrightGreen: DOCTEST_SET_ATTR(FOREGROUND_INTENSITY | FOREGROUND_GREEN); break; case Color::BrightWhite: DOCTEST_SET_ATTR(FOREGROUND_INTENSITY | FOREGROUND_GREEN | FOREGROUND_RED | FOREGROUND_BLUE); break; case Color::None: case Color::Bright: // invalid default: DOCTEST_SET_ATTR(g_originalForegroundAttributes); } // clang-format on #undef DOCTEST_SET_ATTR #endif // DOCTEST_CONFIG_COLORS_WINDOWS return s; } } // namespace Color std::vector& getExceptionTranslators() { static std::vector data; return data; } void registerExceptionTranslatorImpl(const IExceptionTranslator* translateFunction) { if(std::find(getExceptionTranslators().begin(), getExceptionTranslators().end(), translateFunction) == getExceptionTranslators().end()) getExceptionTranslators().push_back(translateFunction); } String translateActiveException() { #ifndef DOCTEST_CONFIG_NO_EXCEPTIONS String res; std::vector& translators = getExceptionTranslators(); for(size_t i = 0; i < translators.size(); ++i) if(translators[i]->translate(res)) return res; // clang-format off try { throw; } catch(std::exception& ex) { return ex.what(); } catch(std::string& msg) { return msg.c_str(); } catch(const char* msg) { return msg; } catch(...) { return "unknown exception"; } // clang-format on #else // DOCTEST_CONFIG_NO_EXCEPTIONS return ""; #endif // DOCTEST_CONFIG_NO_EXCEPTIONS } void writeStringToStream(std::ostream* s, const String& str) { *s << str; } #ifdef DOCTEST_CONFIG_TREAT_CHAR_STAR_AS_STRING void toStream(std::ostream* s, char* in) { *s << in; } void toStream(std::ostream* s, const char* in) { *s << in; } #endif // DOCTEST_CONFIG_TREAT_CHAR_STAR_AS_STRING void toStream(std::ostream* s, bool in) { *s << std::boolalpha << in << std::noboolalpha; } void toStream(std::ostream* s, float in) { *s << in; } void toStream(std::ostream* s, double in) { *s << in; } void toStream(std::ostream* s, double long in) { *s << in; } void toStream(std::ostream* s, char in) { *s << in; } void toStream(std::ostream* s, char signed in) { *s << in; } void toStream(std::ostream* s, char unsigned in) { *s << in; } void toStream(std::ostream* s, int short in) { *s << in; } void toStream(std::ostream* s, int short unsigned in) { *s << in; } void toStream(std::ostream* s, int in) { *s << in; } void toStream(std::ostream* s, int unsigned in) { *s << in; } void toStream(std::ostream* s, int long in) { *s << in; } void toStream(std::ostream* s, int long unsigned in) { *s << in; } #ifdef DOCTEST_CONFIG_WITH_LONG_LONG void toStream(std::ostream* s, int long long in) { *s << in; } void toStream(std::ostream* s, int long long unsigned in) { *s << in; } #endif // DOCTEST_CONFIG_WITH_LONG_LONG void addToContexts(IContextScope* ptr) { contextState->contexts.push_back(ptr); } void popFromContexts() { contextState->contexts.pop_back(); } DOCTEST_MSVC_SUPPRESS_WARNING_WITH_PUSH(4996) // std::uncaught_exception is deprecated in C++17 DOCTEST_GCC_SUPPRESS_WARNING_WITH_PUSH("-Wdeprecated-declarations") void useContextIfExceptionOccurred(IContextScope* ptr) { if(std::uncaught_exception()) { std::ostringstream s; ptr->build(&s); contextState->exceptionalContexts.push_back(s.str()); } } DOCTEST_GCC_SUPPRESS_WARNING_POP DOCTEST_MSVC_SUPPRESS_WARNING_POP void printSummary(std::ostream& s); #if !defined(DOCTEST_CONFIG_POSIX_SIGNALS) && !defined(DOCTEST_CONFIG_WINDOWS_SEH) void reportFatal(const std::string&) {} struct FatalConditionHandler { void reset() {} }; #else // DOCTEST_CONFIG_POSIX_SIGNALS || DOCTEST_CONFIG_WINDOWS_SEH void reportFatal(const std::string&); #ifdef DOCTEST_PLATFORM_WINDOWS 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. 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(size_t i = 0; i < DOCTEST_COUNTOF(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 doctest to handle stack overflow, // but the value was found experimentally, so there is no strong guarantee guaranteeSize = 32 * 1024; exceptionHandlerHandle = 0; // 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 = 0; 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 = 0; #else // DOCTEST_PLATFORM_WINDOWS struct SignalDefs { int id; const char* name; }; 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[DOCTEST_COUNTOF(signalDefs)]; static stack_t oldSigStack; static char altStackMem[4 * SIGSTKSZ]; static void handleSignal(int sig) { std::string name = ""; for(std::size_t i = 0; i < DOCTEST_COUNTOF(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 = sizeof(altStackMem); sigStack.ss_flags = 0; sigaltstack(&sigStack, &oldSigStack); struct sigaction sa = {0}; sa.sa_handler = handleSignal; // NOLINT sa.sa_flags = SA_ONSTACK; for(std::size_t i = 0; i < DOCTEST_COUNTOF(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 < DOCTEST_COUNTOF(signalDefs); ++i) { sigaction(signalDefs[i].id, &oldSigActions[i], 0); } // Return the old stack sigaltstack(&oldSigStack, 0); isSet = false; } } }; bool FatalConditionHandler::isSet = false; struct sigaction FatalConditionHandler::oldSigActions[DOCTEST_COUNTOF(signalDefs)] = {}; stack_t FatalConditionHandler::oldSigStack = {}; char FatalConditionHandler::altStackMem[] = {}; #endif // DOCTEST_PLATFORM_WINDOWS #endif // DOCTEST_CONFIG_POSIX_SIGNALS || DOCTEST_CONFIG_WINDOWS_SEH void separator_to_stream(std::ostream& s) { s << Color::Yellow << "===============================================================================\n"; } // depending on the current options this will remove the path of filenames const char* fileForOutput(const char* file) { if(contextState->no_path_in_filenames) { const char* back = std::strrchr(file, '\\'); const char* forward = std::strrchr(file, '/'); if(back || forward) { if(back > forward) forward = back; return forward + 1; } } return file; } void file_line_to_stream(std::ostream& s, const char* file, int line, const char* tail = "") { s << Color::LightGrey << fileForOutput(file) << (contextState->gnu_file_line ? ":" : "(") << (contextState->no_line_numbers ? 0 : line) // 0 or the real num depending on the option << (contextState->gnu_file_line ? ":" : "):") << tail; } const char* getSuccessOrFailString(bool success, assertType::Enum at, const char* success_str) { if(success) return success_str; if(at & assertType::is_warn) //!OCLINT bitwise operator in conditional return "WARNING: "; if(at & assertType::is_check) //!OCLINT bitwise operator in conditional return "ERROR: "; if(at & assertType::is_require) //!OCLINT bitwise operator in conditional return "FATAL ERROR: "; return ""; } Color::Code getSuccessOrFailColor(bool success, assertType::Enum at) { return success ? Color::BrightGreen : (at & assertType::is_warn) ? Color::Yellow : Color::Red; } void successOrFailColoredStringToStream(std::ostream& s, bool success, assertType::Enum at, const char* success_str = "SUCCESS: ") { s << getSuccessOrFailColor(success, at) << getSuccessOrFailString(success, at, success_str); } #ifdef DOCTEST_PLATFORM_MAC #include #include #include // The following function is taken directly from the following technical note: // http://developer.apple.com/library/mac/#qa/qa2004/qa1361.html // Returns true if the current process is being debugged (either // running under the debugger or has a debugger attached post facto). bool isDebuggerActive() { int mib[4]; kinfo_proc info; size_t size; // Initialize the flags so that, if sysctl fails for some bizarre // reason, we get a predictable result. info.kp_proc.p_flag = 0; // Initialize mib, which tells sysctl the info we want, in this case // we're looking for information about a specific process ID. mib[0] = CTL_KERN; mib[1] = KERN_PROC; mib[2] = KERN_PROC_PID; mib[3] = getpid(); // Call sysctl. size = sizeof(info); if(sysctl(mib, DOCTEST_COUNTOF(mib), &info, &size, 0, 0) != 0) { fprintf(stderr, "\n** Call to sysctl failed - unable to determine if debugger is " "active **\n\n"); return false; } // We're being debugged if the P_TRACED flag is set. return ((info.kp_proc.p_flag & P_TRACED) != 0); } #elif DOCTEST_MSVC || defined(__MINGW32__) bool isDebuggerActive() { return ::IsDebuggerPresent() != 0; } #else bool isDebuggerActive() { return false; } #endif // Platform #ifdef DOCTEST_PLATFORM_WINDOWS void myOutputDebugString(const String& text) { ::OutputDebugStringA(text.c_str()); } #else // TODO: integration with XCode and other IDEs void myOutputDebugString(const String&) {} #endif // Platform void printToDebugConsole(const String& text) { if(isDebuggerActive()) myOutputDebugString(text); } void addFailedAssert(assertType::Enum at) { if((at & assertType::is_warn) == 0) { //!OCLINT bitwise operator in conditional contextState->numFailedAssertions++; contextState->numFailedAssertionsForCurrentTestcase++; contextState->hasCurrentTestFailed = true; } } std::ostream& operator<<(std::ostream& s, const std::vector& contexts) { if(!contexts.empty()) s << Color::None << " logged: "; for(size_t i = 0; i < contexts.size(); ++i) { s << (i == 0 ? "" : " "); contexts[i]->build(&s); s << "\n"; } s << "\n"; return s; } void logTestStart(std::ostream& s, const TestCase& tc) { separator_to_stream(s); file_line_to_stream(s, tc.m_file, tc.m_line, "\n"); if(tc.m_description) s << Color::Yellow << "DESCRIPTION: " << Color::None << tc.m_description << "\n"; if(tc.m_test_suite && tc.m_test_suite[0] != '\0') s << Color::Yellow << "TEST SUITE: " << Color::None << tc.m_test_suite << "\n"; if(strncmp(tc.m_name, " Scenario:", 11) != 0) s << Color::None << "TEST CASE: "; s << Color::None << tc.m_name << "\n"; std::vector& subcasesStack = contextState->subcasesStack; for(unsigned i = 0; i < subcasesStack.size(); ++i) if(subcasesStack[i].m_signature.m_name[0] != '\0') s << " " << subcasesStack[i].m_signature.m_name << "\n"; s << "\n"; } void logTestEnd() {} void logTestException_impl(std::ostream& s, const TestCase& tc, const String& str, bool crash) { file_line_to_stream(s, tc.m_file, tc.m_line, " "); successOrFailColoredStringToStream(s, false, crash ? assertType::is_require : assertType::is_check); s << Color::Red << (crash ? "test case CRASHED: " : "test case THREW exception: ") << Color::Cyan << str << "\n"; if(!contextState->exceptionalContexts.empty()) { s << Color::None << " logged: "; for(size_t i = contextState->exceptionalContexts.size(); i > 0; --i) s << (i == contextState->exceptionalContexts.size() ? "" : " ") << contextState->exceptionalContexts[i - 1] << "\n"; } s << "\n"; } void logTestException(const TestCase& tc, const String& what, bool crash) { logTestException_impl(std::cout, tc, what, crash); DOCTEST_PRINT_TO_OUTPUT_WINDOW_IN_IDE_BEGIN; logTestException_impl(oss, tc, what, crash); DOCTEST_PRINT_TO_OUTPUT_WINDOW_IN_IDE_END; } #if defined(DOCTEST_CONFIG_POSIX_SIGNALS) || defined(DOCTEST_CONFIG_WINDOWS_SEH) void reportFatal(const std::string& message) { DOCTEST_LOG_START(std::cout); contextState->numAssertions += contextState->numAssertionsForCurrentTestcase; logTestException(*contextState->currentTest, message.c_str(), true); logTestEnd(); contextState->numFailed++; printSummary(std::cout); } #endif // DOCTEST_CONFIG_POSIX_SIGNALS || DOCTEST_CONFIG_WINDOWS_SEH void logAssert_impl(std::ostream& s, bool passed, const String& dec, bool threw, const String& ex, const char* expr, assertType::Enum at, const char* file, int line) { file_line_to_stream(s, file, line, " "); successOrFailColoredStringToStream(s, passed, at); s << Color::Cyan << assertString(at) << "( " << expr << " ) " << Color::None << (threw ? "THREW exception: " : (passed ? "is correct!\n" : "is NOT correct!\n")); if(threw) s << ex << "\n"; else s << " values: " << assertString(at) << "( " << dec << " )\n"; s << contextState->contexts; } void logAssert(bool passed, const String& dec, bool threw, const String& ex, const char* expr, assertType::Enum at, const char* file, int line) { logAssert_impl(std::cout, passed, dec, threw, ex, expr, at, file, line); DOCTEST_PRINT_TO_OUTPUT_WINDOW_IN_IDE_BEGIN; logAssert_impl(oss, passed, dec, threw, ex, expr, at, file, line); DOCTEST_PRINT_TO_OUTPUT_WINDOW_IN_IDE_END; } void logAssertThrows_impl(std::ostream& s, bool threw, const char* expr, assertType::Enum at, const char* file, int line) { file_line_to_stream(s, file, line, " "); successOrFailColoredStringToStream(s, threw, at); s << Color::Cyan << assertString(at) << "( " << expr << " ) " << Color::None << (threw ? "threw as expected!" : "did NOT throw at all!") << "\n"; s << contextState->contexts; } void logAssertThrows(bool threw, const char* expr, assertType::Enum at, const char* file, int line) { logAssertThrows_impl(std::cout, threw, expr, at, file, line); DOCTEST_PRINT_TO_OUTPUT_WINDOW_IN_IDE_BEGIN; logAssertThrows_impl(oss, threw, expr, at, file, line); DOCTEST_PRINT_TO_OUTPUT_WINDOW_IN_IDE_END; } void logAssertThrowsAs_impl(std::ostream& s, bool threw, bool threw_as, const char* as, const String& ex, const char* expr, assertType::Enum at, const char* file, int line) { file_line_to_stream(s, file, line, " "); successOrFailColoredStringToStream(s, threw_as, at); s << Color::Cyan << assertString(at) << "( " << expr << ", " << as << " ) " << Color::None << (threw ? (threw_as ? "threw as expected!" : "threw a DIFFERENT exception: ") : "did NOT throw at all!") << Color::Cyan << ex << "\n"; s << contextState->contexts; } void logAssertThrowsAs(bool threw, bool threw_as, const char* as, const String& ex, const char* expr, assertType::Enum at, const char* file, int line) { logAssertThrowsAs_impl(std::cout, threw, threw_as, as, ex, expr, at, file, line); DOCTEST_PRINT_TO_OUTPUT_WINDOW_IN_IDE_BEGIN; logAssertThrowsAs_impl(oss, threw, threw_as, as, ex, expr, at, file, line); DOCTEST_PRINT_TO_OUTPUT_WINDOW_IN_IDE_END; } void logAssertNothrow_impl(std::ostream& s, bool threw, const String& ex, const char* expr, assertType::Enum at, const char* file, int line) { file_line_to_stream(s, file, line, " "); successOrFailColoredStringToStream(s, !threw, at); s << Color::Cyan << assertString(at) << "( " << expr << " ) " << Color::None << (threw ? "THREW exception: " : "didn't throw!") << Color::Cyan << ex << "\n"; s << contextState->contexts; } void logAssertNothrow(bool threw, const String& ex, const char* expr, assertType::Enum at, const char* file, int line) { logAssertNothrow_impl(std::cout, threw, ex, expr, at, file, line); DOCTEST_PRINT_TO_OUTPUT_WINDOW_IN_IDE_BEGIN; logAssertNothrow_impl(oss, threw, ex, expr, at, file, line); DOCTEST_PRINT_TO_OUTPUT_WINDOW_IN_IDE_END; } ResultBuilder::ResultBuilder(assertType::Enum at, const char* file, int line, const char* expr, const char* exception_type) : m_assert_type(at) , m_file(file) , m_line(line) , m_expr(expr) , m_exception_type(exception_type) , m_threw(false) , m_threw_as(false) , m_failed(false) { #if DOCTEST_MSVC if(m_expr[0] == ' ') // this happens when variadic macros are disabled under MSVC ++m_expr; #endif // MSVC } ResultBuilder::~ResultBuilder() {} void ResultBuilder::unexpectedExceptionOccurred() { m_threw = true; m_exception = translateActiveException(); } bool ResultBuilder::log() { if((m_assert_type & assertType::is_warn) == 0) //!OCLINT bitwise operator in conditional contextState->numAssertionsForCurrentTestcase++; if(m_assert_type & assertType::is_throws) { //!OCLINT bitwise operator in conditional m_failed = !m_threw; } else if(m_assert_type & //!OCLINT bitwise operator in conditional assertType::is_throws_as) { m_failed = !m_threw_as; } else if(m_assert_type & //!OCLINT bitwise operator in conditional assertType::is_nothrow) { m_failed = m_threw; } else { m_failed = m_result; } if(m_failed || contextState->success) { DOCTEST_LOG_START(std::cout); if(m_assert_type & assertType::is_throws) { //!OCLINT bitwise operator in conditional logAssertThrows(m_threw, m_expr, m_assert_type, m_file, m_line); } else if(m_assert_type & //!OCLINT bitwise operator in conditional assertType::is_throws_as) { logAssertThrowsAs(m_threw, m_threw_as, m_exception_type, m_exception, m_expr, m_assert_type, m_file, m_line); } else if(m_assert_type & //!OCLINT bitwise operator in conditional assertType::is_nothrow) { logAssertNothrow(m_threw, m_exception, m_expr, m_assert_type, m_file, m_line); } else { logAssert(m_result.m_passed, m_result.m_decomposition, m_threw, m_exception, m_expr, m_assert_type, m_file, m_line); } } if(m_failed) addFailedAssert(m_assert_type); return m_failed && isDebuggerActive() && !contextState->no_breaks; // break into debugger } void ResultBuilder::react() const { if(m_failed && checkIfShouldThrow(m_assert_type)) throwException(); } MessageBuilder::MessageBuilder(const char* file, int line, assertType::Enum severity) : m_stream(createStream()) , m_file(file) , m_line(line) , m_severity(severity) {} void MessageBuilder::log(std::ostream& s) { file_line_to_stream(s, m_file, m_line, " "); s << getSuccessOrFailColor(false, m_severity) << getSuccessOrFailString(m_severity & assertType::is_warn, m_severity, "MESSAGE: "); s << Color::None << getStreamResult(m_stream) << "\n"; s << contextState->contexts; } bool MessageBuilder::log() { DOCTEST_LOG_START(std::cout); log(std::cout); DOCTEST_PRINT_TO_OUTPUT_WINDOW_IN_IDE_BEGIN; log(oss); DOCTEST_PRINT_TO_OUTPUT_WINDOW_IN_IDE_END; const bool isWarn = m_severity & assertType::is_warn; // warn is just a message in this context so we dont treat it as an assert if(!isWarn) { contextState->numAssertionsForCurrentTestcase++; addFailedAssert(m_severity); } return isDebuggerActive() && !contextState->no_breaks && !isWarn; // break into debugger } void MessageBuilder::react() { if(m_severity & assertType::is_require) //!OCLINT bitwise operator in conditional throwException(); } MessageBuilder::~MessageBuilder() { freeStream(m_stream); } // the implementation of parseFlag() bool parseFlagImpl(int argc, const char* const* argv, const char* pattern) { for(int i = argc - 1; i >= 0; --i) { const char* temp = std::strstr(argv[i], pattern); if(temp && strlen(temp) == strlen(pattern)) { // eliminate strings in which the chars before the option are not '-' bool noBadCharsFound = true; //!OCLINT prefer early exits and continue while(temp != argv[i]) { if(*--temp != '-') { noBadCharsFound = false; break; } } if(noBadCharsFound && argv[i][0] == '-') return true; } } return false; } // locates a flag on the command line bool parseFlag(int argc, const char* const* argv, const char* pattern) { #ifndef DOCTEST_CONFIG_NO_UNPREFIXED_OPTIONS if(!parseFlagImpl(argc, argv, pattern)) return parseFlagImpl(argc, argv, pattern + 3); // 3 for "dt-" return true; #else // DOCTEST_CONFIG_NO_UNPREFIXED_OPTIONS return parseFlagImpl(argc, argv, pattern); #endif // DOCTEST_CONFIG_NO_UNPREFIXED_OPTIONS } // the implementation of parseOption() bool parseOptionImpl(int argc, const char* const* argv, const char* pattern, String& res) { for(int i = argc - 1; i >= 0; --i) { const char* temp = std::strstr(argv[i], pattern); if(temp) { //!OCLINT prefer early exits and continue // eliminate matches in which the chars before the option are not '-' bool noBadCharsFound = true; const char* curr = argv[i]; while(curr != temp) { if(*curr++ != '-') { noBadCharsFound = false; break; } } if(noBadCharsFound && argv[i][0] == '-') { temp += strlen(pattern); const unsigned len = strlen(temp); if(len) { res = temp; return true; } } } } return false; } // parses an option and returns the string after the '=' character bool parseOption(int argc, const char* const* argv, const char* pattern, String& res, const String& defaultVal = String()) { res = defaultVal; #ifndef DOCTEST_CONFIG_NO_UNPREFIXED_OPTIONS if(!parseOptionImpl(argc, argv, pattern, res)) return parseOptionImpl(argc, argv, pattern + 3, res); // 3 for "dt-" return true; #else // DOCTEST_CONFIG_NO_UNPREFIXED_OPTIONS return parseOptionImpl(argc, argv, pattern, res); #endif // DOCTEST_CONFIG_NO_UNPREFIXED_OPTIONS } // parses a comma separated list of words after a pattern in one of the arguments in argv bool parseCommaSepArgs(int argc, const char* const* argv, const char* pattern, std::vector& res) { String filtersString; if(parseOption(argc, argv, pattern, filtersString)) { // tokenize with "," as a separator // cppcheck-suppress strtokCalled char* pch = std::strtok(filtersString.c_str(), ","); // modifies the string while(pch != 0) { if(strlen(pch)) res.push_back(pch); // uses the strtok() internal state to go to the next token // cppcheck-suppress strtokCalled pch = std::strtok(0, ","); } return true; } return false; } enum optionType { option_bool, option_int }; // parses an int/bool option from the command line bool parseIntOption(int argc, const char* const* argv, const char* pattern, optionType type, int& res) { String parsedValue; if(!parseOption(argc, argv, pattern, parsedValue)) return false; if(type == 0) { // boolean const char positive[][5] = {"1", "true", "on", "yes"}; // 5 - strlen("true") + 1 const char negative[][6] = {"0", "false", "off", "no"}; // 6 - strlen("false") + 1 // if the value matches any of the positive/negative possibilities for(unsigned i = 0; i < 4; i++) { if(parsedValue.compare(positive[i], true) == 0) { res = 1; //!OCLINT parameter reassignment return true; } if(parsedValue.compare(negative[i], true) == 0) { res = 0; //!OCLINT parameter reassignment return true; } } } else { // integer int theInt = std::atoi(parsedValue.c_str()); // NOLINT if(theInt != 0) { res = theInt; //!OCLINT parameter reassignment return true; } } return false; } void printVersion(std::ostream& s) { if(contextState->no_version == false) s << Color::Cyan << "[doctest] " << Color::None << "doctest version is \"" << DOCTEST_VERSION_STR << "\"\n"; } void printHelp(std::ostream& s) { printVersion(s); // clang-format off s << Color::Cyan << "[doctest]\n" << Color::None; s << Color::Cyan << "[doctest] " << Color::None; s << "boolean values: \"1/on/yes/true\" or \"0/off/no/false\"\n"; s << Color::Cyan << "[doctest] " << Color::None; s << "filter values: \"str1,str2,str3\" (comma separated strings)\n"; s << Color::Cyan << "[doctest]\n" << Color::None; s << Color::Cyan << "[doctest] " << Color::None; s << "filters use wildcards for matching strings\n"; s << Color::Cyan << "[doctest] " << Color::None; s << "something passes a filter if any of the strings in a filter matches\n"; s << Color::Cyan << "[doctest]\n" << Color::None; s << Color::Cyan << "[doctest] " << Color::None; s << "ALL FLAGS, OPTIONS AND FILTERS ALSO AVAILABLE WITH A \"dt-\" PREFIX!!!\n"; s << Color::Cyan << "[doctest]\n" << Color::None; s << Color::Cyan << "[doctest] " << Color::None; s << "Query flags - the program quits after them. Available:\n\n"; s << " -?, --help, -h prints this message\n"; s << " -v, --version prints the version\n"; s << " -c, --count prints the number of matching tests\n"; s << " -ltc, --list-test-cases lists all matching tests by name\n"; s << " -lts, --list-test-suites lists all matching test suites\n\n"; // ================================================================================== << 79 s << Color::Cyan << "[doctest] " << Color::None; s << "The available / options/filters are:\n\n"; s << " -tc, --test-case= filters tests by their name\n"; s << " -tce, --test-case-exclude= filters OUT tests by their name\n"; s << " -sf, --source-file= filters tests by their file\n"; s << " -sfe, --source-file-exclude= filters OUT tests by their file\n"; s << " -ts, --test-suite= filters tests by their test suite\n"; s << " -tse, --test-suite-exclude= filters OUT tests by their test suite\n"; s << " -sc, --subcase= filters subcases by their name\n"; s << " -sce, --subcase-exclude= filters OUT subcases by their name\n"; s << " -ob, --order-by= how the tests should be ordered\n"; s << " - by [file/suite/name/rand]\n"; s << " -rs, --rand-seed= seed for random ordering\n"; s << " -f, --first= the first test passing the filters to\n"; s << " execute - for range-based execution\n"; s << " -l, --last= the last test passing the filters to\n"; s << " execute - for range-based execution\n"; s << " -aa, --abort-after= stop after failed assertions\n"; s << " -scfl,--subcase-filter-levels= apply filters for the first levels\n"; s << Color::Cyan << "\n[doctest] " << Color::None; s << "Bool options - can be used like flags and true is assumed. Available:\n\n"; s << " -s, --success= include successful assertions in output\n"; s << " -cs, --case-sensitive= filters being treated as case sensitive\n"; s << " -e, --exit= exits after the tests finish\n"; s << " -d, --duration= prints the time duration of each test\n"; s << " -nt, --no-throw= skips exceptions-related assert checks\n"; s << " -ne, --no-exitcode= returns (or exits) always with success\n"; s << " -nr, --no-run= skips all runtime doctest operations\n"; s << " -nv, --no-version= omit the framework version in the output\n"; s << " -nc, --no-colors= disables colors in output\n"; s << " -fc, --force-colors= use colors even when not in a tty\n"; s << " -nb, --no-breaks= disables breakpoints in debuggers\n"; s << " -ns, --no-skip= don't skip test cases marked as skip\n"; s << " -gfl, --gnu-file-line= :n: vs (n): for line numbers in output\n"; s << " -npf, --no-path-filenames= only filenames and no paths in output\n"; s << " -nln, --no-line-numbers= 0 instead of real line numbers in output\n"; // ================================================================================== << 79 // clang-format on s << Color::Cyan << "\n[doctest] " << Color::None; s << "for more information visit the project documentation\n\n"; } void printSummary(std::ostream& s) { const ContextState* p = contextState; separator_to_stream(s); if(p->count || p->list_test_cases) { s << Color::Cyan << "[doctest] " << Color::None << "unskipped test cases passing the current filters: " << p->numTestsPassingFilters << "\n"; } else if(p->list_test_suites) { s << Color::Cyan << "[doctest] " << Color::None << "unskipped test cases passing the current filters: " << p->numTestsPassingFilters << "\n"; s << Color::Cyan << "[doctest] " << Color::None << "test suites with unskipped test cases passing the current filters: " << p->numTestSuitesPassingFilters << "\n"; } else { const bool anythingFailed = p->numFailed > 0 || p->numFailedAssertions > 0; s << Color::Cyan << "[doctest] " << Color::None << "test cases: " << std::setw(6) << p->numTestsPassingFilters << " | " << ((p->numTestsPassingFilters == 0 || anythingFailed) ? Color::None : Color::Green) << std::setw(6) << p->numTestsPassingFilters - p->numFailed << " passed" << Color::None << " | " << (p->numFailed > 0 ? Color::Red : Color::None) << std::setw(6) << p->numFailed << " failed" << Color::None << " | "; if(p->no_skipped_summary == false) { const int numSkipped = static_cast(getRegisteredTests().size()) - p->numTestsPassingFilters; s << (numSkipped == 0 ? Color::None : Color::Yellow) << std::setw(6) << numSkipped << " skipped" << Color::None; } s << "\n"; s << Color::Cyan << "[doctest] " << Color::None << "assertions: " << std::setw(6) << p->numAssertions << " | " << ((p->numAssertions == 0 || anythingFailed) ? Color::None : Color::Green) << std::setw(6) << (p->numAssertions - p->numFailedAssertions) << " passed" << Color::None << " | " << (p->numFailedAssertions > 0 ? Color::Red : Color::None) << std::setw(6) << p->numFailedAssertions << " failed" << Color::None << " |\n"; s << Color::Cyan << "[doctest] " << Color::None << "Status: " << (p->numFailed > 0 ? Color::Red : Color::Green) << ((p->numFailed > 0) ? "FAILURE!\n" : "SUCCESS!\n"); } // remove any coloring s << Color::None; } } // namespace detail bool isRunningInTest() { return detail::contextState != 0; } Context::Context(int argc, const char* const* argv) : p(new detail::ContextState) { parseArgs(argc, argv, true); } Context::~Context() { delete p; } void Context::applyCommandLine(int argc, const char* const* argv) { parseArgs(argc, argv); } // parses args void Context::parseArgs(int argc, const char* const* argv, bool withDefaults) { using namespace detail; // clang-format off parseCommaSepArgs(argc, argv, "dt-source-file=", p->filters[0]); parseCommaSepArgs(argc, argv, "dt-sf=", p->filters[0]); parseCommaSepArgs(argc, argv, "dt-source-file-exclude=",p->filters[1]); parseCommaSepArgs(argc, argv, "dt-sfe=", p->filters[1]); parseCommaSepArgs(argc, argv, "dt-test-suite=", p->filters[2]); parseCommaSepArgs(argc, argv, "dt-ts=", p->filters[2]); parseCommaSepArgs(argc, argv, "dt-test-suite-exclude=", p->filters[3]); parseCommaSepArgs(argc, argv, "dt-tse=", p->filters[3]); parseCommaSepArgs(argc, argv, "dt-test-case=", p->filters[4]); parseCommaSepArgs(argc, argv, "dt-tc=", p->filters[4]); parseCommaSepArgs(argc, argv, "dt-test-case-exclude=", p->filters[5]); parseCommaSepArgs(argc, argv, "dt-tce=", p->filters[5]); parseCommaSepArgs(argc, argv, "dt-subcase=", p->filters[6]); parseCommaSepArgs(argc, argv, "dt-sc=", p->filters[6]); parseCommaSepArgs(argc, argv, "dt-subcase-exclude=", p->filters[7]); parseCommaSepArgs(argc, argv, "dt-sce=", p->filters[7]); // clang-format on int intRes = 0; String strRes; #define DOCTEST_PARSE_AS_BOOL_OR_FLAG(name, sname, var, default) \ if(parseIntOption(argc, argv, name "=", option_bool, intRes) || \ parseIntOption(argc, argv, sname "=", option_bool, intRes)) \ p->var = !!intRes; \ else if(parseFlag(argc, argv, name) || parseFlag(argc, argv, sname)) \ p->var = true; \ else if(withDefaults) \ p->var = default #define DOCTEST_PARSE_INT_OPTION(name, sname, var, default) \ if(parseIntOption(argc, argv, name "=", option_int, intRes) || \ parseIntOption(argc, argv, sname "=", option_int, intRes)) \ p->var = intRes; \ else if(withDefaults) \ p->var = default #define DOCTEST_PARSE_STR_OPTION(name, sname, var, default) \ if(parseOption(argc, argv, name "=", strRes, default) || \ parseOption(argc, argv, sname "=", strRes, default) || withDefaults) \ p->var = strRes // clang-format off DOCTEST_PARSE_STR_OPTION("dt-order-by", "dt-ob", order_by, "file"); DOCTEST_PARSE_INT_OPTION("dt-rand-seed", "dt-rs", rand_seed, 0); DOCTEST_PARSE_INT_OPTION("dt-first", "dt-f", first, 1); DOCTEST_PARSE_INT_OPTION("dt-last", "dt-l", last, 0); DOCTEST_PARSE_INT_OPTION("dt-abort-after", "dt-aa", abort_after, 0); DOCTEST_PARSE_INT_OPTION("dt-subcase-filter-levels", "dt-scfl", subcase_filter_levels, 2000000000); DOCTEST_PARSE_AS_BOOL_OR_FLAG("dt-success", "dt-s", success, false); DOCTEST_PARSE_AS_BOOL_OR_FLAG("dt-case-sensitive", "dt-cs", case_sensitive, false); DOCTEST_PARSE_AS_BOOL_OR_FLAG("dt-exit", "dt-e", exit, false); DOCTEST_PARSE_AS_BOOL_OR_FLAG("dt-duration", "dt-d", duration, false); DOCTEST_PARSE_AS_BOOL_OR_FLAG("dt-no-throw", "dt-nt", no_throw, false); DOCTEST_PARSE_AS_BOOL_OR_FLAG("dt-no-exitcode", "dt-ne", no_exitcode, false); DOCTEST_PARSE_AS_BOOL_OR_FLAG("dt-no-run", "dt-nr", no_run, false); DOCTEST_PARSE_AS_BOOL_OR_FLAG("dt-no-version", "dt-nv", no_version, false); DOCTEST_PARSE_AS_BOOL_OR_FLAG("dt-no-colors", "dt-nc", no_colors, false); DOCTEST_PARSE_AS_BOOL_OR_FLAG("dt-force-colors", "dt-fc", force_colors, false); DOCTEST_PARSE_AS_BOOL_OR_FLAG("dt-no-breaks", "dt-nb", no_breaks, false); DOCTEST_PARSE_AS_BOOL_OR_FLAG("dt-no-skip", "dt-ns", no_skip, false); DOCTEST_PARSE_AS_BOOL_OR_FLAG("dt-gnu-file-line", "dt-gfl", gnu_file_line, bool(DOCTEST_GCC) || bool(DOCTEST_CLANG)); DOCTEST_PARSE_AS_BOOL_OR_FLAG("dt-no-path-filenames", "dt-npf", no_path_in_filenames, false); DOCTEST_PARSE_AS_BOOL_OR_FLAG("dt-no-line-numbers", "dt-nln", no_line_numbers, false); DOCTEST_PARSE_AS_BOOL_OR_FLAG("dt-no-skipped-summary", "dt-nss", no_skipped_summary, false); // clang-format on #undef DOCTEST_PARSE_STR_OPTION #undef DOCTEST_PARSE_INT_OPTION #undef DOCTEST_PARSE_AS_BOOL_OR_FLAG if(withDefaults) { p->help = false; p->version = false; p->count = false; p->list_test_cases = false; p->list_test_suites = false; } if(parseFlag(argc, argv, "dt-help") || parseFlag(argc, argv, "dt-h") || parseFlag(argc, argv, "dt-?")) { p->help = true; p->exit = true; } if(parseFlag(argc, argv, "dt-version") || parseFlag(argc, argv, "dt-v")) { p->version = true; p->exit = true; } if(parseFlag(argc, argv, "dt-count") || parseFlag(argc, argv, "dt-c")) { p->count = true; p->exit = true; } if(parseFlag(argc, argv, "dt-list-test-cases") || parseFlag(argc, argv, "dt-ltc")) { p->list_test_cases = true; p->exit = true; } if(parseFlag(argc, argv, "dt-list-test-suites") || parseFlag(argc, argv, "dt-lts")) { p->list_test_suites = true; p->exit = true; } } // allows the user to add procedurally to the filters from the command line void Context::addFilter(const char* filter, const char* value) { setOption(filter, value); } // allows the user to clear all filters from the command line void Context::clearFilters() { for(unsigned i = 0; i < p->filters.size(); ++i) p->filters[i].clear(); } // allows the user to override procedurally the int/bool options from the command line void Context::setOption(const char* option, int value) { setOption(option, toString(value).c_str()); } // allows the user to override procedurally the string options from the command line void Context::setOption(const char* option, const char* value) { String argv = String("-") + option + "=" + value; const char* lvalue = argv.c_str(); parseArgs(1, &lvalue); } // users should query this in their main() and exit the program if true bool Context::shouldExit() { return p->exit; } // the main function that does all the filtering and test running int Context::run() { using namespace detail; Color::init(); contextState = p; p->resetRunData(); // handle version, help and no_run if(p->no_run || p->version || p->help) { if(p->version) printVersion(std::cout); if(p->help) printHelp(std::cout); contextState = 0; return EXIT_SUCCESS; } printVersion(std::cout); std::cout << Color::Cyan << "[doctest] " << Color::None << "run with \"--help\" for options\n"; unsigned i = 0; // counter used for loops - here for VC6 std::set& registeredTests = getRegisteredTests(); std::vector testArray; for(std::set::iterator it = registeredTests.begin(); it != registeredTests.end(); ++it) testArray.push_back(&(*it)); // sort the collected records if(!testArray.empty()) { if(p->order_by.compare("file", true) == 0) { std::qsort(&testArray[0], testArray.size(), sizeof(TestCase*), fileOrderComparator); } else if(p->order_by.compare("suite", true) == 0) { std::qsort(&testArray[0], testArray.size(), sizeof(TestCase*), suiteOrderComparator); } else if(p->order_by.compare("name", true) == 0) { std::qsort(&testArray[0], testArray.size(), sizeof(TestCase*), nameOrderComparator); } else if(p->order_by.compare("rand", true) == 0) { std::srand(p->rand_seed); // random_shuffle implementation const TestCase** first = &testArray[0]; for(i = testArray.size() - 1; i > 0; --i) { int idxToSwap = std::rand() % (i + 1); // NOLINT const TestCase* temp = first[i]; first[i] = first[idxToSwap]; first[idxToSwap] = temp; } } } if(p->list_test_cases) { std::cout << Color::Cyan << "[doctest] " << Color::None << "listing all test case names\n"; separator_to_stream(std::cout); } std::set testSuitesPassingFilters; if(p->list_test_suites) { std::cout << Color::Cyan << "[doctest] " << Color::None << "listing all test suites\n"; separator_to_stream(std::cout); } // invoke the registered functions if they match the filter criteria (or just count them) for(i = 0; i < testArray.size(); i++) { const TestCase& data = *testArray[i]; if(data.m_skip && !p->no_skip) continue; if(!matchesAny(data.m_file, p->filters[0], 1, p->case_sensitive)) continue; if(matchesAny(data.m_file, p->filters[1], 0, p->case_sensitive)) continue; if(!matchesAny(data.m_test_suite, p->filters[2], 1, p->case_sensitive)) continue; if(matchesAny(data.m_test_suite, p->filters[3], 0, p->case_sensitive)) continue; if(!matchesAny(data.m_name, p->filters[4], 1, p->case_sensitive)) continue; if(matchesAny(data.m_name, p->filters[5], 0, p->case_sensitive)) continue; p->numTestsPassingFilters++; // do not execute the test if we are to only count the number of filter passing tests if(p->count) continue; // print the name of the test and don't execute it if(p->list_test_cases) { std::cout << Color::None << data.m_name << "\n"; continue; } // print the name of the test suite if not done already and don't execute it if(p->list_test_suites) { if((testSuitesPassingFilters.count(data.m_test_suite) == 0) && data.m_test_suite[0] != '\0') { std::cout << Color::None << data.m_test_suite << "\n"; testSuitesPassingFilters.insert(data.m_test_suite); p->numTestSuitesPassingFilters++; } continue; } // skip the test if it is not in the execution range if((p->last < p->numTestsPassingFilters && p->first <= p->last) || (p->first > p->numTestsPassingFilters)) continue; // execute the test if it passes all the filtering { p->currentTest = &data; bool failed = false; p->hasLoggedCurrentTestStart = false; p->numFailedAssertionsForCurrentTestcase = 0; p->subcasesPassed.clear(); double duration = 0; Timer timer; timer.start(); do { // if the start has been logged from a previous iteration of this loop if(p->hasLoggedCurrentTestStart) logTestEnd(); p->hasLoggedCurrentTestStart = false; // if logging successful tests - force the start log if(p->success) DOCTEST_LOG_START(std::cout); // reset the assertion state p->numAssertionsForCurrentTestcase = 0; p->hasCurrentTestFailed = false; // reset some of the fields for subcases (except for the set of fully passed ones) p->subcasesHasSkipped = false; p->subcasesCurrentLevel = 0; p->subcasesEnteredLevels.clear(); // reset stuff for logging with INFO() p->exceptionalContexts.clear(); // execute the test #ifndef DOCTEST_CONFIG_NO_EXCEPTIONS try { #endif // DOCTEST_CONFIG_NO_EXCEPTIONS FatalConditionHandler fatalConditionHandler; // Handle signals data.m_test(); fatalConditionHandler.reset(); if(contextState->hasCurrentTestFailed) failed = true; #ifndef DOCTEST_CONFIG_NO_EXCEPTIONS } catch(const TestFailureException&) { failed = true; } catch(...) { DOCTEST_LOG_START(std::cout); logTestException(*contextState->currentTest, translateActiveException(), false); failed = true; } #endif // DOCTEST_CONFIG_NO_EXCEPTIONS p->numAssertions += p->numAssertionsForCurrentTestcase; // exit this loop if enough assertions have failed if(p->abort_after > 0 && p->numFailedAssertions >= p->abort_after) { p->subcasesHasSkipped = false; std::cout << Color::Red << "Aborting - too many failed asserts!\n"; } } while(p->subcasesHasSkipped == true); duration = timer.getElapsedSeconds(); if(Approx(p->currentTest->m_timeout).epsilon(DBL_EPSILON) != 0 && Approx(duration).epsilon(DBL_EPSILON) > p->currentTest->m_timeout) { failed = true; DOCTEST_LOG_START(std::cout); std::cout << Color::Red << "Test case exceeded time limit of " << std::setprecision(6) << std::fixed << p->currentTest->m_timeout << "!\n"; } if(p->duration) std::cout << Color::None << std::setprecision(6) << std::fixed << duration << " s: " << p->currentTest->m_name << "\n"; if(data.m_should_fail) { DOCTEST_LOG_START(std::cout); if(failed) std::cout << Color::Yellow << "Failed as expected so marking it as not failed\n"; else std::cout << Color::Red << "Should have failed but didn't! Marking it as failed!\n"; failed = !failed; } else if(failed && data.m_may_fail) { DOCTEST_LOG_START(std::cout); failed = false; std::cout << Color::Yellow << "Allowed to fail so marking it as not failed\n"; } else if(data.m_expected_failures > 0) { DOCTEST_LOG_START(std::cout); if(p->numFailedAssertionsForCurrentTestcase == data.m_expected_failures) { failed = false; std::cout << Color::Yellow << "Failed exactly " << data.m_expected_failures << " times as expected so marking it as not failed!\n"; } else { failed = true; std::cout << Color::Red << "Didn't fail exactly " << data.m_expected_failures << " times so marking it as failed!\n"; } } std::cout << Color::None; if(p->hasLoggedCurrentTestStart) logTestEnd(); if(failed) // if any subcase has failed - the whole test case has failed p->numFailed++; // stop executing tests if enough assertions have failed if(p->abort_after > 0 && p->numFailedAssertions >= p->abort_after) break; } } printSummary(std::cout); contextState = 0; if(p->numFailed && !p->no_exitcode) return EXIT_FAILURE; return EXIT_SUCCESS; } } // namespace doctest #endif // DOCTEST_CONFIG_DISABLE #ifdef DOCTEST_CONFIG_IMPLEMENT_WITH_MAIN int main(int argc, char** argv) { return doctest::Context(argc, argv).run(); } #endif // DOCTEST_CONFIG_IMPLEMENT_WITH_MAIN DOCTEST_CLANG_SUPPRESS_WARNING_POP DOCTEST_MSVC_SUPPRESS_WARNING_POP DOCTEST_GCC_SUPPRESS_WARNING_POP #endif // DOCTEST_LIBRARY_IMPLEMENTATION #endif // DOCTEST_CONFIG_IMPLEMENT plog-1.1.11/test/doctest/2.4.11/000077500000000000000000000000001504622126500157065ustar00rootroot00000000000000plog-1.1.11/test/doctest/2.4.11/doctest.h000066400000000000000000011641541504622126500175400ustar00rootroot00000000000000// ====================================================================== lgtm [cpp/missing-header-guard] // == DO NOT MODIFY THIS FILE BY HAND - IT IS AUTO GENERATED BY CMAKE! == // ====================================================================== // // doctest.h - the lightest feature-rich C++ single-header testing framework for unit tests and TDD // // Copyright (c) 2016-2023 Viktor Kirilov // // Distributed under the MIT Software License // See accompanying file LICENSE.txt or copy at // https://opensource.org/licenses/MIT // // The documentation can be found at the library's page: // https://github.com/doctest/doctest/blob/master/doc/markdown/readme.md // // ================================================================================================= // ================================================================================================= // ================================================================================================= // // The library is heavily influenced by Catch - https://github.com/catchorg/Catch2 // which uses the Boost Software License - Version 1.0 // see here - https://github.com/catchorg/Catch2/blob/master/LICENSE.txt // // The concept of subcases (sections in Catch) and expression decomposition are from there. // Some parts of the code are taken directly: // - stringification - the detection of "ostream& operator<<(ostream&, const T&)" and StringMaker<> // - the Approx() helper class for floating point comparison // - colors in the console // - breaking into a debugger // - signal / SEH handling // - timer // - XmlWriter class - thanks to Phil Nash for allowing the direct reuse (AKA copy/paste) // // The expression decomposing templates are taken from lest - https://github.com/martinmoene/lest // which uses the Boost Software License - Version 1.0 // see here - https://github.com/martinmoene/lest/blob/master/LICENSE.txt // // ================================================================================================= // ================================================================================================= // ================================================================================================= #ifndef DOCTEST_LIBRARY_INCLUDED #define DOCTEST_LIBRARY_INCLUDED // ================================================================================================= // == VERSION ====================================================================================== // ================================================================================================= #define DOCTEST_VERSION_MAJOR 2 #define DOCTEST_VERSION_MINOR 4 #define DOCTEST_VERSION_PATCH 11 // util we need here #define DOCTEST_TOSTR_IMPL(x) #x #define DOCTEST_TOSTR(x) DOCTEST_TOSTR_IMPL(x) #define DOCTEST_VERSION_STR \ DOCTEST_TOSTR(DOCTEST_VERSION_MAJOR) "." \ DOCTEST_TOSTR(DOCTEST_VERSION_MINOR) "." \ DOCTEST_TOSTR(DOCTEST_VERSION_PATCH) #define DOCTEST_VERSION \ (DOCTEST_VERSION_MAJOR * 10000 + DOCTEST_VERSION_MINOR * 100 + DOCTEST_VERSION_PATCH) // ================================================================================================= // == COMPILER VERSION ============================================================================= // ================================================================================================= // ideas for the version stuff are taken from here: https://github.com/cxxstuff/cxx_detect #ifdef _MSC_VER #define DOCTEST_CPLUSPLUS _MSVC_LANG #else #define DOCTEST_CPLUSPLUS __cplusplus #endif #define DOCTEST_COMPILER(MAJOR, MINOR, PATCH) ((MAJOR)*10000000 + (MINOR)*100000 + (PATCH)) // GCC/Clang and GCC/MSVC are mutually exclusive, but Clang/MSVC are not because of clang-cl... #if defined(_MSC_VER) && defined(_MSC_FULL_VER) #if _MSC_VER == _MSC_FULL_VER / 10000 #define DOCTEST_MSVC DOCTEST_COMPILER(_MSC_VER / 100, _MSC_VER % 100, _MSC_FULL_VER % 10000) #else // MSVC #define DOCTEST_MSVC \ DOCTEST_COMPILER(_MSC_VER / 100, (_MSC_FULL_VER / 100000) % 100, _MSC_FULL_VER % 100000) #endif // MSVC #endif // MSVC #if defined(__clang__) && defined(__clang_minor__) && defined(__clang_patchlevel__) #define DOCTEST_CLANG DOCTEST_COMPILER(__clang_major__, __clang_minor__, __clang_patchlevel__) #elif defined(__GNUC__) && defined(__GNUC_MINOR__) && defined(__GNUC_PATCHLEVEL__) && \ !defined(__INTEL_COMPILER) #define DOCTEST_GCC DOCTEST_COMPILER(__GNUC__, __GNUC_MINOR__, __GNUC_PATCHLEVEL__) #endif // GCC #if defined(__INTEL_COMPILER) #define DOCTEST_ICC DOCTEST_COMPILER(__INTEL_COMPILER / 100, __INTEL_COMPILER % 100, 0) #endif // ICC #ifndef DOCTEST_MSVC #define DOCTEST_MSVC 0 #endif // DOCTEST_MSVC #ifndef DOCTEST_CLANG #define DOCTEST_CLANG 0 #endif // DOCTEST_CLANG #ifndef DOCTEST_GCC #define DOCTEST_GCC 0 #endif // DOCTEST_GCC #ifndef DOCTEST_ICC #define DOCTEST_ICC 0 #endif // DOCTEST_ICC // ================================================================================================= // == COMPILER WARNINGS HELPERS ==================================================================== // ================================================================================================= #if DOCTEST_CLANG && !DOCTEST_ICC #define DOCTEST_PRAGMA_TO_STR(x) _Pragma(#x) #define DOCTEST_CLANG_SUPPRESS_WARNING_PUSH _Pragma("clang diagnostic push") #define DOCTEST_CLANG_SUPPRESS_WARNING(w) DOCTEST_PRAGMA_TO_STR(clang diagnostic ignored w) #define DOCTEST_CLANG_SUPPRESS_WARNING_POP _Pragma("clang diagnostic pop") #define DOCTEST_CLANG_SUPPRESS_WARNING_WITH_PUSH(w) \ DOCTEST_CLANG_SUPPRESS_WARNING_PUSH DOCTEST_CLANG_SUPPRESS_WARNING(w) #else // DOCTEST_CLANG #define DOCTEST_CLANG_SUPPRESS_WARNING_PUSH #define DOCTEST_CLANG_SUPPRESS_WARNING(w) #define DOCTEST_CLANG_SUPPRESS_WARNING_POP #define DOCTEST_CLANG_SUPPRESS_WARNING_WITH_PUSH(w) #endif // DOCTEST_CLANG #if DOCTEST_GCC #define DOCTEST_PRAGMA_TO_STR(x) _Pragma(#x) #define DOCTEST_GCC_SUPPRESS_WARNING_PUSH _Pragma("GCC diagnostic push") #define DOCTEST_GCC_SUPPRESS_WARNING(w) DOCTEST_PRAGMA_TO_STR(GCC diagnostic ignored w) #define DOCTEST_GCC_SUPPRESS_WARNING_POP _Pragma("GCC diagnostic pop") #define DOCTEST_GCC_SUPPRESS_WARNING_WITH_PUSH(w) \ DOCTEST_GCC_SUPPRESS_WARNING_PUSH DOCTEST_GCC_SUPPRESS_WARNING(w) #else // DOCTEST_GCC #define DOCTEST_GCC_SUPPRESS_WARNING_PUSH #define DOCTEST_GCC_SUPPRESS_WARNING(w) #define DOCTEST_GCC_SUPPRESS_WARNING_POP #define DOCTEST_GCC_SUPPRESS_WARNING_WITH_PUSH(w) #endif // DOCTEST_GCC #if DOCTEST_MSVC #define DOCTEST_MSVC_SUPPRESS_WARNING_PUSH __pragma(warning(push)) #define DOCTEST_MSVC_SUPPRESS_WARNING(w) __pragma(warning(disable : w)) #define DOCTEST_MSVC_SUPPRESS_WARNING_POP __pragma(warning(pop)) #define DOCTEST_MSVC_SUPPRESS_WARNING_WITH_PUSH(w) \ DOCTEST_MSVC_SUPPRESS_WARNING_PUSH DOCTEST_MSVC_SUPPRESS_WARNING(w) #else // DOCTEST_MSVC #define DOCTEST_MSVC_SUPPRESS_WARNING_PUSH #define DOCTEST_MSVC_SUPPRESS_WARNING(w) #define DOCTEST_MSVC_SUPPRESS_WARNING_POP #define DOCTEST_MSVC_SUPPRESS_WARNING_WITH_PUSH(w) #endif // DOCTEST_MSVC // ================================================================================================= // == COMPILER WARNINGS ============================================================================ // ================================================================================================= // both the header and the implementation suppress all of these, // so it only makes sense to aggregate them like so #define DOCTEST_SUPPRESS_COMMON_WARNINGS_PUSH \ DOCTEST_CLANG_SUPPRESS_WARNING_PUSH \ DOCTEST_CLANG_SUPPRESS_WARNING("-Wunknown-pragmas") \ DOCTEST_CLANG_SUPPRESS_WARNING("-Wweak-vtables") \ DOCTEST_CLANG_SUPPRESS_WARNING("-Wpadded") \ DOCTEST_CLANG_SUPPRESS_WARNING("-Wmissing-prototypes") \ DOCTEST_CLANG_SUPPRESS_WARNING("-Wc++98-compat") \ DOCTEST_CLANG_SUPPRESS_WARNING("-Wc++98-compat-pedantic") \ \ DOCTEST_GCC_SUPPRESS_WARNING_PUSH \ DOCTEST_GCC_SUPPRESS_WARNING("-Wunknown-pragmas") \ DOCTEST_GCC_SUPPRESS_WARNING("-Wpragmas") \ DOCTEST_GCC_SUPPRESS_WARNING("-Weffc++") \ DOCTEST_GCC_SUPPRESS_WARNING("-Wstrict-overflow") \ DOCTEST_GCC_SUPPRESS_WARNING("-Wstrict-aliasing") \ DOCTEST_GCC_SUPPRESS_WARNING("-Wmissing-declarations") \ DOCTEST_GCC_SUPPRESS_WARNING("-Wuseless-cast") \ DOCTEST_GCC_SUPPRESS_WARNING("-Wnoexcept") \ \ DOCTEST_MSVC_SUPPRESS_WARNING_PUSH \ /* these 4 also disabled globally via cmake: */ \ DOCTEST_MSVC_SUPPRESS_WARNING(4514) /* unreferenced inline function has been removed */ \ DOCTEST_MSVC_SUPPRESS_WARNING(4571) /* SEH related */ \ DOCTEST_MSVC_SUPPRESS_WARNING(4710) /* function not inlined */ \ DOCTEST_MSVC_SUPPRESS_WARNING(4711) /* function selected for inline expansion*/ \ /* common ones */ \ DOCTEST_MSVC_SUPPRESS_WARNING(4616) /* invalid compiler warning */ \ DOCTEST_MSVC_SUPPRESS_WARNING(4619) /* invalid compiler warning */ \ DOCTEST_MSVC_SUPPRESS_WARNING(4996) /* The compiler encountered a deprecated declaration */ \ DOCTEST_MSVC_SUPPRESS_WARNING(4706) /* assignment within conditional expression */ \ DOCTEST_MSVC_SUPPRESS_WARNING(4512) /* 'class' : assignment operator could not be generated */ \ DOCTEST_MSVC_SUPPRESS_WARNING(4127) /* conditional expression is constant */ \ DOCTEST_MSVC_SUPPRESS_WARNING(4820) /* padding */ \ DOCTEST_MSVC_SUPPRESS_WARNING(4625) /* copy constructor was implicitly deleted */ \ DOCTEST_MSVC_SUPPRESS_WARNING(4626) /* assignment operator was implicitly deleted */ \ DOCTEST_MSVC_SUPPRESS_WARNING(5027) /* move assignment operator implicitly deleted */ \ DOCTEST_MSVC_SUPPRESS_WARNING(5026) /* move constructor was implicitly deleted */ \ DOCTEST_MSVC_SUPPRESS_WARNING(4640) /* construction of local static object not thread-safe */ \ DOCTEST_MSVC_SUPPRESS_WARNING(5045) /* Spectre mitigation for memory load */ \ DOCTEST_MSVC_SUPPRESS_WARNING(5264) /* 'variable-name': 'const' variable is not used */ \ /* static analysis */ \ DOCTEST_MSVC_SUPPRESS_WARNING(26439) /* Function may not throw. Declare it 'noexcept' */ \ DOCTEST_MSVC_SUPPRESS_WARNING(26495) /* Always initialize a member variable */ \ DOCTEST_MSVC_SUPPRESS_WARNING(26451) /* Arithmetic overflow ... */ \ DOCTEST_MSVC_SUPPRESS_WARNING(26444) /* Avoid unnamed objects with custom ctor and dtor... */ \ DOCTEST_MSVC_SUPPRESS_WARNING(26812) /* Prefer 'enum class' over 'enum' */ #define DOCTEST_SUPPRESS_COMMON_WARNINGS_POP \ DOCTEST_CLANG_SUPPRESS_WARNING_POP \ DOCTEST_GCC_SUPPRESS_WARNING_POP \ DOCTEST_MSVC_SUPPRESS_WARNING_POP DOCTEST_SUPPRESS_COMMON_WARNINGS_PUSH DOCTEST_CLANG_SUPPRESS_WARNING_PUSH DOCTEST_CLANG_SUPPRESS_WARNING("-Wnon-virtual-dtor") DOCTEST_CLANG_SUPPRESS_WARNING("-Wdeprecated") DOCTEST_GCC_SUPPRESS_WARNING_PUSH DOCTEST_GCC_SUPPRESS_WARNING("-Wctor-dtor-privacy") DOCTEST_GCC_SUPPRESS_WARNING("-Wnon-virtual-dtor") DOCTEST_GCC_SUPPRESS_WARNING("-Wsign-promo") DOCTEST_MSVC_SUPPRESS_WARNING_PUSH DOCTEST_MSVC_SUPPRESS_WARNING(4623) // default constructor was implicitly defined as deleted #define DOCTEST_MAKE_STD_HEADERS_CLEAN_FROM_WARNINGS_ON_WALL_BEGIN \ DOCTEST_MSVC_SUPPRESS_WARNING_PUSH \ DOCTEST_MSVC_SUPPRESS_WARNING(4548) /* before comma no effect; expected side - effect */ \ DOCTEST_MSVC_SUPPRESS_WARNING(4265) /* virtual functions, but destructor is not virtual */ \ DOCTEST_MSVC_SUPPRESS_WARNING(4986) /* exception specification does not match previous */ \ DOCTEST_MSVC_SUPPRESS_WARNING(4350) /* 'member1' called instead of 'member2' */ \ DOCTEST_MSVC_SUPPRESS_WARNING(4668) /* not defined as a preprocessor macro */ \ DOCTEST_MSVC_SUPPRESS_WARNING(4365) /* signed/unsigned mismatch */ \ DOCTEST_MSVC_SUPPRESS_WARNING(4774) /* format string not a string literal */ \ DOCTEST_MSVC_SUPPRESS_WARNING(4820) /* padding */ \ DOCTEST_MSVC_SUPPRESS_WARNING(4625) /* copy constructor was implicitly deleted */ \ DOCTEST_MSVC_SUPPRESS_WARNING(4626) /* assignment operator was implicitly deleted */ \ DOCTEST_MSVC_SUPPRESS_WARNING(5027) /* move assignment operator implicitly deleted */ \ DOCTEST_MSVC_SUPPRESS_WARNING(5026) /* move constructor was implicitly deleted */ \ DOCTEST_MSVC_SUPPRESS_WARNING(4623) /* default constructor was implicitly deleted */ \ DOCTEST_MSVC_SUPPRESS_WARNING(5039) /* pointer to pot. throwing function passed to extern C */ \ DOCTEST_MSVC_SUPPRESS_WARNING(5045) /* Spectre mitigation for memory load */ \ DOCTEST_MSVC_SUPPRESS_WARNING(5105) /* macro producing 'defined' has undefined behavior */ \ DOCTEST_MSVC_SUPPRESS_WARNING(4738) /* storing float result in memory, loss of performance */ \ DOCTEST_MSVC_SUPPRESS_WARNING(5262) /* implicit fall-through */ #define DOCTEST_MAKE_STD_HEADERS_CLEAN_FROM_WARNINGS_ON_WALL_END DOCTEST_MSVC_SUPPRESS_WARNING_POP // ================================================================================================= // == FEATURE DETECTION ============================================================================ // ================================================================================================= // general compiler feature support table: https://en.cppreference.com/w/cpp/compiler_support // MSVC C++11 feature support table: https://msdn.microsoft.com/en-us/library/hh567368.aspx // GCC C++11 feature support table: https://gcc.gnu.org/projects/cxx-status.html // MSVC version table: // https://en.wikipedia.org/wiki/Microsoft_Visual_C%2B%2B#Internal_version_numbering // MSVC++ 14.3 (17) _MSC_VER == 1930 (Visual Studio 2022) // MSVC++ 14.2 (16) _MSC_VER == 1920 (Visual Studio 2019) // MSVC++ 14.1 (15) _MSC_VER == 1910 (Visual Studio 2017) // MSVC++ 14.0 _MSC_VER == 1900 (Visual Studio 2015) // MSVC++ 12.0 _MSC_VER == 1800 (Visual Studio 2013) // MSVC++ 11.0 _MSC_VER == 1700 (Visual Studio 2012) // MSVC++ 10.0 _MSC_VER == 1600 (Visual Studio 2010) // MSVC++ 9.0 _MSC_VER == 1500 (Visual Studio 2008) // MSVC++ 8.0 _MSC_VER == 1400 (Visual Studio 2005) // Universal Windows Platform support #if defined(WINAPI_FAMILY) && (WINAPI_FAMILY == WINAPI_FAMILY_APP) #define DOCTEST_CONFIG_NO_WINDOWS_SEH #endif // WINAPI_FAMILY #if DOCTEST_MSVC && !defined(DOCTEST_CONFIG_WINDOWS_SEH) #define DOCTEST_CONFIG_WINDOWS_SEH #endif // MSVC #if defined(DOCTEST_CONFIG_NO_WINDOWS_SEH) && defined(DOCTEST_CONFIG_WINDOWS_SEH) #undef DOCTEST_CONFIG_WINDOWS_SEH #endif // DOCTEST_CONFIG_NO_WINDOWS_SEH #if !defined(_WIN32) && !defined(__QNX__) && !defined(DOCTEST_CONFIG_POSIX_SIGNALS) && \ !defined(__EMSCRIPTEN__) && !defined(__wasi__) #define DOCTEST_CONFIG_POSIX_SIGNALS #endif // _WIN32 #if defined(DOCTEST_CONFIG_NO_POSIX_SIGNALS) && defined(DOCTEST_CONFIG_POSIX_SIGNALS) #undef DOCTEST_CONFIG_POSIX_SIGNALS #endif // DOCTEST_CONFIG_NO_POSIX_SIGNALS #ifndef DOCTEST_CONFIG_NO_EXCEPTIONS #if !defined(__cpp_exceptions) && !defined(__EXCEPTIONS) && !defined(_CPPUNWIND) \ || defined(__wasi__) #define DOCTEST_CONFIG_NO_EXCEPTIONS #endif // no exceptions #endif // DOCTEST_CONFIG_NO_EXCEPTIONS #ifdef DOCTEST_CONFIG_NO_EXCEPTIONS_BUT_WITH_ALL_ASSERTS #ifndef DOCTEST_CONFIG_NO_EXCEPTIONS #define DOCTEST_CONFIG_NO_EXCEPTIONS #endif // DOCTEST_CONFIG_NO_EXCEPTIONS #endif // DOCTEST_CONFIG_NO_EXCEPTIONS_BUT_WITH_ALL_ASSERTS #if defined(DOCTEST_CONFIG_NO_EXCEPTIONS) && !defined(DOCTEST_CONFIG_NO_TRY_CATCH_IN_ASSERTS) #define DOCTEST_CONFIG_NO_TRY_CATCH_IN_ASSERTS #endif // DOCTEST_CONFIG_NO_EXCEPTIONS && !DOCTEST_CONFIG_NO_TRY_CATCH_IN_ASSERTS #ifdef __wasi__ #define DOCTEST_CONFIG_NO_MULTITHREADING #endif #if defined(DOCTEST_CONFIG_IMPLEMENT_WITH_MAIN) && !defined(DOCTEST_CONFIG_IMPLEMENT) #define DOCTEST_CONFIG_IMPLEMENT #endif // DOCTEST_CONFIG_IMPLEMENT_WITH_MAIN #if defined(_WIN32) || defined(__CYGWIN__) #if DOCTEST_MSVC #define DOCTEST_SYMBOL_EXPORT __declspec(dllexport) #define DOCTEST_SYMBOL_IMPORT __declspec(dllimport) #else // MSVC #define DOCTEST_SYMBOL_EXPORT __attribute__((dllexport)) #define DOCTEST_SYMBOL_IMPORT __attribute__((dllimport)) #endif // MSVC #else // _WIN32 #define DOCTEST_SYMBOL_EXPORT __attribute__((visibility("default"))) #define DOCTEST_SYMBOL_IMPORT #endif // _WIN32 #ifdef DOCTEST_CONFIG_IMPLEMENTATION_IN_DLL #ifdef DOCTEST_CONFIG_IMPLEMENT #define DOCTEST_INTERFACE DOCTEST_SYMBOL_EXPORT #else // DOCTEST_CONFIG_IMPLEMENT #define DOCTEST_INTERFACE DOCTEST_SYMBOL_IMPORT #endif // DOCTEST_CONFIG_IMPLEMENT #else // DOCTEST_CONFIG_IMPLEMENTATION_IN_DLL #define DOCTEST_INTERFACE #endif // DOCTEST_CONFIG_IMPLEMENTATION_IN_DLL // needed for extern template instantiations // see https://github.com/fmtlib/fmt/issues/2228 #if DOCTEST_MSVC #define DOCTEST_INTERFACE_DECL #define DOCTEST_INTERFACE_DEF DOCTEST_INTERFACE #else // DOCTEST_MSVC #define DOCTEST_INTERFACE_DECL DOCTEST_INTERFACE #define DOCTEST_INTERFACE_DEF #endif // DOCTEST_MSVC #define DOCTEST_EMPTY #if DOCTEST_MSVC #define DOCTEST_NOINLINE __declspec(noinline) #define DOCTEST_UNUSED #define DOCTEST_ALIGNMENT(x) #elif DOCTEST_CLANG && DOCTEST_CLANG < DOCTEST_COMPILER(3, 5, 0) #define DOCTEST_NOINLINE #define DOCTEST_UNUSED #define DOCTEST_ALIGNMENT(x) #else #define DOCTEST_NOINLINE __attribute__((noinline)) #define DOCTEST_UNUSED __attribute__((unused)) #define DOCTEST_ALIGNMENT(x) __attribute__((aligned(x))) #endif #ifdef DOCTEST_CONFIG_NO_CONTRADICTING_INLINE #define DOCTEST_INLINE_NOINLINE inline #else #define DOCTEST_INLINE_NOINLINE inline DOCTEST_NOINLINE #endif #ifndef DOCTEST_NORETURN #if DOCTEST_MSVC && (DOCTEST_MSVC < DOCTEST_COMPILER(19, 0, 0)) #define DOCTEST_NORETURN #else // DOCTEST_MSVC #define DOCTEST_NORETURN [[noreturn]] #endif // DOCTEST_MSVC #endif // DOCTEST_NORETURN #ifndef DOCTEST_NOEXCEPT #if DOCTEST_MSVC && (DOCTEST_MSVC < DOCTEST_COMPILER(19, 0, 0)) #define DOCTEST_NOEXCEPT #else // DOCTEST_MSVC #define DOCTEST_NOEXCEPT noexcept #endif // DOCTEST_MSVC #endif // DOCTEST_NOEXCEPT #ifndef DOCTEST_CONSTEXPR #if DOCTEST_MSVC && (DOCTEST_MSVC < DOCTEST_COMPILER(19, 0, 0)) #define DOCTEST_CONSTEXPR const #define DOCTEST_CONSTEXPR_FUNC inline #else // DOCTEST_MSVC #define DOCTEST_CONSTEXPR constexpr #define DOCTEST_CONSTEXPR_FUNC constexpr #endif // DOCTEST_MSVC #endif // DOCTEST_CONSTEXPR #ifndef DOCTEST_NO_SANITIZE_INTEGER #if DOCTEST_CLANG >= DOCTEST_COMPILER(3, 7, 0) #define DOCTEST_NO_SANITIZE_INTEGER __attribute__((no_sanitize("integer"))) #else #define DOCTEST_NO_SANITIZE_INTEGER #endif #endif // DOCTEST_NO_SANITIZE_INTEGER // ================================================================================================= // == FEATURE DETECTION END ======================================================================== // ================================================================================================= #define DOCTEST_DECLARE_INTERFACE(name) \ virtual ~name(); \ name() = default; \ name(const name&) = delete; \ name(name&&) = delete; \ name& operator=(const name&) = delete; \ name& operator=(name&&) = delete; #define DOCTEST_DEFINE_INTERFACE(name) \ name::~name() = default; // internal macros for string concatenation and anonymous variable name generation #define DOCTEST_CAT_IMPL(s1, s2) s1##s2 #define DOCTEST_CAT(s1, s2) DOCTEST_CAT_IMPL(s1, s2) #ifdef __COUNTER__ // not standard and may be missing for some compilers #define DOCTEST_ANONYMOUS(x) DOCTEST_CAT(x, __COUNTER__) #else // __COUNTER__ #define DOCTEST_ANONYMOUS(x) DOCTEST_CAT(x, __LINE__) #endif // __COUNTER__ #ifndef DOCTEST_CONFIG_ASSERTION_PARAMETERS_BY_VALUE #define DOCTEST_REF_WRAP(x) x& #else // DOCTEST_CONFIG_ASSERTION_PARAMETERS_BY_VALUE #define DOCTEST_REF_WRAP(x) x #endif // DOCTEST_CONFIG_ASSERTION_PARAMETERS_BY_VALUE // not using __APPLE__ because... this is how Catch does it #ifdef __MAC_OS_X_VERSION_MIN_REQUIRED #define DOCTEST_PLATFORM_MAC #elif defined(__IPHONE_OS_VERSION_MIN_REQUIRED) #define DOCTEST_PLATFORM_IPHONE #elif defined(_WIN32) #define DOCTEST_PLATFORM_WINDOWS #elif defined(__wasi__) #define DOCTEST_PLATFORM_WASI #else // DOCTEST_PLATFORM #define DOCTEST_PLATFORM_LINUX #endif // DOCTEST_PLATFORM namespace doctest { namespace detail { static DOCTEST_CONSTEXPR int consume(const int*, int) noexcept { return 0; } }} #define DOCTEST_GLOBAL_NO_WARNINGS(var, ...) \ DOCTEST_CLANG_SUPPRESS_WARNING_WITH_PUSH("-Wglobal-constructors") \ static const int var = doctest::detail::consume(&var, __VA_ARGS__); \ DOCTEST_CLANG_SUPPRESS_WARNING_POP #ifndef DOCTEST_BREAK_INTO_DEBUGGER // should probably take a look at https://github.com/scottt/debugbreak #ifdef DOCTEST_PLATFORM_LINUX #if defined(__GNUC__) && (defined(__i386) || defined(__x86_64)) // Break at the location of the failing check if possible #define DOCTEST_BREAK_INTO_DEBUGGER() __asm__("int $3\n" : :) // NOLINT(hicpp-no-assembler) #else #include #define DOCTEST_BREAK_INTO_DEBUGGER() raise(SIGTRAP) #endif #elif defined(DOCTEST_PLATFORM_MAC) #if defined(__x86_64) || defined(__x86_64__) || defined(__amd64__) || defined(__i386) #define DOCTEST_BREAK_INTO_DEBUGGER() __asm__("int $3\n" : :) // NOLINT(hicpp-no-assembler) #elif defined(__ppc__) || defined(__ppc64__) // https://www.cocoawithlove.com/2008/03/break-into-debugger.html #define DOCTEST_BREAK_INTO_DEBUGGER() __asm__("li r0, 20\nsc\nnop\nli r0, 37\nli r4, 2\nsc\nnop\n": : : "memory","r0","r3","r4") // NOLINT(hicpp-no-assembler) #else #define DOCTEST_BREAK_INTO_DEBUGGER() __asm__("brk #0"); // NOLINT(hicpp-no-assembler) #endif #elif DOCTEST_MSVC #define DOCTEST_BREAK_INTO_DEBUGGER() __debugbreak() #elif defined(__MINGW32__) DOCTEST_GCC_SUPPRESS_WARNING_WITH_PUSH("-Wredundant-decls") extern "C" __declspec(dllimport) void __stdcall DebugBreak(); DOCTEST_GCC_SUPPRESS_WARNING_POP #define DOCTEST_BREAK_INTO_DEBUGGER() ::DebugBreak() #else // linux #define DOCTEST_BREAK_INTO_DEBUGGER() (static_cast(0)) #endif // linux #endif // DOCTEST_BREAK_INTO_DEBUGGER // this is kept here for backwards compatibility since the config option was changed #ifdef DOCTEST_CONFIG_USE_IOSFWD #ifndef DOCTEST_CONFIG_USE_STD_HEADERS #define DOCTEST_CONFIG_USE_STD_HEADERS #endif #endif // DOCTEST_CONFIG_USE_IOSFWD // for clang - always include ciso646 (which drags some std stuff) because // we want to check if we are using libc++ with the _LIBCPP_VERSION macro in // which case we don't want to forward declare stuff from std - for reference: // https://github.com/doctest/doctest/issues/126 // https://github.com/doctest/doctest/issues/356 #if DOCTEST_CLANG #include #endif // clang #ifdef _LIBCPP_VERSION #ifndef DOCTEST_CONFIG_USE_STD_HEADERS #define DOCTEST_CONFIG_USE_STD_HEADERS #endif #endif // _LIBCPP_VERSION #ifdef DOCTEST_CONFIG_USE_STD_HEADERS #ifndef DOCTEST_CONFIG_INCLUDE_TYPE_TRAITS #define DOCTEST_CONFIG_INCLUDE_TYPE_TRAITS #endif // DOCTEST_CONFIG_INCLUDE_TYPE_TRAITS DOCTEST_MAKE_STD_HEADERS_CLEAN_FROM_WARNINGS_ON_WALL_BEGIN #include #include #include DOCTEST_MAKE_STD_HEADERS_CLEAN_FROM_WARNINGS_ON_WALL_END #else // DOCTEST_CONFIG_USE_STD_HEADERS // Forward declaring 'X' in namespace std is not permitted by the C++ Standard. DOCTEST_MSVC_SUPPRESS_WARNING_WITH_PUSH(4643) namespace std { // NOLINT(cert-dcl58-cpp) typedef decltype(nullptr) nullptr_t; // NOLINT(modernize-use-using) typedef decltype(sizeof(void*)) size_t; // NOLINT(modernize-use-using) template struct char_traits; template <> struct char_traits; template class basic_ostream; // NOLINT(fuchsia-virtual-inheritance) typedef basic_ostream> ostream; // NOLINT(modernize-use-using) template // NOLINTNEXTLINE basic_ostream& operator<<(basic_ostream&, const char*); template class basic_istream; typedef basic_istream> istream; // NOLINT(modernize-use-using) template class tuple; #if DOCTEST_MSVC >= DOCTEST_COMPILER(19, 20, 0) // see this issue on why this is needed: https://github.com/doctest/doctest/issues/183 template class allocator; template class basic_string; using string = basic_string, allocator>; #endif // VS 2019 } // namespace std DOCTEST_MSVC_SUPPRESS_WARNING_POP #endif // DOCTEST_CONFIG_USE_STD_HEADERS #ifdef DOCTEST_CONFIG_INCLUDE_TYPE_TRAITS #include #endif // DOCTEST_CONFIG_INCLUDE_TYPE_TRAITS namespace doctest { using std::size_t; DOCTEST_INTERFACE extern bool is_running_in_test; #ifndef DOCTEST_CONFIG_STRING_SIZE_TYPE #define DOCTEST_CONFIG_STRING_SIZE_TYPE unsigned #endif // A 24 byte string class (can be as small as 17 for x64 and 13 for x86) that can hold strings with length // of up to 23 chars on the stack before going on the heap - the last byte of the buffer is used for: // - "is small" bit - the highest bit - if "0" then it is small - otherwise its "1" (128) // - if small - capacity left before going on the heap - using the lowest 5 bits // - if small - 2 bits are left unused - the second and third highest ones // - if small - acts as a null terminator if strlen() is 23 (24 including the null terminator) // and the "is small" bit remains "0" ("as well as the capacity left") so its OK // Idea taken from this lecture about the string implementation of facebook/folly - fbstring // https://www.youtube.com/watch?v=kPR8h4-qZdk // TODO: // - optimizations - like not deleting memory unnecessarily in operator= and etc. // - resize/reserve/clear // - replace // - back/front // - iterator stuff // - find & friends // - push_back/pop_back // - assign/insert/erase // - relational operators as free functions - taking const char* as one of the params class DOCTEST_INTERFACE String { public: using size_type = DOCTEST_CONFIG_STRING_SIZE_TYPE; private: static DOCTEST_CONSTEXPR size_type len = 24; //!OCLINT avoid private static members static DOCTEST_CONSTEXPR size_type last = len - 1; //!OCLINT avoid private static members struct view // len should be more than sizeof(view) - because of the final byte for flags { char* ptr; size_type size; size_type capacity; }; union { char buf[len]; // NOLINT(*-avoid-c-arrays) view data; }; char* allocate(size_type sz); bool isOnStack() const noexcept { return (buf[last] & 128) == 0; } void setOnHeap() noexcept; void setLast(size_type in = last) noexcept; void setSize(size_type sz) noexcept; void copy(const String& other); public: static DOCTEST_CONSTEXPR size_type npos = static_cast(-1); String() noexcept; ~String(); // cppcheck-suppress noExplicitConstructor String(const char* in); String(const char* in, size_type in_size); String(std::istream& in, size_type in_size); String(const String& other); String& operator=(const String& other); String& operator+=(const String& other); String(String&& other) noexcept; String& operator=(String&& other) noexcept; char operator[](size_type i) const; char& operator[](size_type i); // the only functions I'm willing to leave in the interface - available for inlining const char* c_str() const { return const_cast(this)->c_str(); } // NOLINT char* c_str() { if (isOnStack()) { return reinterpret_cast(buf); } return data.ptr; } size_type size() const; size_type capacity() const; String substr(size_type pos, size_type cnt = npos) &&; String substr(size_type pos, size_type cnt = npos) const &; size_type find(char ch, size_type pos = 0) const; size_type rfind(char ch, size_type pos = npos) const; int compare(const char* other, bool no_case = false) const; int compare(const String& other, bool no_case = false) const; friend DOCTEST_INTERFACE std::ostream& operator<<(std::ostream& s, const String& in); }; DOCTEST_INTERFACE String operator+(const String& lhs, const String& rhs); DOCTEST_INTERFACE bool operator==(const String& lhs, const String& rhs); DOCTEST_INTERFACE bool operator!=(const String& lhs, const String& rhs); DOCTEST_INTERFACE bool operator<(const String& lhs, const String& rhs); DOCTEST_INTERFACE bool operator>(const String& lhs, const String& rhs); DOCTEST_INTERFACE bool operator<=(const String& lhs, const String& rhs); DOCTEST_INTERFACE bool operator>=(const String& lhs, const String& rhs); class DOCTEST_INTERFACE Contains { public: explicit Contains(const String& string); bool checkWith(const String& other) const; String string; }; DOCTEST_INTERFACE String toString(const Contains& in); DOCTEST_INTERFACE bool operator==(const String& lhs, const Contains& rhs); DOCTEST_INTERFACE bool operator==(const Contains& lhs, const String& rhs); DOCTEST_INTERFACE bool operator!=(const String& lhs, const Contains& rhs); DOCTEST_INTERFACE bool operator!=(const Contains& lhs, const String& rhs); namespace Color { enum Enum { None = 0, White, Red, Green, Blue, Cyan, Yellow, Grey, Bright = 0x10, BrightRed = Bright | Red, BrightGreen = Bright | Green, LightGrey = Bright | Grey, BrightWhite = Bright | White }; DOCTEST_INTERFACE std::ostream& operator<<(std::ostream& s, Color::Enum code); } // namespace Color namespace assertType { enum Enum { // macro traits is_warn = 1, is_check = 2 * is_warn, is_require = 2 * is_check, is_normal = 2 * is_require, is_throws = 2 * is_normal, is_throws_as = 2 * is_throws, is_throws_with = 2 * is_throws_as, is_nothrow = 2 * is_throws_with, is_false = 2 * is_nothrow, is_unary = 2 * is_false, // not checked anywhere - used just to distinguish the types is_eq = 2 * is_unary, is_ne = 2 * is_eq, is_lt = 2 * is_ne, is_gt = 2 * is_lt, is_ge = 2 * is_gt, is_le = 2 * is_ge, // macro types DT_WARN = is_normal | is_warn, DT_CHECK = is_normal | is_check, DT_REQUIRE = is_normal | is_require, DT_WARN_FALSE = is_normal | is_false | is_warn, DT_CHECK_FALSE = is_normal | is_false | is_check, DT_REQUIRE_FALSE = is_normal | is_false | is_require, DT_WARN_THROWS = is_throws | is_warn, DT_CHECK_THROWS = is_throws | is_check, DT_REQUIRE_THROWS = is_throws | is_require, DT_WARN_THROWS_AS = is_throws_as | is_warn, DT_CHECK_THROWS_AS = is_throws_as | is_check, DT_REQUIRE_THROWS_AS = is_throws_as | is_require, DT_WARN_THROWS_WITH = is_throws_with | is_warn, DT_CHECK_THROWS_WITH = is_throws_with | is_check, DT_REQUIRE_THROWS_WITH = is_throws_with | is_require, DT_WARN_THROWS_WITH_AS = is_throws_with | is_throws_as | is_warn, DT_CHECK_THROWS_WITH_AS = is_throws_with | is_throws_as | is_check, DT_REQUIRE_THROWS_WITH_AS = is_throws_with | is_throws_as | is_require, DT_WARN_NOTHROW = is_nothrow | is_warn, DT_CHECK_NOTHROW = is_nothrow | is_check, DT_REQUIRE_NOTHROW = is_nothrow | is_require, DT_WARN_EQ = is_normal | is_eq | is_warn, DT_CHECK_EQ = is_normal | is_eq | is_check, DT_REQUIRE_EQ = is_normal | is_eq | is_require, DT_WARN_NE = is_normal | is_ne | is_warn, DT_CHECK_NE = is_normal | is_ne | is_check, DT_REQUIRE_NE = is_normal | is_ne | is_require, DT_WARN_GT = is_normal | is_gt | is_warn, DT_CHECK_GT = is_normal | is_gt | is_check, DT_REQUIRE_GT = is_normal | is_gt | is_require, DT_WARN_LT = is_normal | is_lt | is_warn, DT_CHECK_LT = is_normal | is_lt | is_check, DT_REQUIRE_LT = is_normal | is_lt | is_require, DT_WARN_GE = is_normal | is_ge | is_warn, DT_CHECK_GE = is_normal | is_ge | is_check, DT_REQUIRE_GE = is_normal | is_ge | is_require, DT_WARN_LE = is_normal | is_le | is_warn, DT_CHECK_LE = is_normal | is_le | is_check, DT_REQUIRE_LE = is_normal | is_le | is_require, DT_WARN_UNARY = is_normal | is_unary | is_warn, DT_CHECK_UNARY = is_normal | is_unary | is_check, DT_REQUIRE_UNARY = is_normal | is_unary | is_require, DT_WARN_UNARY_FALSE = is_normal | is_false | is_unary | is_warn, DT_CHECK_UNARY_FALSE = is_normal | is_false | is_unary | is_check, DT_REQUIRE_UNARY_FALSE = is_normal | is_false | is_unary | is_require, }; } // namespace assertType DOCTEST_INTERFACE const char* assertString(assertType::Enum at); DOCTEST_INTERFACE const char* failureString(assertType::Enum at); DOCTEST_INTERFACE const char* skipPathFromFilename(const char* file); struct DOCTEST_INTERFACE TestCaseData { String m_file; // the file in which the test was registered (using String - see #350) unsigned m_line; // the line where the test was registered const char* m_name; // name of the test case const char* m_test_suite; // the test suite in which the test was added const char* m_description; bool m_skip; bool m_no_breaks; bool m_no_output; bool m_may_fail; bool m_should_fail; int m_expected_failures; double m_timeout; }; struct DOCTEST_INTERFACE AssertData { // common - for all asserts const TestCaseData* m_test_case; assertType::Enum m_at; const char* m_file; int m_line; const char* m_expr; bool m_failed; // exception-related - for all asserts bool m_threw; String m_exception; // for normal asserts String m_decomp; // for specific exception-related asserts bool m_threw_as; const char* m_exception_type; class DOCTEST_INTERFACE StringContains { private: Contains content; bool isContains; public: StringContains(const String& str) : content(str), isContains(false) { } StringContains(Contains cntn) : content(static_cast(cntn)), isContains(true) { } bool check(const String& str) { return isContains ? (content == str) : (content.string == str); } operator const String&() const { return content.string; } const char* c_str() const { return content.string.c_str(); } } m_exception_string; AssertData(assertType::Enum at, const char* file, int line, const char* expr, const char* exception_type, const StringContains& exception_string); }; struct DOCTEST_INTERFACE MessageData { String m_string; const char* m_file; int m_line; assertType::Enum m_severity; }; struct DOCTEST_INTERFACE SubcaseSignature { String m_name; const char* m_file; int m_line; bool operator==(const SubcaseSignature& other) const; bool operator<(const SubcaseSignature& other) const; }; struct DOCTEST_INTERFACE IContextScope { DOCTEST_DECLARE_INTERFACE(IContextScope) virtual void stringify(std::ostream*) const = 0; }; namespace detail { struct DOCTEST_INTERFACE TestCase; } // namespace detail struct ContextOptions //!OCLINT too many fields { std::ostream* cout = nullptr; // stdout stream String binary_name; // the test binary name const detail::TestCase* currentTest = nullptr; // == parameters from the command line String out; // output filename String order_by; // how tests should be ordered unsigned rand_seed; // the seed for rand ordering unsigned first; // the first (matching) test to be executed unsigned last; // the last (matching) test to be executed int abort_after; // stop tests after this many failed assertions int subcase_filter_levels; // apply the subcase filters for the first N levels bool success; // include successful assertions in output bool case_sensitive; // if filtering should be case sensitive bool exit; // if the program should be exited after the tests are ran/whatever bool duration; // print the time duration of each test case bool minimal; // minimal console output (only test failures) bool quiet; // no console output bool no_throw; // to skip exceptions-related assertion macros bool no_exitcode; // if the framework should return 0 as the exitcode bool no_run; // to not run the tests at all (can be done with an "*" exclude) bool no_intro; // to not print the intro of the framework bool no_version; // to not print the version of the framework bool no_colors; // if output to the console should be colorized bool force_colors; // forces the use of colors even when a tty cannot be detected bool no_breaks; // to not break into the debugger bool no_skip; // don't skip test cases which are marked to be skipped bool gnu_file_line; // if line numbers should be surrounded with :x: and not (x): bool no_path_in_filenames; // if the path to files should be removed from the output bool no_line_numbers; // if source code line numbers should be omitted from the output bool no_debug_output; // no output in the debug console when a debugger is attached bool no_skipped_summary; // don't print "skipped" in the summary !!! UNDOCUMENTED !!! bool no_time_in_output; // omit any time/timestamps from output !!! UNDOCUMENTED !!! bool help; // to print the help bool version; // to print the version bool count; // if only the count of matching tests is to be retrieved bool list_test_cases; // to list all tests matching the filters bool list_test_suites; // to list all suites matching the filters bool list_reporters; // lists all registered reporters }; namespace detail { namespace types { #ifdef DOCTEST_CONFIG_INCLUDE_TYPE_TRAITS using namespace std; #else template struct enable_if { }; template struct enable_if { using type = T; }; struct true_type { static DOCTEST_CONSTEXPR bool value = true; }; struct false_type { static DOCTEST_CONSTEXPR bool value = false; }; template struct remove_reference { using type = T; }; template struct remove_reference { using type = T; }; template struct remove_reference { using type = T; }; template struct is_rvalue_reference : false_type { }; template struct is_rvalue_reference : true_type { }; template struct remove_const { using type = T; }; template struct remove_const { using type = T; }; // Compiler intrinsics template struct is_enum { static DOCTEST_CONSTEXPR bool value = __is_enum(T); }; template struct underlying_type { using type = __underlying_type(T); }; template struct is_pointer : false_type { }; template struct is_pointer : true_type { }; template struct is_array : false_type { }; // NOLINTNEXTLINE(*-avoid-c-arrays) template struct is_array : true_type { }; #endif } // template T&& declval(); template DOCTEST_CONSTEXPR_FUNC T&& forward(typename types::remove_reference::type& t) DOCTEST_NOEXCEPT { return static_cast(t); } template DOCTEST_CONSTEXPR_FUNC T&& forward(typename types::remove_reference::type&& t) DOCTEST_NOEXCEPT { return static_cast(t); } template struct deferred_false : types::false_type { }; // MSVS 2015 :( #if !DOCTEST_CLANG && defined(_MSC_VER) && _MSC_VER <= 1900 template struct has_global_insertion_operator : types::false_type { }; template struct has_global_insertion_operator(), declval()), void())> : types::true_type { }; template struct has_insertion_operator { static DOCTEST_CONSTEXPR bool value = has_global_insertion_operator::value; }; template struct insert_hack; template struct insert_hack { static void insert(std::ostream& os, const T& t) { ::operator<<(os, t); } }; template struct insert_hack { static void insert(std::ostream& os, const T& t) { operator<<(os, t); } }; template using insert_hack_t = insert_hack::value>; #else template struct has_insertion_operator : types::false_type { }; #endif template struct has_insertion_operator(), declval()), void())> : types::true_type { }; template struct should_stringify_as_underlying_type { static DOCTEST_CONSTEXPR bool value = detail::types::is_enum::value && !doctest::detail::has_insertion_operator::value; }; DOCTEST_INTERFACE std::ostream* tlssPush(); DOCTEST_INTERFACE String tlssPop(); template struct StringMakerBase { template static String convert(const DOCTEST_REF_WRAP(T)) { #ifdef DOCTEST_CONFIG_REQUIRE_STRINGIFICATION_FOR_ALL_USED_TYPES static_assert(deferred_false::value, "No stringification detected for type T. See string conversion manual"); #endif return "{?}"; } }; template struct filldata; template void filloss(std::ostream* stream, const T& in) { filldata::fill(stream, in); } template void filloss(std::ostream* stream, const T (&in)[N]) { // NOLINT(*-avoid-c-arrays) // T[N], T(&)[N], T(&&)[N] have same behaviour. // Hence remove reference. filloss::type>(stream, in); } template String toStream(const T& in) { std::ostream* stream = tlssPush(); filloss(stream, in); return tlssPop(); } template <> struct StringMakerBase { template static String convert(const DOCTEST_REF_WRAP(T) in) { return toStream(in); } }; } // namespace detail template struct StringMaker : public detail::StringMakerBase< detail::has_insertion_operator::value || detail::types::is_pointer::value || detail::types::is_array::value> {}; #ifndef DOCTEST_STRINGIFY #ifdef DOCTEST_CONFIG_DOUBLE_STRINGIFY #define DOCTEST_STRINGIFY(...) toString(toString(__VA_ARGS__)) #else #define DOCTEST_STRINGIFY(...) toString(__VA_ARGS__) #endif #endif template String toString() { #if DOCTEST_CLANG == 0 && DOCTEST_GCC == 0 && DOCTEST_ICC == 0 String ret = __FUNCSIG__; // class doctest::String __cdecl doctest::toString(void) String::size_type beginPos = ret.find('<'); return ret.substr(beginPos + 1, ret.size() - beginPos - static_cast(sizeof(">(void)"))); #else String ret = __PRETTY_FUNCTION__; // doctest::String toString() [with T = TYPE] String::size_type begin = ret.find('=') + 2; return ret.substr(begin, ret.size() - begin - 1); #endif } template ::value, bool>::type = true> String toString(const DOCTEST_REF_WRAP(T) value) { return StringMaker::convert(value); } #ifdef DOCTEST_CONFIG_TREAT_CHAR_STAR_AS_STRING DOCTEST_INTERFACE String toString(const char* in); #endif // DOCTEST_CONFIG_TREAT_CHAR_STAR_AS_STRING #if DOCTEST_MSVC >= DOCTEST_COMPILER(19, 20, 0) // see this issue on why this is needed: https://github.com/doctest/doctest/issues/183 DOCTEST_INTERFACE String toString(const std::string& in); #endif // VS 2019 DOCTEST_INTERFACE String toString(String in); DOCTEST_INTERFACE String toString(std::nullptr_t); DOCTEST_INTERFACE String toString(bool in); DOCTEST_INTERFACE String toString(float in); DOCTEST_INTERFACE String toString(double in); DOCTEST_INTERFACE String toString(double long in); DOCTEST_INTERFACE String toString(char in); DOCTEST_INTERFACE String toString(char signed in); DOCTEST_INTERFACE String toString(char unsigned in); DOCTEST_INTERFACE String toString(short in); DOCTEST_INTERFACE String toString(short unsigned in); DOCTEST_INTERFACE String toString(signed in); DOCTEST_INTERFACE String toString(unsigned in); DOCTEST_INTERFACE String toString(long in); DOCTEST_INTERFACE String toString(long unsigned in); DOCTEST_INTERFACE String toString(long long in); DOCTEST_INTERFACE String toString(long long unsigned in); template ::value, bool>::type = true> String toString(const DOCTEST_REF_WRAP(T) value) { using UT = typename detail::types::underlying_type::type; return (DOCTEST_STRINGIFY(static_cast(value))); } namespace detail { template struct filldata { static void fill(std::ostream* stream, const T& in) { #if defined(_MSC_VER) && _MSC_VER <= 1900 insert_hack_t::insert(*stream, in); #else operator<<(*stream, in); #endif } }; DOCTEST_MSVC_SUPPRESS_WARNING_WITH_PUSH(4866) // NOLINTBEGIN(*-avoid-c-arrays) template struct filldata { static void fill(std::ostream* stream, const T(&in)[N]) { *stream << "["; for (size_t i = 0; i < N; i++) { if (i != 0) { *stream << ", "; } *stream << (DOCTEST_STRINGIFY(in[i])); } *stream << "]"; } }; // NOLINTEND(*-avoid-c-arrays) DOCTEST_MSVC_SUPPRESS_WARNING_POP // Specialized since we don't want the terminating null byte! // NOLINTBEGIN(*-avoid-c-arrays) template struct filldata { static void fill(std::ostream* stream, const char (&in)[N]) { *stream << String(in, in[N - 1] ? N : N - 1); } // NOLINT(clang-analyzer-cplusplus.NewDeleteLeaks) }; // NOLINTEND(*-avoid-c-arrays) template <> struct filldata { static void fill(std::ostream* stream, const void* in); }; template struct filldata { DOCTEST_MSVC_SUPPRESS_WARNING_WITH_PUSH(4180) static void fill(std::ostream* stream, const T* in) { DOCTEST_MSVC_SUPPRESS_WARNING_POP DOCTEST_CLANG_SUPPRESS_WARNING_WITH_PUSH("-Wmicrosoft-cast") filldata::fill(stream, #if DOCTEST_GCC == 0 || DOCTEST_GCC >= DOCTEST_COMPILER(4, 9, 0) reinterpret_cast(in) #else *reinterpret_cast(&in) #endif ); DOCTEST_CLANG_SUPPRESS_WARNING_POP } }; } struct DOCTEST_INTERFACE Approx { Approx(double value); Approx operator()(double value) const; #ifdef DOCTEST_CONFIG_INCLUDE_TYPE_TRAITS template explicit Approx(const T& value, typename detail::types::enable_if::value>::type* = static_cast(nullptr)) { *this = static_cast(value); } #endif // DOCTEST_CONFIG_INCLUDE_TYPE_TRAITS Approx& epsilon(double newEpsilon); #ifdef DOCTEST_CONFIG_INCLUDE_TYPE_TRAITS template typename std::enable_if::value, Approx&>::type epsilon( const T& newEpsilon) { m_epsilon = static_cast(newEpsilon); return *this; } #endif // DOCTEST_CONFIG_INCLUDE_TYPE_TRAITS Approx& scale(double newScale); #ifdef DOCTEST_CONFIG_INCLUDE_TYPE_TRAITS template typename std::enable_if::value, Approx&>::type scale( const T& newScale) { m_scale = static_cast(newScale); return *this; } #endif // DOCTEST_CONFIG_INCLUDE_TYPE_TRAITS // clang-format off DOCTEST_INTERFACE friend bool operator==(double lhs, const Approx & rhs); DOCTEST_INTERFACE friend bool operator==(const Approx & lhs, double rhs); DOCTEST_INTERFACE friend bool operator!=(double lhs, const Approx & rhs); DOCTEST_INTERFACE friend bool operator!=(const Approx & lhs, double rhs); DOCTEST_INTERFACE friend bool operator<=(double lhs, const Approx & rhs); DOCTEST_INTERFACE friend bool operator<=(const Approx & lhs, double rhs); DOCTEST_INTERFACE friend bool operator>=(double lhs, const Approx & rhs); DOCTEST_INTERFACE friend bool operator>=(const Approx & lhs, double rhs); DOCTEST_INTERFACE friend bool operator< (double lhs, const Approx & rhs); DOCTEST_INTERFACE friend bool operator< (const Approx & lhs, double rhs); DOCTEST_INTERFACE friend bool operator> (double lhs, const Approx & rhs); DOCTEST_INTERFACE friend bool operator> (const Approx & lhs, double rhs); #ifdef DOCTEST_CONFIG_INCLUDE_TYPE_TRAITS #define DOCTEST_APPROX_PREFIX \ template friend typename std::enable_if::value, bool>::type DOCTEST_APPROX_PREFIX operator==(const T& lhs, const Approx& rhs) { return operator==(static_cast(lhs), rhs); } DOCTEST_APPROX_PREFIX operator==(const Approx& lhs, const T& rhs) { return operator==(rhs, lhs); } DOCTEST_APPROX_PREFIX operator!=(const T& lhs, const Approx& rhs) { return !operator==(lhs, rhs); } DOCTEST_APPROX_PREFIX operator!=(const Approx& lhs, const T& rhs) { return !operator==(rhs, lhs); } DOCTEST_APPROX_PREFIX operator<=(const T& lhs, const Approx& rhs) { return static_cast(lhs) < rhs.m_value || lhs == rhs; } DOCTEST_APPROX_PREFIX operator<=(const Approx& lhs, const T& rhs) { return lhs.m_value < static_cast(rhs) || lhs == rhs; } DOCTEST_APPROX_PREFIX operator>=(const T& lhs, const Approx& rhs) { return static_cast(lhs) > rhs.m_value || lhs == rhs; } DOCTEST_APPROX_PREFIX operator>=(const Approx& lhs, const T& rhs) { return lhs.m_value > static_cast(rhs) || lhs == rhs; } DOCTEST_APPROX_PREFIX operator< (const T& lhs, const Approx& rhs) { return static_cast(lhs) < rhs.m_value && lhs != rhs; } DOCTEST_APPROX_PREFIX operator< (const Approx& lhs, const T& rhs) { return lhs.m_value < static_cast(rhs) && lhs != rhs; } DOCTEST_APPROX_PREFIX operator> (const T& lhs, const Approx& rhs) { return static_cast(lhs) > rhs.m_value && lhs != rhs; } DOCTEST_APPROX_PREFIX operator> (const Approx& lhs, const T& rhs) { return lhs.m_value > static_cast(rhs) && lhs != rhs; } #undef DOCTEST_APPROX_PREFIX #endif // DOCTEST_CONFIG_INCLUDE_TYPE_TRAITS // clang-format on double m_epsilon; double m_scale; double m_value; }; DOCTEST_INTERFACE String toString(const Approx& in); DOCTEST_INTERFACE const ContextOptions* getContextOptions(); template struct DOCTEST_INTERFACE_DECL IsNaN { F value; bool flipped; IsNaN(F f, bool flip = false) : value(f), flipped(flip) { } IsNaN operator!() const { return { value, !flipped }; } operator bool() const; }; #ifndef __MINGW32__ extern template struct DOCTEST_INTERFACE_DECL IsNaN; extern template struct DOCTEST_INTERFACE_DECL IsNaN; extern template struct DOCTEST_INTERFACE_DECL IsNaN; #endif DOCTEST_INTERFACE String toString(IsNaN in); DOCTEST_INTERFACE String toString(IsNaN in); DOCTEST_INTERFACE String toString(IsNaN in); #ifndef DOCTEST_CONFIG_DISABLE namespace detail { // clang-format off #ifdef DOCTEST_CONFIG_TREAT_CHAR_STAR_AS_STRING template struct decay_array { using type = T; }; template struct decay_array { using type = T*; }; template struct decay_array { using type = T*; }; template struct not_char_pointer { static DOCTEST_CONSTEXPR int value = 1; }; template<> struct not_char_pointer { static DOCTEST_CONSTEXPR int value = 0; }; template<> struct not_char_pointer { static DOCTEST_CONSTEXPR int value = 0; }; template struct can_use_op : public not_char_pointer::type> {}; #endif // DOCTEST_CONFIG_TREAT_CHAR_STAR_AS_STRING // clang-format on struct DOCTEST_INTERFACE TestFailureException { }; DOCTEST_INTERFACE bool checkIfShouldThrow(assertType::Enum at); #ifndef DOCTEST_CONFIG_NO_EXCEPTIONS DOCTEST_NORETURN #endif // DOCTEST_CONFIG_NO_EXCEPTIONS DOCTEST_INTERFACE void throwException(); struct DOCTEST_INTERFACE Subcase { SubcaseSignature m_signature; bool m_entered = false; Subcase(const String& name, const char* file, int line); Subcase(const Subcase&) = delete; Subcase(Subcase&&) = delete; Subcase& operator=(const Subcase&) = delete; Subcase& operator=(Subcase&&) = delete; ~Subcase(); operator bool() const; private: bool checkFilters(); }; template String stringifyBinaryExpr(const DOCTEST_REF_WRAP(L) lhs, const char* op, const DOCTEST_REF_WRAP(R) rhs) { return (DOCTEST_STRINGIFY(lhs)) + op + (DOCTEST_STRINGIFY(rhs)); } #if DOCTEST_CLANG && DOCTEST_CLANG < DOCTEST_COMPILER(3, 6, 0) DOCTEST_CLANG_SUPPRESS_WARNING_WITH_PUSH("-Wunused-comparison") #endif // This will check if there is any way it could find a operator like member or friend and uses it. // If not it doesn't find the operator or if the operator at global scope is defined after // this template, the template won't be instantiated due to SFINAE. Once the template is not // instantiated it can look for global operator using normal conversions. #ifdef __NVCC__ #define SFINAE_OP(ret,op) ret #else #define SFINAE_OP(ret,op) decltype((void)(doctest::detail::declval() op doctest::detail::declval()),ret{}) #endif #define DOCTEST_DO_BINARY_EXPRESSION_COMPARISON(op, op_str, op_macro) \ template \ DOCTEST_NOINLINE SFINAE_OP(Result,op) operator op(R&& rhs) { \ bool res = op_macro(doctest::detail::forward(lhs), doctest::detail::forward(rhs)); \ if(m_at & assertType::is_false) \ res = !res; \ if(!res || doctest::getContextOptions()->success) \ return Result(res, stringifyBinaryExpr(lhs, op_str, rhs)); \ return Result(res); \ } // more checks could be added - like in Catch: // https://github.com/catchorg/Catch2/pull/1480/files // https://github.com/catchorg/Catch2/pull/1481/files #define DOCTEST_FORBIT_EXPRESSION(rt, op) \ template \ rt& operator op(const R&) { \ static_assert(deferred_false::value, \ "Expression Too Complex Please Rewrite As Binary Comparison!"); \ return *this; \ } struct DOCTEST_INTERFACE Result // NOLINT(*-member-init) { bool m_passed; String m_decomp; Result() = default; // TODO: Why do we need this? (To remove NOLINT) Result(bool passed, const String& decomposition = String()); // forbidding some expressions based on this table: https://en.cppreference.com/w/cpp/language/operator_precedence DOCTEST_FORBIT_EXPRESSION(Result, &) DOCTEST_FORBIT_EXPRESSION(Result, ^) DOCTEST_FORBIT_EXPRESSION(Result, |) DOCTEST_FORBIT_EXPRESSION(Result, &&) DOCTEST_FORBIT_EXPRESSION(Result, ||) DOCTEST_FORBIT_EXPRESSION(Result, ==) DOCTEST_FORBIT_EXPRESSION(Result, !=) DOCTEST_FORBIT_EXPRESSION(Result, <) DOCTEST_FORBIT_EXPRESSION(Result, >) DOCTEST_FORBIT_EXPRESSION(Result, <=) DOCTEST_FORBIT_EXPRESSION(Result, >=) DOCTEST_FORBIT_EXPRESSION(Result, =) DOCTEST_FORBIT_EXPRESSION(Result, +=) DOCTEST_FORBIT_EXPRESSION(Result, -=) DOCTEST_FORBIT_EXPRESSION(Result, *=) DOCTEST_FORBIT_EXPRESSION(Result, /=) DOCTEST_FORBIT_EXPRESSION(Result, %=) DOCTEST_FORBIT_EXPRESSION(Result, <<=) DOCTEST_FORBIT_EXPRESSION(Result, >>=) DOCTEST_FORBIT_EXPRESSION(Result, &=) DOCTEST_FORBIT_EXPRESSION(Result, ^=) DOCTEST_FORBIT_EXPRESSION(Result, |=) }; #ifndef DOCTEST_CONFIG_NO_COMPARISON_WARNING_SUPPRESSION DOCTEST_CLANG_SUPPRESS_WARNING_PUSH DOCTEST_CLANG_SUPPRESS_WARNING("-Wsign-conversion") DOCTEST_CLANG_SUPPRESS_WARNING("-Wsign-compare") //DOCTEST_CLANG_SUPPRESS_WARNING("-Wdouble-promotion") //DOCTEST_CLANG_SUPPRESS_WARNING("-Wconversion") //DOCTEST_CLANG_SUPPRESS_WARNING("-Wfloat-equal") DOCTEST_GCC_SUPPRESS_WARNING_PUSH DOCTEST_GCC_SUPPRESS_WARNING("-Wsign-conversion") DOCTEST_GCC_SUPPRESS_WARNING("-Wsign-compare") //DOCTEST_GCC_SUPPRESS_WARNING("-Wdouble-promotion") //DOCTEST_GCC_SUPPRESS_WARNING("-Wconversion") //DOCTEST_GCC_SUPPRESS_WARNING("-Wfloat-equal") DOCTEST_MSVC_SUPPRESS_WARNING_PUSH // https://stackoverflow.com/questions/39479163 what's the difference between 4018 and 4389 DOCTEST_MSVC_SUPPRESS_WARNING(4388) // signed/unsigned mismatch DOCTEST_MSVC_SUPPRESS_WARNING(4389) // 'operator' : signed/unsigned mismatch DOCTEST_MSVC_SUPPRESS_WARNING(4018) // 'expression' : signed/unsigned mismatch //DOCTEST_MSVC_SUPPRESS_WARNING(4805) // 'operation' : unsafe mix of type 'type' and type 'type' in operation #endif // DOCTEST_CONFIG_NO_COMPARISON_WARNING_SUPPRESSION // clang-format off #ifndef DOCTEST_CONFIG_TREAT_CHAR_STAR_AS_STRING #define DOCTEST_COMPARISON_RETURN_TYPE bool #else // DOCTEST_CONFIG_TREAT_CHAR_STAR_AS_STRING #define DOCTEST_COMPARISON_RETURN_TYPE typename types::enable_if::value || can_use_op::value, bool>::type inline bool eq(const char* lhs, const char* rhs) { return String(lhs) == String(rhs); } inline bool ne(const char* lhs, const char* rhs) { return String(lhs) != String(rhs); } inline bool lt(const char* lhs, const char* rhs) { return String(lhs) < String(rhs); } inline bool gt(const char* lhs, const char* rhs) { return String(lhs) > String(rhs); } inline bool le(const char* lhs, const char* rhs) { return String(lhs) <= String(rhs); } inline bool ge(const char* lhs, const char* rhs) { return String(lhs) >= String(rhs); } #endif // DOCTEST_CONFIG_TREAT_CHAR_STAR_AS_STRING // clang-format on #define DOCTEST_RELATIONAL_OP(name, op) \ template \ DOCTEST_COMPARISON_RETURN_TYPE name(const DOCTEST_REF_WRAP(L) lhs, \ const DOCTEST_REF_WRAP(R) rhs) { \ return lhs op rhs; \ } DOCTEST_RELATIONAL_OP(eq, ==) DOCTEST_RELATIONAL_OP(ne, !=) DOCTEST_RELATIONAL_OP(lt, <) DOCTEST_RELATIONAL_OP(gt, >) DOCTEST_RELATIONAL_OP(le, <=) DOCTEST_RELATIONAL_OP(ge, >=) #ifndef DOCTEST_CONFIG_TREAT_CHAR_STAR_AS_STRING #define DOCTEST_CMP_EQ(l, r) l == r #define DOCTEST_CMP_NE(l, r) l != r #define DOCTEST_CMP_GT(l, r) l > r #define DOCTEST_CMP_LT(l, r) l < r #define DOCTEST_CMP_GE(l, r) l >= r #define DOCTEST_CMP_LE(l, r) l <= r #else // DOCTEST_CONFIG_TREAT_CHAR_STAR_AS_STRING #define DOCTEST_CMP_EQ(l, r) eq(l, r) #define DOCTEST_CMP_NE(l, r) ne(l, r) #define DOCTEST_CMP_GT(l, r) gt(l, r) #define DOCTEST_CMP_LT(l, r) lt(l, r) #define DOCTEST_CMP_GE(l, r) ge(l, r) #define DOCTEST_CMP_LE(l, r) le(l, r) #endif // DOCTEST_CONFIG_TREAT_CHAR_STAR_AS_STRING template // cppcheck-suppress copyCtorAndEqOperator struct Expression_lhs { L lhs; assertType::Enum m_at; explicit Expression_lhs(L&& in, assertType::Enum at) : lhs(static_cast(in)) , m_at(at) {} DOCTEST_NOINLINE operator Result() { // this is needed only for MSVC 2015 DOCTEST_MSVC_SUPPRESS_WARNING_WITH_PUSH(4800) // 'int': forcing value to bool bool res = static_cast(lhs); DOCTEST_MSVC_SUPPRESS_WARNING_POP if(m_at & assertType::is_false) { //!OCLINT bitwise operator in conditional res = !res; } if(!res || getContextOptions()->success) { return { res, (DOCTEST_STRINGIFY(lhs)) }; } return { res }; } /* This is required for user-defined conversions from Expression_lhs to L */ operator L() const { return lhs; } // clang-format off DOCTEST_DO_BINARY_EXPRESSION_COMPARISON(==, " == ", DOCTEST_CMP_EQ) //!OCLINT bitwise operator in conditional DOCTEST_DO_BINARY_EXPRESSION_COMPARISON(!=, " != ", DOCTEST_CMP_NE) //!OCLINT bitwise operator in conditional DOCTEST_DO_BINARY_EXPRESSION_COMPARISON(>, " > ", DOCTEST_CMP_GT) //!OCLINT bitwise operator in conditional DOCTEST_DO_BINARY_EXPRESSION_COMPARISON(<, " < ", DOCTEST_CMP_LT) //!OCLINT bitwise operator in conditional DOCTEST_DO_BINARY_EXPRESSION_COMPARISON(>=, " >= ", DOCTEST_CMP_GE) //!OCLINT bitwise operator in conditional DOCTEST_DO_BINARY_EXPRESSION_COMPARISON(<=, " <= ", DOCTEST_CMP_LE) //!OCLINT bitwise operator in conditional // clang-format on // forbidding some expressions based on this table: https://en.cppreference.com/w/cpp/language/operator_precedence DOCTEST_FORBIT_EXPRESSION(Expression_lhs, &) DOCTEST_FORBIT_EXPRESSION(Expression_lhs, ^) DOCTEST_FORBIT_EXPRESSION(Expression_lhs, |) DOCTEST_FORBIT_EXPRESSION(Expression_lhs, &&) DOCTEST_FORBIT_EXPRESSION(Expression_lhs, ||) DOCTEST_FORBIT_EXPRESSION(Expression_lhs, =) DOCTEST_FORBIT_EXPRESSION(Expression_lhs, +=) DOCTEST_FORBIT_EXPRESSION(Expression_lhs, -=) DOCTEST_FORBIT_EXPRESSION(Expression_lhs, *=) DOCTEST_FORBIT_EXPRESSION(Expression_lhs, /=) DOCTEST_FORBIT_EXPRESSION(Expression_lhs, %=) DOCTEST_FORBIT_EXPRESSION(Expression_lhs, <<=) DOCTEST_FORBIT_EXPRESSION(Expression_lhs, >>=) DOCTEST_FORBIT_EXPRESSION(Expression_lhs, &=) DOCTEST_FORBIT_EXPRESSION(Expression_lhs, ^=) DOCTEST_FORBIT_EXPRESSION(Expression_lhs, |=) // these 2 are unfortunate because they should be allowed - they have higher precedence over the comparisons, but the // ExpressionDecomposer class uses the left shift operator to capture the left operand of the binary expression... DOCTEST_FORBIT_EXPRESSION(Expression_lhs, <<) DOCTEST_FORBIT_EXPRESSION(Expression_lhs, >>) }; #ifndef DOCTEST_CONFIG_NO_COMPARISON_WARNING_SUPPRESSION DOCTEST_CLANG_SUPPRESS_WARNING_POP DOCTEST_MSVC_SUPPRESS_WARNING_POP DOCTEST_GCC_SUPPRESS_WARNING_POP #endif // DOCTEST_CONFIG_NO_COMPARISON_WARNING_SUPPRESSION #if DOCTEST_CLANG && DOCTEST_CLANG < DOCTEST_COMPILER(3, 6, 0) DOCTEST_CLANG_SUPPRESS_WARNING_POP #endif struct DOCTEST_INTERFACE ExpressionDecomposer { assertType::Enum m_at; ExpressionDecomposer(assertType::Enum at); // The right operator for capturing expressions is "<=" instead of "<<" (based on the operator precedence table) // but then there will be warnings from GCC about "-Wparentheses" and since "_Pragma()" is problematic this will stay for now... // https://github.com/catchorg/Catch2/issues/870 // https://github.com/catchorg/Catch2/issues/565 template Expression_lhs operator<<(L&& operand) { return Expression_lhs(static_cast(operand), m_at); } template ::value,void >::type* = nullptr> Expression_lhs operator<<(const L &operand) { return Expression_lhs(operand, m_at); } }; struct DOCTEST_INTERFACE TestSuite { const char* m_test_suite = nullptr; const char* m_description = nullptr; bool m_skip = false; bool m_no_breaks = false; bool m_no_output = false; bool m_may_fail = false; bool m_should_fail = false; int m_expected_failures = 0; double m_timeout = 0; TestSuite& operator*(const char* in); template TestSuite& operator*(const T& in) { in.fill(*this); return *this; } }; using funcType = void (*)(); struct DOCTEST_INTERFACE TestCase : public TestCaseData { funcType m_test; // a function pointer to the test case String m_type; // for templated test cases - gets appended to the real name int m_template_id; // an ID used to distinguish between the different versions of a templated test case String m_full_name; // contains the name (only for templated test cases!) + the template type TestCase(funcType test, const char* file, unsigned line, const TestSuite& test_suite, const String& type = String(), int template_id = -1); TestCase(const TestCase& other); TestCase(TestCase&&) = delete; DOCTEST_MSVC_SUPPRESS_WARNING_WITH_PUSH(26434) // hides a non-virtual function TestCase& operator=(const TestCase& other); DOCTEST_MSVC_SUPPRESS_WARNING_POP TestCase& operator=(TestCase&&) = delete; TestCase& operator*(const char* in); template TestCase& operator*(const T& in) { in.fill(*this); return *this; } bool operator<(const TestCase& other) const; ~TestCase() = default; }; // forward declarations of functions used by the macros DOCTEST_INTERFACE int regTest(const TestCase& tc); DOCTEST_INTERFACE int setTestSuite(const TestSuite& ts); DOCTEST_INTERFACE bool isDebuggerActive(); template int instantiationHelper(const T&) { return 0; } namespace binaryAssertComparison { enum Enum { eq = 0, ne, gt, lt, ge, le }; } // namespace binaryAssertComparison // clang-format off template struct RelationalComparator { bool operator()(const DOCTEST_REF_WRAP(L), const DOCTEST_REF_WRAP(R) ) const { return false; } }; #define DOCTEST_BINARY_RELATIONAL_OP(n, op) \ template struct RelationalComparator { bool operator()(const DOCTEST_REF_WRAP(L) lhs, const DOCTEST_REF_WRAP(R) rhs) const { return op(lhs, rhs); } }; // clang-format on DOCTEST_BINARY_RELATIONAL_OP(0, doctest::detail::eq) DOCTEST_BINARY_RELATIONAL_OP(1, doctest::detail::ne) DOCTEST_BINARY_RELATIONAL_OP(2, doctest::detail::gt) DOCTEST_BINARY_RELATIONAL_OP(3, doctest::detail::lt) DOCTEST_BINARY_RELATIONAL_OP(4, doctest::detail::ge) DOCTEST_BINARY_RELATIONAL_OP(5, doctest::detail::le) struct DOCTEST_INTERFACE ResultBuilder : public AssertData { ResultBuilder(assertType::Enum at, const char* file, int line, const char* expr, const char* exception_type = "", const String& exception_string = ""); ResultBuilder(assertType::Enum at, const char* file, int line, const char* expr, const char* exception_type, const Contains& exception_string); void setResult(const Result& res); template DOCTEST_NOINLINE bool binary_assert(const DOCTEST_REF_WRAP(L) lhs, const DOCTEST_REF_WRAP(R) rhs) { m_failed = !RelationalComparator()(lhs, rhs); if (m_failed || getContextOptions()->success) { m_decomp = stringifyBinaryExpr(lhs, ", ", rhs); } return !m_failed; } template DOCTEST_NOINLINE bool unary_assert(const DOCTEST_REF_WRAP(L) val) { m_failed = !val; if (m_at & assertType::is_false) { //!OCLINT bitwise operator in conditional m_failed = !m_failed; } if (m_failed || getContextOptions()->success) { m_decomp = (DOCTEST_STRINGIFY(val)); } return !m_failed; } void translateException(); bool log(); void react() const; }; namespace assertAction { enum Enum { nothing = 0, dbgbreak = 1, shouldthrow = 2 }; } // namespace assertAction DOCTEST_INTERFACE void failed_out_of_a_testing_context(const AssertData& ad); DOCTEST_INTERFACE bool decomp_assert(assertType::Enum at, const char* file, int line, const char* expr, const Result& result); #define DOCTEST_ASSERT_OUT_OF_TESTS(decomp) \ do { \ if(!is_running_in_test) { \ if(failed) { \ ResultBuilder rb(at, file, line, expr); \ rb.m_failed = failed; \ rb.m_decomp = decomp; \ failed_out_of_a_testing_context(rb); \ if(isDebuggerActive() && !getContextOptions()->no_breaks) \ DOCTEST_BREAK_INTO_DEBUGGER(); \ if(checkIfShouldThrow(at)) \ throwException(); \ } \ return !failed; \ } \ } while(false) #define DOCTEST_ASSERT_IN_TESTS(decomp) \ ResultBuilder rb(at, file, line, expr); \ rb.m_failed = failed; \ if(rb.m_failed || getContextOptions()->success) \ rb.m_decomp = decomp; \ if(rb.log()) \ DOCTEST_BREAK_INTO_DEBUGGER(); \ if(rb.m_failed && checkIfShouldThrow(at)) \ throwException() template DOCTEST_NOINLINE bool binary_assert(assertType::Enum at, const char* file, int line, const char* expr, const DOCTEST_REF_WRAP(L) lhs, const DOCTEST_REF_WRAP(R) rhs) { bool failed = !RelationalComparator()(lhs, rhs); // ################################################################################### // IF THE DEBUGGER BREAKS HERE - GO 1 LEVEL UP IN THE CALLSTACK FOR THE FAILING ASSERT // THIS IS THE EFFECT OF HAVING 'DOCTEST_CONFIG_SUPER_FAST_ASSERTS' DEFINED // ################################################################################### DOCTEST_ASSERT_OUT_OF_TESTS(stringifyBinaryExpr(lhs, ", ", rhs)); DOCTEST_ASSERT_IN_TESTS(stringifyBinaryExpr(lhs, ", ", rhs)); return !failed; } template DOCTEST_NOINLINE bool unary_assert(assertType::Enum at, const char* file, int line, const char* expr, const DOCTEST_REF_WRAP(L) val) { bool failed = !val; if(at & assertType::is_false) //!OCLINT bitwise operator in conditional failed = !failed; // ################################################################################### // IF THE DEBUGGER BREAKS HERE - GO 1 LEVEL UP IN THE CALLSTACK FOR THE FAILING ASSERT // THIS IS THE EFFECT OF HAVING 'DOCTEST_CONFIG_SUPER_FAST_ASSERTS' DEFINED // ################################################################################### DOCTEST_ASSERT_OUT_OF_TESTS((DOCTEST_STRINGIFY(val))); DOCTEST_ASSERT_IN_TESTS((DOCTEST_STRINGIFY(val))); return !failed; } struct DOCTEST_INTERFACE IExceptionTranslator { DOCTEST_DECLARE_INTERFACE(IExceptionTranslator) virtual bool translate(String&) const = 0; }; template class ExceptionTranslator : public IExceptionTranslator //!OCLINT destructor of virtual class { public: explicit ExceptionTranslator(String (*translateFunction)(T)) : m_translateFunction(translateFunction) {} bool translate(String& res) const override { #ifndef DOCTEST_CONFIG_NO_EXCEPTIONS try { throw; // lgtm [cpp/rethrow-no-exception] // cppcheck-suppress catchExceptionByValue } catch(const T& ex) { res = m_translateFunction(ex); //!OCLINT parameter reassignment return true; } catch(...) {} //!OCLINT - empty catch statement #endif // DOCTEST_CONFIG_NO_EXCEPTIONS static_cast(res); // to silence -Wunused-parameter return false; } private: String (*m_translateFunction)(T); }; DOCTEST_INTERFACE void registerExceptionTranslatorImpl(const IExceptionTranslator* et); // ContextScope base class used to allow implementing methods of ContextScope // that don't depend on the template parameter in doctest.cpp. struct DOCTEST_INTERFACE ContextScopeBase : public IContextScope { ContextScopeBase(const ContextScopeBase&) = delete; ContextScopeBase& operator=(const ContextScopeBase&) = delete; ContextScopeBase& operator=(ContextScopeBase&&) = delete; ~ContextScopeBase() override = default; protected: ContextScopeBase(); ContextScopeBase(ContextScopeBase&& other) noexcept; void destroy(); bool need_to_destroy{true}; }; template class ContextScope : public ContextScopeBase { L lambda_; public: explicit ContextScope(const L &lambda) : lambda_(lambda) {} explicit ContextScope(L&& lambda) : lambda_(static_cast(lambda)) { } ContextScope(const ContextScope&) = delete; ContextScope(ContextScope&&) noexcept = default; ContextScope& operator=(const ContextScope&) = delete; ContextScope& operator=(ContextScope&&) = delete; void stringify(std::ostream* s) const override { lambda_(s); } ~ContextScope() override { if (need_to_destroy) { destroy(); } } }; struct DOCTEST_INTERFACE MessageBuilder : public MessageData { std::ostream* m_stream; bool logged = false; MessageBuilder(const char* file, int line, assertType::Enum severity); MessageBuilder(const MessageBuilder&) = delete; MessageBuilder(MessageBuilder&&) = delete; MessageBuilder& operator=(const MessageBuilder&) = delete; MessageBuilder& operator=(MessageBuilder&&) = delete; ~MessageBuilder(); // the preferred way of chaining parameters for stringification DOCTEST_MSVC_SUPPRESS_WARNING_WITH_PUSH(4866) template MessageBuilder& operator,(const T& in) { *m_stream << (DOCTEST_STRINGIFY(in)); return *this; } DOCTEST_MSVC_SUPPRESS_WARNING_POP // kept here just for backwards-compatibility - the comma operator should be preferred now template MessageBuilder& operator<<(const T& in) { return this->operator,(in); } // the `,` operator has the lowest operator precedence - if `<<` is used by the user then // the `,` operator will be called last which is not what we want and thus the `*` operator // is used first (has higher operator precedence compared to `<<`) so that we guarantee that // an operator of the MessageBuilder class is called first before the rest of the parameters template MessageBuilder& operator*(const T& in) { return this->operator,(in); } bool log(); void react(); }; template ContextScope MakeContextScope(const L &lambda) { return ContextScope(lambda); } } // namespace detail #define DOCTEST_DEFINE_DECORATOR(name, type, def) \ struct name \ { \ type data; \ name(type in = def) \ : data(in) {} \ void fill(detail::TestCase& state) const { state.DOCTEST_CAT(m_, name) = data; } \ void fill(detail::TestSuite& state) const { state.DOCTEST_CAT(m_, name) = data; } \ } DOCTEST_DEFINE_DECORATOR(test_suite, const char*, ""); DOCTEST_DEFINE_DECORATOR(description, const char*, ""); DOCTEST_DEFINE_DECORATOR(skip, bool, true); DOCTEST_DEFINE_DECORATOR(no_breaks, bool, true); DOCTEST_DEFINE_DECORATOR(no_output, bool, true); DOCTEST_DEFINE_DECORATOR(timeout, double, 0); DOCTEST_DEFINE_DECORATOR(may_fail, bool, true); DOCTEST_DEFINE_DECORATOR(should_fail, bool, true); DOCTEST_DEFINE_DECORATOR(expected_failures, int, 0); template int registerExceptionTranslator(String (*translateFunction)(T)) { DOCTEST_CLANG_SUPPRESS_WARNING_WITH_PUSH("-Wexit-time-destructors") static detail::ExceptionTranslator exceptionTranslator(translateFunction); DOCTEST_CLANG_SUPPRESS_WARNING_POP detail::registerExceptionTranslatorImpl(&exceptionTranslator); return 0; } } // namespace doctest // in a separate namespace outside of doctest because the DOCTEST_TEST_SUITE macro // introduces an anonymous namespace in which getCurrentTestSuite gets overridden namespace doctest_detail_test_suite_ns { DOCTEST_INTERFACE doctest::detail::TestSuite& getCurrentTestSuite(); } // namespace doctest_detail_test_suite_ns namespace doctest { #else // DOCTEST_CONFIG_DISABLE template int registerExceptionTranslator(String (*)(T)) { return 0; } #endif // DOCTEST_CONFIG_DISABLE namespace detail { using assert_handler = void (*)(const AssertData&); struct ContextState; } // namespace detail class DOCTEST_INTERFACE Context { detail::ContextState* p; void parseArgs(int argc, const char* const* argv, bool withDefaults = false); public: explicit Context(int argc = 0, const char* const* argv = nullptr); Context(const Context&) = delete; Context(Context&&) = delete; Context& operator=(const Context&) = delete; Context& operator=(Context&&) = delete; ~Context(); // NOLINT(performance-trivially-destructible) void applyCommandLine(int argc, const char* const* argv); void addFilter(const char* filter, const char* value); void clearFilters(); void setOption(const char* option, bool value); void setOption(const char* option, int value); void setOption(const char* option, const char* value); bool shouldExit(); void setAsDefaultForAssertsOutOfTestCases(); void setAssertHandler(detail::assert_handler ah); void setCout(std::ostream* out); int run(); }; namespace TestCaseFailureReason { enum Enum { None = 0, AssertFailure = 1, // an assertion has failed in the test case Exception = 2, // test case threw an exception Crash = 4, // a crash... TooManyFailedAsserts = 8, // the abort-after option Timeout = 16, // see the timeout decorator ShouldHaveFailedButDidnt = 32, // see the should_fail decorator ShouldHaveFailedAndDid = 64, // see the should_fail decorator DidntFailExactlyNumTimes = 128, // see the expected_failures decorator FailedExactlyNumTimes = 256, // see the expected_failures decorator CouldHaveFailedAndDid = 512 // see the may_fail decorator }; } // namespace TestCaseFailureReason struct DOCTEST_INTERFACE CurrentTestCaseStats { int numAssertsCurrentTest; int numAssertsFailedCurrentTest; double seconds; int failure_flags; // use TestCaseFailureReason::Enum bool testCaseSuccess; }; struct DOCTEST_INTERFACE TestCaseException { String error_string; bool is_crash; }; struct DOCTEST_INTERFACE TestRunStats { unsigned numTestCases; unsigned numTestCasesPassingFilters; unsigned numTestSuitesPassingFilters; unsigned numTestCasesFailed; int numAsserts; int numAssertsFailed; }; struct QueryData { const TestRunStats* run_stats = nullptr; const TestCaseData** data = nullptr; unsigned num_data = 0; }; struct DOCTEST_INTERFACE IReporter { // The constructor has to accept "const ContextOptions&" as a single argument // which has most of the options for the run + a pointer to the stdout stream // Reporter(const ContextOptions& in) // called when a query should be reported (listing test cases, printing the version, etc.) virtual void report_query(const QueryData&) = 0; // called when the whole test run starts virtual void test_run_start() = 0; // called when the whole test run ends (caching a pointer to the input doesn't make sense here) virtual void test_run_end(const TestRunStats&) = 0; // called when a test case is started (safe to cache a pointer to the input) virtual void test_case_start(const TestCaseData&) = 0; // called when a test case is reentered because of unfinished subcases (safe to cache a pointer to the input) virtual void test_case_reenter(const TestCaseData&) = 0; // called when a test case has ended virtual void test_case_end(const CurrentTestCaseStats&) = 0; // called when an exception is thrown from the test case (or it crashes) virtual void test_case_exception(const TestCaseException&) = 0; // called whenever a subcase is entered (don't cache pointers to the input) virtual void subcase_start(const SubcaseSignature&) = 0; // called whenever a subcase is exited (don't cache pointers to the input) virtual void subcase_end() = 0; // called for each assert (don't cache pointers to the input) virtual void log_assert(const AssertData&) = 0; // called for each message (don't cache pointers to the input) virtual void log_message(const MessageData&) = 0; // called when a test case is skipped either because it doesn't pass the filters, has a skip decorator // or isn't in the execution range (between first and last) (safe to cache a pointer to the input) virtual void test_case_skipped(const TestCaseData&) = 0; DOCTEST_DECLARE_INTERFACE(IReporter) // can obtain all currently active contexts and stringify them if one wishes to do so static int get_num_active_contexts(); static const IContextScope* const* get_active_contexts(); // can iterate through contexts which have been stringified automatically in their destructors when an exception has been thrown static int get_num_stringified_contexts(); static const String* get_stringified_contexts(); }; namespace detail { using reporterCreatorFunc = IReporter* (*)(const ContextOptions&); DOCTEST_INTERFACE void registerReporterImpl(const char* name, int prio, reporterCreatorFunc c, bool isReporter); template IReporter* reporterCreator(const ContextOptions& o) { return new Reporter(o); } } // namespace detail template int registerReporter(const char* name, int priority, bool isReporter) { detail::registerReporterImpl(name, priority, detail::reporterCreator, isReporter); return 0; } } // namespace doctest #ifdef DOCTEST_CONFIG_ASSERTS_RETURN_VALUES #define DOCTEST_FUNC_EMPTY [] { return false; }() #else #define DOCTEST_FUNC_EMPTY (void)0 #endif // if registering is not disabled #ifndef DOCTEST_CONFIG_DISABLE #ifdef DOCTEST_CONFIG_ASSERTS_RETURN_VALUES #define DOCTEST_FUNC_SCOPE_BEGIN [&] #define DOCTEST_FUNC_SCOPE_END () #define DOCTEST_FUNC_SCOPE_RET(v) return v #else #define DOCTEST_FUNC_SCOPE_BEGIN do #define DOCTEST_FUNC_SCOPE_END while(false) #define DOCTEST_FUNC_SCOPE_RET(v) (void)0 #endif // common code in asserts - for convenience #define DOCTEST_ASSERT_LOG_REACT_RETURN(b) \ if(b.log()) DOCTEST_BREAK_INTO_DEBUGGER(); \ b.react(); \ DOCTEST_FUNC_SCOPE_RET(!b.m_failed) #ifdef DOCTEST_CONFIG_NO_TRY_CATCH_IN_ASSERTS #define DOCTEST_WRAP_IN_TRY(x) x; #else // DOCTEST_CONFIG_NO_TRY_CATCH_IN_ASSERTS #define DOCTEST_WRAP_IN_TRY(x) \ try { \ x; \ } catch(...) { DOCTEST_RB.translateException(); } #endif // DOCTEST_CONFIG_NO_TRY_CATCH_IN_ASSERTS #ifdef DOCTEST_CONFIG_VOID_CAST_EXPRESSIONS #define DOCTEST_CAST_TO_VOID(...) \ DOCTEST_GCC_SUPPRESS_WARNING_WITH_PUSH("-Wuseless-cast") \ static_cast(__VA_ARGS__); \ DOCTEST_GCC_SUPPRESS_WARNING_POP #else // DOCTEST_CONFIG_VOID_CAST_EXPRESSIONS #define DOCTEST_CAST_TO_VOID(...) __VA_ARGS__; #endif // DOCTEST_CONFIG_VOID_CAST_EXPRESSIONS // registers the test by initializing a dummy var with a function #define DOCTEST_REGISTER_FUNCTION(global_prefix, f, decorators) \ global_prefix DOCTEST_GLOBAL_NO_WARNINGS(DOCTEST_ANONYMOUS(DOCTEST_ANON_VAR_), /* NOLINT */ \ doctest::detail::regTest( \ doctest::detail::TestCase( \ f, __FILE__, __LINE__, \ doctest_detail_test_suite_ns::getCurrentTestSuite()) * \ decorators)) #define DOCTEST_IMPLEMENT_FIXTURE(der, base, func, decorators) \ namespace { /* NOLINT */ \ struct der : public base \ { \ void f(); \ }; \ static DOCTEST_INLINE_NOINLINE void func() { \ der v; \ v.f(); \ } \ DOCTEST_REGISTER_FUNCTION(DOCTEST_EMPTY, func, decorators) \ } \ DOCTEST_INLINE_NOINLINE void der::f() // NOLINT(misc-definitions-in-headers) #define DOCTEST_CREATE_AND_REGISTER_FUNCTION(f, decorators) \ static void f(); \ DOCTEST_REGISTER_FUNCTION(DOCTEST_EMPTY, f, decorators) \ static void f() #define DOCTEST_CREATE_AND_REGISTER_FUNCTION_IN_CLASS(f, proxy, decorators) \ static doctest::detail::funcType proxy() { return f; } \ DOCTEST_REGISTER_FUNCTION(inline, proxy(), decorators) \ static void f() // for registering tests #define DOCTEST_TEST_CASE(decorators) \ DOCTEST_CREATE_AND_REGISTER_FUNCTION(DOCTEST_ANONYMOUS(DOCTEST_ANON_FUNC_), decorators) // for registering tests in classes - requires C++17 for inline variables! #if DOCTEST_CPLUSPLUS >= 201703L #define DOCTEST_TEST_CASE_CLASS(decorators) \ DOCTEST_CREATE_AND_REGISTER_FUNCTION_IN_CLASS(DOCTEST_ANONYMOUS(DOCTEST_ANON_FUNC_), \ DOCTEST_ANONYMOUS(DOCTEST_ANON_PROXY_), \ decorators) #else // DOCTEST_TEST_CASE_CLASS #define DOCTEST_TEST_CASE_CLASS(...) \ TEST_CASES_CAN_BE_REGISTERED_IN_CLASSES_ONLY_IN_CPP17_MODE_OR_WITH_VS_2017_OR_NEWER #endif // DOCTEST_TEST_CASE_CLASS // for registering tests with a fixture #define DOCTEST_TEST_CASE_FIXTURE(c, decorators) \ DOCTEST_IMPLEMENT_FIXTURE(DOCTEST_ANONYMOUS(DOCTEST_ANON_CLASS_), c, \ DOCTEST_ANONYMOUS(DOCTEST_ANON_FUNC_), decorators) // for converting types to strings without the header and demangling #define DOCTEST_TYPE_TO_STRING_AS(str, ...) \ namespace doctest { \ template <> \ inline String toString<__VA_ARGS__>() { \ return str; \ } \ } \ static_assert(true, "") #define DOCTEST_TYPE_TO_STRING(...) DOCTEST_TYPE_TO_STRING_AS(#__VA_ARGS__, __VA_ARGS__) #define DOCTEST_TEST_CASE_TEMPLATE_DEFINE_IMPL(dec, T, iter, func) \ template \ static void func(); \ namespace { /* NOLINT */ \ template \ struct iter; \ template \ struct iter> \ { \ iter(const char* file, unsigned line, int index) { \ doctest::detail::regTest(doctest::detail::TestCase(func, file, line, \ doctest_detail_test_suite_ns::getCurrentTestSuite(), \ doctest::toString(), \ int(line) * 1000 + index) \ * dec); \ iter>(file, line, index + 1); \ } \ }; \ template <> \ struct iter> \ { \ iter(const char*, unsigned, int) {} \ }; \ } \ template \ static void func() #define DOCTEST_TEST_CASE_TEMPLATE_DEFINE(dec, T, id) \ DOCTEST_TEST_CASE_TEMPLATE_DEFINE_IMPL(dec, T, DOCTEST_CAT(id, ITERATOR), \ DOCTEST_ANONYMOUS(DOCTEST_ANON_TMP_)) #define DOCTEST_TEST_CASE_TEMPLATE_INSTANTIATE_IMPL(id, anon, ...) \ DOCTEST_GLOBAL_NO_WARNINGS(DOCTEST_CAT(anon, DUMMY), /* NOLINT(cert-err58-cpp, fuchsia-statically-constructed-objects) */ \ doctest::detail::instantiationHelper( \ DOCTEST_CAT(id, ITERATOR)<__VA_ARGS__>(__FILE__, __LINE__, 0))) #define DOCTEST_TEST_CASE_TEMPLATE_INVOKE(id, ...) \ DOCTEST_TEST_CASE_TEMPLATE_INSTANTIATE_IMPL(id, DOCTEST_ANONYMOUS(DOCTEST_ANON_TMP_), std::tuple<__VA_ARGS__>) \ static_assert(true, "") #define DOCTEST_TEST_CASE_TEMPLATE_APPLY(id, ...) \ DOCTEST_TEST_CASE_TEMPLATE_INSTANTIATE_IMPL(id, DOCTEST_ANONYMOUS(DOCTEST_ANON_TMP_), __VA_ARGS__) \ static_assert(true, "") #define DOCTEST_TEST_CASE_TEMPLATE_IMPL(dec, T, anon, ...) \ DOCTEST_TEST_CASE_TEMPLATE_DEFINE_IMPL(dec, T, DOCTEST_CAT(anon, ITERATOR), anon); \ DOCTEST_TEST_CASE_TEMPLATE_INSTANTIATE_IMPL(anon, anon, std::tuple<__VA_ARGS__>) \ template \ static void anon() #define DOCTEST_TEST_CASE_TEMPLATE(dec, T, ...) \ DOCTEST_TEST_CASE_TEMPLATE_IMPL(dec, T, DOCTEST_ANONYMOUS(DOCTEST_ANON_TMP_), __VA_ARGS__) // for subcases #define DOCTEST_SUBCASE(name) \ if(const doctest::detail::Subcase & DOCTEST_ANONYMOUS(DOCTEST_ANON_SUBCASE_) DOCTEST_UNUSED = \ doctest::detail::Subcase(name, __FILE__, __LINE__)) // for grouping tests in test suites by using code blocks #define DOCTEST_TEST_SUITE_IMPL(decorators, ns_name) \ namespace ns_name { namespace doctest_detail_test_suite_ns { \ static DOCTEST_NOINLINE doctest::detail::TestSuite& getCurrentTestSuite() noexcept { \ DOCTEST_MSVC_SUPPRESS_WARNING_WITH_PUSH(4640) \ DOCTEST_CLANG_SUPPRESS_WARNING_WITH_PUSH("-Wexit-time-destructors") \ DOCTEST_GCC_SUPPRESS_WARNING_WITH_PUSH("-Wmissing-field-initializers") \ static doctest::detail::TestSuite data{}; \ static bool inited = false; \ DOCTEST_MSVC_SUPPRESS_WARNING_POP \ DOCTEST_CLANG_SUPPRESS_WARNING_POP \ DOCTEST_GCC_SUPPRESS_WARNING_POP \ if(!inited) { \ data* decorators; \ inited = true; \ } \ return data; \ } \ } \ } \ namespace ns_name #define DOCTEST_TEST_SUITE(decorators) \ DOCTEST_TEST_SUITE_IMPL(decorators, DOCTEST_ANONYMOUS(DOCTEST_ANON_SUITE_)) // for starting a testsuite block #define DOCTEST_TEST_SUITE_BEGIN(decorators) \ DOCTEST_GLOBAL_NO_WARNINGS(DOCTEST_ANONYMOUS(DOCTEST_ANON_VAR_), /* NOLINT(cert-err58-cpp) */ \ doctest::detail::setTestSuite(doctest::detail::TestSuite() * decorators)) \ static_assert(true, "") // for ending a testsuite block #define DOCTEST_TEST_SUITE_END \ DOCTEST_GLOBAL_NO_WARNINGS(DOCTEST_ANONYMOUS(DOCTEST_ANON_VAR_), /* NOLINT(cert-err58-cpp) */ \ doctest::detail::setTestSuite(doctest::detail::TestSuite() * "")) \ using DOCTEST_ANONYMOUS(DOCTEST_ANON_FOR_SEMICOLON_) = int // for registering exception translators #define DOCTEST_REGISTER_EXCEPTION_TRANSLATOR_IMPL(translatorName, signature) \ inline doctest::String translatorName(signature); \ DOCTEST_GLOBAL_NO_WARNINGS(DOCTEST_ANONYMOUS(DOCTEST_ANON_TRANSLATOR_), /* NOLINT(cert-err58-cpp) */ \ doctest::registerExceptionTranslator(translatorName)) \ doctest::String translatorName(signature) #define DOCTEST_REGISTER_EXCEPTION_TRANSLATOR(signature) \ DOCTEST_REGISTER_EXCEPTION_TRANSLATOR_IMPL(DOCTEST_ANONYMOUS(DOCTEST_ANON_TRANSLATOR_), \ signature) // for registering reporters #define DOCTEST_REGISTER_REPORTER(name, priority, reporter) \ DOCTEST_GLOBAL_NO_WARNINGS(DOCTEST_ANONYMOUS(DOCTEST_ANON_REPORTER_), /* NOLINT(cert-err58-cpp) */ \ doctest::registerReporter(name, priority, true)) \ static_assert(true, "") // for registering listeners #define DOCTEST_REGISTER_LISTENER(name, priority, reporter) \ DOCTEST_GLOBAL_NO_WARNINGS(DOCTEST_ANONYMOUS(DOCTEST_ANON_REPORTER_), /* NOLINT(cert-err58-cpp) */ \ doctest::registerReporter(name, priority, false)) \ static_assert(true, "") // clang-format off // for logging - disabling formatting because it's important to have these on 2 separate lines - see PR #557 #define DOCTEST_INFO(...) \ DOCTEST_INFO_IMPL(DOCTEST_ANONYMOUS(DOCTEST_CAPTURE_), \ DOCTEST_ANONYMOUS(DOCTEST_CAPTURE_OTHER_), \ __VA_ARGS__) // clang-format on #define DOCTEST_INFO_IMPL(mb_name, s_name, ...) \ auto DOCTEST_ANONYMOUS(DOCTEST_CAPTURE_) = doctest::detail::MakeContextScope( \ [&](std::ostream* s_name) { \ doctest::detail::MessageBuilder mb_name(__FILE__, __LINE__, doctest::assertType::is_warn); \ mb_name.m_stream = s_name; \ mb_name * __VA_ARGS__; \ }) #define DOCTEST_CAPTURE(x) DOCTEST_INFO(#x " := ", x) #define DOCTEST_ADD_AT_IMPL(type, file, line, mb, ...) \ DOCTEST_FUNC_SCOPE_BEGIN { \ doctest::detail::MessageBuilder mb(file, line, doctest::assertType::type); \ mb * __VA_ARGS__; \ if(mb.log()) \ DOCTEST_BREAK_INTO_DEBUGGER(); \ mb.react(); \ } DOCTEST_FUNC_SCOPE_END // clang-format off #define DOCTEST_ADD_MESSAGE_AT(file, line, ...) DOCTEST_ADD_AT_IMPL(is_warn, file, line, DOCTEST_ANONYMOUS(DOCTEST_MESSAGE_), __VA_ARGS__) #define DOCTEST_ADD_FAIL_CHECK_AT(file, line, ...) DOCTEST_ADD_AT_IMPL(is_check, file, line, DOCTEST_ANONYMOUS(DOCTEST_MESSAGE_), __VA_ARGS__) #define DOCTEST_ADD_FAIL_AT(file, line, ...) DOCTEST_ADD_AT_IMPL(is_require, file, line, DOCTEST_ANONYMOUS(DOCTEST_MESSAGE_), __VA_ARGS__) // clang-format on #define DOCTEST_MESSAGE(...) DOCTEST_ADD_MESSAGE_AT(__FILE__, __LINE__, __VA_ARGS__) #define DOCTEST_FAIL_CHECK(...) DOCTEST_ADD_FAIL_CHECK_AT(__FILE__, __LINE__, __VA_ARGS__) #define DOCTEST_FAIL(...) DOCTEST_ADD_FAIL_AT(__FILE__, __LINE__, __VA_ARGS__) #define DOCTEST_TO_LVALUE(...) __VA_ARGS__ // Not removed to keep backwards compatibility. #ifndef DOCTEST_CONFIG_SUPER_FAST_ASSERTS #define DOCTEST_ASSERT_IMPLEMENT_2(assert_type, ...) \ DOCTEST_CLANG_SUPPRESS_WARNING_WITH_PUSH("-Woverloaded-shift-op-parentheses") \ /* NOLINTNEXTLINE(clang-analyzer-cplusplus.NewDeleteLeaks) */ \ doctest::detail::ResultBuilder DOCTEST_RB(doctest::assertType::assert_type, __FILE__, \ __LINE__, #__VA_ARGS__); \ DOCTEST_WRAP_IN_TRY(DOCTEST_RB.setResult( \ doctest::detail::ExpressionDecomposer(doctest::assertType::assert_type) \ << __VA_ARGS__)) /* NOLINTNEXTLINE(clang-analyzer-cplusplus.NewDeleteLeaks) */ \ DOCTEST_ASSERT_LOG_REACT_RETURN(DOCTEST_RB) \ DOCTEST_CLANG_SUPPRESS_WARNING_POP #define DOCTEST_ASSERT_IMPLEMENT_1(assert_type, ...) \ DOCTEST_FUNC_SCOPE_BEGIN { \ DOCTEST_ASSERT_IMPLEMENT_2(assert_type, __VA_ARGS__); \ } DOCTEST_FUNC_SCOPE_END // NOLINT(clang-analyzer-cplusplus.NewDeleteLeaks) #define DOCTEST_BINARY_ASSERT(assert_type, comp, ...) \ DOCTEST_FUNC_SCOPE_BEGIN { \ doctest::detail::ResultBuilder DOCTEST_RB(doctest::assertType::assert_type, __FILE__, \ __LINE__, #__VA_ARGS__); \ DOCTEST_WRAP_IN_TRY( \ DOCTEST_RB.binary_assert( \ __VA_ARGS__)) \ DOCTEST_ASSERT_LOG_REACT_RETURN(DOCTEST_RB); \ } DOCTEST_FUNC_SCOPE_END #define DOCTEST_UNARY_ASSERT(assert_type, ...) \ DOCTEST_FUNC_SCOPE_BEGIN { \ doctest::detail::ResultBuilder DOCTEST_RB(doctest::assertType::assert_type, __FILE__, \ __LINE__, #__VA_ARGS__); \ DOCTEST_WRAP_IN_TRY(DOCTEST_RB.unary_assert(__VA_ARGS__)) \ DOCTEST_ASSERT_LOG_REACT_RETURN(DOCTEST_RB); \ } DOCTEST_FUNC_SCOPE_END #else // DOCTEST_CONFIG_SUPER_FAST_ASSERTS // necessary for _MESSAGE #define DOCTEST_ASSERT_IMPLEMENT_2 DOCTEST_ASSERT_IMPLEMENT_1 #define DOCTEST_ASSERT_IMPLEMENT_1(assert_type, ...) \ DOCTEST_CLANG_SUPPRESS_WARNING_WITH_PUSH("-Woverloaded-shift-op-parentheses") \ doctest::detail::decomp_assert( \ doctest::assertType::assert_type, __FILE__, __LINE__, #__VA_ARGS__, \ doctest::detail::ExpressionDecomposer(doctest::assertType::assert_type) \ << __VA_ARGS__) DOCTEST_CLANG_SUPPRESS_WARNING_POP #define DOCTEST_BINARY_ASSERT(assert_type, comparison, ...) \ doctest::detail::binary_assert( \ doctest::assertType::assert_type, __FILE__, __LINE__, #__VA_ARGS__, __VA_ARGS__) #define DOCTEST_UNARY_ASSERT(assert_type, ...) \ doctest::detail::unary_assert(doctest::assertType::assert_type, __FILE__, __LINE__, \ #__VA_ARGS__, __VA_ARGS__) #endif // DOCTEST_CONFIG_SUPER_FAST_ASSERTS #define DOCTEST_WARN(...) DOCTEST_ASSERT_IMPLEMENT_1(DT_WARN, __VA_ARGS__) #define DOCTEST_CHECK(...) DOCTEST_ASSERT_IMPLEMENT_1(DT_CHECK, __VA_ARGS__) #define DOCTEST_REQUIRE(...) DOCTEST_ASSERT_IMPLEMENT_1(DT_REQUIRE, __VA_ARGS__) #define DOCTEST_WARN_FALSE(...) DOCTEST_ASSERT_IMPLEMENT_1(DT_WARN_FALSE, __VA_ARGS__) #define DOCTEST_CHECK_FALSE(...) DOCTEST_ASSERT_IMPLEMENT_1(DT_CHECK_FALSE, __VA_ARGS__) #define DOCTEST_REQUIRE_FALSE(...) DOCTEST_ASSERT_IMPLEMENT_1(DT_REQUIRE_FALSE, __VA_ARGS__) // clang-format off #define DOCTEST_WARN_MESSAGE(cond, ...) DOCTEST_FUNC_SCOPE_BEGIN { DOCTEST_INFO(__VA_ARGS__); DOCTEST_ASSERT_IMPLEMENT_2(DT_WARN, cond); } DOCTEST_FUNC_SCOPE_END #define DOCTEST_CHECK_MESSAGE(cond, ...) DOCTEST_FUNC_SCOPE_BEGIN { DOCTEST_INFO(__VA_ARGS__); DOCTEST_ASSERT_IMPLEMENT_2(DT_CHECK, cond); } DOCTEST_FUNC_SCOPE_END #define DOCTEST_REQUIRE_MESSAGE(cond, ...) DOCTEST_FUNC_SCOPE_BEGIN { DOCTEST_INFO(__VA_ARGS__); DOCTEST_ASSERT_IMPLEMENT_2(DT_REQUIRE, cond); } DOCTEST_FUNC_SCOPE_END #define DOCTEST_WARN_FALSE_MESSAGE(cond, ...) DOCTEST_FUNC_SCOPE_BEGIN { DOCTEST_INFO(__VA_ARGS__); DOCTEST_ASSERT_IMPLEMENT_2(DT_WARN_FALSE, cond); } DOCTEST_FUNC_SCOPE_END #define DOCTEST_CHECK_FALSE_MESSAGE(cond, ...) DOCTEST_FUNC_SCOPE_BEGIN { DOCTEST_INFO(__VA_ARGS__); DOCTEST_ASSERT_IMPLEMENT_2(DT_CHECK_FALSE, cond); } DOCTEST_FUNC_SCOPE_END #define DOCTEST_REQUIRE_FALSE_MESSAGE(cond, ...) DOCTEST_FUNC_SCOPE_BEGIN { DOCTEST_INFO(__VA_ARGS__); DOCTEST_ASSERT_IMPLEMENT_2(DT_REQUIRE_FALSE, cond); } DOCTEST_FUNC_SCOPE_END // clang-format on #define DOCTEST_WARN_EQ(...) DOCTEST_BINARY_ASSERT(DT_WARN_EQ, eq, __VA_ARGS__) #define DOCTEST_CHECK_EQ(...) DOCTEST_BINARY_ASSERT(DT_CHECK_EQ, eq, __VA_ARGS__) #define DOCTEST_REQUIRE_EQ(...) DOCTEST_BINARY_ASSERT(DT_REQUIRE_EQ, eq, __VA_ARGS__) #define DOCTEST_WARN_NE(...) DOCTEST_BINARY_ASSERT(DT_WARN_NE, ne, __VA_ARGS__) #define DOCTEST_CHECK_NE(...) DOCTEST_BINARY_ASSERT(DT_CHECK_NE, ne, __VA_ARGS__) #define DOCTEST_REQUIRE_NE(...) DOCTEST_BINARY_ASSERT(DT_REQUIRE_NE, ne, __VA_ARGS__) #define DOCTEST_WARN_GT(...) DOCTEST_BINARY_ASSERT(DT_WARN_GT, gt, __VA_ARGS__) #define DOCTEST_CHECK_GT(...) DOCTEST_BINARY_ASSERT(DT_CHECK_GT, gt, __VA_ARGS__) #define DOCTEST_REQUIRE_GT(...) DOCTEST_BINARY_ASSERT(DT_REQUIRE_GT, gt, __VA_ARGS__) #define DOCTEST_WARN_LT(...) DOCTEST_BINARY_ASSERT(DT_WARN_LT, lt, __VA_ARGS__) #define DOCTEST_CHECK_LT(...) DOCTEST_BINARY_ASSERT(DT_CHECK_LT, lt, __VA_ARGS__) #define DOCTEST_REQUIRE_LT(...) DOCTEST_BINARY_ASSERT(DT_REQUIRE_LT, lt, __VA_ARGS__) #define DOCTEST_WARN_GE(...) DOCTEST_BINARY_ASSERT(DT_WARN_GE, ge, __VA_ARGS__) #define DOCTEST_CHECK_GE(...) DOCTEST_BINARY_ASSERT(DT_CHECK_GE, ge, __VA_ARGS__) #define DOCTEST_REQUIRE_GE(...) DOCTEST_BINARY_ASSERT(DT_REQUIRE_GE, ge, __VA_ARGS__) #define DOCTEST_WARN_LE(...) DOCTEST_BINARY_ASSERT(DT_WARN_LE, le, __VA_ARGS__) #define DOCTEST_CHECK_LE(...) DOCTEST_BINARY_ASSERT(DT_CHECK_LE, le, __VA_ARGS__) #define DOCTEST_REQUIRE_LE(...) DOCTEST_BINARY_ASSERT(DT_REQUIRE_LE, le, __VA_ARGS__) #define DOCTEST_WARN_UNARY(...) DOCTEST_UNARY_ASSERT(DT_WARN_UNARY, __VA_ARGS__) #define DOCTEST_CHECK_UNARY(...) DOCTEST_UNARY_ASSERT(DT_CHECK_UNARY, __VA_ARGS__) #define DOCTEST_REQUIRE_UNARY(...) DOCTEST_UNARY_ASSERT(DT_REQUIRE_UNARY, __VA_ARGS__) #define DOCTEST_WARN_UNARY_FALSE(...) DOCTEST_UNARY_ASSERT(DT_WARN_UNARY_FALSE, __VA_ARGS__) #define DOCTEST_CHECK_UNARY_FALSE(...) DOCTEST_UNARY_ASSERT(DT_CHECK_UNARY_FALSE, __VA_ARGS__) #define DOCTEST_REQUIRE_UNARY_FALSE(...) DOCTEST_UNARY_ASSERT(DT_REQUIRE_UNARY_FALSE, __VA_ARGS__) #ifndef DOCTEST_CONFIG_NO_EXCEPTIONS #define DOCTEST_ASSERT_THROWS_AS(expr, assert_type, message, ...) \ DOCTEST_FUNC_SCOPE_BEGIN { \ if(!doctest::getContextOptions()->no_throw) { \ doctest::detail::ResultBuilder DOCTEST_RB(doctest::assertType::assert_type, __FILE__, \ __LINE__, #expr, #__VA_ARGS__, message); \ try { \ DOCTEST_CAST_TO_VOID(expr) \ } catch(const typename doctest::detail::types::remove_const< \ typename doctest::detail::types::remove_reference<__VA_ARGS__>::type>::type&) {\ DOCTEST_RB.translateException(); \ DOCTEST_RB.m_threw_as = true; \ } catch(...) { DOCTEST_RB.translateException(); } \ DOCTEST_ASSERT_LOG_REACT_RETURN(DOCTEST_RB); \ } else { /* NOLINT(*-else-after-return) */ \ DOCTEST_FUNC_SCOPE_RET(false); \ } \ } DOCTEST_FUNC_SCOPE_END #define DOCTEST_ASSERT_THROWS_WITH(expr, expr_str, assert_type, ...) \ DOCTEST_FUNC_SCOPE_BEGIN { \ if(!doctest::getContextOptions()->no_throw) { \ doctest::detail::ResultBuilder DOCTEST_RB(doctest::assertType::assert_type, __FILE__, \ __LINE__, expr_str, "", __VA_ARGS__); \ try { \ DOCTEST_CAST_TO_VOID(expr) \ } catch(...) { DOCTEST_RB.translateException(); } \ DOCTEST_ASSERT_LOG_REACT_RETURN(DOCTEST_RB); \ } else { /* NOLINT(*-else-after-return) */ \ DOCTEST_FUNC_SCOPE_RET(false); \ } \ } DOCTEST_FUNC_SCOPE_END #define DOCTEST_ASSERT_NOTHROW(assert_type, ...) \ DOCTEST_FUNC_SCOPE_BEGIN { \ doctest::detail::ResultBuilder DOCTEST_RB(doctest::assertType::assert_type, __FILE__, \ __LINE__, #__VA_ARGS__); \ try { \ DOCTEST_CAST_TO_VOID(__VA_ARGS__) \ } catch(...) { DOCTEST_RB.translateException(); } \ DOCTEST_ASSERT_LOG_REACT_RETURN(DOCTEST_RB); \ } DOCTEST_FUNC_SCOPE_END // clang-format off #define DOCTEST_WARN_THROWS(...) DOCTEST_ASSERT_THROWS_WITH((__VA_ARGS__), #__VA_ARGS__, DT_WARN_THROWS, "") #define DOCTEST_CHECK_THROWS(...) DOCTEST_ASSERT_THROWS_WITH((__VA_ARGS__), #__VA_ARGS__, DT_CHECK_THROWS, "") #define DOCTEST_REQUIRE_THROWS(...) DOCTEST_ASSERT_THROWS_WITH((__VA_ARGS__), #__VA_ARGS__, DT_REQUIRE_THROWS, "") #define DOCTEST_WARN_THROWS_AS(expr, ...) DOCTEST_ASSERT_THROWS_AS(expr, DT_WARN_THROWS_AS, "", __VA_ARGS__) #define DOCTEST_CHECK_THROWS_AS(expr, ...) DOCTEST_ASSERT_THROWS_AS(expr, DT_CHECK_THROWS_AS, "", __VA_ARGS__) #define DOCTEST_REQUIRE_THROWS_AS(expr, ...) DOCTEST_ASSERT_THROWS_AS(expr, DT_REQUIRE_THROWS_AS, "", __VA_ARGS__) #define DOCTEST_WARN_THROWS_WITH(expr, ...) DOCTEST_ASSERT_THROWS_WITH(expr, #expr, DT_WARN_THROWS_WITH, __VA_ARGS__) #define DOCTEST_CHECK_THROWS_WITH(expr, ...) DOCTEST_ASSERT_THROWS_WITH(expr, #expr, DT_CHECK_THROWS_WITH, __VA_ARGS__) #define DOCTEST_REQUIRE_THROWS_WITH(expr, ...) DOCTEST_ASSERT_THROWS_WITH(expr, #expr, DT_REQUIRE_THROWS_WITH, __VA_ARGS__) #define DOCTEST_WARN_THROWS_WITH_AS(expr, message, ...) DOCTEST_ASSERT_THROWS_AS(expr, DT_WARN_THROWS_WITH_AS, message, __VA_ARGS__) #define DOCTEST_CHECK_THROWS_WITH_AS(expr, message, ...) DOCTEST_ASSERT_THROWS_AS(expr, DT_CHECK_THROWS_WITH_AS, message, __VA_ARGS__) #define DOCTEST_REQUIRE_THROWS_WITH_AS(expr, message, ...) DOCTEST_ASSERT_THROWS_AS(expr, DT_REQUIRE_THROWS_WITH_AS, message, __VA_ARGS__) #define DOCTEST_WARN_NOTHROW(...) DOCTEST_ASSERT_NOTHROW(DT_WARN_NOTHROW, __VA_ARGS__) #define DOCTEST_CHECK_NOTHROW(...) DOCTEST_ASSERT_NOTHROW(DT_CHECK_NOTHROW, __VA_ARGS__) #define DOCTEST_REQUIRE_NOTHROW(...) DOCTEST_ASSERT_NOTHROW(DT_REQUIRE_NOTHROW, __VA_ARGS__) #define DOCTEST_WARN_THROWS_MESSAGE(expr, ...) DOCTEST_FUNC_SCOPE_BEGIN { DOCTEST_INFO(__VA_ARGS__); DOCTEST_WARN_THROWS(expr); } DOCTEST_FUNC_SCOPE_END #define DOCTEST_CHECK_THROWS_MESSAGE(expr, ...) DOCTEST_FUNC_SCOPE_BEGIN { DOCTEST_INFO(__VA_ARGS__); DOCTEST_CHECK_THROWS(expr); } DOCTEST_FUNC_SCOPE_END #define DOCTEST_REQUIRE_THROWS_MESSAGE(expr, ...) DOCTEST_FUNC_SCOPE_BEGIN { DOCTEST_INFO(__VA_ARGS__); DOCTEST_REQUIRE_THROWS(expr); } DOCTEST_FUNC_SCOPE_END #define DOCTEST_WARN_THROWS_AS_MESSAGE(expr, ex, ...) DOCTEST_FUNC_SCOPE_BEGIN { DOCTEST_INFO(__VA_ARGS__); DOCTEST_WARN_THROWS_AS(expr, ex); } DOCTEST_FUNC_SCOPE_END #define DOCTEST_CHECK_THROWS_AS_MESSAGE(expr, ex, ...) DOCTEST_FUNC_SCOPE_BEGIN { DOCTEST_INFO(__VA_ARGS__); DOCTEST_CHECK_THROWS_AS(expr, ex); } DOCTEST_FUNC_SCOPE_END #define DOCTEST_REQUIRE_THROWS_AS_MESSAGE(expr, ex, ...) DOCTEST_FUNC_SCOPE_BEGIN { DOCTEST_INFO(__VA_ARGS__); DOCTEST_REQUIRE_THROWS_AS(expr, ex); } DOCTEST_FUNC_SCOPE_END #define DOCTEST_WARN_THROWS_WITH_MESSAGE(expr, with, ...) DOCTEST_FUNC_SCOPE_BEGIN { DOCTEST_INFO(__VA_ARGS__); DOCTEST_WARN_THROWS_WITH(expr, with); } DOCTEST_FUNC_SCOPE_END #define DOCTEST_CHECK_THROWS_WITH_MESSAGE(expr, with, ...) DOCTEST_FUNC_SCOPE_BEGIN { DOCTEST_INFO(__VA_ARGS__); DOCTEST_CHECK_THROWS_WITH(expr, with); } DOCTEST_FUNC_SCOPE_END #define DOCTEST_REQUIRE_THROWS_WITH_MESSAGE(expr, with, ...) DOCTEST_FUNC_SCOPE_BEGIN { DOCTEST_INFO(__VA_ARGS__); DOCTEST_REQUIRE_THROWS_WITH(expr, with); } DOCTEST_FUNC_SCOPE_END #define DOCTEST_WARN_THROWS_WITH_AS_MESSAGE(expr, with, ex, ...) DOCTEST_FUNC_SCOPE_BEGIN { DOCTEST_INFO(__VA_ARGS__); DOCTEST_WARN_THROWS_WITH_AS(expr, with, ex); } DOCTEST_FUNC_SCOPE_END #define DOCTEST_CHECK_THROWS_WITH_AS_MESSAGE(expr, with, ex, ...) DOCTEST_FUNC_SCOPE_BEGIN { DOCTEST_INFO(__VA_ARGS__); DOCTEST_CHECK_THROWS_WITH_AS(expr, with, ex); } DOCTEST_FUNC_SCOPE_END #define DOCTEST_REQUIRE_THROWS_WITH_AS_MESSAGE(expr, with, ex, ...) DOCTEST_FUNC_SCOPE_BEGIN { DOCTEST_INFO(__VA_ARGS__); DOCTEST_REQUIRE_THROWS_WITH_AS(expr, with, ex); } DOCTEST_FUNC_SCOPE_END #define DOCTEST_WARN_NOTHROW_MESSAGE(expr, ...) DOCTEST_FUNC_SCOPE_BEGIN { DOCTEST_INFO(__VA_ARGS__); DOCTEST_WARN_NOTHROW(expr); } DOCTEST_FUNC_SCOPE_END #define DOCTEST_CHECK_NOTHROW_MESSAGE(expr, ...) DOCTEST_FUNC_SCOPE_BEGIN { DOCTEST_INFO(__VA_ARGS__); DOCTEST_CHECK_NOTHROW(expr); } DOCTEST_FUNC_SCOPE_END #define DOCTEST_REQUIRE_NOTHROW_MESSAGE(expr, ...) DOCTEST_FUNC_SCOPE_BEGIN { DOCTEST_INFO(__VA_ARGS__); DOCTEST_REQUIRE_NOTHROW(expr); } DOCTEST_FUNC_SCOPE_END // clang-format on #endif // DOCTEST_CONFIG_NO_EXCEPTIONS // ================================================================================================= // == WHAT FOLLOWS IS VERSIONS OF THE MACROS THAT DO NOT DO ANY REGISTERING! == // == THIS CAN BE ENABLED BY DEFINING DOCTEST_CONFIG_DISABLE GLOBALLY! == // ================================================================================================= #else // DOCTEST_CONFIG_DISABLE #define DOCTEST_IMPLEMENT_FIXTURE(der, base, func, name) \ namespace /* NOLINT */ { \ template \ struct der : public base \ { void f(); }; \ } \ template \ inline void der::f() #define DOCTEST_CREATE_AND_REGISTER_FUNCTION(f, name) \ template \ static inline void f() // for registering tests #define DOCTEST_TEST_CASE(name) \ DOCTEST_CREATE_AND_REGISTER_FUNCTION(DOCTEST_ANONYMOUS(DOCTEST_ANON_FUNC_), name) // for registering tests in classes #define DOCTEST_TEST_CASE_CLASS(name) \ DOCTEST_CREATE_AND_REGISTER_FUNCTION(DOCTEST_ANONYMOUS(DOCTEST_ANON_FUNC_), name) // for registering tests with a fixture #define DOCTEST_TEST_CASE_FIXTURE(x, name) \ DOCTEST_IMPLEMENT_FIXTURE(DOCTEST_ANONYMOUS(DOCTEST_ANON_CLASS_), x, \ DOCTEST_ANONYMOUS(DOCTEST_ANON_FUNC_), name) // for converting types to strings without the header and demangling #define DOCTEST_TYPE_TO_STRING_AS(str, ...) static_assert(true, "") #define DOCTEST_TYPE_TO_STRING(...) static_assert(true, "") // for typed tests #define DOCTEST_TEST_CASE_TEMPLATE(name, type, ...) \ template \ inline void DOCTEST_ANONYMOUS(DOCTEST_ANON_TMP_)() #define DOCTEST_TEST_CASE_TEMPLATE_DEFINE(name, type, id) \ template \ inline void DOCTEST_ANONYMOUS(DOCTEST_ANON_TMP_)() #define DOCTEST_TEST_CASE_TEMPLATE_INVOKE(id, ...) static_assert(true, "") #define DOCTEST_TEST_CASE_TEMPLATE_APPLY(id, ...) static_assert(true, "") // for subcases #define DOCTEST_SUBCASE(name) // for a testsuite block #define DOCTEST_TEST_SUITE(name) namespace // NOLINT // for starting a testsuite block #define DOCTEST_TEST_SUITE_BEGIN(name) static_assert(true, "") // for ending a testsuite block #define DOCTEST_TEST_SUITE_END using DOCTEST_ANONYMOUS(DOCTEST_ANON_FOR_SEMICOLON_) = int #define DOCTEST_REGISTER_EXCEPTION_TRANSLATOR(signature) \ template \ static inline doctest::String DOCTEST_ANONYMOUS(DOCTEST_ANON_TRANSLATOR_)(signature) #define DOCTEST_REGISTER_REPORTER(name, priority, reporter) #define DOCTEST_REGISTER_LISTENER(name, priority, reporter) #define DOCTEST_INFO(...) (static_cast(0)) #define DOCTEST_CAPTURE(x) (static_cast(0)) #define DOCTEST_ADD_MESSAGE_AT(file, line, ...) (static_cast(0)) #define DOCTEST_ADD_FAIL_CHECK_AT(file, line, ...) (static_cast(0)) #define DOCTEST_ADD_FAIL_AT(file, line, ...) (static_cast(0)) #define DOCTEST_MESSAGE(...) (static_cast(0)) #define DOCTEST_FAIL_CHECK(...) (static_cast(0)) #define DOCTEST_FAIL(...) (static_cast(0)) #if defined(DOCTEST_CONFIG_EVALUATE_ASSERTS_EVEN_WHEN_DISABLED) \ && defined(DOCTEST_CONFIG_ASSERTS_RETURN_VALUES) #define DOCTEST_WARN(...) [&] { return __VA_ARGS__; }() #define DOCTEST_CHECK(...) [&] { return __VA_ARGS__; }() #define DOCTEST_REQUIRE(...) [&] { return __VA_ARGS__; }() #define DOCTEST_WARN_FALSE(...) [&] { return !(__VA_ARGS__); }() #define DOCTEST_CHECK_FALSE(...) [&] { return !(__VA_ARGS__); }() #define DOCTEST_REQUIRE_FALSE(...) [&] { return !(__VA_ARGS__); }() #define DOCTEST_WARN_MESSAGE(cond, ...) [&] { return cond; }() #define DOCTEST_CHECK_MESSAGE(cond, ...) [&] { return cond; }() #define DOCTEST_REQUIRE_MESSAGE(cond, ...) [&] { return cond; }() #define DOCTEST_WARN_FALSE_MESSAGE(cond, ...) [&] { return !(cond); }() #define DOCTEST_CHECK_FALSE_MESSAGE(cond, ...) [&] { return !(cond); }() #define DOCTEST_REQUIRE_FALSE_MESSAGE(cond, ...) [&] { return !(cond); }() namespace doctest { namespace detail { #define DOCTEST_RELATIONAL_OP(name, op) \ template \ bool name(const DOCTEST_REF_WRAP(L) lhs, const DOCTEST_REF_WRAP(R) rhs) { return lhs op rhs; } DOCTEST_RELATIONAL_OP(eq, ==) DOCTEST_RELATIONAL_OP(ne, !=) DOCTEST_RELATIONAL_OP(lt, <) DOCTEST_RELATIONAL_OP(gt, >) DOCTEST_RELATIONAL_OP(le, <=) DOCTEST_RELATIONAL_OP(ge, >=) } // namespace detail } // namespace doctest #define DOCTEST_WARN_EQ(...) [&] { return doctest::detail::eq(__VA_ARGS__); }() #define DOCTEST_CHECK_EQ(...) [&] { return doctest::detail::eq(__VA_ARGS__); }() #define DOCTEST_REQUIRE_EQ(...) [&] { return doctest::detail::eq(__VA_ARGS__); }() #define DOCTEST_WARN_NE(...) [&] { return doctest::detail::ne(__VA_ARGS__); }() #define DOCTEST_CHECK_NE(...) [&] { return doctest::detail::ne(__VA_ARGS__); }() #define DOCTEST_REQUIRE_NE(...) [&] { return doctest::detail::ne(__VA_ARGS__); }() #define DOCTEST_WARN_LT(...) [&] { return doctest::detail::lt(__VA_ARGS__); }() #define DOCTEST_CHECK_LT(...) [&] { return doctest::detail::lt(__VA_ARGS__); }() #define DOCTEST_REQUIRE_LT(...) [&] { return doctest::detail::lt(__VA_ARGS__); }() #define DOCTEST_WARN_GT(...) [&] { return doctest::detail::gt(__VA_ARGS__); }() #define DOCTEST_CHECK_GT(...) [&] { return doctest::detail::gt(__VA_ARGS__); }() #define DOCTEST_REQUIRE_GT(...) [&] { return doctest::detail::gt(__VA_ARGS__); }() #define DOCTEST_WARN_LE(...) [&] { return doctest::detail::le(__VA_ARGS__); }() #define DOCTEST_CHECK_LE(...) [&] { return doctest::detail::le(__VA_ARGS__); }() #define DOCTEST_REQUIRE_LE(...) [&] { return doctest::detail::le(__VA_ARGS__); }() #define DOCTEST_WARN_GE(...) [&] { return doctest::detail::ge(__VA_ARGS__); }() #define DOCTEST_CHECK_GE(...) [&] { return doctest::detail::ge(__VA_ARGS__); }() #define DOCTEST_REQUIRE_GE(...) [&] { return doctest::detail::ge(__VA_ARGS__); }() #define DOCTEST_WARN_UNARY(...) [&] { return __VA_ARGS__; }() #define DOCTEST_CHECK_UNARY(...) [&] { return __VA_ARGS__; }() #define DOCTEST_REQUIRE_UNARY(...) [&] { return __VA_ARGS__; }() #define DOCTEST_WARN_UNARY_FALSE(...) [&] { return !(__VA_ARGS__); }() #define DOCTEST_CHECK_UNARY_FALSE(...) [&] { return !(__VA_ARGS__); }() #define DOCTEST_REQUIRE_UNARY_FALSE(...) [&] { return !(__VA_ARGS__); }() #ifndef DOCTEST_CONFIG_NO_EXCEPTIONS #define DOCTEST_WARN_THROWS_WITH(expr, with, ...) [] { static_assert(false, "Exception translation is not available when doctest is disabled."); return false; }() #define DOCTEST_CHECK_THROWS_WITH(expr, with, ...) DOCTEST_WARN_THROWS_WITH(,,) #define DOCTEST_REQUIRE_THROWS_WITH(expr, with, ...) DOCTEST_WARN_THROWS_WITH(,,) #define DOCTEST_WARN_THROWS_WITH_AS(expr, with, ex, ...) DOCTEST_WARN_THROWS_WITH(,,) #define DOCTEST_CHECK_THROWS_WITH_AS(expr, with, ex, ...) DOCTEST_WARN_THROWS_WITH(,,) #define DOCTEST_REQUIRE_THROWS_WITH_AS(expr, with, ex, ...) DOCTEST_WARN_THROWS_WITH(,,) #define DOCTEST_WARN_THROWS_WITH_MESSAGE(expr, with, ...) DOCTEST_WARN_THROWS_WITH(,,) #define DOCTEST_CHECK_THROWS_WITH_MESSAGE(expr, with, ...) DOCTEST_WARN_THROWS_WITH(,,) #define DOCTEST_REQUIRE_THROWS_WITH_MESSAGE(expr, with, ...) DOCTEST_WARN_THROWS_WITH(,,) #define DOCTEST_WARN_THROWS_WITH_AS_MESSAGE(expr, with, ex, ...) DOCTEST_WARN_THROWS_WITH(,,) #define DOCTEST_CHECK_THROWS_WITH_AS_MESSAGE(expr, with, ex, ...) DOCTEST_WARN_THROWS_WITH(,,) #define DOCTEST_REQUIRE_THROWS_WITH_AS_MESSAGE(expr, with, ex, ...) DOCTEST_WARN_THROWS_WITH(,,) #define DOCTEST_WARN_THROWS(...) [&] { try { __VA_ARGS__; return false; } catch (...) { return true; } }() #define DOCTEST_CHECK_THROWS(...) [&] { try { __VA_ARGS__; return false; } catch (...) { return true; } }() #define DOCTEST_REQUIRE_THROWS(...) [&] { try { __VA_ARGS__; return false; } catch (...) { return true; } }() #define DOCTEST_WARN_THROWS_AS(expr, ...) [&] { try { expr; } catch (__VA_ARGS__) { return true; } catch (...) { } return false; }() #define DOCTEST_CHECK_THROWS_AS(expr, ...) [&] { try { expr; } catch (__VA_ARGS__) { return true; } catch (...) { } return false; }() #define DOCTEST_REQUIRE_THROWS_AS(expr, ...) [&] { try { expr; } catch (__VA_ARGS__) { return true; } catch (...) { } return false; }() #define DOCTEST_WARN_NOTHROW(...) [&] { try { __VA_ARGS__; return true; } catch (...) { return false; } }() #define DOCTEST_CHECK_NOTHROW(...) [&] { try { __VA_ARGS__; return true; } catch (...) { return false; } }() #define DOCTEST_REQUIRE_NOTHROW(...) [&] { try { __VA_ARGS__; return true; } catch (...) { return false; } }() #define DOCTEST_WARN_THROWS_MESSAGE(expr, ...) [&] { try { __VA_ARGS__; return false; } catch (...) { return true; } }() #define DOCTEST_CHECK_THROWS_MESSAGE(expr, ...) [&] { try { __VA_ARGS__; return false; } catch (...) { return true; } }() #define DOCTEST_REQUIRE_THROWS_MESSAGE(expr, ...) [&] { try { __VA_ARGS__; return false; } catch (...) { return true; } }() #define DOCTEST_WARN_THROWS_AS_MESSAGE(expr, ex, ...) [&] { try { expr; } catch (__VA_ARGS__) { return true; } catch (...) { } return false; }() #define DOCTEST_CHECK_THROWS_AS_MESSAGE(expr, ex, ...) [&] { try { expr; } catch (__VA_ARGS__) { return true; } catch (...) { } return false; }() #define DOCTEST_REQUIRE_THROWS_AS_MESSAGE(expr, ex, ...) [&] { try { expr; } catch (__VA_ARGS__) { return true; } catch (...) { } return false; }() #define DOCTEST_WARN_NOTHROW_MESSAGE(expr, ...) [&] { try { __VA_ARGS__; return true; } catch (...) { return false; } }() #define DOCTEST_CHECK_NOTHROW_MESSAGE(expr, ...) [&] { try { __VA_ARGS__; return true; } catch (...) { return false; } }() #define DOCTEST_REQUIRE_NOTHROW_MESSAGE(expr, ...) [&] { try { __VA_ARGS__; return true; } catch (...) { return false; } }() #endif // DOCTEST_CONFIG_NO_EXCEPTIONS #else // DOCTEST_CONFIG_EVALUATE_ASSERTS_EVEN_WHEN_DISABLED #define DOCTEST_WARN(...) DOCTEST_FUNC_EMPTY #define DOCTEST_CHECK(...) DOCTEST_FUNC_EMPTY #define DOCTEST_REQUIRE(...) DOCTEST_FUNC_EMPTY #define DOCTEST_WARN_FALSE(...) DOCTEST_FUNC_EMPTY #define DOCTEST_CHECK_FALSE(...) DOCTEST_FUNC_EMPTY #define DOCTEST_REQUIRE_FALSE(...) DOCTEST_FUNC_EMPTY #define DOCTEST_WARN_MESSAGE(cond, ...) DOCTEST_FUNC_EMPTY #define DOCTEST_CHECK_MESSAGE(cond, ...) DOCTEST_FUNC_EMPTY #define DOCTEST_REQUIRE_MESSAGE(cond, ...) DOCTEST_FUNC_EMPTY #define DOCTEST_WARN_FALSE_MESSAGE(cond, ...) DOCTEST_FUNC_EMPTY #define DOCTEST_CHECK_FALSE_MESSAGE(cond, ...) DOCTEST_FUNC_EMPTY #define DOCTEST_REQUIRE_FALSE_MESSAGE(cond, ...) DOCTEST_FUNC_EMPTY #define DOCTEST_WARN_EQ(...) DOCTEST_FUNC_EMPTY #define DOCTEST_CHECK_EQ(...) DOCTEST_FUNC_EMPTY #define DOCTEST_REQUIRE_EQ(...) DOCTEST_FUNC_EMPTY #define DOCTEST_WARN_NE(...) DOCTEST_FUNC_EMPTY #define DOCTEST_CHECK_NE(...) DOCTEST_FUNC_EMPTY #define DOCTEST_REQUIRE_NE(...) DOCTEST_FUNC_EMPTY #define DOCTEST_WARN_GT(...) DOCTEST_FUNC_EMPTY #define DOCTEST_CHECK_GT(...) DOCTEST_FUNC_EMPTY #define DOCTEST_REQUIRE_GT(...) DOCTEST_FUNC_EMPTY #define DOCTEST_WARN_LT(...) DOCTEST_FUNC_EMPTY #define DOCTEST_CHECK_LT(...) DOCTEST_FUNC_EMPTY #define DOCTEST_REQUIRE_LT(...) DOCTEST_FUNC_EMPTY #define DOCTEST_WARN_GE(...) DOCTEST_FUNC_EMPTY #define DOCTEST_CHECK_GE(...) DOCTEST_FUNC_EMPTY #define DOCTEST_REQUIRE_GE(...) DOCTEST_FUNC_EMPTY #define DOCTEST_WARN_LE(...) DOCTEST_FUNC_EMPTY #define DOCTEST_CHECK_LE(...) DOCTEST_FUNC_EMPTY #define DOCTEST_REQUIRE_LE(...) DOCTEST_FUNC_EMPTY #define DOCTEST_WARN_UNARY(...) DOCTEST_FUNC_EMPTY #define DOCTEST_CHECK_UNARY(...) DOCTEST_FUNC_EMPTY #define DOCTEST_REQUIRE_UNARY(...) DOCTEST_FUNC_EMPTY #define DOCTEST_WARN_UNARY_FALSE(...) DOCTEST_FUNC_EMPTY #define DOCTEST_CHECK_UNARY_FALSE(...) DOCTEST_FUNC_EMPTY #define DOCTEST_REQUIRE_UNARY_FALSE(...) DOCTEST_FUNC_EMPTY #ifndef DOCTEST_CONFIG_NO_EXCEPTIONS #define DOCTEST_WARN_THROWS(...) DOCTEST_FUNC_EMPTY #define DOCTEST_CHECK_THROWS(...) DOCTEST_FUNC_EMPTY #define DOCTEST_REQUIRE_THROWS(...) DOCTEST_FUNC_EMPTY #define DOCTEST_WARN_THROWS_AS(expr, ...) DOCTEST_FUNC_EMPTY #define DOCTEST_CHECK_THROWS_AS(expr, ...) DOCTEST_FUNC_EMPTY #define DOCTEST_REQUIRE_THROWS_AS(expr, ...) DOCTEST_FUNC_EMPTY #define DOCTEST_WARN_THROWS_WITH(expr, ...) DOCTEST_FUNC_EMPTY #define DOCTEST_CHECK_THROWS_WITH(expr, ...) DOCTEST_FUNC_EMPTY #define DOCTEST_REQUIRE_THROWS_WITH(expr, ...) DOCTEST_FUNC_EMPTY #define DOCTEST_WARN_THROWS_WITH_AS(expr, with, ...) DOCTEST_FUNC_EMPTY #define DOCTEST_CHECK_THROWS_WITH_AS(expr, with, ...) DOCTEST_FUNC_EMPTY #define DOCTEST_REQUIRE_THROWS_WITH_AS(expr, with, ...) DOCTEST_FUNC_EMPTY #define DOCTEST_WARN_NOTHROW(...) DOCTEST_FUNC_EMPTY #define DOCTEST_CHECK_NOTHROW(...) DOCTEST_FUNC_EMPTY #define DOCTEST_REQUIRE_NOTHROW(...) DOCTEST_FUNC_EMPTY #define DOCTEST_WARN_THROWS_MESSAGE(expr, ...) DOCTEST_FUNC_EMPTY #define DOCTEST_CHECK_THROWS_MESSAGE(expr, ...) DOCTEST_FUNC_EMPTY #define DOCTEST_REQUIRE_THROWS_MESSAGE(expr, ...) DOCTEST_FUNC_EMPTY #define DOCTEST_WARN_THROWS_AS_MESSAGE(expr, ex, ...) DOCTEST_FUNC_EMPTY #define DOCTEST_CHECK_THROWS_AS_MESSAGE(expr, ex, ...) DOCTEST_FUNC_EMPTY #define DOCTEST_REQUIRE_THROWS_AS_MESSAGE(expr, ex, ...) DOCTEST_FUNC_EMPTY #define DOCTEST_WARN_THROWS_WITH_MESSAGE(expr, with, ...) DOCTEST_FUNC_EMPTY #define DOCTEST_CHECK_THROWS_WITH_MESSAGE(expr, with, ...) DOCTEST_FUNC_EMPTY #define DOCTEST_REQUIRE_THROWS_WITH_MESSAGE(expr, with, ...) DOCTEST_FUNC_EMPTY #define DOCTEST_WARN_THROWS_WITH_AS_MESSAGE(expr, with, ex, ...) DOCTEST_FUNC_EMPTY #define DOCTEST_CHECK_THROWS_WITH_AS_MESSAGE(expr, with, ex, ...) DOCTEST_FUNC_EMPTY #define DOCTEST_REQUIRE_THROWS_WITH_AS_MESSAGE(expr, with, ex, ...) DOCTEST_FUNC_EMPTY #define DOCTEST_WARN_NOTHROW_MESSAGE(expr, ...) DOCTEST_FUNC_EMPTY #define DOCTEST_CHECK_NOTHROW_MESSAGE(expr, ...) DOCTEST_FUNC_EMPTY #define DOCTEST_REQUIRE_NOTHROW_MESSAGE(expr, ...) DOCTEST_FUNC_EMPTY #endif // DOCTEST_CONFIG_NO_EXCEPTIONS #endif // DOCTEST_CONFIG_EVALUATE_ASSERTS_EVEN_WHEN_DISABLED #endif // DOCTEST_CONFIG_DISABLE #ifdef DOCTEST_CONFIG_NO_EXCEPTIONS #ifdef DOCTEST_CONFIG_NO_EXCEPTIONS_BUT_WITH_ALL_ASSERTS #define DOCTEST_EXCEPTION_EMPTY_FUNC DOCTEST_FUNC_EMPTY #else // DOCTEST_CONFIG_NO_EXCEPTIONS_BUT_WITH_ALL_ASSERTS #define DOCTEST_EXCEPTION_EMPTY_FUNC [] { static_assert(false, "Exceptions are disabled! " \ "Use DOCTEST_CONFIG_NO_EXCEPTIONS_BUT_WITH_ALL_ASSERTS if you want to compile with exceptions disabled."); return false; }() #undef DOCTEST_REQUIRE #undef DOCTEST_REQUIRE_FALSE #undef DOCTEST_REQUIRE_MESSAGE #undef DOCTEST_REQUIRE_FALSE_MESSAGE #undef DOCTEST_REQUIRE_EQ #undef DOCTEST_REQUIRE_NE #undef DOCTEST_REQUIRE_GT #undef DOCTEST_REQUIRE_LT #undef DOCTEST_REQUIRE_GE #undef DOCTEST_REQUIRE_LE #undef DOCTEST_REQUIRE_UNARY #undef DOCTEST_REQUIRE_UNARY_FALSE #define DOCTEST_REQUIRE DOCTEST_EXCEPTION_EMPTY_FUNC #define DOCTEST_REQUIRE_FALSE DOCTEST_EXCEPTION_EMPTY_FUNC #define DOCTEST_REQUIRE_MESSAGE DOCTEST_EXCEPTION_EMPTY_FUNC #define DOCTEST_REQUIRE_FALSE_MESSAGE DOCTEST_EXCEPTION_EMPTY_FUNC #define DOCTEST_REQUIRE_EQ DOCTEST_EXCEPTION_EMPTY_FUNC #define DOCTEST_REQUIRE_NE DOCTEST_EXCEPTION_EMPTY_FUNC #define DOCTEST_REQUIRE_GT DOCTEST_EXCEPTION_EMPTY_FUNC #define DOCTEST_REQUIRE_LT DOCTEST_EXCEPTION_EMPTY_FUNC #define DOCTEST_REQUIRE_GE DOCTEST_EXCEPTION_EMPTY_FUNC #define DOCTEST_REQUIRE_LE DOCTEST_EXCEPTION_EMPTY_FUNC #define DOCTEST_REQUIRE_UNARY DOCTEST_EXCEPTION_EMPTY_FUNC #define DOCTEST_REQUIRE_UNARY_FALSE DOCTEST_EXCEPTION_EMPTY_FUNC #endif // DOCTEST_CONFIG_NO_EXCEPTIONS_BUT_WITH_ALL_ASSERTS #define DOCTEST_WARN_THROWS(...) DOCTEST_EXCEPTION_EMPTY_FUNC #define DOCTEST_CHECK_THROWS(...) DOCTEST_EXCEPTION_EMPTY_FUNC #define DOCTEST_REQUIRE_THROWS(...) DOCTEST_EXCEPTION_EMPTY_FUNC #define DOCTEST_WARN_THROWS_AS(expr, ...) DOCTEST_EXCEPTION_EMPTY_FUNC #define DOCTEST_CHECK_THROWS_AS(expr, ...) DOCTEST_EXCEPTION_EMPTY_FUNC #define DOCTEST_REQUIRE_THROWS_AS(expr, ...) DOCTEST_EXCEPTION_EMPTY_FUNC #define DOCTEST_WARN_THROWS_WITH(expr, ...) DOCTEST_EXCEPTION_EMPTY_FUNC #define DOCTEST_CHECK_THROWS_WITH(expr, ...) DOCTEST_EXCEPTION_EMPTY_FUNC #define DOCTEST_REQUIRE_THROWS_WITH(expr, ...) DOCTEST_EXCEPTION_EMPTY_FUNC #define DOCTEST_WARN_THROWS_WITH_AS(expr, with, ...) DOCTEST_EXCEPTION_EMPTY_FUNC #define DOCTEST_CHECK_THROWS_WITH_AS(expr, with, ...) DOCTEST_EXCEPTION_EMPTY_FUNC #define DOCTEST_REQUIRE_THROWS_WITH_AS(expr, with, ...) DOCTEST_EXCEPTION_EMPTY_FUNC #define DOCTEST_WARN_NOTHROW(...) DOCTEST_EXCEPTION_EMPTY_FUNC #define DOCTEST_CHECK_NOTHROW(...) DOCTEST_EXCEPTION_EMPTY_FUNC #define DOCTEST_REQUIRE_NOTHROW(...) DOCTEST_EXCEPTION_EMPTY_FUNC #define DOCTEST_WARN_THROWS_MESSAGE(expr, ...) DOCTEST_EXCEPTION_EMPTY_FUNC #define DOCTEST_CHECK_THROWS_MESSAGE(expr, ...) DOCTEST_EXCEPTION_EMPTY_FUNC #define DOCTEST_REQUIRE_THROWS_MESSAGE(expr, ...) DOCTEST_EXCEPTION_EMPTY_FUNC #define DOCTEST_WARN_THROWS_AS_MESSAGE(expr, ex, ...) DOCTEST_EXCEPTION_EMPTY_FUNC #define DOCTEST_CHECK_THROWS_AS_MESSAGE(expr, ex, ...) DOCTEST_EXCEPTION_EMPTY_FUNC #define DOCTEST_REQUIRE_THROWS_AS_MESSAGE(expr, ex, ...) DOCTEST_EXCEPTION_EMPTY_FUNC #define DOCTEST_WARN_THROWS_WITH_MESSAGE(expr, with, ...) DOCTEST_EXCEPTION_EMPTY_FUNC #define DOCTEST_CHECK_THROWS_WITH_MESSAGE(expr, with, ...) DOCTEST_EXCEPTION_EMPTY_FUNC #define DOCTEST_REQUIRE_THROWS_WITH_MESSAGE(expr, with, ...) DOCTEST_EXCEPTION_EMPTY_FUNC #define DOCTEST_WARN_THROWS_WITH_AS_MESSAGE(expr, with, ex, ...) DOCTEST_EXCEPTION_EMPTY_FUNC #define DOCTEST_CHECK_THROWS_WITH_AS_MESSAGE(expr, with, ex, ...) DOCTEST_EXCEPTION_EMPTY_FUNC #define DOCTEST_REQUIRE_THROWS_WITH_AS_MESSAGE(expr, with, ex, ...) DOCTEST_EXCEPTION_EMPTY_FUNC #define DOCTEST_WARN_NOTHROW_MESSAGE(expr, ...) DOCTEST_EXCEPTION_EMPTY_FUNC #define DOCTEST_CHECK_NOTHROW_MESSAGE(expr, ...) DOCTEST_EXCEPTION_EMPTY_FUNC #define DOCTEST_REQUIRE_NOTHROW_MESSAGE(expr, ...) DOCTEST_EXCEPTION_EMPTY_FUNC #endif // DOCTEST_CONFIG_NO_EXCEPTIONS // clang-format off // KEPT FOR BACKWARDS COMPATIBILITY - FORWARDING TO THE RIGHT MACROS #define DOCTEST_FAST_WARN_EQ DOCTEST_WARN_EQ #define DOCTEST_FAST_CHECK_EQ DOCTEST_CHECK_EQ #define DOCTEST_FAST_REQUIRE_EQ DOCTEST_REQUIRE_EQ #define DOCTEST_FAST_WARN_NE DOCTEST_WARN_NE #define DOCTEST_FAST_CHECK_NE DOCTEST_CHECK_NE #define DOCTEST_FAST_REQUIRE_NE DOCTEST_REQUIRE_NE #define DOCTEST_FAST_WARN_GT DOCTEST_WARN_GT #define DOCTEST_FAST_CHECK_GT DOCTEST_CHECK_GT #define DOCTEST_FAST_REQUIRE_GT DOCTEST_REQUIRE_GT #define DOCTEST_FAST_WARN_LT DOCTEST_WARN_LT #define DOCTEST_FAST_CHECK_LT DOCTEST_CHECK_LT #define DOCTEST_FAST_REQUIRE_LT DOCTEST_REQUIRE_LT #define DOCTEST_FAST_WARN_GE DOCTEST_WARN_GE #define DOCTEST_FAST_CHECK_GE DOCTEST_CHECK_GE #define DOCTEST_FAST_REQUIRE_GE DOCTEST_REQUIRE_GE #define DOCTEST_FAST_WARN_LE DOCTEST_WARN_LE #define DOCTEST_FAST_CHECK_LE DOCTEST_CHECK_LE #define DOCTEST_FAST_REQUIRE_LE DOCTEST_REQUIRE_LE #define DOCTEST_FAST_WARN_UNARY DOCTEST_WARN_UNARY #define DOCTEST_FAST_CHECK_UNARY DOCTEST_CHECK_UNARY #define DOCTEST_FAST_REQUIRE_UNARY DOCTEST_REQUIRE_UNARY #define DOCTEST_FAST_WARN_UNARY_FALSE DOCTEST_WARN_UNARY_FALSE #define DOCTEST_FAST_CHECK_UNARY_FALSE DOCTEST_CHECK_UNARY_FALSE #define DOCTEST_FAST_REQUIRE_UNARY_FALSE DOCTEST_REQUIRE_UNARY_FALSE #define DOCTEST_TEST_CASE_TEMPLATE_INSTANTIATE(id, ...) DOCTEST_TEST_CASE_TEMPLATE_INVOKE(id,__VA_ARGS__) // clang-format on // BDD style macros // clang-format off #define DOCTEST_SCENARIO(name) DOCTEST_TEST_CASE(" Scenario: " name) #define DOCTEST_SCENARIO_CLASS(name) DOCTEST_TEST_CASE_CLASS(" Scenario: " name) #define DOCTEST_SCENARIO_TEMPLATE(name, T, ...) DOCTEST_TEST_CASE_TEMPLATE(" Scenario: " name, T, __VA_ARGS__) #define DOCTEST_SCENARIO_TEMPLATE_DEFINE(name, T, id) DOCTEST_TEST_CASE_TEMPLATE_DEFINE(" Scenario: " name, T, id) #define DOCTEST_GIVEN(name) DOCTEST_SUBCASE(" Given: " name) #define DOCTEST_WHEN(name) DOCTEST_SUBCASE(" When: " name) #define DOCTEST_AND_WHEN(name) DOCTEST_SUBCASE("And when: " name) #define DOCTEST_THEN(name) DOCTEST_SUBCASE(" Then: " name) #define DOCTEST_AND_THEN(name) DOCTEST_SUBCASE(" And: " name) // clang-format on // == SHORT VERSIONS OF THE MACROS #ifndef DOCTEST_CONFIG_NO_SHORT_MACRO_NAMES #define TEST_CASE(name) DOCTEST_TEST_CASE(name) #define TEST_CASE_CLASS(name) DOCTEST_TEST_CASE_CLASS(name) #define TEST_CASE_FIXTURE(x, name) DOCTEST_TEST_CASE_FIXTURE(x, name) #define TYPE_TO_STRING_AS(str, ...) DOCTEST_TYPE_TO_STRING_AS(str, __VA_ARGS__) #define TYPE_TO_STRING(...) DOCTEST_TYPE_TO_STRING(__VA_ARGS__) #define TEST_CASE_TEMPLATE(name, T, ...) DOCTEST_TEST_CASE_TEMPLATE(name, T, __VA_ARGS__) #define TEST_CASE_TEMPLATE_DEFINE(name, T, id) DOCTEST_TEST_CASE_TEMPLATE_DEFINE(name, T, id) #define TEST_CASE_TEMPLATE_INVOKE(id, ...) DOCTEST_TEST_CASE_TEMPLATE_INVOKE(id, __VA_ARGS__) #define TEST_CASE_TEMPLATE_APPLY(id, ...) DOCTEST_TEST_CASE_TEMPLATE_APPLY(id, __VA_ARGS__) #define SUBCASE(name) DOCTEST_SUBCASE(name) #define TEST_SUITE(decorators) DOCTEST_TEST_SUITE(decorators) #define TEST_SUITE_BEGIN(name) DOCTEST_TEST_SUITE_BEGIN(name) #define TEST_SUITE_END DOCTEST_TEST_SUITE_END #define REGISTER_EXCEPTION_TRANSLATOR(signature) DOCTEST_REGISTER_EXCEPTION_TRANSLATOR(signature) #define REGISTER_REPORTER(name, priority, reporter) DOCTEST_REGISTER_REPORTER(name, priority, reporter) #define REGISTER_LISTENER(name, priority, reporter) DOCTEST_REGISTER_LISTENER(name, priority, reporter) #define INFO(...) DOCTEST_INFO(__VA_ARGS__) #define CAPTURE(x) DOCTEST_CAPTURE(x) #define ADD_MESSAGE_AT(file, line, ...) DOCTEST_ADD_MESSAGE_AT(file, line, __VA_ARGS__) #define ADD_FAIL_CHECK_AT(file, line, ...) DOCTEST_ADD_FAIL_CHECK_AT(file, line, __VA_ARGS__) #define ADD_FAIL_AT(file, line, ...) DOCTEST_ADD_FAIL_AT(file, line, __VA_ARGS__) #define MESSAGE(...) DOCTEST_MESSAGE(__VA_ARGS__) #define FAIL_CHECK(...) DOCTEST_FAIL_CHECK(__VA_ARGS__) #define FAIL(...) DOCTEST_FAIL(__VA_ARGS__) #define TO_LVALUE(...) DOCTEST_TO_LVALUE(__VA_ARGS__) #define WARN(...) DOCTEST_WARN(__VA_ARGS__) #define WARN_FALSE(...) DOCTEST_WARN_FALSE(__VA_ARGS__) #define WARN_THROWS(...) DOCTEST_WARN_THROWS(__VA_ARGS__) #define WARN_THROWS_AS(expr, ...) DOCTEST_WARN_THROWS_AS(expr, __VA_ARGS__) #define WARN_THROWS_WITH(expr, ...) DOCTEST_WARN_THROWS_WITH(expr, __VA_ARGS__) #define WARN_THROWS_WITH_AS(expr, with, ...) DOCTEST_WARN_THROWS_WITH_AS(expr, with, __VA_ARGS__) #define WARN_NOTHROW(...) DOCTEST_WARN_NOTHROW(__VA_ARGS__) #define CHECK(...) DOCTEST_CHECK(__VA_ARGS__) #define CHECK_FALSE(...) DOCTEST_CHECK_FALSE(__VA_ARGS__) #define CHECK_THROWS(...) DOCTEST_CHECK_THROWS(__VA_ARGS__) #define CHECK_THROWS_AS(expr, ...) DOCTEST_CHECK_THROWS_AS(expr, __VA_ARGS__) #define CHECK_THROWS_WITH(expr, ...) DOCTEST_CHECK_THROWS_WITH(expr, __VA_ARGS__) #define CHECK_THROWS_WITH_AS(expr, with, ...) DOCTEST_CHECK_THROWS_WITH_AS(expr, with, __VA_ARGS__) #define CHECK_NOTHROW(...) DOCTEST_CHECK_NOTHROW(__VA_ARGS__) #define REQUIRE(...) DOCTEST_REQUIRE(__VA_ARGS__) #define REQUIRE_FALSE(...) DOCTEST_REQUIRE_FALSE(__VA_ARGS__) #define REQUIRE_THROWS(...) DOCTEST_REQUIRE_THROWS(__VA_ARGS__) #define REQUIRE_THROWS_AS(expr, ...) DOCTEST_REQUIRE_THROWS_AS(expr, __VA_ARGS__) #define REQUIRE_THROWS_WITH(expr, ...) DOCTEST_REQUIRE_THROWS_WITH(expr, __VA_ARGS__) #define REQUIRE_THROWS_WITH_AS(expr, with, ...) DOCTEST_REQUIRE_THROWS_WITH_AS(expr, with, __VA_ARGS__) #define REQUIRE_NOTHROW(...) DOCTEST_REQUIRE_NOTHROW(__VA_ARGS__) #define WARN_MESSAGE(cond, ...) DOCTEST_WARN_MESSAGE(cond, __VA_ARGS__) #define WARN_FALSE_MESSAGE(cond, ...) DOCTEST_WARN_FALSE_MESSAGE(cond, __VA_ARGS__) #define WARN_THROWS_MESSAGE(expr, ...) DOCTEST_WARN_THROWS_MESSAGE(expr, __VA_ARGS__) #define WARN_THROWS_AS_MESSAGE(expr, ex, ...) DOCTEST_WARN_THROWS_AS_MESSAGE(expr, ex, __VA_ARGS__) #define WARN_THROWS_WITH_MESSAGE(expr, with, ...) DOCTEST_WARN_THROWS_WITH_MESSAGE(expr, with, __VA_ARGS__) #define WARN_THROWS_WITH_AS_MESSAGE(expr, with, ex, ...) DOCTEST_WARN_THROWS_WITH_AS_MESSAGE(expr, with, ex, __VA_ARGS__) #define WARN_NOTHROW_MESSAGE(expr, ...) DOCTEST_WARN_NOTHROW_MESSAGE(expr, __VA_ARGS__) #define CHECK_MESSAGE(cond, ...) DOCTEST_CHECK_MESSAGE(cond, __VA_ARGS__) #define CHECK_FALSE_MESSAGE(cond, ...) DOCTEST_CHECK_FALSE_MESSAGE(cond, __VA_ARGS__) #define CHECK_THROWS_MESSAGE(expr, ...) DOCTEST_CHECK_THROWS_MESSAGE(expr, __VA_ARGS__) #define CHECK_THROWS_AS_MESSAGE(expr, ex, ...) DOCTEST_CHECK_THROWS_AS_MESSAGE(expr, ex, __VA_ARGS__) #define CHECK_THROWS_WITH_MESSAGE(expr, with, ...) DOCTEST_CHECK_THROWS_WITH_MESSAGE(expr, with, __VA_ARGS__) #define CHECK_THROWS_WITH_AS_MESSAGE(expr, with, ex, ...) DOCTEST_CHECK_THROWS_WITH_AS_MESSAGE(expr, with, ex, __VA_ARGS__) #define CHECK_NOTHROW_MESSAGE(expr, ...) DOCTEST_CHECK_NOTHROW_MESSAGE(expr, __VA_ARGS__) #define REQUIRE_MESSAGE(cond, ...) DOCTEST_REQUIRE_MESSAGE(cond, __VA_ARGS__) #define REQUIRE_FALSE_MESSAGE(cond, ...) DOCTEST_REQUIRE_FALSE_MESSAGE(cond, __VA_ARGS__) #define REQUIRE_THROWS_MESSAGE(expr, ...) DOCTEST_REQUIRE_THROWS_MESSAGE(expr, __VA_ARGS__) #define REQUIRE_THROWS_AS_MESSAGE(expr, ex, ...) DOCTEST_REQUIRE_THROWS_AS_MESSAGE(expr, ex, __VA_ARGS__) #define REQUIRE_THROWS_WITH_MESSAGE(expr, with, ...) DOCTEST_REQUIRE_THROWS_WITH_MESSAGE(expr, with, __VA_ARGS__) #define REQUIRE_THROWS_WITH_AS_MESSAGE(expr, with, ex, ...) DOCTEST_REQUIRE_THROWS_WITH_AS_MESSAGE(expr, with, ex, __VA_ARGS__) #define REQUIRE_NOTHROW_MESSAGE(expr, ...) DOCTEST_REQUIRE_NOTHROW_MESSAGE(expr, __VA_ARGS__) #define SCENARIO(name) DOCTEST_SCENARIO(name) #define SCENARIO_CLASS(name) DOCTEST_SCENARIO_CLASS(name) #define SCENARIO_TEMPLATE(name, T, ...) DOCTEST_SCENARIO_TEMPLATE(name, T, __VA_ARGS__) #define SCENARIO_TEMPLATE_DEFINE(name, T, id) DOCTEST_SCENARIO_TEMPLATE_DEFINE(name, T, id) #define GIVEN(name) DOCTEST_GIVEN(name) #define WHEN(name) DOCTEST_WHEN(name) #define AND_WHEN(name) DOCTEST_AND_WHEN(name) #define THEN(name) DOCTEST_THEN(name) #define AND_THEN(name) DOCTEST_AND_THEN(name) #define WARN_EQ(...) DOCTEST_WARN_EQ(__VA_ARGS__) #define CHECK_EQ(...) DOCTEST_CHECK_EQ(__VA_ARGS__) #define REQUIRE_EQ(...) DOCTEST_REQUIRE_EQ(__VA_ARGS__) #define WARN_NE(...) DOCTEST_WARN_NE(__VA_ARGS__) #define CHECK_NE(...) DOCTEST_CHECK_NE(__VA_ARGS__) #define REQUIRE_NE(...) DOCTEST_REQUIRE_NE(__VA_ARGS__) #define WARN_GT(...) DOCTEST_WARN_GT(__VA_ARGS__) #define CHECK_GT(...) DOCTEST_CHECK_GT(__VA_ARGS__) #define REQUIRE_GT(...) DOCTEST_REQUIRE_GT(__VA_ARGS__) #define WARN_LT(...) DOCTEST_WARN_LT(__VA_ARGS__) #define CHECK_LT(...) DOCTEST_CHECK_LT(__VA_ARGS__) #define REQUIRE_LT(...) DOCTEST_REQUIRE_LT(__VA_ARGS__) #define WARN_GE(...) DOCTEST_WARN_GE(__VA_ARGS__) #define CHECK_GE(...) DOCTEST_CHECK_GE(__VA_ARGS__) #define REQUIRE_GE(...) DOCTEST_REQUIRE_GE(__VA_ARGS__) #define WARN_LE(...) DOCTEST_WARN_LE(__VA_ARGS__) #define CHECK_LE(...) DOCTEST_CHECK_LE(__VA_ARGS__) #define REQUIRE_LE(...) DOCTEST_REQUIRE_LE(__VA_ARGS__) #define WARN_UNARY(...) DOCTEST_WARN_UNARY(__VA_ARGS__) #define CHECK_UNARY(...) DOCTEST_CHECK_UNARY(__VA_ARGS__) #define REQUIRE_UNARY(...) DOCTEST_REQUIRE_UNARY(__VA_ARGS__) #define WARN_UNARY_FALSE(...) DOCTEST_WARN_UNARY_FALSE(__VA_ARGS__) #define CHECK_UNARY_FALSE(...) DOCTEST_CHECK_UNARY_FALSE(__VA_ARGS__) #define REQUIRE_UNARY_FALSE(...) DOCTEST_REQUIRE_UNARY_FALSE(__VA_ARGS__) // KEPT FOR BACKWARDS COMPATIBILITY #define FAST_WARN_EQ(...) DOCTEST_FAST_WARN_EQ(__VA_ARGS__) #define FAST_CHECK_EQ(...) DOCTEST_FAST_CHECK_EQ(__VA_ARGS__) #define FAST_REQUIRE_EQ(...) DOCTEST_FAST_REQUIRE_EQ(__VA_ARGS__) #define FAST_WARN_NE(...) DOCTEST_FAST_WARN_NE(__VA_ARGS__) #define FAST_CHECK_NE(...) DOCTEST_FAST_CHECK_NE(__VA_ARGS__) #define FAST_REQUIRE_NE(...) DOCTEST_FAST_REQUIRE_NE(__VA_ARGS__) #define FAST_WARN_GT(...) DOCTEST_FAST_WARN_GT(__VA_ARGS__) #define FAST_CHECK_GT(...) DOCTEST_FAST_CHECK_GT(__VA_ARGS__) #define FAST_REQUIRE_GT(...) DOCTEST_FAST_REQUIRE_GT(__VA_ARGS__) #define FAST_WARN_LT(...) DOCTEST_FAST_WARN_LT(__VA_ARGS__) #define FAST_CHECK_LT(...) DOCTEST_FAST_CHECK_LT(__VA_ARGS__) #define FAST_REQUIRE_LT(...) DOCTEST_FAST_REQUIRE_LT(__VA_ARGS__) #define FAST_WARN_GE(...) DOCTEST_FAST_WARN_GE(__VA_ARGS__) #define FAST_CHECK_GE(...) DOCTEST_FAST_CHECK_GE(__VA_ARGS__) #define FAST_REQUIRE_GE(...) DOCTEST_FAST_REQUIRE_GE(__VA_ARGS__) #define FAST_WARN_LE(...) DOCTEST_FAST_WARN_LE(__VA_ARGS__) #define FAST_CHECK_LE(...) DOCTEST_FAST_CHECK_LE(__VA_ARGS__) #define FAST_REQUIRE_LE(...) DOCTEST_FAST_REQUIRE_LE(__VA_ARGS__) #define FAST_WARN_UNARY(...) DOCTEST_FAST_WARN_UNARY(__VA_ARGS__) #define FAST_CHECK_UNARY(...) DOCTEST_FAST_CHECK_UNARY(__VA_ARGS__) #define FAST_REQUIRE_UNARY(...) DOCTEST_FAST_REQUIRE_UNARY(__VA_ARGS__) #define FAST_WARN_UNARY_FALSE(...) DOCTEST_FAST_WARN_UNARY_FALSE(__VA_ARGS__) #define FAST_CHECK_UNARY_FALSE(...) DOCTEST_FAST_CHECK_UNARY_FALSE(__VA_ARGS__) #define FAST_REQUIRE_UNARY_FALSE(...) DOCTEST_FAST_REQUIRE_UNARY_FALSE(__VA_ARGS__) #define TEST_CASE_TEMPLATE_INSTANTIATE(id, ...) DOCTEST_TEST_CASE_TEMPLATE_INSTANTIATE(id, __VA_ARGS__) #endif // DOCTEST_CONFIG_NO_SHORT_MACRO_NAMES #ifndef DOCTEST_CONFIG_DISABLE // this is here to clear the 'current test suite' for the current translation unit - at the top DOCTEST_TEST_SUITE_END(); #endif // DOCTEST_CONFIG_DISABLE DOCTEST_CLANG_SUPPRESS_WARNING_POP DOCTEST_MSVC_SUPPRESS_WARNING_POP DOCTEST_GCC_SUPPRESS_WARNING_POP DOCTEST_SUPPRESS_COMMON_WARNINGS_POP #endif // DOCTEST_LIBRARY_INCLUDED #ifndef DOCTEST_SINGLE_HEADER #define DOCTEST_SINGLE_HEADER #endif // DOCTEST_SINGLE_HEADER #if defined(DOCTEST_CONFIG_IMPLEMENT) || !defined(DOCTEST_SINGLE_HEADER) #ifndef DOCTEST_SINGLE_HEADER #include "doctest_fwd.h" #endif // DOCTEST_SINGLE_HEADER DOCTEST_CLANG_SUPPRESS_WARNING_WITH_PUSH("-Wunused-macros") #ifndef DOCTEST_LIBRARY_IMPLEMENTATION #define DOCTEST_LIBRARY_IMPLEMENTATION DOCTEST_CLANG_SUPPRESS_WARNING_POP DOCTEST_SUPPRESS_COMMON_WARNINGS_PUSH DOCTEST_CLANG_SUPPRESS_WARNING_PUSH DOCTEST_CLANG_SUPPRESS_WARNING("-Wglobal-constructors") DOCTEST_CLANG_SUPPRESS_WARNING("-Wexit-time-destructors") DOCTEST_CLANG_SUPPRESS_WARNING("-Wsign-conversion") DOCTEST_CLANG_SUPPRESS_WARNING("-Wshorten-64-to-32") DOCTEST_CLANG_SUPPRESS_WARNING("-Wmissing-variable-declarations") DOCTEST_CLANG_SUPPRESS_WARNING("-Wswitch") DOCTEST_CLANG_SUPPRESS_WARNING("-Wswitch-enum") DOCTEST_CLANG_SUPPRESS_WARNING("-Wcovered-switch-default") DOCTEST_CLANG_SUPPRESS_WARNING("-Wmissing-noreturn") DOCTEST_CLANG_SUPPRESS_WARNING("-Wdisabled-macro-expansion") DOCTEST_CLANG_SUPPRESS_WARNING("-Wmissing-braces") DOCTEST_CLANG_SUPPRESS_WARNING("-Wmissing-field-initializers") DOCTEST_CLANG_SUPPRESS_WARNING("-Wunused-member-function") DOCTEST_CLANG_SUPPRESS_WARNING("-Wnonportable-system-include-path") DOCTEST_GCC_SUPPRESS_WARNING_PUSH DOCTEST_GCC_SUPPRESS_WARNING("-Wconversion") DOCTEST_GCC_SUPPRESS_WARNING("-Wsign-conversion") DOCTEST_GCC_SUPPRESS_WARNING("-Wmissing-field-initializers") DOCTEST_GCC_SUPPRESS_WARNING("-Wmissing-braces") DOCTEST_GCC_SUPPRESS_WARNING("-Wswitch") DOCTEST_GCC_SUPPRESS_WARNING("-Wswitch-enum") DOCTEST_GCC_SUPPRESS_WARNING("-Wswitch-default") DOCTEST_GCC_SUPPRESS_WARNING("-Wunsafe-loop-optimizations") DOCTEST_GCC_SUPPRESS_WARNING("-Wold-style-cast") DOCTEST_GCC_SUPPRESS_WARNING("-Wunused-function") DOCTEST_GCC_SUPPRESS_WARNING("-Wmultiple-inheritance") DOCTEST_GCC_SUPPRESS_WARNING("-Wsuggest-attribute") DOCTEST_MSVC_SUPPRESS_WARNING_PUSH DOCTEST_MSVC_SUPPRESS_WARNING(4267) // 'var' : conversion from 'x' to 'y', possible loss of data DOCTEST_MSVC_SUPPRESS_WARNING(4530) // C++ exception handler used, but unwind semantics not enabled DOCTEST_MSVC_SUPPRESS_WARNING(4577) // 'noexcept' used with no exception handling mode specified DOCTEST_MSVC_SUPPRESS_WARNING(4774) // format string expected in argument is not a string literal DOCTEST_MSVC_SUPPRESS_WARNING(4365) // conversion from 'int' to 'unsigned', signed/unsigned mismatch DOCTEST_MSVC_SUPPRESS_WARNING(5039) // pointer to potentially throwing function passed to extern C DOCTEST_MSVC_SUPPRESS_WARNING(4800) // forcing value to bool 'true' or 'false' (performance warning) DOCTEST_MSVC_SUPPRESS_WARNING(5245) // unreferenced function with internal linkage has been removed DOCTEST_MAKE_STD_HEADERS_CLEAN_FROM_WARNINGS_ON_WALL_BEGIN // required includes - will go only in one translation unit! #include #include #include // borland (Embarcadero) compiler requires math.h and not cmath - https://github.com/doctest/doctest/pull/37 #ifdef __BORLANDC__ #include #endif // __BORLANDC__ #include #include #include #include #include #include #include #include #ifndef DOCTEST_CONFIG_NO_INCLUDE_IOSTREAM #include #endif // DOCTEST_CONFIG_NO_INCLUDE_IOSTREAM #include #include #include #ifndef DOCTEST_CONFIG_NO_MULTITHREADING #include #include #define DOCTEST_DECLARE_MUTEX(name) std::mutex name; #define DOCTEST_DECLARE_STATIC_MUTEX(name) static DOCTEST_DECLARE_MUTEX(name) #define DOCTEST_LOCK_MUTEX(name) std::lock_guard DOCTEST_ANONYMOUS(DOCTEST_ANON_LOCK_)(name); #else // DOCTEST_CONFIG_NO_MULTITHREADING #define DOCTEST_DECLARE_MUTEX(name) #define DOCTEST_DECLARE_STATIC_MUTEX(name) #define DOCTEST_LOCK_MUTEX(name) #endif // DOCTEST_CONFIG_NO_MULTITHREADING #include #include #include #include #include #include #include #include #include #include #ifdef DOCTEST_PLATFORM_MAC #include #include #include #endif // DOCTEST_PLATFORM_MAC #ifdef DOCTEST_PLATFORM_WINDOWS // defines for a leaner windows.h #ifndef WIN32_LEAN_AND_MEAN #define WIN32_LEAN_AND_MEAN #define DOCTEST_UNDEF_WIN32_LEAN_AND_MEAN #endif // WIN32_LEAN_AND_MEAN #ifndef NOMINMAX #define NOMINMAX #define DOCTEST_UNDEF_NOMINMAX #endif // NOMINMAX // not sure what AfxWin.h is for - here I do what Catch does #ifdef __AFXDLL #include #else #include #endif #include #else // DOCTEST_PLATFORM_WINDOWS #include #include #endif // DOCTEST_PLATFORM_WINDOWS // this is a fix for https://github.com/doctest/doctest/issues/348 // https://mail.gnome.org/archives/xml/2012-January/msg00000.html #if !defined(HAVE_UNISTD_H) && !defined(STDOUT_FILENO) #define STDOUT_FILENO fileno(stdout) #endif // HAVE_UNISTD_H DOCTEST_MAKE_STD_HEADERS_CLEAN_FROM_WARNINGS_ON_WALL_END // counts the number of elements in a C array #define DOCTEST_COUNTOF(x) (sizeof(x) / sizeof(x[0])) #ifdef DOCTEST_CONFIG_DISABLE #define DOCTEST_BRANCH_ON_DISABLED(if_disabled, if_not_disabled) if_disabled #else // DOCTEST_CONFIG_DISABLE #define DOCTEST_BRANCH_ON_DISABLED(if_disabled, if_not_disabled) if_not_disabled #endif // DOCTEST_CONFIG_DISABLE #ifndef DOCTEST_CONFIG_OPTIONS_PREFIX #define DOCTEST_CONFIG_OPTIONS_PREFIX "dt-" #endif #ifndef DOCTEST_THREAD_LOCAL #if defined(DOCTEST_CONFIG_NO_MULTITHREADING) || DOCTEST_MSVC && (DOCTEST_MSVC < DOCTEST_COMPILER(19, 0, 0)) #define DOCTEST_THREAD_LOCAL #else // DOCTEST_MSVC #define DOCTEST_THREAD_LOCAL thread_local #endif // DOCTEST_MSVC #endif // DOCTEST_THREAD_LOCAL #ifndef DOCTEST_MULTI_LANE_ATOMICS_THREAD_LANES #define DOCTEST_MULTI_LANE_ATOMICS_THREAD_LANES 32 #endif #ifndef DOCTEST_MULTI_LANE_ATOMICS_CACHE_LINE_SIZE #define DOCTEST_MULTI_LANE_ATOMICS_CACHE_LINE_SIZE 64 #endif #ifdef DOCTEST_CONFIG_NO_UNPREFIXED_OPTIONS #define DOCTEST_OPTIONS_PREFIX_DISPLAY DOCTEST_CONFIG_OPTIONS_PREFIX #else #define DOCTEST_OPTIONS_PREFIX_DISPLAY "" #endif #if defined(WINAPI_FAMILY) && (WINAPI_FAMILY == WINAPI_FAMILY_APP) #define DOCTEST_CONFIG_NO_MULTI_LANE_ATOMICS #endif #ifndef DOCTEST_CDECL #define DOCTEST_CDECL __cdecl #endif namespace doctest { bool is_running_in_test = false; namespace { using namespace detail; template DOCTEST_NORETURN void throw_exception(Ex const& e) { #ifndef DOCTEST_CONFIG_NO_EXCEPTIONS throw e; #else // DOCTEST_CONFIG_NO_EXCEPTIONS #ifdef DOCTEST_CONFIG_HANDLE_EXCEPTION DOCTEST_CONFIG_HANDLE_EXCEPTION(e); #else // DOCTEST_CONFIG_HANDLE_EXCEPTION #ifndef DOCTEST_CONFIG_NO_INCLUDE_IOSTREAM std::cerr << "doctest will terminate because it needed to throw an exception.\n" << "The message was: " << e.what() << '\n'; #endif // DOCTEST_CONFIG_NO_INCLUDE_IOSTREAM #endif // DOCTEST_CONFIG_HANDLE_EXCEPTION std::terminate(); #endif // DOCTEST_CONFIG_NO_EXCEPTIONS } #ifndef DOCTEST_INTERNAL_ERROR #define DOCTEST_INTERNAL_ERROR(msg) \ throw_exception(std::logic_error( \ __FILE__ ":" DOCTEST_TOSTR(__LINE__) ": Internal doctest error: " msg)) #endif // DOCTEST_INTERNAL_ERROR // case insensitive strcmp int stricmp(const char* a, const char* b) { for(;; a++, b++) { const int d = tolower(*a) - tolower(*b); if(d != 0 || !*a) return d; } } struct Endianness { enum Arch { Big, Little }; static Arch which() { int x = 1; // casting any data pointer to char* is allowed auto ptr = reinterpret_cast(&x); if(*ptr) return Little; return Big; } }; } // namespace namespace detail { DOCTEST_THREAD_LOCAL class { std::vector stack; std::stringstream ss; public: std::ostream* push() { stack.push_back(ss.tellp()); return &ss; } String pop() { if (stack.empty()) DOCTEST_INTERNAL_ERROR("TLSS was empty when trying to pop!"); std::streampos pos = stack.back(); stack.pop_back(); unsigned sz = static_cast(ss.tellp() - pos); ss.rdbuf()->pubseekpos(pos, std::ios::in | std::ios::out); return String(ss, sz); } } g_oss; std::ostream* tlssPush() { return g_oss.push(); } String tlssPop() { return g_oss.pop(); } #ifndef DOCTEST_CONFIG_DISABLE namespace timer_large_integer { #if defined(DOCTEST_PLATFORM_WINDOWS) using type = ULONGLONG; #else // DOCTEST_PLATFORM_WINDOWS using type = std::uint64_t; #endif // DOCTEST_PLATFORM_WINDOWS } using ticks_t = timer_large_integer::type; #ifdef DOCTEST_CONFIG_GETCURRENTTICKS ticks_t getCurrentTicks() { return DOCTEST_CONFIG_GETCURRENTTICKS(); } #elif defined(DOCTEST_PLATFORM_WINDOWS) ticks_t getCurrentTicks() { static LARGE_INTEGER hz = { {0} }, hzo = { {0} }; if(!hz.QuadPart) { QueryPerformanceFrequency(&hz); QueryPerformanceCounter(&hzo); } LARGE_INTEGER t; QueryPerformanceCounter(&t); return ((t.QuadPart - hzo.QuadPart) * LONGLONG(1000000)) / hz.QuadPart; } #else // DOCTEST_PLATFORM_WINDOWS ticks_t getCurrentTicks() { timeval t; gettimeofday(&t, nullptr); return static_cast(t.tv_sec) * 1000000 + static_cast(t.tv_usec); } #endif // DOCTEST_PLATFORM_WINDOWS struct Timer { void start() { m_ticks = getCurrentTicks(); } unsigned int getElapsedMicroseconds() const { return static_cast(getCurrentTicks() - m_ticks); } //unsigned int getElapsedMilliseconds() const { // return static_cast(getElapsedMicroseconds() / 1000); //} double getElapsedSeconds() const { return static_cast(getCurrentTicks() - m_ticks) / 1000000.0; } private: ticks_t m_ticks = 0; }; #ifdef DOCTEST_CONFIG_NO_MULTITHREADING template using Atomic = T; #else // DOCTEST_CONFIG_NO_MULTITHREADING template using Atomic = std::atomic; #endif // DOCTEST_CONFIG_NO_MULTITHREADING #if defined(DOCTEST_CONFIG_NO_MULTI_LANE_ATOMICS) || defined(DOCTEST_CONFIG_NO_MULTITHREADING) template using MultiLaneAtomic = Atomic; #else // DOCTEST_CONFIG_NO_MULTI_LANE_ATOMICS // Provides a multilane implementation of an atomic variable that supports add, sub, load, // store. Instead of using a single atomic variable, this splits up into multiple ones, // each sitting on a separate cache line. The goal is to provide a speedup when most // operations are modifying. It achieves this with two properties: // // * Multiple atomics are used, so chance of congestion from the same atomic is reduced. // * Each atomic sits on a separate cache line, so false sharing is reduced. // // The disadvantage is that there is a small overhead due to the use of TLS, and load/store // is slower because all atomics have to be accessed. template class MultiLaneAtomic { struct CacheLineAlignedAtomic { Atomic atomic{}; char padding[DOCTEST_MULTI_LANE_ATOMICS_CACHE_LINE_SIZE - sizeof(Atomic)]; }; CacheLineAlignedAtomic m_atomics[DOCTEST_MULTI_LANE_ATOMICS_THREAD_LANES]; static_assert(sizeof(CacheLineAlignedAtomic) == DOCTEST_MULTI_LANE_ATOMICS_CACHE_LINE_SIZE, "guarantee one atomic takes exactly one cache line"); public: T operator++() DOCTEST_NOEXCEPT { return fetch_add(1) + 1; } T operator++(int) DOCTEST_NOEXCEPT { return fetch_add(1); } T fetch_add(T arg, std::memory_order order = std::memory_order_seq_cst) DOCTEST_NOEXCEPT { return myAtomic().fetch_add(arg, order); } T fetch_sub(T arg, std::memory_order order = std::memory_order_seq_cst) DOCTEST_NOEXCEPT { return myAtomic().fetch_sub(arg, order); } operator T() const DOCTEST_NOEXCEPT { return load(); } T load(std::memory_order order = std::memory_order_seq_cst) const DOCTEST_NOEXCEPT { auto result = T(); for(auto const& c : m_atomics) { result += c.atomic.load(order); } return result; } T operator=(T desired) DOCTEST_NOEXCEPT { // lgtm [cpp/assignment-does-not-return-this] store(desired); return desired; } void store(T desired, std::memory_order order = std::memory_order_seq_cst) DOCTEST_NOEXCEPT { // first value becomes desired", all others become 0. for(auto& c : m_atomics) { c.atomic.store(desired, order); desired = {}; } } private: // Each thread has a different atomic that it operates on. If more than NumLanes threads // use this, some will use the same atomic. So performance will degrade a bit, but still // everything will work. // // The logic here is a bit tricky. The call should be as fast as possible, so that there // is minimal to no overhead in determining the correct atomic for the current thread. // // 1. A global static counter laneCounter counts continuously up. // 2. Each successive thread will use modulo operation of that counter so it gets an atomic // assigned in a round-robin fashion. // 3. This tlsLaneIdx is stored in the thread local data, so it is directly available with // little overhead. Atomic& myAtomic() DOCTEST_NOEXCEPT { static Atomic laneCounter; DOCTEST_THREAD_LOCAL size_t tlsLaneIdx = laneCounter++ % DOCTEST_MULTI_LANE_ATOMICS_THREAD_LANES; return m_atomics[tlsLaneIdx].atomic; } }; #endif // DOCTEST_CONFIG_NO_MULTI_LANE_ATOMICS // this holds both parameters from the command line and runtime data for tests struct ContextState : ContextOptions, TestRunStats, CurrentTestCaseStats { MultiLaneAtomic numAssertsCurrentTest_atomic; MultiLaneAtomic numAssertsFailedCurrentTest_atomic; std::vector> filters = decltype(filters)(9); // 9 different filters std::vector reporters_currently_used; assert_handler ah = nullptr; Timer timer; std::vector stringifiedContexts; // logging from INFO() due to an exception // stuff for subcases bool reachedLeaf; std::vector subcaseStack; std::vector nextSubcaseStack; std::unordered_set fullyTraversedSubcases; size_t currentSubcaseDepth; Atomic shouldLogCurrentException; void resetRunData() { numTestCases = 0; numTestCasesPassingFilters = 0; numTestSuitesPassingFilters = 0; numTestCasesFailed = 0; numAsserts = 0; numAssertsFailed = 0; numAssertsCurrentTest = 0; numAssertsFailedCurrentTest = 0; } void finalizeTestCaseData() { seconds = timer.getElapsedSeconds(); // update the non-atomic counters numAsserts += numAssertsCurrentTest_atomic; numAssertsFailed += numAssertsFailedCurrentTest_atomic; numAssertsCurrentTest = numAssertsCurrentTest_atomic; numAssertsFailedCurrentTest = numAssertsFailedCurrentTest_atomic; if(numAssertsFailedCurrentTest) failure_flags |= TestCaseFailureReason::AssertFailure; if(Approx(currentTest->m_timeout).epsilon(DBL_EPSILON) != 0 && Approx(seconds).epsilon(DBL_EPSILON) > currentTest->m_timeout) failure_flags |= TestCaseFailureReason::Timeout; if(currentTest->m_should_fail) { if(failure_flags) { failure_flags |= TestCaseFailureReason::ShouldHaveFailedAndDid; } else { failure_flags |= TestCaseFailureReason::ShouldHaveFailedButDidnt; } } else if(failure_flags && currentTest->m_may_fail) { failure_flags |= TestCaseFailureReason::CouldHaveFailedAndDid; } else if(currentTest->m_expected_failures > 0) { if(numAssertsFailedCurrentTest == currentTest->m_expected_failures) { failure_flags |= TestCaseFailureReason::FailedExactlyNumTimes; } else { failure_flags |= TestCaseFailureReason::DidntFailExactlyNumTimes; } } bool ok_to_fail = (TestCaseFailureReason::ShouldHaveFailedAndDid & failure_flags) || (TestCaseFailureReason::CouldHaveFailedAndDid & failure_flags) || (TestCaseFailureReason::FailedExactlyNumTimes & failure_flags); // if any subcase has failed - the whole test case has failed testCaseSuccess = !(failure_flags && !ok_to_fail); if(!testCaseSuccess) numTestCasesFailed++; } }; ContextState* g_cs = nullptr; // used to avoid locks for the debug output // TODO: figure out if this is indeed necessary/correct - seems like either there still // could be a race or that there wouldn't be a race even if using the context directly DOCTEST_THREAD_LOCAL bool g_no_colors; #endif // DOCTEST_CONFIG_DISABLE } // namespace detail char* String::allocate(size_type sz) { if (sz <= last) { buf[sz] = '\0'; setLast(last - sz); return buf; } else { setOnHeap(); data.size = sz; data.capacity = data.size + 1; data.ptr = new char[data.capacity]; data.ptr[sz] = '\0'; return data.ptr; } } void String::setOnHeap() noexcept { *reinterpret_cast(&buf[last]) = 128; } void String::setLast(size_type in) noexcept { buf[last] = char(in); } void String::setSize(size_type sz) noexcept { if (isOnStack()) { buf[sz] = '\0'; setLast(last - sz); } else { data.ptr[sz] = '\0'; data.size = sz; } } void String::copy(const String& other) { if(other.isOnStack()) { memcpy(buf, other.buf, len); } else { memcpy(allocate(other.data.size), other.data.ptr, other.data.size); } } String::String() noexcept { buf[0] = '\0'; setLast(); } String::~String() { if(!isOnStack()) delete[] data.ptr; } // NOLINT(clang-analyzer-cplusplus.NewDeleteLeaks) String::String(const char* in) : String(in, strlen(in)) {} String::String(const char* in, size_type in_size) { memcpy(allocate(in_size), in, in_size); } String::String(std::istream& in, size_type in_size) { in.read(allocate(in_size), in_size); } String::String(const String& other) { copy(other); } String& String::operator=(const String& other) { if(this != &other) { if(!isOnStack()) delete[] data.ptr; copy(other); } return *this; } String& String::operator+=(const String& other) { const size_type my_old_size = size(); const size_type other_size = other.size(); const size_type total_size = my_old_size + other_size; if(isOnStack()) { if(total_size < len) { // append to the current stack space memcpy(buf + my_old_size, other.c_str(), other_size + 1); // NOLINTNEXTLINE(clang-analyzer-cplusplus.NewDeleteLeaks) setLast(last - total_size); } else { // alloc new chunk char* temp = new char[total_size + 1]; // copy current data to new location before writing in the union memcpy(temp, buf, my_old_size); // skip the +1 ('\0') for speed // update data in union setOnHeap(); data.size = total_size; data.capacity = data.size + 1; data.ptr = temp; // transfer the rest of the data memcpy(data.ptr + my_old_size, other.c_str(), other_size + 1); } } else { if(data.capacity > total_size) { // append to the current heap block data.size = total_size; memcpy(data.ptr + my_old_size, other.c_str(), other_size + 1); } else { // resize data.capacity *= 2; if(data.capacity <= total_size) data.capacity = total_size + 1; // alloc new chunk char* temp = new char[data.capacity]; // copy current data to new location before releasing it memcpy(temp, data.ptr, my_old_size); // skip the +1 ('\0') for speed // release old chunk delete[] data.ptr; // update the rest of the union members data.size = total_size; data.ptr = temp; // transfer the rest of the data memcpy(data.ptr + my_old_size, other.c_str(), other_size + 1); } } return *this; } String::String(String&& other) noexcept { memcpy(buf, other.buf, len); other.buf[0] = '\0'; other.setLast(); } String& String::operator=(String&& other) noexcept { if(this != &other) { if(!isOnStack()) delete[] data.ptr; memcpy(buf, other.buf, len); other.buf[0] = '\0'; other.setLast(); } return *this; } char String::operator[](size_type i) const { return const_cast(this)->operator[](i); } char& String::operator[](size_type i) { if(isOnStack()) return reinterpret_cast(buf)[i]; return data.ptr[i]; } DOCTEST_GCC_SUPPRESS_WARNING_WITH_PUSH("-Wmaybe-uninitialized") String::size_type String::size() const { if(isOnStack()) return last - (size_type(buf[last]) & 31); // using "last" would work only if "len" is 32 return data.size; } DOCTEST_GCC_SUPPRESS_WARNING_POP String::size_type String::capacity() const { if(isOnStack()) return len; return data.capacity; } String String::substr(size_type pos, size_type cnt) && { cnt = std::min(cnt, size() - 1 - pos); char* cptr = c_str(); memmove(cptr, cptr + pos, cnt); setSize(cnt); return std::move(*this); } String String::substr(size_type pos, size_type cnt) const & { cnt = std::min(cnt, size() - 1 - pos); return String{ c_str() + pos, cnt }; } String::size_type String::find(char ch, size_type pos) const { const char* begin = c_str(); const char* end = begin + size(); const char* it = begin + pos; for (; it < end && *it != ch; it++); if (it < end) { return static_cast(it - begin); } else { return npos; } } String::size_type String::rfind(char ch, size_type pos) const { const char* begin = c_str(); const char* it = begin + std::min(pos, size() - 1); for (; it >= begin && *it != ch; it--); if (it >= begin) { return static_cast(it - begin); } else { return npos; } } int String::compare(const char* other, bool no_case) const { if(no_case) return doctest::stricmp(c_str(), other); return std::strcmp(c_str(), other); } int String::compare(const String& other, bool no_case) const { return compare(other.c_str(), no_case); } String operator+(const String& lhs, const String& rhs) { return String(lhs) += rhs; } bool operator==(const String& lhs, const String& rhs) { return lhs.compare(rhs) == 0; } bool operator!=(const String& lhs, const String& rhs) { return lhs.compare(rhs) != 0; } bool operator< (const String& lhs, const String& rhs) { return lhs.compare(rhs) < 0; } bool operator> (const String& lhs, const String& rhs) { return lhs.compare(rhs) > 0; } bool operator<=(const String& lhs, const String& rhs) { return (lhs != rhs) ? lhs.compare(rhs) < 0 : true; } bool operator>=(const String& lhs, const String& rhs) { return (lhs != rhs) ? lhs.compare(rhs) > 0 : true; } std::ostream& operator<<(std::ostream& s, const String& in) { return s << in.c_str(); } Contains::Contains(const String& str) : string(str) { } bool Contains::checkWith(const String& other) const { return strstr(other.c_str(), string.c_str()) != nullptr; } String toString(const Contains& in) { return "Contains( " + in.string + " )"; } bool operator==(const String& lhs, const Contains& rhs) { return rhs.checkWith(lhs); } bool operator==(const Contains& lhs, const String& rhs) { return lhs.checkWith(rhs); } bool operator!=(const String& lhs, const Contains& rhs) { return !rhs.checkWith(lhs); } bool operator!=(const Contains& lhs, const String& rhs) { return !lhs.checkWith(rhs); } namespace { void color_to_stream(std::ostream&, Color::Enum) DOCTEST_BRANCH_ON_DISABLED({}, ;) } // namespace namespace Color { std::ostream& operator<<(std::ostream& s, Color::Enum code) { color_to_stream(s, code); return s; } } // namespace Color // clang-format off const char* assertString(assertType::Enum at) { DOCTEST_MSVC_SUPPRESS_WARNING_WITH_PUSH(4061) // enum 'x' in switch of enum 'y' is not explicitly handled #define DOCTEST_GENERATE_ASSERT_TYPE_CASE(assert_type) case assertType::DT_ ## assert_type: return #assert_type #define DOCTEST_GENERATE_ASSERT_TYPE_CASES(assert_type) \ DOCTEST_GENERATE_ASSERT_TYPE_CASE(WARN_ ## assert_type); \ DOCTEST_GENERATE_ASSERT_TYPE_CASE(CHECK_ ## assert_type); \ DOCTEST_GENERATE_ASSERT_TYPE_CASE(REQUIRE_ ## assert_type) switch(at) { DOCTEST_GENERATE_ASSERT_TYPE_CASE(WARN); DOCTEST_GENERATE_ASSERT_TYPE_CASE(CHECK); DOCTEST_GENERATE_ASSERT_TYPE_CASE(REQUIRE); DOCTEST_GENERATE_ASSERT_TYPE_CASES(FALSE); DOCTEST_GENERATE_ASSERT_TYPE_CASES(THROWS); DOCTEST_GENERATE_ASSERT_TYPE_CASES(THROWS_AS); DOCTEST_GENERATE_ASSERT_TYPE_CASES(THROWS_WITH); DOCTEST_GENERATE_ASSERT_TYPE_CASES(THROWS_WITH_AS); DOCTEST_GENERATE_ASSERT_TYPE_CASES(NOTHROW); DOCTEST_GENERATE_ASSERT_TYPE_CASES(EQ); DOCTEST_GENERATE_ASSERT_TYPE_CASES(NE); DOCTEST_GENERATE_ASSERT_TYPE_CASES(GT); DOCTEST_GENERATE_ASSERT_TYPE_CASES(LT); DOCTEST_GENERATE_ASSERT_TYPE_CASES(GE); DOCTEST_GENERATE_ASSERT_TYPE_CASES(LE); DOCTEST_GENERATE_ASSERT_TYPE_CASES(UNARY); DOCTEST_GENERATE_ASSERT_TYPE_CASES(UNARY_FALSE); default: DOCTEST_INTERNAL_ERROR("Tried stringifying invalid assert type!"); } DOCTEST_MSVC_SUPPRESS_WARNING_POP } // clang-format on const char* failureString(assertType::Enum at) { if(at & assertType::is_warn) //!OCLINT bitwise operator in conditional return "WARNING"; if(at & assertType::is_check) //!OCLINT bitwise operator in conditional return "ERROR"; if(at & assertType::is_require) //!OCLINT bitwise operator in conditional return "FATAL ERROR"; return ""; } DOCTEST_CLANG_SUPPRESS_WARNING_WITH_PUSH("-Wnull-dereference") DOCTEST_GCC_SUPPRESS_WARNING_WITH_PUSH("-Wnull-dereference") // depending on the current options this will remove the path of filenames const char* skipPathFromFilename(const char* file) { #ifndef DOCTEST_CONFIG_DISABLE if(getContextOptions()->no_path_in_filenames) { auto back = std::strrchr(file, '\\'); auto forward = std::strrchr(file, '/'); if(back || forward) { if(back > forward) forward = back; return forward + 1; } } #endif // DOCTEST_CONFIG_DISABLE return file; } DOCTEST_CLANG_SUPPRESS_WARNING_POP DOCTEST_GCC_SUPPRESS_WARNING_POP bool SubcaseSignature::operator==(const SubcaseSignature& other) const { return m_line == other.m_line && std::strcmp(m_file, other.m_file) == 0 && m_name == other.m_name; } bool SubcaseSignature::operator<(const SubcaseSignature& other) const { if(m_line != other.m_line) return m_line < other.m_line; if(std::strcmp(m_file, other.m_file) != 0) return std::strcmp(m_file, other.m_file) < 0; return m_name.compare(other.m_name) < 0; } DOCTEST_DEFINE_INTERFACE(IContextScope) namespace detail { void filldata::fill(std::ostream* stream, const void* in) { if (in) { *stream << in; } else { *stream << "nullptr"; } } template String toStreamLit(T t) { std::ostream* os = tlssPush(); os->operator<<(t); return tlssPop(); } } #ifdef DOCTEST_CONFIG_TREAT_CHAR_STAR_AS_STRING String toString(const char* in) { return String("\"") + (in ? in : "{null string}") + "\""; } #endif // DOCTEST_CONFIG_TREAT_CHAR_STAR_AS_STRING #if DOCTEST_MSVC >= DOCTEST_COMPILER(19, 20, 0) // see this issue on why this is needed: https://github.com/doctest/doctest/issues/183 String toString(const std::string& in) { return in.c_str(); } #endif // VS 2019 String toString(String in) { return in; } String toString(std::nullptr_t) { return "nullptr"; } String toString(bool in) { return in ? "true" : "false"; } String toString(float in) { return toStreamLit(in); } String toString(double in) { return toStreamLit(in); } String toString(double long in) { return toStreamLit(in); } String toString(char in) { return toStreamLit(static_cast(in)); } String toString(char signed in) { return toStreamLit(static_cast(in)); } String toString(char unsigned in) { return toStreamLit(static_cast(in)); } String toString(short in) { return toStreamLit(in); } String toString(short unsigned in) { return toStreamLit(in); } String toString(signed in) { return toStreamLit(in); } String toString(unsigned in) { return toStreamLit(in); } String toString(long in) { return toStreamLit(in); } String toString(long unsigned in) { return toStreamLit(in); } String toString(long long in) { return toStreamLit(in); } String toString(long long unsigned in) { return toStreamLit(in); } Approx::Approx(double value) : m_epsilon(static_cast(std::numeric_limits::epsilon()) * 100) , m_scale(1.0) , m_value(value) {} Approx Approx::operator()(double value) const { Approx approx(value); approx.epsilon(m_epsilon); approx.scale(m_scale); return approx; } Approx& Approx::epsilon(double newEpsilon) { m_epsilon = newEpsilon; return *this; } Approx& Approx::scale(double newScale) { m_scale = newScale; return *this; } bool operator==(double lhs, const Approx& rhs) { // Thanks to Richard Harris for his help refining this formula return std::fabs(lhs - rhs.m_value) < rhs.m_epsilon * (rhs.m_scale + std::max(std::fabs(lhs), std::fabs(rhs.m_value))); } bool operator==(const Approx& lhs, double rhs) { return operator==(rhs, lhs); } bool operator!=(double lhs, const Approx& rhs) { return !operator==(lhs, rhs); } bool operator!=(const Approx& lhs, double rhs) { return !operator==(rhs, lhs); } bool operator<=(double lhs, const Approx& rhs) { return lhs < rhs.m_value || lhs == rhs; } bool operator<=(const Approx& lhs, double rhs) { return lhs.m_value < rhs || lhs == rhs; } bool operator>=(double lhs, const Approx& rhs) { return lhs > rhs.m_value || lhs == rhs; } bool operator>=(const Approx& lhs, double rhs) { return lhs.m_value > rhs || lhs == rhs; } bool operator<(double lhs, const Approx& rhs) { return lhs < rhs.m_value && lhs != rhs; } bool operator<(const Approx& lhs, double rhs) { return lhs.m_value < rhs && lhs != rhs; } bool operator>(double lhs, const Approx& rhs) { return lhs > rhs.m_value && lhs != rhs; } bool operator>(const Approx& lhs, double rhs) { return lhs.m_value > rhs && lhs != rhs; } String toString(const Approx& in) { return "Approx( " + doctest::toString(in.m_value) + " )"; } const ContextOptions* getContextOptions() { return DOCTEST_BRANCH_ON_DISABLED(nullptr, g_cs); } DOCTEST_MSVC_SUPPRESS_WARNING_WITH_PUSH(4738) template IsNaN::operator bool() const { return std::isnan(value) ^ flipped; } DOCTEST_MSVC_SUPPRESS_WARNING_POP template struct DOCTEST_INTERFACE_DEF IsNaN; template struct DOCTEST_INTERFACE_DEF IsNaN; template struct DOCTEST_INTERFACE_DEF IsNaN; template String toString(IsNaN in) { return String(in.flipped ? "! " : "") + "IsNaN( " + doctest::toString(in.value) + " )"; } String toString(IsNaN in) { return toString(in); } String toString(IsNaN in) { return toString(in); } String toString(IsNaN in) { return toString(in); } } // namespace doctest #ifdef DOCTEST_CONFIG_DISABLE namespace doctest { Context::Context(int, const char* const*) {} Context::~Context() = default; void Context::applyCommandLine(int, const char* const*) {} void Context::addFilter(const char*, const char*) {} void Context::clearFilters() {} void Context::setOption(const char*, bool) {} void Context::setOption(const char*, int) {} void Context::setOption(const char*, const char*) {} bool Context::shouldExit() { return false; } void Context::setAsDefaultForAssertsOutOfTestCases() {} void Context::setAssertHandler(detail::assert_handler) {} void Context::setCout(std::ostream*) {} int Context::run() { return 0; } int IReporter::get_num_active_contexts() { return 0; } const IContextScope* const* IReporter::get_active_contexts() { return nullptr; } int IReporter::get_num_stringified_contexts() { return 0; } const String* IReporter::get_stringified_contexts() { return nullptr; } int registerReporter(const char*, int, IReporter*) { return 0; } } // namespace doctest #else // DOCTEST_CONFIG_DISABLE #if !defined(DOCTEST_CONFIG_COLORS_NONE) #if !defined(DOCTEST_CONFIG_COLORS_WINDOWS) && !defined(DOCTEST_CONFIG_COLORS_ANSI) #ifdef DOCTEST_PLATFORM_WINDOWS #define DOCTEST_CONFIG_COLORS_WINDOWS #else // linux #define DOCTEST_CONFIG_COLORS_ANSI #endif // platform #endif // DOCTEST_CONFIG_COLORS_WINDOWS && DOCTEST_CONFIG_COLORS_ANSI #endif // DOCTEST_CONFIG_COLORS_NONE namespace doctest_detail_test_suite_ns { // holds the current test suite doctest::detail::TestSuite& getCurrentTestSuite() { static doctest::detail::TestSuite data{}; return data; } } // namespace doctest_detail_test_suite_ns namespace doctest { namespace { // the int (priority) is part of the key for automatic sorting - sadly one can register a // reporter with a duplicate name and a different priority but hopefully that won't happen often :| using reporterMap = std::map, reporterCreatorFunc>; reporterMap& getReporters() { static reporterMap data; return data; } reporterMap& getListeners() { static reporterMap data; return data; } } // namespace namespace detail { #define DOCTEST_ITERATE_THROUGH_REPORTERS(function, ...) \ for(auto& curr_rep : g_cs->reporters_currently_used) \ curr_rep->function(__VA_ARGS__) bool checkIfShouldThrow(assertType::Enum at) { if(at & assertType::is_require) //!OCLINT bitwise operator in conditional return true; if((at & assertType::is_check) //!OCLINT bitwise operator in conditional && getContextOptions()->abort_after > 0 && (g_cs->numAssertsFailed + g_cs->numAssertsFailedCurrentTest_atomic) >= getContextOptions()->abort_after) return true; return false; } #ifndef DOCTEST_CONFIG_NO_EXCEPTIONS DOCTEST_NORETURN void throwException() { g_cs->shouldLogCurrentException = false; throw TestFailureException(); // NOLINT(hicpp-exception-baseclass) } #else // DOCTEST_CONFIG_NO_EXCEPTIONS void throwException() {} #endif // DOCTEST_CONFIG_NO_EXCEPTIONS } // namespace detail namespace { using namespace detail; // matching of a string against a wildcard mask (case sensitivity configurable) taken from // https://www.codeproject.com/Articles/1088/Wildcard-string-compare-globbing int wildcmp(const char* str, const char* wild, bool caseSensitive) { const char* cp = str; const char* mp = wild; while((*str) && (*wild != '*')) { if((caseSensitive ? (*wild != *str) : (tolower(*wild) != tolower(*str))) && (*wild != '?')) { return 0; } wild++; str++; } while(*str) { if(*wild == '*') { if(!*++wild) { return 1; } mp = wild; cp = str + 1; } else if((caseSensitive ? (*wild == *str) : (tolower(*wild) == tolower(*str))) || (*wild == '?')) { wild++; str++; } else { wild = mp; //!OCLINT parameter reassignment str = cp++; //!OCLINT parameter reassignment } } while(*wild == '*') { wild++; } return !*wild; } // checks if the name matches any of the filters (and can be configured what to do when empty) bool matchesAny(const char* name, const std::vector& filters, bool matchEmpty, bool caseSensitive) { if (filters.empty() && matchEmpty) return true; for (auto& curr : filters) if (wildcmp(name, curr.c_str(), caseSensitive)) return true; return false; } DOCTEST_NO_SANITIZE_INTEGER unsigned long long hash(unsigned long long a, unsigned long long b) { return (a << 5) + b; } // C string hash function (djb2) - taken from http://www.cse.yorku.ca/~oz/hash.html DOCTEST_NO_SANITIZE_INTEGER unsigned long long hash(const char* str) { unsigned long long hash = 5381; char c; while ((c = *str++)) hash = ((hash << 5) + hash) + c; // hash * 33 + c return hash; } unsigned long long hash(const SubcaseSignature& sig) { return hash(hash(hash(sig.m_file), hash(sig.m_name.c_str())), sig.m_line); } unsigned long long hash(const std::vector& sigs, size_t count) { unsigned long long running = 0; auto end = sigs.begin() + count; for (auto it = sigs.begin(); it != end; it++) { running = hash(running, hash(*it)); } return running; } unsigned long long hash(const std::vector& sigs) { unsigned long long running = 0; for (const SubcaseSignature& sig : sigs) { running = hash(running, hash(sig)); } return running; } } // namespace namespace detail { bool Subcase::checkFilters() { if (g_cs->subcaseStack.size() < size_t(g_cs->subcase_filter_levels)) { if (!matchesAny(m_signature.m_name.c_str(), g_cs->filters[6], true, g_cs->case_sensitive)) return true; if (matchesAny(m_signature.m_name.c_str(), g_cs->filters[7], false, g_cs->case_sensitive)) return true; } return false; } Subcase::Subcase(const String& name, const char* file, int line) : m_signature({name, file, line}) { if (!g_cs->reachedLeaf) { if (g_cs->nextSubcaseStack.size() <= g_cs->subcaseStack.size() || g_cs->nextSubcaseStack[g_cs->subcaseStack.size()] == m_signature) { // Going down. if (checkFilters()) { return; } g_cs->subcaseStack.push_back(m_signature); g_cs->currentSubcaseDepth++; m_entered = true; DOCTEST_ITERATE_THROUGH_REPORTERS(subcase_start, m_signature); } } else { if (g_cs->subcaseStack[g_cs->currentSubcaseDepth] == m_signature) { // This subcase is reentered via control flow. g_cs->currentSubcaseDepth++; m_entered = true; DOCTEST_ITERATE_THROUGH_REPORTERS(subcase_start, m_signature); } else if (g_cs->nextSubcaseStack.size() <= g_cs->currentSubcaseDepth && g_cs->fullyTraversedSubcases.find(hash(hash(g_cs->subcaseStack, g_cs->currentSubcaseDepth), hash(m_signature))) == g_cs->fullyTraversedSubcases.end()) { if (checkFilters()) { return; } // This subcase is part of the one to be executed next. g_cs->nextSubcaseStack.clear(); g_cs->nextSubcaseStack.insert(g_cs->nextSubcaseStack.end(), g_cs->subcaseStack.begin(), g_cs->subcaseStack.begin() + g_cs->currentSubcaseDepth); g_cs->nextSubcaseStack.push_back(m_signature); } } } DOCTEST_MSVC_SUPPRESS_WARNING_WITH_PUSH(4996) // std::uncaught_exception is deprecated in C++17 DOCTEST_GCC_SUPPRESS_WARNING_WITH_PUSH("-Wdeprecated-declarations") DOCTEST_CLANG_SUPPRESS_WARNING_WITH_PUSH("-Wdeprecated-declarations") Subcase::~Subcase() { if (m_entered) { g_cs->currentSubcaseDepth--; if (!g_cs->reachedLeaf) { // Leaf. g_cs->fullyTraversedSubcases.insert(hash(g_cs->subcaseStack)); g_cs->nextSubcaseStack.clear(); g_cs->reachedLeaf = true; } else if (g_cs->nextSubcaseStack.empty()) { // All children are finished. g_cs->fullyTraversedSubcases.insert(hash(g_cs->subcaseStack)); } #if defined(__cpp_lib_uncaught_exceptions) && __cpp_lib_uncaught_exceptions >= 201411L && (!defined(__MAC_OS_X_VERSION_MIN_REQUIRED) || __MAC_OS_X_VERSION_MIN_REQUIRED >= 101200) if(std::uncaught_exceptions() > 0 #else if(std::uncaught_exception() #endif && g_cs->shouldLogCurrentException) { DOCTEST_ITERATE_THROUGH_REPORTERS( test_case_exception, {"exception thrown in subcase - will translate later " "when the whole test case has been exited (cannot " "translate while there is an active exception)", false}); g_cs->shouldLogCurrentException = false; } DOCTEST_ITERATE_THROUGH_REPORTERS(subcase_end, DOCTEST_EMPTY); } } DOCTEST_CLANG_SUPPRESS_WARNING_POP DOCTEST_GCC_SUPPRESS_WARNING_POP DOCTEST_MSVC_SUPPRESS_WARNING_POP Subcase::operator bool() const { return m_entered; } Result::Result(bool passed, const String& decomposition) : m_passed(passed) , m_decomp(decomposition) {} ExpressionDecomposer::ExpressionDecomposer(assertType::Enum at) : m_at(at) {} TestSuite& TestSuite::operator*(const char* in) { m_test_suite = in; return *this; } TestCase::TestCase(funcType test, const char* file, unsigned line, const TestSuite& test_suite, const String& type, int template_id) { m_file = file; m_line = line; m_name = nullptr; // will be later overridden in operator* m_test_suite = test_suite.m_test_suite; m_description = test_suite.m_description; m_skip = test_suite.m_skip; m_no_breaks = test_suite.m_no_breaks; m_no_output = test_suite.m_no_output; m_may_fail = test_suite.m_may_fail; m_should_fail = test_suite.m_should_fail; m_expected_failures = test_suite.m_expected_failures; m_timeout = test_suite.m_timeout; m_test = test; m_type = type; m_template_id = template_id; } TestCase::TestCase(const TestCase& other) : TestCaseData() { *this = other; } DOCTEST_MSVC_SUPPRESS_WARNING_WITH_PUSH(26434) // hides a non-virtual function TestCase& TestCase::operator=(const TestCase& other) { TestCaseData::operator=(other); m_test = other.m_test; m_type = other.m_type; m_template_id = other.m_template_id; m_full_name = other.m_full_name; if(m_template_id != -1) m_name = m_full_name.c_str(); return *this; } DOCTEST_MSVC_SUPPRESS_WARNING_POP TestCase& TestCase::operator*(const char* in) { m_name = in; // make a new name with an appended type for templated test case if(m_template_id != -1) { m_full_name = String(m_name) + "<" + m_type + ">"; // redirect the name to point to the newly constructed full name m_name = m_full_name.c_str(); } return *this; } bool TestCase::operator<(const TestCase& other) const { // this will be used only to differentiate between test cases - not relevant for sorting if(m_line != other.m_line) return m_line < other.m_line; const int name_cmp = strcmp(m_name, other.m_name); if(name_cmp != 0) return name_cmp < 0; const int file_cmp = m_file.compare(other.m_file); if(file_cmp != 0) return file_cmp < 0; return m_template_id < other.m_template_id; } // all the registered tests std::set& getRegisteredTests() { static std::set data; return data; } } // namespace detail namespace { using namespace detail; // for sorting tests by file/line bool fileOrderComparator(const TestCase* lhs, const TestCase* rhs) { // this is needed because MSVC gives different case for drive letters // for __FILE__ when evaluated in a header and a source file const int res = lhs->m_file.compare(rhs->m_file, bool(DOCTEST_MSVC)); if(res != 0) return res < 0; if(lhs->m_line != rhs->m_line) return lhs->m_line < rhs->m_line; return lhs->m_template_id < rhs->m_template_id; } // for sorting tests by suite/file/line bool suiteOrderComparator(const TestCase* lhs, const TestCase* rhs) { const int res = std::strcmp(lhs->m_test_suite, rhs->m_test_suite); if(res != 0) return res < 0; return fileOrderComparator(lhs, rhs); } // for sorting tests by name/suite/file/line bool nameOrderComparator(const TestCase* lhs, const TestCase* rhs) { const int res = std::strcmp(lhs->m_name, rhs->m_name); if(res != 0) return res < 0; return suiteOrderComparator(lhs, rhs); } DOCTEST_CLANG_SUPPRESS_WARNING_WITH_PUSH("-Wdeprecated-declarations") void color_to_stream(std::ostream& s, Color::Enum code) { static_cast(s); // for DOCTEST_CONFIG_COLORS_NONE or DOCTEST_CONFIG_COLORS_WINDOWS static_cast(code); // for DOCTEST_CONFIG_COLORS_NONE #ifdef DOCTEST_CONFIG_COLORS_ANSI if(g_no_colors || (isatty(STDOUT_FILENO) == false && getContextOptions()->force_colors == false)) return; auto col = ""; // clang-format off switch(code) { //!OCLINT missing break in switch statement / unnecessary default statement in covered switch statement case Color::Red: col = "[0;31m"; break; case Color::Green: col = "[0;32m"; break; case Color::Blue: col = "[0;34m"; break; case Color::Cyan: col = "[0;36m"; break; case Color::Yellow: col = "[0;33m"; break; case Color::Grey: col = "[1;30m"; break; case Color::LightGrey: col = "[0;37m"; break; case Color::BrightRed: col = "[1;31m"; break; case Color::BrightGreen: col = "[1;32m"; break; case Color::BrightWhite: col = "[1;37m"; break; case Color::Bright: // invalid case Color::None: case Color::White: default: col = "[0m"; } // clang-format on s << "\033" << col; #endif // DOCTEST_CONFIG_COLORS_ANSI #ifdef DOCTEST_CONFIG_COLORS_WINDOWS if(g_no_colors || (_isatty(_fileno(stdout)) == false && getContextOptions()->force_colors == false)) return; static struct ConsoleHelper { HANDLE stdoutHandle; WORD origFgAttrs; WORD origBgAttrs; ConsoleHelper() { stdoutHandle = GetStdHandle(STD_OUTPUT_HANDLE); CONSOLE_SCREEN_BUFFER_INFO csbiInfo; GetConsoleScreenBufferInfo(stdoutHandle, &csbiInfo); origFgAttrs = csbiInfo.wAttributes & ~(BACKGROUND_GREEN | BACKGROUND_RED | BACKGROUND_BLUE | BACKGROUND_INTENSITY); origBgAttrs = csbiInfo.wAttributes & ~(FOREGROUND_GREEN | FOREGROUND_RED | FOREGROUND_BLUE | FOREGROUND_INTENSITY); } } ch; #define DOCTEST_SET_ATTR(x) SetConsoleTextAttribute(ch.stdoutHandle, x | ch.origBgAttrs) // clang-format off switch (code) { case Color::White: DOCTEST_SET_ATTR(FOREGROUND_GREEN | FOREGROUND_RED | FOREGROUND_BLUE); break; case Color::Red: DOCTEST_SET_ATTR(FOREGROUND_RED); break; case Color::Green: DOCTEST_SET_ATTR(FOREGROUND_GREEN); break; case Color::Blue: DOCTEST_SET_ATTR(FOREGROUND_BLUE); break; case Color::Cyan: DOCTEST_SET_ATTR(FOREGROUND_BLUE | FOREGROUND_GREEN); break; case Color::Yellow: DOCTEST_SET_ATTR(FOREGROUND_RED | FOREGROUND_GREEN); break; case Color::Grey: DOCTEST_SET_ATTR(0); break; case Color::LightGrey: DOCTEST_SET_ATTR(FOREGROUND_INTENSITY); break; case Color::BrightRed: DOCTEST_SET_ATTR(FOREGROUND_INTENSITY | FOREGROUND_RED); break; case Color::BrightGreen: DOCTEST_SET_ATTR(FOREGROUND_INTENSITY | FOREGROUND_GREEN); break; case Color::BrightWhite: DOCTEST_SET_ATTR(FOREGROUND_INTENSITY | FOREGROUND_GREEN | FOREGROUND_RED | FOREGROUND_BLUE); break; case Color::None: case Color::Bright: // invalid default: DOCTEST_SET_ATTR(ch.origFgAttrs); } // clang-format on #endif // DOCTEST_CONFIG_COLORS_WINDOWS } DOCTEST_CLANG_SUPPRESS_WARNING_POP std::vector& getExceptionTranslators() { static std::vector data; return data; } String translateActiveException() { #ifndef DOCTEST_CONFIG_NO_EXCEPTIONS String res; auto& translators = getExceptionTranslators(); for(auto& curr : translators) if(curr->translate(res)) return res; // clang-format off DOCTEST_GCC_SUPPRESS_WARNING_WITH_PUSH("-Wcatch-value") try { throw; } catch(std::exception& ex) { return ex.what(); } catch(std::string& msg) { return msg.c_str(); } catch(const char* msg) { return msg; } catch(...) { return "unknown exception"; } DOCTEST_GCC_SUPPRESS_WARNING_POP // clang-format on #else // DOCTEST_CONFIG_NO_EXCEPTIONS return ""; #endif // DOCTEST_CONFIG_NO_EXCEPTIONS } } // namespace namespace detail { // used by the macros for registering tests int regTest(const TestCase& tc) { getRegisteredTests().insert(tc); return 0; } // sets the current test suite int setTestSuite(const TestSuite& ts) { doctest_detail_test_suite_ns::getCurrentTestSuite() = ts; return 0; } #ifdef DOCTEST_IS_DEBUGGER_ACTIVE bool isDebuggerActive() { return DOCTEST_IS_DEBUGGER_ACTIVE(); } #else // DOCTEST_IS_DEBUGGER_ACTIVE #ifdef DOCTEST_PLATFORM_LINUX class ErrnoGuard { public: ErrnoGuard() : m_oldErrno(errno) {} ~ErrnoGuard() { errno = m_oldErrno; } private: int m_oldErrno; }; // See the comments in Catch2 for the reasoning behind this implementation: // https://github.com/catchorg/Catch2/blob/v2.13.1/include/internal/catch_debugger.cpp#L79-L102 bool isDebuggerActive() { ErrnoGuard guard; std::ifstream in("/proc/self/status"); for(std::string line; std::getline(in, line);) { static const int PREFIX_LEN = 11; if(line.compare(0, PREFIX_LEN, "TracerPid:\t") == 0) { return line.length() > PREFIX_LEN && line[PREFIX_LEN] != '0'; } } return false; } #elif defined(DOCTEST_PLATFORM_MAC) // The following function is taken directly from the following technical note: // https://developer.apple.com/library/archive/qa/qa1361/_index.html // Returns true if the current process is being debugged (either // running under the debugger or has a debugger attached post facto). bool isDebuggerActive() { int mib[4]; kinfo_proc info; size_t size; // Initialize the flags so that, if sysctl fails for some bizarre // reason, we get a predictable result. info.kp_proc.p_flag = 0; // Initialize mib, which tells sysctl the info we want, in this case // we're looking for information about a specific process ID. mib[0] = CTL_KERN; mib[1] = KERN_PROC; mib[2] = KERN_PROC_PID; mib[3] = getpid(); // Call sysctl. size = sizeof(info); if(sysctl(mib, DOCTEST_COUNTOF(mib), &info, &size, 0, 0) != 0) { std::cerr << "\nCall to sysctl failed - unable to determine if debugger is active **\n"; return false; } // We're being debugged if the P_TRACED flag is set. return ((info.kp_proc.p_flag & P_TRACED) != 0); } #elif DOCTEST_MSVC || defined(__MINGW32__) || defined(__MINGW64__) bool isDebuggerActive() { return ::IsDebuggerPresent() != 0; } #else bool isDebuggerActive() { return false; } #endif // Platform #endif // DOCTEST_IS_DEBUGGER_ACTIVE void registerExceptionTranslatorImpl(const IExceptionTranslator* et) { if(std::find(getExceptionTranslators().begin(), getExceptionTranslators().end(), et) == getExceptionTranslators().end()) getExceptionTranslators().push_back(et); } DOCTEST_THREAD_LOCAL std::vector g_infoContexts; // for logging with INFO() ContextScopeBase::ContextScopeBase() { g_infoContexts.push_back(this); } ContextScopeBase::ContextScopeBase(ContextScopeBase&& other) noexcept { if (other.need_to_destroy) { other.destroy(); } other.need_to_destroy = false; g_infoContexts.push_back(this); } DOCTEST_MSVC_SUPPRESS_WARNING_WITH_PUSH(4996) // std::uncaught_exception is deprecated in C++17 DOCTEST_GCC_SUPPRESS_WARNING_WITH_PUSH("-Wdeprecated-declarations") DOCTEST_CLANG_SUPPRESS_WARNING_WITH_PUSH("-Wdeprecated-declarations") // destroy cannot be inlined into the destructor because that would mean calling stringify after // ContextScope has been destroyed (base class destructors run after derived class destructors). // Instead, ContextScope calls this method directly from its destructor. void ContextScopeBase::destroy() { #if defined(__cpp_lib_uncaught_exceptions) && __cpp_lib_uncaught_exceptions >= 201411L && (!defined(__MAC_OS_X_VERSION_MIN_REQUIRED) || __MAC_OS_X_VERSION_MIN_REQUIRED >= 101200) if(std::uncaught_exceptions() > 0) { #else if(std::uncaught_exception()) { #endif std::ostringstream s; this->stringify(&s); g_cs->stringifiedContexts.push_back(s.str().c_str()); } g_infoContexts.pop_back(); } DOCTEST_CLANG_SUPPRESS_WARNING_POP DOCTEST_GCC_SUPPRESS_WARNING_POP DOCTEST_MSVC_SUPPRESS_WARNING_POP } // namespace detail namespace { using namespace detail; #if !defined(DOCTEST_CONFIG_POSIX_SIGNALS) && !defined(DOCTEST_CONFIG_WINDOWS_SEH) struct FatalConditionHandler { static void reset() {} static void allocateAltStackMem() {} static void freeAltStackMem() {} }; #else // DOCTEST_CONFIG_POSIX_SIGNALS || DOCTEST_CONFIG_WINDOWS_SEH void reportFatal(const std::string&); #ifdef DOCTEST_PLATFORM_WINDOWS 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. SignalDefs signalDefs[] = { {static_cast(EXCEPTION_ILLEGAL_INSTRUCTION), "SIGILL - Illegal instruction signal"}, {static_cast(EXCEPTION_STACK_OVERFLOW), "SIGSEGV - Stack overflow"}, {static_cast(EXCEPTION_ACCESS_VIOLATION), "SIGSEGV - Segmentation violation signal"}, {static_cast(EXCEPTION_INT_DIVIDE_BY_ZERO), "Divide by zero error"}, }; struct FatalConditionHandler { static LONG CALLBACK handleException(PEXCEPTION_POINTERS ExceptionInfo) { // Multiple threads may enter this filter/handler at once. We want the error message to be printed on the // console just once no matter how many threads have crashed. DOCTEST_DECLARE_STATIC_MUTEX(mutex) static bool execute = true; { DOCTEST_LOCK_MUTEX(mutex) if(execute) { bool reported = false; for(size_t i = 0; i < DOCTEST_COUNTOF(signalDefs); ++i) { if(ExceptionInfo->ExceptionRecord->ExceptionCode == signalDefs[i].id) { reportFatal(signalDefs[i].name); reported = true; break; } } if(reported == false) reportFatal("Unhandled SEH exception caught"); if(isDebuggerActive() && !g_cs->no_breaks) DOCTEST_BREAK_INTO_DEBUGGER(); } execute = false; } std::exit(EXIT_FAILURE); } static void allocateAltStackMem() {} static void freeAltStackMem() {} FatalConditionHandler() { isSet = true; // 32k seems enough for doctest to handle stack overflow, // but the value was found experimentally, so there is no strong guarantee guaranteeSize = 32 * 1024; // Register an unhandled exception filter previousTop = SetUnhandledExceptionFilter(handleException); // Pass in guarantee size to be filled SetThreadStackGuarantee(&guaranteeSize); // On Windows uncaught exceptions from another thread, exceptions from // destructors, or calls to std::terminate are not a SEH exception // The terminal handler gets called when: // - std::terminate is called FROM THE TEST RUNNER THREAD // - an exception is thrown from a destructor FROM THE TEST RUNNER THREAD original_terminate_handler = std::get_terminate(); std::set_terminate([]() DOCTEST_NOEXCEPT { reportFatal("Terminate handler called"); if(isDebuggerActive() && !g_cs->no_breaks) DOCTEST_BREAK_INTO_DEBUGGER(); std::exit(EXIT_FAILURE); // explicitly exit - otherwise the SIGABRT handler may be called as well }); // SIGABRT is raised when: // - std::terminate is called FROM A DIFFERENT THREAD // - an exception is thrown from a destructor FROM A DIFFERENT THREAD // - an uncaught exception is thrown FROM A DIFFERENT THREAD prev_sigabrt_handler = std::signal(SIGABRT, [](int signal) DOCTEST_NOEXCEPT { if(signal == SIGABRT) { reportFatal("SIGABRT - Abort (abnormal termination) signal"); if(isDebuggerActive() && !g_cs->no_breaks) DOCTEST_BREAK_INTO_DEBUGGER(); std::exit(EXIT_FAILURE); } }); // The following settings are taken from google test, and more // specifically from UnitTest::Run() inside of gtest.cc // the user does not want to see pop-up dialogs about crashes prev_error_mode_1 = SetErrorMode(SEM_FAILCRITICALERRORS | SEM_NOALIGNMENTFAULTEXCEPT | SEM_NOGPFAULTERRORBOX | SEM_NOOPENFILEERRORBOX); // This forces the abort message to go to stderr in all circumstances. prev_error_mode_2 = _set_error_mode(_OUT_TO_STDERR); // In the debug version, Visual Studio pops up a separate dialog // offering a choice to debug the aborted program - we want to disable that. prev_abort_behavior = _set_abort_behavior(0x0, _WRITE_ABORT_MSG | _CALL_REPORTFAULT); // In debug mode, the Windows CRT can crash with an assertion over invalid // input (e.g. passing an invalid file descriptor). The default handling // for these assertions is to pop up a dialog and wait for user input. // Instead ask the CRT to dump such assertions to stderr non-interactively. prev_report_mode = _CrtSetReportMode(_CRT_ASSERT, _CRTDBG_MODE_FILE | _CRTDBG_MODE_DEBUG); prev_report_file = _CrtSetReportFile(_CRT_ASSERT, _CRTDBG_FILE_STDERR); } static void reset() { if(isSet) { // Unregister handler and restore the old guarantee SetUnhandledExceptionFilter(previousTop); SetThreadStackGuarantee(&guaranteeSize); std::set_terminate(original_terminate_handler); std::signal(SIGABRT, prev_sigabrt_handler); SetErrorMode(prev_error_mode_1); _set_error_mode(prev_error_mode_2); _set_abort_behavior(prev_abort_behavior, _WRITE_ABORT_MSG | _CALL_REPORTFAULT); static_cast(_CrtSetReportMode(_CRT_ASSERT, prev_report_mode)); static_cast(_CrtSetReportFile(_CRT_ASSERT, prev_report_file)); isSet = false; } } ~FatalConditionHandler() { reset(); } private: static UINT prev_error_mode_1; static int prev_error_mode_2; static unsigned int prev_abort_behavior; static int prev_report_mode; static _HFILE prev_report_file; static void (DOCTEST_CDECL *prev_sigabrt_handler)(int); static std::terminate_handler original_terminate_handler; static bool isSet; static ULONG guaranteeSize; static LPTOP_LEVEL_EXCEPTION_FILTER previousTop; }; UINT FatalConditionHandler::prev_error_mode_1; int FatalConditionHandler::prev_error_mode_2; unsigned int FatalConditionHandler::prev_abort_behavior; int FatalConditionHandler::prev_report_mode; _HFILE FatalConditionHandler::prev_report_file; void (DOCTEST_CDECL *FatalConditionHandler::prev_sigabrt_handler)(int); std::terminate_handler FatalConditionHandler::original_terminate_handler; bool FatalConditionHandler::isSet = false; ULONG FatalConditionHandler::guaranteeSize = 0; LPTOP_LEVEL_EXCEPTION_FILTER FatalConditionHandler::previousTop = nullptr; #else // DOCTEST_PLATFORM_WINDOWS struct SignalDefs { int id; const char* name; }; 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[DOCTEST_COUNTOF(signalDefs)]; static stack_t oldSigStack; static size_t altStackSize; static char* altStackMem; static void handleSignal(int sig) { const char* name = ""; for(std::size_t i = 0; i < DOCTEST_COUNTOF(signalDefs); ++i) { SignalDefs& def = signalDefs[i]; if(sig == def.id) { name = def.name; break; } } reset(); reportFatal(name); raise(sig); } static void allocateAltStackMem() { altStackMem = new char[altStackSize]; } static void freeAltStackMem() { delete[] altStackMem; } FatalConditionHandler() { isSet = true; stack_t sigStack; sigStack.ss_sp = altStackMem; sigStack.ss_size = altStackSize; 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 < DOCTEST_COUNTOF(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 < DOCTEST_COUNTOF(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[DOCTEST_COUNTOF(signalDefs)] = {}; stack_t FatalConditionHandler::oldSigStack = {}; size_t FatalConditionHandler::altStackSize = 4 * SIGSTKSZ; char* FatalConditionHandler::altStackMem = nullptr; #endif // DOCTEST_PLATFORM_WINDOWS #endif // DOCTEST_CONFIG_POSIX_SIGNALS || DOCTEST_CONFIG_WINDOWS_SEH } // namespace namespace { using namespace detail; #ifdef DOCTEST_PLATFORM_WINDOWS #define DOCTEST_OUTPUT_DEBUG_STRING(text) ::OutputDebugStringA(text) #else // TODO: integration with XCode and other IDEs #define DOCTEST_OUTPUT_DEBUG_STRING(text) #endif // Platform void addAssert(assertType::Enum at) { if((at & assertType::is_warn) == 0) //!OCLINT bitwise operator in conditional g_cs->numAssertsCurrentTest_atomic++; } void addFailedAssert(assertType::Enum at) { if((at & assertType::is_warn) == 0) //!OCLINT bitwise operator in conditional g_cs->numAssertsFailedCurrentTest_atomic++; } #if defined(DOCTEST_CONFIG_POSIX_SIGNALS) || defined(DOCTEST_CONFIG_WINDOWS_SEH) void reportFatal(const std::string& message) { g_cs->failure_flags |= TestCaseFailureReason::Crash; DOCTEST_ITERATE_THROUGH_REPORTERS(test_case_exception, {message.c_str(), true}); while (g_cs->subcaseStack.size()) { g_cs->subcaseStack.pop_back(); DOCTEST_ITERATE_THROUGH_REPORTERS(subcase_end, DOCTEST_EMPTY); } g_cs->finalizeTestCaseData(); DOCTEST_ITERATE_THROUGH_REPORTERS(test_case_end, *g_cs); DOCTEST_ITERATE_THROUGH_REPORTERS(test_run_end, *g_cs); } #endif // DOCTEST_CONFIG_POSIX_SIGNALS || DOCTEST_CONFIG_WINDOWS_SEH } // namespace AssertData::AssertData(assertType::Enum at, const char* file, int line, const char* expr, const char* exception_type, const StringContains& exception_string) : m_test_case(g_cs->currentTest), m_at(at), m_file(file), m_line(line), m_expr(expr), m_failed(true), m_threw(false), m_threw_as(false), m_exception_type(exception_type), m_exception_string(exception_string) { #if DOCTEST_MSVC if (m_expr[0] == ' ') // this happens when variadic macros are disabled under MSVC ++m_expr; #endif // MSVC } namespace detail { ResultBuilder::ResultBuilder(assertType::Enum at, const char* file, int line, const char* expr, const char* exception_type, const String& exception_string) : AssertData(at, file, line, expr, exception_type, exception_string) { } ResultBuilder::ResultBuilder(assertType::Enum at, const char* file, int line, const char* expr, const char* exception_type, const Contains& exception_string) : AssertData(at, file, line, expr, exception_type, exception_string) { } void ResultBuilder::setResult(const Result& res) { m_decomp = res.m_decomp; m_failed = !res.m_passed; } void ResultBuilder::translateException() { m_threw = true; m_exception = translateActiveException(); } bool ResultBuilder::log() { if(m_at & assertType::is_throws) { //!OCLINT bitwise operator in conditional m_failed = !m_threw; } else if((m_at & assertType::is_throws_as) && (m_at & assertType::is_throws_with)) { //!OCLINT m_failed = !m_threw_as || !m_exception_string.check(m_exception); } else if(m_at & assertType::is_throws_as) { //!OCLINT bitwise operator in conditional m_failed = !m_threw_as; } else if(m_at & assertType::is_throws_with) { //!OCLINT bitwise operator in conditional m_failed = !m_exception_string.check(m_exception); } else if(m_at & assertType::is_nothrow) { //!OCLINT bitwise operator in conditional m_failed = m_threw; } if(m_exception.size()) m_exception = "\"" + m_exception + "\""; if(is_running_in_test) { addAssert(m_at); DOCTEST_ITERATE_THROUGH_REPORTERS(log_assert, *this); if(m_failed) addFailedAssert(m_at); } else if(m_failed) { failed_out_of_a_testing_context(*this); } return m_failed && isDebuggerActive() && !getContextOptions()->no_breaks && (g_cs->currentTest == nullptr || !g_cs->currentTest->m_no_breaks); // break into debugger } void ResultBuilder::react() const { if(m_failed && checkIfShouldThrow(m_at)) throwException(); } void failed_out_of_a_testing_context(const AssertData& ad) { if(g_cs->ah) g_cs->ah(ad); else std::abort(); } bool decomp_assert(assertType::Enum at, const char* file, int line, const char* expr, const Result& result) { bool failed = !result.m_passed; // ################################################################################### // IF THE DEBUGGER BREAKS HERE - GO 1 LEVEL UP IN THE CALLSTACK FOR THE FAILING ASSERT // THIS IS THE EFFECT OF HAVING 'DOCTEST_CONFIG_SUPER_FAST_ASSERTS' DEFINED // ################################################################################### DOCTEST_ASSERT_OUT_OF_TESTS(result.m_decomp); DOCTEST_ASSERT_IN_TESTS(result.m_decomp); return !failed; } MessageBuilder::MessageBuilder(const char* file, int line, assertType::Enum severity) { m_stream = tlssPush(); m_file = file; m_line = line; m_severity = severity; } MessageBuilder::~MessageBuilder() { if (!logged) tlssPop(); } DOCTEST_DEFINE_INTERFACE(IExceptionTranslator) bool MessageBuilder::log() { if (!logged) { m_string = tlssPop(); logged = true; } DOCTEST_ITERATE_THROUGH_REPORTERS(log_message, *this); const bool isWarn = m_severity & assertType::is_warn; // warn is just a message in this context so we don't treat it as an assert if(!isWarn) { addAssert(m_severity); addFailedAssert(m_severity); } return isDebuggerActive() && !getContextOptions()->no_breaks && !isWarn && (g_cs->currentTest == nullptr || !g_cs->currentTest->m_no_breaks); // break into debugger } void MessageBuilder::react() { if(m_severity & assertType::is_require) //!OCLINT bitwise operator in conditional throwException(); } } // namespace detail namespace { using namespace detail; // clang-format off // ================================================================================================= // The following code has been taken verbatim from Catch2/include/internal/catch_xmlwriter.h/cpp // This is done so cherry-picking bug fixes is trivial - even the style/formatting is untouched. // ================================================================================================= 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 ) DOCTEST_NOEXCEPT; ScopedElement& operator=( ScopedElement&& other ) DOCTEST_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; }; #ifndef DOCTEST_CONFIG_NO_INCLUDE_IOSTREAM XmlWriter( std::ostream& os = std::cout ); #else // DOCTEST_CONFIG_NO_INCLUDE_IOSTREAM XmlWriter( std::ostream& os ); #endif // DOCTEST_CONFIG_NO_INCLUDE_IOSTREAM ~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, const char* attribute ); XmlWriter& writeAttribute( std::string const& name, bool attribute ); template XmlWriter& writeAttribute( std::string const& name, T const& attribute ) { std::stringstream rss; rss << attribute; return writeAttribute( name, rss.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(); void writeDeclaration(); private: void newlineIfNecessary(); bool m_tagIsOpen = false; bool m_needsNewline = false; std::vector m_tags; std::string m_indent; std::ostream& m_os; }; // ================================================================================================= // The following code has been taken verbatim from Catch2/include/internal/catch_xmlwriter.h/cpp // This is done so cherry-picking bug fixes is trivial - even the style/formatting is untouched. // ================================================================================================= using uchar = unsigned char; namespace { size_t trailingBytes(unsigned char c) { if ((c & 0xE0) == 0xC0) { return 2; } if ((c & 0xF0) == 0xE0) { return 3; } if ((c & 0xF8) == 0xF0) { return 4; } DOCTEST_INTERNAL_ERROR("Invalid multibyte utf-8 start byte encountered"); } uint32_t headerValue(unsigned char c) { if ((c & 0xE0) == 0xC0) { return c & 0x1F; } if ((c & 0xF0) == 0xE0) { return c & 0x0F; } if ((c & 0xF8) == 0xF0) { return c & 0x07; } DOCTEST_INTERNAL_ERROR("Invalid multibyte utf-8 start byte encountered"); } void hexEscapeChar(std::ostream& os, unsigned char c) { std::ios_base::fmtflags f(os.flags()); os << "\\x" << std::uppercase << std::hex << std::setfill('0') << std::setw(2) << static_cast(c); os.flags(f); } } // anonymous namespace 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: https://www.w3.org/TR/xml/#syntax) for( std::size_t idx = 0; idx < m_str.size(); ++ idx ) { uchar c = m_str[idx]; switch (c) { case '<': os << "<"; break; case '&': os << "&"; break; case '>': // See: https://www.w3.org/TR/xml/#syntax if (idx > 2 && m_str[idx - 1] == ']' && m_str[idx - 2] == ']') os << ">"; else os << c; break; case '\"': if (m_forWhat == ForAttributes) os << """; else os << c; break; default: // Check for control characters and invalid utf-8 // Escape control characters in standard ascii // see https://stackoverflow.com/questions/404107/why-are-control-characters-illegal-in-xml-1-0 if (c < 0x09 || (c > 0x0D && c < 0x20) || c == 0x7F) { hexEscapeChar(os, c); break; } // Plain ASCII: Write it to stream if (c < 0x7F) { os << c; break; } // UTF-8 territory // Check if the encoding is valid and if it is not, hex escape bytes. // Important: We do not check the exact decoded values for validity, only the encoding format // First check that this bytes is a valid lead byte: // This means that it is not encoded as 1111 1XXX // Or as 10XX XXXX if (c < 0xC0 || c >= 0xF8) { hexEscapeChar(os, c); break; } auto encBytes = trailingBytes(c); // Are there enough bytes left to avoid accessing out-of-bounds memory? if (idx + encBytes - 1 >= m_str.size()) { hexEscapeChar(os, c); break; } // The header is valid, check data // The next encBytes bytes must together be a valid utf-8 // This means: bitpattern 10XX XXXX and the extracted value is sane (ish) bool valid = true; uint32_t value = headerValue(c); for (std::size_t n = 1; n < encBytes; ++n) { uchar nc = m_str[idx + n]; valid &= ((nc & 0xC0) == 0x80); value = (value << 6) | (nc & 0x3F); } if ( // Wrong bit pattern of following bytes (!valid) || // Overlong encodings (value < 0x80) || ( value < 0x800 && encBytes > 2) || // removed "0x80 <= value &&" because redundant (0x800 < value && value < 0x10000 && encBytes > 3) || // Encoded value out of range (value >= 0x110000) ) { hexEscapeChar(os, c); break; } // If we got here, this is in fact a valid(ish) utf-8 sequence for (std::size_t n = 0; n < encBytes; ++n) { os << m_str[idx + n]; } idx += encBytes - 1; break; } } } 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 ) DOCTEST_NOEXCEPT : m_writer( other.m_writer ){ other.m_writer = nullptr; } XmlWriter::ScopedElement& XmlWriter::ScopedElement::operator=( ScopedElement&& other ) DOCTEST_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(); // called explicitly by the reporters that use the writer class - see issue #627 } 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, const char* attribute ) { if( !name.empty() && attribute && attribute[0] != '\0' ) 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 of copy-pasted code from Catch // ================================================================================================= // clang-format on struct XmlReporter : public IReporter { XmlWriter xml; DOCTEST_DECLARE_MUTEX(mutex) // caching pointers/references to objects of these types - safe to do const ContextOptions& opt; const TestCaseData* tc = nullptr; XmlReporter(const ContextOptions& co) : xml(*co.cout) , opt(co) {} void log_contexts() { int num_contexts = get_num_active_contexts(); if(num_contexts) { auto contexts = get_active_contexts(); std::stringstream ss; for(int i = 0; i < num_contexts; ++i) { contexts[i]->stringify(&ss); xml.scopedElement("Info").writeText(ss.str()); ss.str(""); } } } unsigned line(unsigned l) const { return opt.no_line_numbers ? 0 : l; } void test_case_start_impl(const TestCaseData& in) { bool open_ts_tag = false; if(tc != nullptr) { // we have already opened a test suite if(std::strcmp(tc->m_test_suite, in.m_test_suite) != 0) { xml.endElement(); open_ts_tag = true; } } else { open_ts_tag = true; // first test case ==> first test suite } if(open_ts_tag) { xml.startElement("TestSuite"); xml.writeAttribute("name", in.m_test_suite); } tc = ∈ xml.startElement("TestCase") .writeAttribute("name", in.m_name) .writeAttribute("filename", skipPathFromFilename(in.m_file.c_str())) .writeAttribute("line", line(in.m_line)) .writeAttribute("description", in.m_description); if(Approx(in.m_timeout) != 0) xml.writeAttribute("timeout", in.m_timeout); if(in.m_may_fail) xml.writeAttribute("may_fail", true); if(in.m_should_fail) xml.writeAttribute("should_fail", true); } // ========================================================================================= // WHAT FOLLOWS ARE OVERRIDES OF THE VIRTUAL METHODS OF THE REPORTER INTERFACE // ========================================================================================= void report_query(const QueryData& in) override { test_run_start(); if(opt.list_reporters) { for(auto& curr : getListeners()) xml.scopedElement("Listener") .writeAttribute("priority", curr.first.first) .writeAttribute("name", curr.first.second); for(auto& curr : getReporters()) xml.scopedElement("Reporter") .writeAttribute("priority", curr.first.first) .writeAttribute("name", curr.first.second); } else if(opt.count || opt.list_test_cases) { for(unsigned i = 0; i < in.num_data; ++i) { xml.scopedElement("TestCase").writeAttribute("name", in.data[i]->m_name) .writeAttribute("testsuite", in.data[i]->m_test_suite) .writeAttribute("filename", skipPathFromFilename(in.data[i]->m_file.c_str())) .writeAttribute("line", line(in.data[i]->m_line)) .writeAttribute("skipped", in.data[i]->m_skip); } xml.scopedElement("OverallResultsTestCases") .writeAttribute("unskipped", in.run_stats->numTestCasesPassingFilters); } else if(opt.list_test_suites) { for(unsigned i = 0; i < in.num_data; ++i) xml.scopedElement("TestSuite").writeAttribute("name", in.data[i]->m_test_suite); xml.scopedElement("OverallResultsTestCases") .writeAttribute("unskipped", in.run_stats->numTestCasesPassingFilters); xml.scopedElement("OverallResultsTestSuites") .writeAttribute("unskipped", in.run_stats->numTestSuitesPassingFilters); } xml.endElement(); } void test_run_start() override { xml.writeDeclaration(); // remove .exe extension - mainly to have the same output on UNIX and Windows std::string binary_name = skipPathFromFilename(opt.binary_name.c_str()); #ifdef DOCTEST_PLATFORM_WINDOWS if(binary_name.rfind(".exe") != std::string::npos) binary_name = binary_name.substr(0, binary_name.length() - 4); #endif // DOCTEST_PLATFORM_WINDOWS xml.startElement("doctest").writeAttribute("binary", binary_name); if(opt.no_version == false) xml.writeAttribute("version", DOCTEST_VERSION_STR); // only the consequential ones (TODO: filters) xml.scopedElement("Options") .writeAttribute("order_by", opt.order_by.c_str()) .writeAttribute("rand_seed", opt.rand_seed) .writeAttribute("first", opt.first) .writeAttribute("last", opt.last) .writeAttribute("abort_after", opt.abort_after) .writeAttribute("subcase_filter_levels", opt.subcase_filter_levels) .writeAttribute("case_sensitive", opt.case_sensitive) .writeAttribute("no_throw", opt.no_throw) .writeAttribute("no_skip", opt.no_skip); } void test_run_end(const TestRunStats& p) override { if(tc) // the TestSuite tag - only if there has been at least 1 test case xml.endElement(); xml.scopedElement("OverallResultsAsserts") .writeAttribute("successes", p.numAsserts - p.numAssertsFailed) .writeAttribute("failures", p.numAssertsFailed); xml.startElement("OverallResultsTestCases") .writeAttribute("successes", p.numTestCasesPassingFilters - p.numTestCasesFailed) .writeAttribute("failures", p.numTestCasesFailed); if(opt.no_skipped_summary == false) xml.writeAttribute("skipped", p.numTestCases - p.numTestCasesPassingFilters); xml.endElement(); xml.endElement(); } void test_case_start(const TestCaseData& in) override { test_case_start_impl(in); xml.ensureTagClosed(); } void test_case_reenter(const TestCaseData&) override {} void test_case_end(const CurrentTestCaseStats& st) override { xml.startElement("OverallResultsAsserts") .writeAttribute("successes", st.numAssertsCurrentTest - st.numAssertsFailedCurrentTest) .writeAttribute("failures", st.numAssertsFailedCurrentTest) .writeAttribute("test_case_success", st.testCaseSuccess); if(opt.duration) xml.writeAttribute("duration", st.seconds); if(tc->m_expected_failures) xml.writeAttribute("expected_failures", tc->m_expected_failures); xml.endElement(); xml.endElement(); } void test_case_exception(const TestCaseException& e) override { DOCTEST_LOCK_MUTEX(mutex) xml.scopedElement("Exception") .writeAttribute("crash", e.is_crash) .writeText(e.error_string.c_str()); } void subcase_start(const SubcaseSignature& in) override { xml.startElement("SubCase") .writeAttribute("name", in.m_name) .writeAttribute("filename", skipPathFromFilename(in.m_file)) .writeAttribute("line", line(in.m_line)); xml.ensureTagClosed(); } void subcase_end() override { xml.endElement(); } void log_assert(const AssertData& rb) override { if(!rb.m_failed && !opt.success) return; DOCTEST_LOCK_MUTEX(mutex) xml.startElement("Expression") .writeAttribute("success", !rb.m_failed) .writeAttribute("type", assertString(rb.m_at)) .writeAttribute("filename", skipPathFromFilename(rb.m_file)) .writeAttribute("line", line(rb.m_line)); xml.scopedElement("Original").writeText(rb.m_expr); if(rb.m_threw) xml.scopedElement("Exception").writeText(rb.m_exception.c_str()); if(rb.m_at & assertType::is_throws_as) xml.scopedElement("ExpectedException").writeText(rb.m_exception_type); if(rb.m_at & assertType::is_throws_with) xml.scopedElement("ExpectedExceptionString").writeText(rb.m_exception_string.c_str()); if((rb.m_at & assertType::is_normal) && !rb.m_threw) xml.scopedElement("Expanded").writeText(rb.m_decomp.c_str()); log_contexts(); xml.endElement(); } void log_message(const MessageData& mb) override { DOCTEST_LOCK_MUTEX(mutex) xml.startElement("Message") .writeAttribute("type", failureString(mb.m_severity)) .writeAttribute("filename", skipPathFromFilename(mb.m_file)) .writeAttribute("line", line(mb.m_line)); xml.scopedElement("Text").writeText(mb.m_string.c_str()); log_contexts(); xml.endElement(); } void test_case_skipped(const TestCaseData& in) override { if(opt.no_skipped_summary == false) { test_case_start_impl(in); xml.writeAttribute("skipped", "true"); xml.endElement(); } } }; DOCTEST_REGISTER_REPORTER("xml", 0, XmlReporter); void fulltext_log_assert_to_stream(std::ostream& s, const AssertData& rb) { if((rb.m_at & (assertType::is_throws_as | assertType::is_throws_with)) == 0) //!OCLINT bitwise operator in conditional s << Color::Cyan << assertString(rb.m_at) << "( " << rb.m_expr << " ) " << Color::None; if(rb.m_at & assertType::is_throws) { //!OCLINT bitwise operator in conditional s << (rb.m_threw ? "threw as expected!" : "did NOT throw at all!") << "\n"; } else if((rb.m_at & assertType::is_throws_as) && (rb.m_at & assertType::is_throws_with)) { //!OCLINT s << Color::Cyan << assertString(rb.m_at) << "( " << rb.m_expr << ", \"" << rb.m_exception_string.c_str() << "\", " << rb.m_exception_type << " ) " << Color::None; if(rb.m_threw) { if(!rb.m_failed) { s << "threw as expected!\n"; } else { s << "threw a DIFFERENT exception! (contents: " << rb.m_exception << ")\n"; } } else { s << "did NOT throw at all!\n"; } } else if(rb.m_at & assertType::is_throws_as) { //!OCLINT bitwise operator in conditional s << Color::Cyan << assertString(rb.m_at) << "( " << rb.m_expr << ", " << rb.m_exception_type << " ) " << Color::None << (rb.m_threw ? (rb.m_threw_as ? "threw as expected!" : "threw a DIFFERENT exception: ") : "did NOT throw at all!") << Color::Cyan << rb.m_exception << "\n"; } else if(rb.m_at & assertType::is_throws_with) { //!OCLINT bitwise operator in conditional s << Color::Cyan << assertString(rb.m_at) << "( " << rb.m_expr << ", \"" << rb.m_exception_string.c_str() << "\" ) " << Color::None << (rb.m_threw ? (!rb.m_failed ? "threw as expected!" : "threw a DIFFERENT exception: ") : "did NOT throw at all!") << Color::Cyan << rb.m_exception << "\n"; } else if(rb.m_at & assertType::is_nothrow) { //!OCLINT bitwise operator in conditional s << (rb.m_threw ? "THREW exception: " : "didn't throw!") << Color::Cyan << rb.m_exception << "\n"; } else { s << (rb.m_threw ? "THREW exception: " : (!rb.m_failed ? "is correct!\n" : "is NOT correct!\n")); if(rb.m_threw) s << rb.m_exception << "\n"; else s << " values: " << assertString(rb.m_at) << "( " << rb.m_decomp << " )\n"; } } // TODO: // - log_message() // - respond to queries // - honor remaining options // - more attributes in tags struct JUnitReporter : public IReporter { XmlWriter xml; DOCTEST_DECLARE_MUTEX(mutex) Timer timer; std::vector deepestSubcaseStackNames; struct JUnitTestCaseData { static 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"); std::tm timeInfo; #ifdef DOCTEST_PLATFORM_WINDOWS gmtime_s(&timeInfo, &rawtime); #else // DOCTEST_PLATFORM_WINDOWS gmtime_r(&rawtime, &timeInfo); #endif // DOCTEST_PLATFORM_WINDOWS char timeStamp[timeStampSize]; const char* const fmt = "%Y-%m-%dT%H:%M:%SZ"; std::strftime(timeStamp, timeStampSize, fmt, &timeInfo); return std::string(timeStamp); } struct JUnitTestMessage { JUnitTestMessage(const std::string& _message, const std::string& _type, const std::string& _details) : message(_message), type(_type), details(_details) {} JUnitTestMessage(const std::string& _message, const std::string& _details) : message(_message), type(), details(_details) {} std::string message, type, details; }; struct JUnitTestCase { JUnitTestCase(const std::string& _classname, const std::string& _name) : classname(_classname), name(_name), time(0), failures() {} std::string classname, name; double time; std::vector failures, errors; }; void add(const std::string& classname, const std::string& name) { testcases.emplace_back(classname, name); } void appendSubcaseNamesToLastTestcase(std::vector nameStack) { for(auto& curr: nameStack) if(curr.size()) testcases.back().name += std::string("/") + curr.c_str(); } void addTime(double time) { if(time < 1e-4) time = 0; testcases.back().time = time; totalSeconds += time; } void addFailure(const std::string& message, const std::string& type, const std::string& details) { testcases.back().failures.emplace_back(message, type, details); ++totalFailures; } void addError(const std::string& message, const std::string& details) { testcases.back().errors.emplace_back(message, details); ++totalErrors; } std::vector testcases; double totalSeconds = 0; int totalErrors = 0, totalFailures = 0; }; JUnitTestCaseData testCaseData; // caching pointers/references to objects of these types - safe to do const ContextOptions& opt; const TestCaseData* tc = nullptr; JUnitReporter(const ContextOptions& co) : xml(*co.cout) , opt(co) {} unsigned line(unsigned l) const { return opt.no_line_numbers ? 0 : l; } // ========================================================================================= // WHAT FOLLOWS ARE OVERRIDES OF THE VIRTUAL METHODS OF THE REPORTER INTERFACE // ========================================================================================= void report_query(const QueryData&) override { xml.writeDeclaration(); } void test_run_start() override { xml.writeDeclaration(); } void test_run_end(const TestRunStats& p) override { // remove .exe extension - mainly to have the same output on UNIX and Windows std::string binary_name = skipPathFromFilename(opt.binary_name.c_str()); #ifdef DOCTEST_PLATFORM_WINDOWS if(binary_name.rfind(".exe") != std::string::npos) binary_name = binary_name.substr(0, binary_name.length() - 4); #endif // DOCTEST_PLATFORM_WINDOWS xml.startElement("testsuites"); xml.startElement("testsuite").writeAttribute("name", binary_name) .writeAttribute("errors", testCaseData.totalErrors) .writeAttribute("failures", testCaseData.totalFailures) .writeAttribute("tests", p.numAsserts); if(opt.no_time_in_output == false) { xml.writeAttribute("time", testCaseData.totalSeconds); xml.writeAttribute("timestamp", JUnitTestCaseData::getCurrentTimestamp()); } if(opt.no_version == false) xml.writeAttribute("doctest_version", DOCTEST_VERSION_STR); for(const auto& testCase : testCaseData.testcases) { xml.startElement("testcase") .writeAttribute("classname", testCase.classname) .writeAttribute("name", testCase.name); if(opt.no_time_in_output == false) xml.writeAttribute("time", testCase.time); // This is not ideal, but it should be enough to mimic gtest's junit output. xml.writeAttribute("status", "run"); for(const auto& failure : testCase.failures) { xml.scopedElement("failure") .writeAttribute("message", failure.message) .writeAttribute("type", failure.type) .writeText(failure.details, false); } for(const auto& error : testCase.errors) { xml.scopedElement("error") .writeAttribute("message", error.message) .writeText(error.details); } xml.endElement(); } xml.endElement(); xml.endElement(); } void test_case_start(const TestCaseData& in) override { testCaseData.add(skipPathFromFilename(in.m_file.c_str()), in.m_name); timer.start(); } void test_case_reenter(const TestCaseData& in) override { testCaseData.addTime(timer.getElapsedSeconds()); testCaseData.appendSubcaseNamesToLastTestcase(deepestSubcaseStackNames); deepestSubcaseStackNames.clear(); timer.start(); testCaseData.add(skipPathFromFilename(in.m_file.c_str()), in.m_name); } void test_case_end(const CurrentTestCaseStats&) override { testCaseData.addTime(timer.getElapsedSeconds()); testCaseData.appendSubcaseNamesToLastTestcase(deepestSubcaseStackNames); deepestSubcaseStackNames.clear(); } void test_case_exception(const TestCaseException& e) override { DOCTEST_LOCK_MUTEX(mutex) testCaseData.addError("exception", e.error_string.c_str()); } void subcase_start(const SubcaseSignature& in) override { deepestSubcaseStackNames.push_back(in.m_name); } void subcase_end() override {} void log_assert(const AssertData& rb) override { if(!rb.m_failed) // report only failures & ignore the `success` option return; DOCTEST_LOCK_MUTEX(mutex) std::ostringstream os; os << skipPathFromFilename(rb.m_file) << (opt.gnu_file_line ? ":" : "(") << line(rb.m_line) << (opt.gnu_file_line ? ":" : "):") << std::endl; fulltext_log_assert_to_stream(os, rb); log_contexts(os); testCaseData.addFailure(rb.m_decomp.c_str(), assertString(rb.m_at), os.str()); } void log_message(const MessageData& mb) override { if(mb.m_severity & assertType::is_warn) // report only failures return; DOCTEST_LOCK_MUTEX(mutex) std::ostringstream os; os << skipPathFromFilename(mb.m_file) << (opt.gnu_file_line ? ":" : "(") << line(mb.m_line) << (opt.gnu_file_line ? ":" : "):") << std::endl; os << mb.m_string.c_str() << "\n"; log_contexts(os); testCaseData.addFailure(mb.m_string.c_str(), mb.m_severity & assertType::is_check ? "FAIL_CHECK" : "FAIL", os.str()); } void test_case_skipped(const TestCaseData&) override {} void log_contexts(std::ostringstream& s) { int num_contexts = get_num_active_contexts(); if(num_contexts) { auto contexts = get_active_contexts(); s << " logged: "; for(int i = 0; i < num_contexts; ++i) { s << (i == 0 ? "" : " "); contexts[i]->stringify(&s); s << std::endl; } } } }; DOCTEST_REGISTER_REPORTER("junit", 0, JUnitReporter); struct Whitespace { int nrSpaces; explicit Whitespace(int nr) : nrSpaces(nr) {} }; std::ostream& operator<<(std::ostream& out, const Whitespace& ws) { if(ws.nrSpaces != 0) out << std::setw(ws.nrSpaces) << ' '; return out; } struct ConsoleReporter : public IReporter { std::ostream& s; bool hasLoggedCurrentTestStart; std::vector subcasesStack; size_t currentSubcaseLevel; DOCTEST_DECLARE_MUTEX(mutex) // caching pointers/references to objects of these types - safe to do const ContextOptions& opt; const TestCaseData* tc; ConsoleReporter(const ContextOptions& co) : s(*co.cout) , opt(co) {} ConsoleReporter(const ContextOptions& co, std::ostream& ostr) : s(ostr) , opt(co) {} // ========================================================================================= // WHAT FOLLOWS ARE HELPERS USED BY THE OVERRIDES OF THE VIRTUAL METHODS OF THE INTERFACE // ========================================================================================= void separator_to_stream() { s << Color::Yellow << "===============================================================================" "\n"; } const char* getSuccessOrFailString(bool success, assertType::Enum at, const char* success_str) { if(success) return success_str; return failureString(at); } Color::Enum getSuccessOrFailColor(bool success, assertType::Enum at) { return success ? Color::BrightGreen : (at & assertType::is_warn) ? Color::Yellow : Color::Red; } void successOrFailColoredStringToStream(bool success, assertType::Enum at, const char* success_str = "SUCCESS") { s << getSuccessOrFailColor(success, at) << getSuccessOrFailString(success, at, success_str) << ": "; } void log_contexts() { int num_contexts = get_num_active_contexts(); if(num_contexts) { auto contexts = get_active_contexts(); s << Color::None << " logged: "; for(int i = 0; i < num_contexts; ++i) { s << (i == 0 ? "" : " "); contexts[i]->stringify(&s); s << "\n"; } } s << "\n"; } // this was requested to be made virtual so users could override it virtual void file_line_to_stream(const char* file, int line, const char* tail = "") { s << Color::LightGrey << skipPathFromFilename(file) << (opt.gnu_file_line ? ":" : "(") << (opt.no_line_numbers ? 0 : line) // 0 or the real num depending on the option << (opt.gnu_file_line ? ":" : "):") << tail; } void logTestStart() { if(hasLoggedCurrentTestStart) return; separator_to_stream(); file_line_to_stream(tc->m_file.c_str(), tc->m_line, "\n"); if(tc->m_description) s << Color::Yellow << "DESCRIPTION: " << Color::None << tc->m_description << "\n"; if(tc->m_test_suite && tc->m_test_suite[0] != '\0') s << Color::Yellow << "TEST SUITE: " << Color::None << tc->m_test_suite << "\n"; if(strncmp(tc->m_name, " Scenario:", 11) != 0) s << Color::Yellow << "TEST CASE: "; s << Color::None << tc->m_name << "\n"; for(size_t i = 0; i < currentSubcaseLevel; ++i) { if(subcasesStack[i].m_name[0] != '\0') s << " " << subcasesStack[i].m_name << "\n"; } if(currentSubcaseLevel != subcasesStack.size()) { s << Color::Yellow << "\nDEEPEST SUBCASE STACK REACHED (DIFFERENT FROM THE CURRENT ONE):\n" << Color::None; for(size_t i = 0; i < subcasesStack.size(); ++i) { if(subcasesStack[i].m_name[0] != '\0') s << " " << subcasesStack[i].m_name << "\n"; } } s << "\n"; hasLoggedCurrentTestStart = true; } void printVersion() { if(opt.no_version == false) s << Color::Cyan << "[doctest] " << Color::None << "doctest version is \"" << DOCTEST_VERSION_STR << "\"\n"; } void printIntro() { if(opt.no_intro == false) { printVersion(); s << Color::Cyan << "[doctest] " << Color::None << "run with \"--" DOCTEST_OPTIONS_PREFIX_DISPLAY "help\" for options\n"; } } void printHelp() { int sizePrefixDisplay = static_cast(strlen(DOCTEST_OPTIONS_PREFIX_DISPLAY)); printVersion(); // clang-format off s << Color::Cyan << "[doctest]\n" << Color::None; s << Color::Cyan << "[doctest] " << Color::None; s << "boolean values: \"1/on/yes/true\" or \"0/off/no/false\"\n"; s << Color::Cyan << "[doctest] " << Color::None; s << "filter values: \"str1,str2,str3\" (comma separated strings)\n"; s << Color::Cyan << "[doctest]\n" << Color::None; s << Color::Cyan << "[doctest] " << Color::None; s << "filters use wildcards for matching strings\n"; s << Color::Cyan << "[doctest] " << Color::None; s << "something passes a filter if any of the strings in a filter matches\n"; #ifndef DOCTEST_CONFIG_NO_UNPREFIXED_OPTIONS s << Color::Cyan << "[doctest]\n" << Color::None; s << Color::Cyan << "[doctest] " << Color::None; s << "ALL FLAGS, OPTIONS AND FILTERS ALSO AVAILABLE WITH A \"" DOCTEST_CONFIG_OPTIONS_PREFIX "\" PREFIX!!!\n"; #endif s << Color::Cyan << "[doctest]\n" << Color::None; s << Color::Cyan << "[doctest] " << Color::None; s << "Query flags - the program quits after them. Available:\n\n"; s << " -" DOCTEST_OPTIONS_PREFIX_DISPLAY "?, --" DOCTEST_OPTIONS_PREFIX_DISPLAY "help, -" DOCTEST_OPTIONS_PREFIX_DISPLAY "h " << Whitespace(sizePrefixDisplay*0) << "prints this message\n"; s << " -" DOCTEST_OPTIONS_PREFIX_DISPLAY "v, --" DOCTEST_OPTIONS_PREFIX_DISPLAY "version " << Whitespace(sizePrefixDisplay*1) << "prints the version\n"; s << " -" DOCTEST_OPTIONS_PREFIX_DISPLAY "c, --" DOCTEST_OPTIONS_PREFIX_DISPLAY "count " << Whitespace(sizePrefixDisplay*1) << "prints the number of matching tests\n"; s << " -" DOCTEST_OPTIONS_PREFIX_DISPLAY "ltc, --" DOCTEST_OPTIONS_PREFIX_DISPLAY "list-test-cases " << Whitespace(sizePrefixDisplay*1) << "lists all matching tests by name\n"; s << " -" DOCTEST_OPTIONS_PREFIX_DISPLAY "lts, --" DOCTEST_OPTIONS_PREFIX_DISPLAY "list-test-suites " << Whitespace(sizePrefixDisplay*1) << "lists all matching test suites\n"; s << " -" DOCTEST_OPTIONS_PREFIX_DISPLAY "lr, --" DOCTEST_OPTIONS_PREFIX_DISPLAY "list-reporters " << Whitespace(sizePrefixDisplay*1) << "lists all registered reporters\n\n"; // ================================================================================== << 79 s << Color::Cyan << "[doctest] " << Color::None; s << "The available / options/filters are:\n\n"; s << " -" DOCTEST_OPTIONS_PREFIX_DISPLAY "tc, --" DOCTEST_OPTIONS_PREFIX_DISPLAY "test-case= " << Whitespace(sizePrefixDisplay*1) << "filters tests by their name\n"; s << " -" DOCTEST_OPTIONS_PREFIX_DISPLAY "tce, --" DOCTEST_OPTIONS_PREFIX_DISPLAY "test-case-exclude= " << Whitespace(sizePrefixDisplay*1) << "filters OUT tests by their name\n"; s << " -" DOCTEST_OPTIONS_PREFIX_DISPLAY "sf, --" DOCTEST_OPTIONS_PREFIX_DISPLAY "source-file= " << Whitespace(sizePrefixDisplay*1) << "filters tests by their file\n"; s << " -" DOCTEST_OPTIONS_PREFIX_DISPLAY "sfe, --" DOCTEST_OPTIONS_PREFIX_DISPLAY "source-file-exclude= " << Whitespace(sizePrefixDisplay*1) << "filters OUT tests by their file\n"; s << " -" DOCTEST_OPTIONS_PREFIX_DISPLAY "ts, --" DOCTEST_OPTIONS_PREFIX_DISPLAY "test-suite= " << Whitespace(sizePrefixDisplay*1) << "filters tests by their test suite\n"; s << " -" DOCTEST_OPTIONS_PREFIX_DISPLAY "tse, --" DOCTEST_OPTIONS_PREFIX_DISPLAY "test-suite-exclude= " << Whitespace(sizePrefixDisplay*1) << "filters OUT tests by their test suite\n"; s << " -" DOCTEST_OPTIONS_PREFIX_DISPLAY "sc, --" DOCTEST_OPTIONS_PREFIX_DISPLAY "subcase= " << Whitespace(sizePrefixDisplay*1) << "filters subcases by their name\n"; s << " -" DOCTEST_OPTIONS_PREFIX_DISPLAY "sce, --" DOCTEST_OPTIONS_PREFIX_DISPLAY "subcase-exclude= " << Whitespace(sizePrefixDisplay*1) << "filters OUT subcases by their name\n"; s << " -" DOCTEST_OPTIONS_PREFIX_DISPLAY "r, --" DOCTEST_OPTIONS_PREFIX_DISPLAY "reporters= " << Whitespace(sizePrefixDisplay*1) << "reporters to use (console is default)\n"; s << " -" DOCTEST_OPTIONS_PREFIX_DISPLAY "o, --" DOCTEST_OPTIONS_PREFIX_DISPLAY "out= " << Whitespace(sizePrefixDisplay*1) << "output filename\n"; s << " -" DOCTEST_OPTIONS_PREFIX_DISPLAY "ob, --" DOCTEST_OPTIONS_PREFIX_DISPLAY "order-by= " << Whitespace(sizePrefixDisplay*1) << "how the tests should be ordered\n"; s << Whitespace(sizePrefixDisplay*3) << " - [file/suite/name/rand/none]\n"; s << " -" DOCTEST_OPTIONS_PREFIX_DISPLAY "rs, --" DOCTEST_OPTIONS_PREFIX_DISPLAY "rand-seed= " << Whitespace(sizePrefixDisplay*1) << "seed for random ordering\n"; s << " -" DOCTEST_OPTIONS_PREFIX_DISPLAY "f, --" DOCTEST_OPTIONS_PREFIX_DISPLAY "first= " << Whitespace(sizePrefixDisplay*1) << "the first test passing the filters to\n"; s << Whitespace(sizePrefixDisplay*3) << " execute - for range-based execution\n"; s << " -" DOCTEST_OPTIONS_PREFIX_DISPLAY "l, --" DOCTEST_OPTIONS_PREFIX_DISPLAY "last= " << Whitespace(sizePrefixDisplay*1) << "the last test passing the filters to\n"; s << Whitespace(sizePrefixDisplay*3) << " execute - for range-based execution\n"; s << " -" DOCTEST_OPTIONS_PREFIX_DISPLAY "aa, --" DOCTEST_OPTIONS_PREFIX_DISPLAY "abort-after= " << Whitespace(sizePrefixDisplay*1) << "stop after failed assertions\n"; s << " -" DOCTEST_OPTIONS_PREFIX_DISPLAY "scfl,--" DOCTEST_OPTIONS_PREFIX_DISPLAY "subcase-filter-levels= " << Whitespace(sizePrefixDisplay*1) << "apply filters for the first levels\n"; s << Color::Cyan << "\n[doctest] " << Color::None; s << "Bool options - can be used like flags and true is assumed. Available:\n\n"; s << " -" DOCTEST_OPTIONS_PREFIX_DISPLAY "s, --" DOCTEST_OPTIONS_PREFIX_DISPLAY "success= " << Whitespace(sizePrefixDisplay*1) << "include successful assertions in output\n"; s << " -" DOCTEST_OPTIONS_PREFIX_DISPLAY "cs, --" DOCTEST_OPTIONS_PREFIX_DISPLAY "case-sensitive= " << Whitespace(sizePrefixDisplay*1) << "filters being treated as case sensitive\n"; s << " -" DOCTEST_OPTIONS_PREFIX_DISPLAY "e, --" DOCTEST_OPTIONS_PREFIX_DISPLAY "exit= " << Whitespace(sizePrefixDisplay*1) << "exits after the tests finish\n"; s << " -" DOCTEST_OPTIONS_PREFIX_DISPLAY "d, --" DOCTEST_OPTIONS_PREFIX_DISPLAY "duration= " << Whitespace(sizePrefixDisplay*1) << "prints the time duration of each test\n"; s << " -" DOCTEST_OPTIONS_PREFIX_DISPLAY "m, --" DOCTEST_OPTIONS_PREFIX_DISPLAY "minimal= " << Whitespace(sizePrefixDisplay*1) << "minimal console output (only failures)\n"; s << " -" DOCTEST_OPTIONS_PREFIX_DISPLAY "q, --" DOCTEST_OPTIONS_PREFIX_DISPLAY "quiet= " << Whitespace(sizePrefixDisplay*1) << "no console output\n"; s << " -" DOCTEST_OPTIONS_PREFIX_DISPLAY "nt, --" DOCTEST_OPTIONS_PREFIX_DISPLAY "no-throw= " << Whitespace(sizePrefixDisplay*1) << "skips exceptions-related assert checks\n"; s << " -" DOCTEST_OPTIONS_PREFIX_DISPLAY "ne, --" DOCTEST_OPTIONS_PREFIX_DISPLAY "no-exitcode= " << Whitespace(sizePrefixDisplay*1) << "returns (or exits) always with success\n"; s << " -" DOCTEST_OPTIONS_PREFIX_DISPLAY "nr, --" DOCTEST_OPTIONS_PREFIX_DISPLAY "no-run= " << Whitespace(sizePrefixDisplay*1) << "skips all runtime doctest operations\n"; s << " -" DOCTEST_OPTIONS_PREFIX_DISPLAY "ni, --" DOCTEST_OPTIONS_PREFIX_DISPLAY "no-intro= " << Whitespace(sizePrefixDisplay*1) << "omit the framework intro in the output\n"; s << " -" DOCTEST_OPTIONS_PREFIX_DISPLAY "nv, --" DOCTEST_OPTIONS_PREFIX_DISPLAY "no-version= " << Whitespace(sizePrefixDisplay*1) << "omit the framework version in the output\n"; s << " -" DOCTEST_OPTIONS_PREFIX_DISPLAY "nc, --" DOCTEST_OPTIONS_PREFIX_DISPLAY "no-colors= " << Whitespace(sizePrefixDisplay*1) << "disables colors in output\n"; s << " -" DOCTEST_OPTIONS_PREFIX_DISPLAY "fc, --" DOCTEST_OPTIONS_PREFIX_DISPLAY "force-colors= " << Whitespace(sizePrefixDisplay*1) << "use colors even when not in a tty\n"; s << " -" DOCTEST_OPTIONS_PREFIX_DISPLAY "nb, --" DOCTEST_OPTIONS_PREFIX_DISPLAY "no-breaks= " << Whitespace(sizePrefixDisplay*1) << "disables breakpoints in debuggers\n"; s << " -" DOCTEST_OPTIONS_PREFIX_DISPLAY "ns, --" DOCTEST_OPTIONS_PREFIX_DISPLAY "no-skip= " << Whitespace(sizePrefixDisplay*1) << "don't skip test cases marked as skip\n"; s << " -" DOCTEST_OPTIONS_PREFIX_DISPLAY "gfl, --" DOCTEST_OPTIONS_PREFIX_DISPLAY "gnu-file-line= " << Whitespace(sizePrefixDisplay*1) << ":n: vs (n): for line numbers in output\n"; s << " -" DOCTEST_OPTIONS_PREFIX_DISPLAY "npf, --" DOCTEST_OPTIONS_PREFIX_DISPLAY "no-path-filenames= " << Whitespace(sizePrefixDisplay*1) << "only filenames and no paths in output\n"; s << " -" DOCTEST_OPTIONS_PREFIX_DISPLAY "nln, --" DOCTEST_OPTIONS_PREFIX_DISPLAY "no-line-numbers= " << Whitespace(sizePrefixDisplay*1) << "0 instead of real line numbers in output\n"; // ================================================================================== << 79 // clang-format on s << Color::Cyan << "\n[doctest] " << Color::None; s << "for more information visit the project documentation\n\n"; } void printRegisteredReporters() { printVersion(); auto printReporters = [this] (const reporterMap& reporters, const char* type) { if(reporters.size()) { s << Color::Cyan << "[doctest] " << Color::None << "listing all registered " << type << "\n"; for(auto& curr : reporters) s << "priority: " << std::setw(5) << curr.first.first << " name: " << curr.first.second << "\n"; } }; printReporters(getListeners(), "listeners"); printReporters(getReporters(), "reporters"); } // ========================================================================================= // WHAT FOLLOWS ARE OVERRIDES OF THE VIRTUAL METHODS OF THE REPORTER INTERFACE // ========================================================================================= void report_query(const QueryData& in) override { if(opt.version) { printVersion(); } else if(opt.help) { printHelp(); } else if(opt.list_reporters) { printRegisteredReporters(); } else if(opt.count || opt.list_test_cases) { if(opt.list_test_cases) { s << Color::Cyan << "[doctest] " << Color::None << "listing all test case names\n"; separator_to_stream(); } for(unsigned i = 0; i < in.num_data; ++i) s << Color::None << in.data[i]->m_name << "\n"; separator_to_stream(); s << Color::Cyan << "[doctest] " << Color::None << "unskipped test cases passing the current filters: " << g_cs->numTestCasesPassingFilters << "\n"; } else if(opt.list_test_suites) { s << Color::Cyan << "[doctest] " << Color::None << "listing all test suites\n"; separator_to_stream(); for(unsigned i = 0; i < in.num_data; ++i) s << Color::None << in.data[i]->m_test_suite << "\n"; separator_to_stream(); s << Color::Cyan << "[doctest] " << Color::None << "unskipped test cases passing the current filters: " << g_cs->numTestCasesPassingFilters << "\n"; s << Color::Cyan << "[doctest] " << Color::None << "test suites with unskipped test cases passing the current filters: " << g_cs->numTestSuitesPassingFilters << "\n"; } } void test_run_start() override { if(!opt.minimal) printIntro(); } void test_run_end(const TestRunStats& p) override { if(opt.minimal && p.numTestCasesFailed == 0) return; separator_to_stream(); s << std::dec; auto totwidth = int(std::ceil(log10(static_cast(std::max(p.numTestCasesPassingFilters, static_cast(p.numAsserts))) + 1))); auto passwidth = int(std::ceil(log10(static_cast(std::max(p.numTestCasesPassingFilters - p.numTestCasesFailed, static_cast(p.numAsserts - p.numAssertsFailed))) + 1))); auto failwidth = int(std::ceil(log10(static_cast(std::max(p.numTestCasesFailed, static_cast(p.numAssertsFailed))) + 1))); const bool anythingFailed = p.numTestCasesFailed > 0 || p.numAssertsFailed > 0; s << Color::Cyan << "[doctest] " << Color::None << "test cases: " << std::setw(totwidth) << p.numTestCasesPassingFilters << " | " << ((p.numTestCasesPassingFilters == 0 || anythingFailed) ? Color::None : Color::Green) << std::setw(passwidth) << p.numTestCasesPassingFilters - p.numTestCasesFailed << " passed" << Color::None << " | " << (p.numTestCasesFailed > 0 ? Color::Red : Color::None) << std::setw(failwidth) << p.numTestCasesFailed << " failed" << Color::None << " |"; if(opt.no_skipped_summary == false) { const int numSkipped = p.numTestCases - p.numTestCasesPassingFilters; s << " " << (numSkipped == 0 ? Color::None : Color::Yellow) << numSkipped << " skipped" << Color::None; } s << "\n"; s << Color::Cyan << "[doctest] " << Color::None << "assertions: " << std::setw(totwidth) << p.numAsserts << " | " << ((p.numAsserts == 0 || anythingFailed) ? Color::None : Color::Green) << std::setw(passwidth) << (p.numAsserts - p.numAssertsFailed) << " passed" << Color::None << " | " << (p.numAssertsFailed > 0 ? Color::Red : Color::None) << std::setw(failwidth) << p.numAssertsFailed << " failed" << Color::None << " |\n"; s << Color::Cyan << "[doctest] " << Color::None << "Status: " << (p.numTestCasesFailed > 0 ? Color::Red : Color::Green) << ((p.numTestCasesFailed > 0) ? "FAILURE!" : "SUCCESS!") << Color::None << std::endl; } void test_case_start(const TestCaseData& in) override { hasLoggedCurrentTestStart = false; tc = ∈ subcasesStack.clear(); currentSubcaseLevel = 0; } void test_case_reenter(const TestCaseData&) override { subcasesStack.clear(); } void test_case_end(const CurrentTestCaseStats& st) override { if(tc->m_no_output) return; // log the preamble of the test case only if there is something // else to print - something other than that an assert has failed if(opt.duration || (st.failure_flags && st.failure_flags != static_cast(TestCaseFailureReason::AssertFailure))) logTestStart(); if(opt.duration) s << Color::None << std::setprecision(6) << std::fixed << st.seconds << " s: " << tc->m_name << "\n"; if(st.failure_flags & TestCaseFailureReason::Timeout) s << Color::Red << "Test case exceeded time limit of " << std::setprecision(6) << std::fixed << tc->m_timeout << "!\n"; if(st.failure_flags & TestCaseFailureReason::ShouldHaveFailedButDidnt) { s << Color::Red << "Should have failed but didn't! Marking it as failed!\n"; } else if(st.failure_flags & TestCaseFailureReason::ShouldHaveFailedAndDid) { s << Color::Yellow << "Failed as expected so marking it as not failed\n"; } else if(st.failure_flags & TestCaseFailureReason::CouldHaveFailedAndDid) { s << Color::Yellow << "Allowed to fail so marking it as not failed\n"; } else if(st.failure_flags & TestCaseFailureReason::DidntFailExactlyNumTimes) { s << Color::Red << "Didn't fail exactly " << tc->m_expected_failures << " times so marking it as failed!\n"; } else if(st.failure_flags & TestCaseFailureReason::FailedExactlyNumTimes) { s << Color::Yellow << "Failed exactly " << tc->m_expected_failures << " times as expected so marking it as not failed!\n"; } if(st.failure_flags & TestCaseFailureReason::TooManyFailedAsserts) { s << Color::Red << "Aborting - too many failed asserts!\n"; } s << Color::None; // lgtm [cpp/useless-expression] } void test_case_exception(const TestCaseException& e) override { DOCTEST_LOCK_MUTEX(mutex) if(tc->m_no_output) return; logTestStart(); file_line_to_stream(tc->m_file.c_str(), tc->m_line, " "); successOrFailColoredStringToStream(false, e.is_crash ? assertType::is_require : assertType::is_check); s << Color::Red << (e.is_crash ? "test case CRASHED: " : "test case THREW exception: ") << Color::Cyan << e.error_string << "\n"; int num_stringified_contexts = get_num_stringified_contexts(); if(num_stringified_contexts) { auto stringified_contexts = get_stringified_contexts(); s << Color::None << " logged: "; for(int i = num_stringified_contexts; i > 0; --i) { s << (i == num_stringified_contexts ? "" : " ") << stringified_contexts[i - 1] << "\n"; } } s << "\n" << Color::None; } void subcase_start(const SubcaseSignature& subc) override { subcasesStack.push_back(subc); ++currentSubcaseLevel; hasLoggedCurrentTestStart = false; } void subcase_end() override { --currentSubcaseLevel; hasLoggedCurrentTestStart = false; } void log_assert(const AssertData& rb) override { if((!rb.m_failed && !opt.success) || tc->m_no_output) return; DOCTEST_LOCK_MUTEX(mutex) logTestStart(); file_line_to_stream(rb.m_file, rb.m_line, " "); successOrFailColoredStringToStream(!rb.m_failed, rb.m_at); fulltext_log_assert_to_stream(s, rb); log_contexts(); } void log_message(const MessageData& mb) override { if(tc->m_no_output) return; DOCTEST_LOCK_MUTEX(mutex) logTestStart(); file_line_to_stream(mb.m_file, mb.m_line, " "); s << getSuccessOrFailColor(false, mb.m_severity) << getSuccessOrFailString(mb.m_severity & assertType::is_warn, mb.m_severity, "MESSAGE") << ": "; s << Color::None << mb.m_string << "\n"; log_contexts(); } void test_case_skipped(const TestCaseData&) override {} }; DOCTEST_REGISTER_REPORTER("console", 0, ConsoleReporter); #ifdef DOCTEST_PLATFORM_WINDOWS struct DebugOutputWindowReporter : public ConsoleReporter { DOCTEST_THREAD_LOCAL static std::ostringstream oss; DebugOutputWindowReporter(const ContextOptions& co) : ConsoleReporter(co, oss) {} #define DOCTEST_DEBUG_OUTPUT_REPORTER_OVERRIDE(func, type, arg) \ void func(type arg) override { \ bool with_col = g_no_colors; \ g_no_colors = false; \ ConsoleReporter::func(arg); \ if(oss.tellp() != std::streampos{}) { \ DOCTEST_OUTPUT_DEBUG_STRING(oss.str().c_str()); \ oss.str(""); \ } \ g_no_colors = with_col; \ } DOCTEST_DEBUG_OUTPUT_REPORTER_OVERRIDE(test_run_start, DOCTEST_EMPTY, DOCTEST_EMPTY) DOCTEST_DEBUG_OUTPUT_REPORTER_OVERRIDE(test_run_end, const TestRunStats&, in) DOCTEST_DEBUG_OUTPUT_REPORTER_OVERRIDE(test_case_start, const TestCaseData&, in) DOCTEST_DEBUG_OUTPUT_REPORTER_OVERRIDE(test_case_reenter, const TestCaseData&, in) DOCTEST_DEBUG_OUTPUT_REPORTER_OVERRIDE(test_case_end, const CurrentTestCaseStats&, in) DOCTEST_DEBUG_OUTPUT_REPORTER_OVERRIDE(test_case_exception, const TestCaseException&, in) DOCTEST_DEBUG_OUTPUT_REPORTER_OVERRIDE(subcase_start, const SubcaseSignature&, in) DOCTEST_DEBUG_OUTPUT_REPORTER_OVERRIDE(subcase_end, DOCTEST_EMPTY, DOCTEST_EMPTY) DOCTEST_DEBUG_OUTPUT_REPORTER_OVERRIDE(log_assert, const AssertData&, in) DOCTEST_DEBUG_OUTPUT_REPORTER_OVERRIDE(log_message, const MessageData&, in) DOCTEST_DEBUG_OUTPUT_REPORTER_OVERRIDE(test_case_skipped, const TestCaseData&, in) }; DOCTEST_THREAD_LOCAL std::ostringstream DebugOutputWindowReporter::oss; #endif // DOCTEST_PLATFORM_WINDOWS // the implementation of parseOption() bool parseOptionImpl(int argc, const char* const* argv, const char* pattern, String* value) { // going from the end to the beginning and stopping on the first occurrence from the end for(int i = argc; i > 0; --i) { auto index = i - 1; auto temp = std::strstr(argv[index], pattern); if(temp && (value || strlen(temp) == strlen(pattern))) { //!OCLINT prefer early exits and continue // eliminate matches in which the chars before the option are not '-' bool noBadCharsFound = true; auto curr = argv[index]; while(curr != temp) { if(*curr++ != '-') { noBadCharsFound = false; break; } } if(noBadCharsFound && argv[index][0] == '-') { if(value) { // parsing the value of an option temp += strlen(pattern); const unsigned len = strlen(temp); if(len) { *value = temp; return true; } } else { // just a flag - no value return true; } } } } return false; } // parses an option and returns the string after the '=' character bool parseOption(int argc, const char* const* argv, const char* pattern, String* value = nullptr, const String& defaultVal = String()) { if(value) *value = defaultVal; #ifndef DOCTEST_CONFIG_NO_UNPREFIXED_OPTIONS // offset (normally 3 for "dt-") to skip prefix if(parseOptionImpl(argc, argv, pattern + strlen(DOCTEST_CONFIG_OPTIONS_PREFIX), value)) return true; #endif // DOCTEST_CONFIG_NO_UNPREFIXED_OPTIONS return parseOptionImpl(argc, argv, pattern, value); } // locates a flag on the command line bool parseFlag(int argc, const char* const* argv, const char* pattern) { return parseOption(argc, argv, pattern); } // parses a comma separated list of words after a pattern in one of the arguments in argv bool parseCommaSepArgs(int argc, const char* const* argv, const char* pattern, std::vector& res) { String filtersString; if(parseOption(argc, argv, pattern, &filtersString)) { // tokenize with "," as a separator, unless escaped with backslash std::ostringstream s; auto flush = [&s, &res]() { auto string = s.str(); if(string.size() > 0) { res.push_back(string.c_str()); } s.str(""); }; bool seenBackslash = false; const char* current = filtersString.c_str(); const char* end = current + strlen(current); while(current != end) { char character = *current++; if(seenBackslash) { seenBackslash = false; if(character == ',' || character == '\\') { s.put(character); continue; } s.put('\\'); } if(character == '\\') { seenBackslash = true; } else if(character == ',') { flush(); } else { s.put(character); } } if(seenBackslash) { s.put('\\'); } flush(); return true; } return false; } enum optionType { option_bool, option_int }; // parses an int/bool option from the command line bool parseIntOption(int argc, const char* const* argv, const char* pattern, optionType type, int& res) { String parsedValue; if(!parseOption(argc, argv, pattern, &parsedValue)) return false; if(type) { // integer // TODO: change this to use std::stoi or something else! currently it uses undefined behavior - assumes '0' on failed parse... int theInt = std::atoi(parsedValue.c_str()); if (theInt != 0) { res = theInt; //!OCLINT parameter reassignment return true; } } else { // boolean const char positive[][5] = { "1", "true", "on", "yes" }; // 5 - strlen("true") + 1 const char negative[][6] = { "0", "false", "off", "no" }; // 6 - strlen("false") + 1 // if the value matches any of the positive/negative possibilities for (unsigned i = 0; i < 4; i++) { if (parsedValue.compare(positive[i], true) == 0) { res = 1; //!OCLINT parameter reassignment return true; } if (parsedValue.compare(negative[i], true) == 0) { res = 0; //!OCLINT parameter reassignment return true; } } } return false; } } // namespace Context::Context(int argc, const char* const* argv) : p(new detail::ContextState) { parseArgs(argc, argv, true); if(argc) p->binary_name = argv[0]; } Context::~Context() { if(g_cs == p) g_cs = nullptr; delete p; } void Context::applyCommandLine(int argc, const char* const* argv) { parseArgs(argc, argv); if(argc) p->binary_name = argv[0]; } // parses args void Context::parseArgs(int argc, const char* const* argv, bool withDefaults) { using namespace detail; // clang-format off parseCommaSepArgs(argc, argv, DOCTEST_CONFIG_OPTIONS_PREFIX "source-file=", p->filters[0]); parseCommaSepArgs(argc, argv, DOCTEST_CONFIG_OPTIONS_PREFIX "sf=", p->filters[0]); parseCommaSepArgs(argc, argv, DOCTEST_CONFIG_OPTIONS_PREFIX "source-file-exclude=",p->filters[1]); parseCommaSepArgs(argc, argv, DOCTEST_CONFIG_OPTIONS_PREFIX "sfe=", p->filters[1]); parseCommaSepArgs(argc, argv, DOCTEST_CONFIG_OPTIONS_PREFIX "test-suite=", p->filters[2]); parseCommaSepArgs(argc, argv, DOCTEST_CONFIG_OPTIONS_PREFIX "ts=", p->filters[2]); parseCommaSepArgs(argc, argv, DOCTEST_CONFIG_OPTIONS_PREFIX "test-suite-exclude=", p->filters[3]); parseCommaSepArgs(argc, argv, DOCTEST_CONFIG_OPTIONS_PREFIX "tse=", p->filters[3]); parseCommaSepArgs(argc, argv, DOCTEST_CONFIG_OPTIONS_PREFIX "test-case=", p->filters[4]); parseCommaSepArgs(argc, argv, DOCTEST_CONFIG_OPTIONS_PREFIX "tc=", p->filters[4]); parseCommaSepArgs(argc, argv, DOCTEST_CONFIG_OPTIONS_PREFIX "test-case-exclude=", p->filters[5]); parseCommaSepArgs(argc, argv, DOCTEST_CONFIG_OPTIONS_PREFIX "tce=", p->filters[5]); parseCommaSepArgs(argc, argv, DOCTEST_CONFIG_OPTIONS_PREFIX "subcase=", p->filters[6]); parseCommaSepArgs(argc, argv, DOCTEST_CONFIG_OPTIONS_PREFIX "sc=", p->filters[6]); parseCommaSepArgs(argc, argv, DOCTEST_CONFIG_OPTIONS_PREFIX "subcase-exclude=", p->filters[7]); parseCommaSepArgs(argc, argv, DOCTEST_CONFIG_OPTIONS_PREFIX "sce=", p->filters[7]); parseCommaSepArgs(argc, argv, DOCTEST_CONFIG_OPTIONS_PREFIX "reporters=", p->filters[8]); parseCommaSepArgs(argc, argv, DOCTEST_CONFIG_OPTIONS_PREFIX "r=", p->filters[8]); // clang-format on int intRes = 0; String strRes; #define DOCTEST_PARSE_AS_BOOL_OR_FLAG(name, sname, var, default) \ if(parseIntOption(argc, argv, DOCTEST_CONFIG_OPTIONS_PREFIX name "=", option_bool, intRes) || \ parseIntOption(argc, argv, DOCTEST_CONFIG_OPTIONS_PREFIX sname "=", option_bool, intRes)) \ p->var = static_cast(intRes); \ else if(parseFlag(argc, argv, DOCTEST_CONFIG_OPTIONS_PREFIX name) || \ parseFlag(argc, argv, DOCTEST_CONFIG_OPTIONS_PREFIX sname)) \ p->var = true; \ else if(withDefaults) \ p->var = default #define DOCTEST_PARSE_INT_OPTION(name, sname, var, default) \ if(parseIntOption(argc, argv, DOCTEST_CONFIG_OPTIONS_PREFIX name "=", option_int, intRes) || \ parseIntOption(argc, argv, DOCTEST_CONFIG_OPTIONS_PREFIX sname "=", option_int, intRes)) \ p->var = intRes; \ else if(withDefaults) \ p->var = default #define DOCTEST_PARSE_STR_OPTION(name, sname, var, default) \ if(parseOption(argc, argv, DOCTEST_CONFIG_OPTIONS_PREFIX name "=", &strRes, default) || \ parseOption(argc, argv, DOCTEST_CONFIG_OPTIONS_PREFIX sname "=", &strRes, default) || \ withDefaults) \ p->var = strRes // clang-format off DOCTEST_PARSE_STR_OPTION("out", "o", out, ""); DOCTEST_PARSE_STR_OPTION("order-by", "ob", order_by, "file"); DOCTEST_PARSE_INT_OPTION("rand-seed", "rs", rand_seed, 0); DOCTEST_PARSE_INT_OPTION("first", "f", first, 0); DOCTEST_PARSE_INT_OPTION("last", "l", last, UINT_MAX); DOCTEST_PARSE_INT_OPTION("abort-after", "aa", abort_after, 0); DOCTEST_PARSE_INT_OPTION("subcase-filter-levels", "scfl", subcase_filter_levels, INT_MAX); DOCTEST_PARSE_AS_BOOL_OR_FLAG("success", "s", success, false); DOCTEST_PARSE_AS_BOOL_OR_FLAG("case-sensitive", "cs", case_sensitive, false); DOCTEST_PARSE_AS_BOOL_OR_FLAG("exit", "e", exit, false); DOCTEST_PARSE_AS_BOOL_OR_FLAG("duration", "d", duration, false); DOCTEST_PARSE_AS_BOOL_OR_FLAG("minimal", "m", minimal, false); DOCTEST_PARSE_AS_BOOL_OR_FLAG("quiet", "q", quiet, false); DOCTEST_PARSE_AS_BOOL_OR_FLAG("no-throw", "nt", no_throw, false); DOCTEST_PARSE_AS_BOOL_OR_FLAG("no-exitcode", "ne", no_exitcode, false); DOCTEST_PARSE_AS_BOOL_OR_FLAG("no-run", "nr", no_run, false); DOCTEST_PARSE_AS_BOOL_OR_FLAG("no-intro", "ni", no_intro, false); DOCTEST_PARSE_AS_BOOL_OR_FLAG("no-version", "nv", no_version, false); DOCTEST_PARSE_AS_BOOL_OR_FLAG("no-colors", "nc", no_colors, false); DOCTEST_PARSE_AS_BOOL_OR_FLAG("force-colors", "fc", force_colors, false); DOCTEST_PARSE_AS_BOOL_OR_FLAG("no-breaks", "nb", no_breaks, false); DOCTEST_PARSE_AS_BOOL_OR_FLAG("no-skip", "ns", no_skip, false); DOCTEST_PARSE_AS_BOOL_OR_FLAG("gnu-file-line", "gfl", gnu_file_line, !bool(DOCTEST_MSVC)); DOCTEST_PARSE_AS_BOOL_OR_FLAG("no-path-filenames", "npf", no_path_in_filenames, false); DOCTEST_PARSE_AS_BOOL_OR_FLAG("no-line-numbers", "nln", no_line_numbers, false); DOCTEST_PARSE_AS_BOOL_OR_FLAG("no-debug-output", "ndo", no_debug_output, false); DOCTEST_PARSE_AS_BOOL_OR_FLAG("no-skipped-summary", "nss", no_skipped_summary, false); DOCTEST_PARSE_AS_BOOL_OR_FLAG("no-time-in-output", "ntio", no_time_in_output, false); // clang-format on if(withDefaults) { p->help = false; p->version = false; p->count = false; p->list_test_cases = false; p->list_test_suites = false; p->list_reporters = false; } if(parseFlag(argc, argv, DOCTEST_CONFIG_OPTIONS_PREFIX "help") || parseFlag(argc, argv, DOCTEST_CONFIG_OPTIONS_PREFIX "h") || parseFlag(argc, argv, DOCTEST_CONFIG_OPTIONS_PREFIX "?")) { p->help = true; p->exit = true; } if(parseFlag(argc, argv, DOCTEST_CONFIG_OPTIONS_PREFIX "version") || parseFlag(argc, argv, DOCTEST_CONFIG_OPTIONS_PREFIX "v")) { p->version = true; p->exit = true; } if(parseFlag(argc, argv, DOCTEST_CONFIG_OPTIONS_PREFIX "count") || parseFlag(argc, argv, DOCTEST_CONFIG_OPTIONS_PREFIX "c")) { p->count = true; p->exit = true; } if(parseFlag(argc, argv, DOCTEST_CONFIG_OPTIONS_PREFIX "list-test-cases") || parseFlag(argc, argv, DOCTEST_CONFIG_OPTIONS_PREFIX "ltc")) { p->list_test_cases = true; p->exit = true; } if(parseFlag(argc, argv, DOCTEST_CONFIG_OPTIONS_PREFIX "list-test-suites") || parseFlag(argc, argv, DOCTEST_CONFIG_OPTIONS_PREFIX "lts")) { p->list_test_suites = true; p->exit = true; } if(parseFlag(argc, argv, DOCTEST_CONFIG_OPTIONS_PREFIX "list-reporters") || parseFlag(argc, argv, DOCTEST_CONFIG_OPTIONS_PREFIX "lr")) { p->list_reporters = true; p->exit = true; } } // allows the user to add procedurally to the filters from the command line void Context::addFilter(const char* filter, const char* value) { setOption(filter, value); } // allows the user to clear all filters from the command line void Context::clearFilters() { for(auto& curr : p->filters) curr.clear(); } // allows the user to override procedurally the bool options from the command line void Context::setOption(const char* option, bool value) { setOption(option, value ? "true" : "false"); } // allows the user to override procedurally the int options from the command line void Context::setOption(const char* option, int value) { setOption(option, toString(value).c_str()); } // allows the user to override procedurally the string options from the command line void Context::setOption(const char* option, const char* value) { auto argv = String("-") + option + "=" + value; auto lvalue = argv.c_str(); parseArgs(1, &lvalue); } // users should query this in their main() and exit the program if true bool Context::shouldExit() { return p->exit; } void Context::setAsDefaultForAssertsOutOfTestCases() { g_cs = p; } void Context::setAssertHandler(detail::assert_handler ah) { p->ah = ah; } void Context::setCout(std::ostream* out) { p->cout = out; } static class DiscardOStream : public std::ostream { private: class : public std::streambuf { private: // allowing some buffering decreases the amount of calls to overflow char buf[1024]; protected: std::streamsize xsputn(const char_type*, std::streamsize count) override { return count; } int_type overflow(int_type ch) override { setp(std::begin(buf), std::end(buf)); return traits_type::not_eof(ch); } } discardBuf; public: DiscardOStream() : std::ostream(&discardBuf) {} } discardOut; // the main function that does all the filtering and test running int Context::run() { using namespace detail; // save the old context state in case such was setup - for using asserts out of a testing context auto old_cs = g_cs; // this is the current contest g_cs = p; is_running_in_test = true; g_no_colors = p->no_colors; p->resetRunData(); std::fstream fstr; if(p->cout == nullptr) { if(p->quiet) { p->cout = &discardOut; } else if(p->out.size()) { // to a file if specified fstr.open(p->out.c_str(), std::fstream::out); p->cout = &fstr; } else { #ifndef DOCTEST_CONFIG_NO_INCLUDE_IOSTREAM // stdout by default p->cout = &std::cout; #else // DOCTEST_CONFIG_NO_INCLUDE_IOSTREAM return EXIT_FAILURE; #endif // DOCTEST_CONFIG_NO_INCLUDE_IOSTREAM } } FatalConditionHandler::allocateAltStackMem(); auto cleanup_and_return = [&]() { FatalConditionHandler::freeAltStackMem(); if(fstr.is_open()) fstr.close(); // restore context g_cs = old_cs; is_running_in_test = false; // we have to free the reporters which were allocated when the run started for(auto& curr : p->reporters_currently_used) delete curr; p->reporters_currently_used.clear(); if(p->numTestCasesFailed && !p->no_exitcode) return EXIT_FAILURE; return EXIT_SUCCESS; }; // setup default reporter if none is given through the command line if(p->filters[8].empty()) p->filters[8].push_back("console"); // check to see if any of the registered reporters has been selected for(auto& curr : getReporters()) { if(matchesAny(curr.first.second.c_str(), p->filters[8], false, p->case_sensitive)) p->reporters_currently_used.push_back(curr.second(*g_cs)); } // TODO: check if there is nothing in reporters_currently_used // prepend all listeners for(auto& curr : getListeners()) p->reporters_currently_used.insert(p->reporters_currently_used.begin(), curr.second(*g_cs)); #ifdef DOCTEST_PLATFORM_WINDOWS if(isDebuggerActive() && p->no_debug_output == false) p->reporters_currently_used.push_back(new DebugOutputWindowReporter(*g_cs)); #endif // DOCTEST_PLATFORM_WINDOWS // handle version, help and no_run if(p->no_run || p->version || p->help || p->list_reporters) { DOCTEST_ITERATE_THROUGH_REPORTERS(report_query, QueryData()); return cleanup_and_return(); } std::vector testArray; for(auto& curr : getRegisteredTests()) testArray.push_back(&curr); p->numTestCases = testArray.size(); // sort the collected records if(!testArray.empty()) { if(p->order_by.compare("file", true) == 0) { std::sort(testArray.begin(), testArray.end(), fileOrderComparator); } else if(p->order_by.compare("suite", true) == 0) { std::sort(testArray.begin(), testArray.end(), suiteOrderComparator); } else if(p->order_by.compare("name", true) == 0) { std::sort(testArray.begin(), testArray.end(), nameOrderComparator); } else if(p->order_by.compare("rand", true) == 0) { std::srand(p->rand_seed); // random_shuffle implementation const auto first = &testArray[0]; for(size_t i = testArray.size() - 1; i > 0; --i) { int idxToSwap = std::rand() % (i + 1); const auto temp = first[i]; first[i] = first[idxToSwap]; first[idxToSwap] = temp; } } else if(p->order_by.compare("none", true) == 0) { // means no sorting - beneficial for death tests which call into the executable // with a specific test case in mind - we don't want to slow down the startup times } } std::set testSuitesPassingFilt; bool query_mode = p->count || p->list_test_cases || p->list_test_suites; std::vector queryResults; if(!query_mode) DOCTEST_ITERATE_THROUGH_REPORTERS(test_run_start, DOCTEST_EMPTY); // invoke the registered functions if they match the filter criteria (or just count them) for(auto& curr : testArray) { const auto& tc = *curr; bool skip_me = false; if(tc.m_skip && !p->no_skip) skip_me = true; if(!matchesAny(tc.m_file.c_str(), p->filters[0], true, p->case_sensitive)) skip_me = true; if(matchesAny(tc.m_file.c_str(), p->filters[1], false, p->case_sensitive)) skip_me = true; if(!matchesAny(tc.m_test_suite, p->filters[2], true, p->case_sensitive)) skip_me = true; if(matchesAny(tc.m_test_suite, p->filters[3], false, p->case_sensitive)) skip_me = true; if(!matchesAny(tc.m_name, p->filters[4], true, p->case_sensitive)) skip_me = true; if(matchesAny(tc.m_name, p->filters[5], false, p->case_sensitive)) skip_me = true; if(!skip_me) p->numTestCasesPassingFilters++; // skip the test if it is not in the execution range if((p->last < p->numTestCasesPassingFilters && p->first <= p->last) || (p->first > p->numTestCasesPassingFilters)) skip_me = true; if(skip_me) { if(!query_mode) DOCTEST_ITERATE_THROUGH_REPORTERS(test_case_skipped, tc); continue; } // do not execute the test if we are to only count the number of filter passing tests if(p->count) continue; // print the name of the test and don't execute it if(p->list_test_cases) { queryResults.push_back(&tc); continue; } // print the name of the test suite if not done already and don't execute it if(p->list_test_suites) { if((testSuitesPassingFilt.count(tc.m_test_suite) == 0) && tc.m_test_suite[0] != '\0') { queryResults.push_back(&tc); testSuitesPassingFilt.insert(tc.m_test_suite); p->numTestSuitesPassingFilters++; } continue; } // execute the test if it passes all the filtering { p->currentTest = &tc; p->failure_flags = TestCaseFailureReason::None; p->seconds = 0; // reset atomic counters p->numAssertsFailedCurrentTest_atomic = 0; p->numAssertsCurrentTest_atomic = 0; p->fullyTraversedSubcases.clear(); DOCTEST_ITERATE_THROUGH_REPORTERS(test_case_start, tc); p->timer.start(); bool run_test = true; do { // reset some of the fields for subcases (except for the set of fully passed ones) p->reachedLeaf = false; // May not be empty if previous subcase exited via exception. p->subcaseStack.clear(); p->currentSubcaseDepth = 0; p->shouldLogCurrentException = true; // reset stuff for logging with INFO() p->stringifiedContexts.clear(); #ifndef DOCTEST_CONFIG_NO_EXCEPTIONS try { #endif // DOCTEST_CONFIG_NO_EXCEPTIONS // MSVC 2015 diagnoses fatalConditionHandler as unused (because reset() is a static method) DOCTEST_MSVC_SUPPRESS_WARNING_WITH_PUSH(4101) // unreferenced local variable FatalConditionHandler fatalConditionHandler; // Handle signals // execute the test tc.m_test(); fatalConditionHandler.reset(); DOCTEST_MSVC_SUPPRESS_WARNING_POP #ifndef DOCTEST_CONFIG_NO_EXCEPTIONS } catch(const TestFailureException&) { p->failure_flags |= TestCaseFailureReason::AssertFailure; } catch(...) { DOCTEST_ITERATE_THROUGH_REPORTERS(test_case_exception, {translateActiveException(), false}); p->failure_flags |= TestCaseFailureReason::Exception; } #endif // DOCTEST_CONFIG_NO_EXCEPTIONS // exit this loop if enough assertions have failed - even if there are more subcases if(p->abort_after > 0 && p->numAssertsFailed + p->numAssertsFailedCurrentTest_atomic >= p->abort_after) { run_test = false; p->failure_flags |= TestCaseFailureReason::TooManyFailedAsserts; } if(!p->nextSubcaseStack.empty() && run_test) DOCTEST_ITERATE_THROUGH_REPORTERS(test_case_reenter, tc); if(p->nextSubcaseStack.empty()) run_test = false; } while(run_test); p->finalizeTestCaseData(); DOCTEST_ITERATE_THROUGH_REPORTERS(test_case_end, *g_cs); p->currentTest = nullptr; // stop executing tests if enough assertions have failed if(p->abort_after > 0 && p->numAssertsFailed >= p->abort_after) break; } } if(!query_mode) { DOCTEST_ITERATE_THROUGH_REPORTERS(test_run_end, *g_cs); } else { QueryData qdata; qdata.run_stats = g_cs; qdata.data = queryResults.data(); qdata.num_data = unsigned(queryResults.size()); DOCTEST_ITERATE_THROUGH_REPORTERS(report_query, qdata); } return cleanup_and_return(); } DOCTEST_DEFINE_INTERFACE(IReporter) int IReporter::get_num_active_contexts() { return detail::g_infoContexts.size(); } const IContextScope* const* IReporter::get_active_contexts() { return get_num_active_contexts() ? &detail::g_infoContexts[0] : nullptr; } int IReporter::get_num_stringified_contexts() { return detail::g_cs->stringifiedContexts.size(); } const String* IReporter::get_stringified_contexts() { return get_num_stringified_contexts() ? &detail::g_cs->stringifiedContexts[0] : nullptr; } namespace detail { void registerReporterImpl(const char* name, int priority, reporterCreatorFunc c, bool isReporter) { if(isReporter) getReporters().insert(reporterMap::value_type(reporterMap::key_type(priority, name), c)); else getListeners().insert(reporterMap::value_type(reporterMap::key_type(priority, name), c)); } } // namespace detail } // namespace doctest #endif // DOCTEST_CONFIG_DISABLE #ifdef DOCTEST_CONFIG_IMPLEMENT_WITH_MAIN DOCTEST_MSVC_SUPPRESS_WARNING_WITH_PUSH(4007) // 'function' : must be 'attribute' - see issue #182 int main(int argc, char** argv) { return doctest::Context(argc, argv).run(); } DOCTEST_MSVC_SUPPRESS_WARNING_POP #endif // DOCTEST_CONFIG_IMPLEMENT_WITH_MAIN DOCTEST_CLANG_SUPPRESS_WARNING_POP DOCTEST_MSVC_SUPPRESS_WARNING_POP DOCTEST_GCC_SUPPRESS_WARNING_POP DOCTEST_SUPPRESS_COMMON_WARNINGS_POP #endif // DOCTEST_LIBRARY_IMPLEMENTATION #endif // DOCTEST_CONFIG_IMPLEMENT #ifdef DOCTEST_UNDEF_WIN32_LEAN_AND_MEAN #undef WIN32_LEAN_AND_MEAN #undef DOCTEST_UNDEF_WIN32_LEAN_AND_MEAN #endif // DOCTEST_UNDEF_WIN32_LEAN_AND_MEAN #ifdef DOCTEST_UNDEF_NOMINMAX #undef NOMINMAX #undef DOCTEST_UNDEF_NOMINMAX #endif // DOCTEST_UNDEF_NOMINMAX