@@ -118,6 +118,7 @@ INCLUDE_DIRECTORIES("${CMAKE_SOURCE_DIR}/" | |||
"${CMAKE_SOURCE_DIR}/contrib/lua-lpeg" | |||
"${CMAKE_SOURCE_DIR}/contrib/frozen/include" | |||
"${CMAKE_SOURCE_DIR}/contrib/fmt/include" | |||
"${CMAKE_SOURCE_DIR}/contrib/doctest" | |||
"${CMAKE_BINARY_DIR}/src" #Stored in the binary dir | |||
"${CMAKE_BINARY_DIR}/src/libcryptobox") | |||
@@ -250,6 +251,7 @@ SET(CMAKE_C_FLAGS "${CMAKE_C_OPT_FLAGS} ${CMAKE_C_FLAGS}") | |||
SET(CMAKE_CXX_FLAGS "${CMAKE_C_OPT_FLAGS} ${CMAKE_CXX_FLAGS}") | |||
ADD_DEFINITIONS(-DHAVE_CONFIG_H) | |||
ADD_DEFINITIONS(-DDOCTEST_CONFIG_SUPER_FAST_ASSERTS) | |||
# Check platform specific includes | |||
CHECK_INCLUDE_FILES(sys/types.h HAVE_SYS_TYPES_H) | |||
@@ -652,6 +654,7 @@ ADD_SUBDIRECTORY(contrib/kann) | |||
ADD_SUBDIRECTORY(contrib/fastutf8) | |||
ADD_SUBDIRECTORY(contrib/google-ced) | |||
ADD_SUBDIRECTORY(contrib/fmt) | |||
ADD_SUBDIRECTORY(contrib/doctest) | |||
IF (NOT WITH_LUAJIT) | |||
ADD_SUBDIRECTORY(contrib/lua-bit) |
@@ -0,0 +1,63 @@ | |||
cmake_minimum_required(VERSION 3.0) | |||
if(POLICY CMP0077) | |||
cmake_policy(SET CMP0077 NEW) | |||
endif() | |||
################################################################################ | |||
## DOCTEST | |||
################################################################################ | |||
project(doctest VERSION 2.4.5 LANGUAGES CXX) | |||
# Determine if doctest is built as a subproject (using add_subdirectory) or if it is the main project. | |||
set(MAIN_PROJECT OFF) | |||
if(CMAKE_CURRENT_SOURCE_DIR STREQUAL CMAKE_SOURCE_DIR) | |||
set(MAIN_PROJECT ON) | |||
endif() | |||
option(DOCTEST_WITH_TESTS "Build tests/examples" ${MAIN_PROJECT}) | |||
option(DOCTEST_WITH_MAIN_IN_STATIC_LIB "Build a static lib (cmake target) with a default main entry point" ON) | |||
option(DOCTEST_NO_INSTALL "Skip the installation process" OFF) | |||
option(DOCTEST_USE_STD_HEADERS "Use std headers" OFF) | |||
add_library(${PROJECT_NAME} INTERFACE) | |||
add_library(${PROJECT_NAME}::${PROJECT_NAME} ALIAS ${PROJECT_NAME}) | |||
if(NOT CMAKE_VERSION VERSION_LESS 3.8) | |||
target_compile_features(${PROJECT_NAME} INTERFACE cxx_std_11) | |||
endif() | |||
set(doctest_parts_folder "${CMAKE_CURRENT_SOURCE_DIR}/doctest/parts") | |||
set(doctest_folder "${CMAKE_CURRENT_SOURCE_DIR}/") # in order to have the mpi extension files, not included into the doctest.h single header | |||
if(MAIN_PROJECT) | |||
# use a special hidden version of the header which directly includes the 2 parts - proper reporting of file/line locations during dev | |||
target_include_directories(${PROJECT_NAME} INTERFACE | |||
$<BUILD_INTERFACE:${CMAKE_CURRENT_SOURCE_DIR}/scripts/development_only/> | |||
$<BUILD_INTERFACE:${doctest_parts_folder}> | |||
$<BUILD_INTERFACE:${doctest_folder}>) | |||
# add a custom target that assembles the single header when any of the parts are touched | |||
add_custom_command( | |||
OUTPUT ${CMAKE_CURRENT_SOURCE_DIR}/doctest/doctest.h | |||
DEPENDS | |||
${doctest_parts_folder}/doctest_fwd.h | |||
${doctest_parts_folder}/doctest.cpp | |||
COMMAND ${CMAKE_COMMAND} -P ${CMAKE_CURRENT_SOURCE_DIR}/scripts/cmake/assemble_single_header.cmake | |||
COMMENT "assembling the single header") | |||
add_custom_target(assemble_single_header ALL DEPENDS ${CMAKE_CURRENT_SOURCE_DIR}/doctest/doctest.h) | |||
else() | |||
target_include_directories(${PROJECT_NAME} INTERFACE $<BUILD_INTERFACE:${CMAKE_CURRENT_SOURCE_DIR}/>) | |||
endif() | |||
# hack to support building on XCode 6 and 7 - propagate the definition to everything | |||
if(DEFINED DOCTEST_THREAD_LOCAL) | |||
target_compile_definitions(${PROJECT_NAME} INTERFACE | |||
DOCTEST_THREAD_LOCAL=${DOCTEST_THREAD_LOCAL}) | |||
endif() | |||
if(DOCTEST_USE_STD_HEADERS) | |||
target_compile_definitions(${PROJECT_NAME} INTERFACE DOCTEST_CONFIG_USE_STD_HEADERS) | |||
endif() |
@@ -0,0 +1,21 @@ | |||
The MIT License (MIT) | |||
Copyright (c) 2016-2021 Viktor Kirilov | |||
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. |
@@ -0,0 +1,153 @@ | |||
<p align="center"><img src="scripts/data/logo/logo_1.svg"></p> | |||
<b> | |||
<table> | |||
<tr> | |||
<td> | |||
master branch | |||
</td> | |||
<td> | |||
Windows <a href="https://ci.appveyor.com/project/onqtam/doctest/branch/master"><img src="https://ci.appveyor.com/api/projects/status/j89qxtahyw1dp4gd/branch/master?svg=true"></a> | |||
</td> | |||
<td> | |||
All <a href="https://github.com/onqtam/doctest/actions?query=branch%3Amaster"><img src="https://github.com/onqtam/doctest/workflows/CI/badge.svg?branch=master"></a> | |||
</td> | |||
<td> | |||
<a href="https://coveralls.io/github/onqtam/doctest?branch=master"><img src="https://coveralls.io/repos/github/onqtam/doctest/badge.svg?branch=master"></a> | |||
</td> | |||
<!-- | |||
<td> | |||
<a href="https://scan.coverity.com/projects/onqtam-doctest"><img src="https://scan.coverity.com/projects/7865/badge.svg"></a> | |||
</td> | |||
--> | |||
</tr> | |||
<tr> | |||
<td> | |||
dev branch | |||
</td> | |||
<td> | |||
Windows <a href="https://ci.appveyor.com/project/onqtam/doctest/branch/dev"><img src="https://ci.appveyor.com/api/projects/status/j89qxtahyw1dp4gd/branch/dev?svg=true"></a> | |||
</td> | |||
<td> | |||
All <a href="https://github.com/onqtam/doctest/actions?query=branch%3Adev"><img src="https://github.com/onqtam/doctest/workflows/CI/badge.svg?branch=dev"></a> | |||
</td> | |||
<td> | |||
<a href="https://coveralls.io/github/onqtam/doctest?branch=dev"><img src="https://coveralls.io/repos/github/onqtam/doctest/badge.svg?branch=dev"></a> | |||
</td> | |||
<!-- | |||
<td> | |||
</td> | |||
--> | |||
</tr> | |||
</table> | |||
</b> | |||
**doctest** is a new C++ testing framework but is by far the fastest both in compile times (by [**orders of magnitude**](doc/markdown/benchmarks.md)) and runtime compared to other feature-rich alternatives. It brings the ability of compiled languages such as [**D**](https://dlang.org/spec/unittest.html) / [**Rust**](https://doc.rust-lang.org/book/second-edition/ch11-00-testing.html) / [**Nim**](https://nim-lang.org/docs/unittest.html) to have tests written directly in the production code thanks to a fast, transparent and flexible test runner with a clean interface. | |||
[![Standard](https://img.shields.io/badge/c%2B%2B-11/14/17/20-blue.svg)](https://en.wikipedia.org/wiki/C%2B%2B#Standardization) | |||
[![License](https://img.shields.io/badge/license-MIT-blue.svg)](https://opensource.org/licenses/MIT) | |||
[![Version](https://badge.fury.io/gh/onqtam%2Fdoctest.svg)](https://github.com/onqtam/doctest/releases) | |||
[![download](https://img.shields.io/badge/download%20%20-link-blue.svg)](https://raw.githubusercontent.com/onqtam/doctest/master/doctest/doctest.h) | |||
[![CII Best Practices](https://bestpractices.coreinfrastructure.org/projects/503/badge)](https://bestpractices.coreinfrastructure.org/projects/503) | |||
[![Language grade: C/C++](https://img.shields.io/lgtm/grade/cpp/g/onqtam/doctest.svg)](https://lgtm.com/projects/g/onqtam/doctest/context:cpp) | |||
[![Join the chat at https://gitter.im/onqtam/doctest](https://badges.gitter.im/onqtam/doctest.svg)](https://gitter.im/onqtam/doctest?utm_source=badge&utm_medium=badge&utm_campaign=pr-badge&utm_content=badge) | |||
[![Try it online](https://img.shields.io/badge/try%20it-online-orange.svg)](https://wandbox.org/permlink/nJIibfbivG7BG7r1) | |||
<!-- | |||
[![Language](https://img.shields.io/badge/language-C++-blue.svg)](https://isocpp.org/) | |||
[![documentation](https://img.shields.io/badge/documentation%20%20-online-blue.svg)](https://github.com/onqtam/doctest/blob/master/doc/markdown/readme.md#reference) | |||
--> | |||
[<img src="https://cloud.githubusercontent.com/assets/8225057/5990484/70413560-a9ab-11e4-8942-1a63607c0b00.png" align="right">](http://www.patreon.com/onqtam) | |||
The framework is and will stay free but needs your support to sustain its development. There are lots of <a href="doc/markdown/roadmap.md"><b>new features</b></a> and maintenance to do. If you work for a company using **doctest** or have the means to do so, please consider financial support. Monthly donations via Patreon and one-offs via PayPal. | |||
[<img src="https://www.paypalobjects.com/en_US/i/btn/btn_donate_LG.gif" align="right">](https://www.paypal.me/onqtam/10) | |||
A complete example with a self-registering test that compiles to an executable looks like this: | |||
![cover-example](scripts/data/using_doctest_888px_wide.gif) | |||
There are many C++ testing frameworks - [Catch](https://github.com/catchorg/Catch2), [Boost.Test](http://www.boost.org/doc/libs/1_64_0/libs/test/doc/html/index.html), [UnitTest++](https://github.com/unittest-cpp/unittest-cpp), [cpputest](https://github.com/cpputest/cpputest), [googletest](https://github.com/google/googletest) and many [other](https://en.wikipedia.org/wiki/List_of_unit_testing_frameworks#C.2B.2B). | |||
The **key** differences between it and other testing frameworks are that it is light and unintrusive: | |||
- Ultra light on compile times both in terms of [**including the header**](doc/markdown/benchmarks.md#cost-of-including-the-header) and writing [**thousands of asserts**](doc/markdown/benchmarks.md#cost-of-an-assertion-macro) | |||
- Doesn't produce any warnings even on the [**most aggressive**](scripts/cmake/common.cmake#L84) warning levels for **MSVC**/**GCC**/**Clang** | |||
- Offers a way to remove **everything** testing-related from the binary with the [**```DOCTEST_CONFIG_DISABLE```**](doc/markdown/configuration.md#doctest_config_disable) identifier | |||
- [**thread-safe**](doc/markdown/faq.md#is-doctest-thread-aware) - asserts (and logging) can be used from multiple threads spawned from a single test case - [**example**](examples/all_features/concurrency.cpp) | |||
- asserts can be used [**outside of a testing context**](doc/markdown/assertions.md#using-asserts-out-of-a-testing-context) - as a general purpose assert library - [**example**](examples/all_features/asserts_used_outside_of_tests.cpp) | |||
- Doesn't pollute the global namespace (everything is in namespace ```doctest```) and doesn't drag **any** headers with it | |||
- Very [**portable**](doc/markdown/features.md#extremely-portable) C++11 (use tag [**1.2.9**](https://github.com/onqtam/doctest/tree/1.2.9) for C++98) with over 180 different CI builds (static analysis, sanitizers...) | |||
- binaries (exe/dll) can use the test runner of another binary - so tests end up in a single registry - [**example**](examples/executable_dll_and_plugin/) | |||
![cost-of-including-the-framework-header](scripts/data/benchmarks/header.png) | |||
This allows the framework to be used in more ways than any other - tests can be written directly in the production code! | |||
*Tests can be considered a form of documentation and should be able to reside near the production code which they test.* | |||
- This makes the barrier for writing tests **much lower** - you don't have to: **1)** make a separate source file **2)** include a bunch of stuff in it **3)** add it to the build system and **4)** add it to source control - You can just write the tests for a class or a piece of functionality at the bottom of its source file - or even header file! | |||
- Tests in the production code can be thought of as documentation or up-to-date comments - showing the use of APIs | |||
- Testing internals that are not exposed through the public API and headers is no longer a mind-bending exercise | |||
- [**Test-driven development**](https://en.wikipedia.org/wiki/Test-driven_development) in C++ has never been easier! | |||
The framework can be used like any other if you don't want/need to mix production code and tests - check out the [**features**](doc/markdown/features.md). | |||
**doctest** is modeled after [**Catch**](https://github.com/catchorg/Catch2) and some parts of the code have been taken directly - check out [**the differences**](doc/markdown/faq.md#how-is-doctest-different-from-catch). | |||
[This table](https://github.com/martinmoene/catch-lest-other-comparison) compares **doctest** / [**Catch**](https://github.com/catchorg/Catch2) / [**lest**](https://github.com/martinmoene/lest) which are all very similar. | |||
Checkout the [**CppCon 2017 talk**](https://cppcon2017.sched.com/event/BgsI/mix-tests-and-production-code-with-doctest-implementing-and-using-the-fastest-modern-c-testing-framework) on [**YouTube**](https://www.youtube.com/watch?v=eH1CxEC29l8) to get a better understanding of how the framework works and read about how to use it in [**the JetBrains article**](https://blog.jetbrains.com/rscpp/better-ways-testing-with-doctest/) - highlighting the unique aspects of the framework! On a short description on how to use the framework along production code you could refer to [**this GitHub issue**](https://github.com/onqtam/doctest/issues/252). There is also an [**older article**](https://accu.org/var/uploads/journals/Overload137.pdf) in the february edition of ACCU Overload 2017. | |||
[![CppCon 2017 talk about doctest on youtube](scripts/data/youtube-cppcon-talk-thumbnail.png)](https://www.youtube.com/watch?v=eH1CxEC29l8) | |||
Documentation | |||
------------- | |||
Project: | |||
- [Features and design goals](doc/markdown/features.md) - the complete list of features | |||
- [Roadmap](doc/markdown/roadmap.md) - upcoming features | |||
- [Benchmarks](doc/markdown/benchmarks.md) - compile-time and runtime supremacy | |||
- [Contributing](CONTRIBUTING.md) - how to make a proper pull request | |||
- [Changelog](CHANGELOG.md) - generated changelog based on closed issues/PRs | |||
Usage: | |||
- [Tutorial](doc/markdown/tutorial.md) - make sure you have read it before the other parts of the documentation | |||
- [Assertion macros](doc/markdown/assertions.md) | |||
- [Test cases, subcases and test fixtures](doc/markdown/testcases.md) | |||
- [Parameterized test cases](doc/markdown/parameterized-tests.md) | |||
- [Command line](doc/markdown/commandline.md) | |||
- [Logging macros](doc/markdown/logging.md) | |||
- [```main()``` entry point](doc/markdown/main.md) | |||
- [Configuration](doc/markdown/configuration.md) | |||
- [String conversions](doc/markdown/stringification.md) | |||
- [Reporters](doc/markdown/reporters.md) | |||
- [Extensions](doc/markdown/extensions.md) | |||
- [FAQ](doc/markdown/faq.md) | |||
- [Build systems](doc/markdown/build-systems.md) | |||
- [Examples](examples) | |||
Contributing | |||
------------ | |||
[<img src="https://cloud.githubusercontent.com/assets/8225057/5990484/70413560-a9ab-11e4-8942-1a63607c0b00.png" align="right">](http://www.patreon.com/onqtam) | |||
Support the development of the project with donations! There is a list of planned features which are all important and big - see the [**roadmap**](doc/markdown/roadmap.md). I took a break from working in the industry to make open source software so every cent is a big deal. | |||
[<img src="https://www.paypalobjects.com/en_US/i/btn/btn_donate_LG.gif" align="right">](https://www.paypal.me/onqtam/10) | |||
If you work for a company using **doctest** or have the means to do so, please consider financial support. | |||
Contributions in the form of issues and pull requests are welcome as well - check out the [**Contributing**](CONTRIBUTING.md) page. | |||
Stargazers over time | |||
------------ | |||
[![Stargazers over time](https://starcharts.herokuapp.com/onqtam/doctest.svg)](https://starcharts.herokuapp.com/onqtam/doctest) | |||
Logo | |||
------------ | |||
The [logo](scripts/data/logo) is licensed under a Creative Commons Attribution 4.0 International License. Copyright © 2019 [area55git](https://github.com/area55git) [![License: CC BY 4.0](https://licensebuttons.net/l/by/4.0/80x15.png)](https://creativecommons.org/licenses/by/4.0/) | |||
<p align="center"><img src="scripts/data/logo/icon_2.svg"></p> |
@@ -0,0 +1,120 @@ | |||
#pragma once | |||
#ifdef DOCTEST_CONFIG_IMPLEMENT | |||
#include "doctest/doctest.h" | |||
#include "mpi_reporter.h" | |||
#else | |||
#include "mpi.h" | |||
#include <numeric> | |||
#include <vector> | |||
#include "doctest/doctest.h" | |||
#include <cassert> | |||
#include <string> | |||
namespace doctest { | |||
inline | |||
int mpi_world_nb_procs() { | |||
int n; | |||
MPI_Comm_size(MPI_COMM_WORLD, &n); | |||
return n; | |||
} | |||
struct mpi_sub_comm { | |||
int nb_procs; | |||
int rank; | |||
MPI_Comm comm; | |||
mpi_sub_comm( mpi_sub_comm const& ) = delete; | |||
mpi_sub_comm& operator=( mpi_sub_comm const& ) = delete; | |||
mpi_sub_comm(int nb_prcs) noexcept | |||
: nb_procs(nb_prcs) | |||
, rank(-1) | |||
, comm(MPI_COMM_NULL) | |||
{ | |||
int comm_world_rank; | |||
MPI_Comm_rank(MPI_COMM_WORLD, &comm_world_rank); | |||
if (nb_procs>mpi_world_nb_procs()) { | |||
if (comm_world_rank==0) { | |||
MESSAGE( | |||
"Unable to run test: need ", std::to_string(nb_procs), " procs", | |||
" but program launched with only ", std::to_string(doctest::mpi_world_nb_procs()), "." | |||
); | |||
CHECK(nb_procs<=mpi_world_nb_procs()); | |||
} | |||
} else { | |||
int color = MPI_UNDEFINED; | |||
if(comm_world_rank < nb_procs){ | |||
color = 0; | |||
} | |||
MPI_Comm_split(MPI_COMM_WORLD, color, comm_world_rank, &comm); | |||
if(comm != MPI_COMM_NULL){ | |||
MPI_Comm_rank(comm, &rank); | |||
assert(rank==comm_world_rank); | |||
} | |||
} | |||
} | |||
~mpi_sub_comm() { | |||
if(comm != MPI_COMM_NULL){ | |||
MPI_Comm_free(&comm); | |||
} | |||
} | |||
}; | |||
template<int nb_procs, class F> | |||
void execute_mpi_test_case(F func) { | |||
mpi_sub_comm sub(nb_procs); | |||
if (sub.comm != MPI_COMM_NULL) { | |||
func(sub.rank,nb_procs,sub.comm,std::integral_constant<int,nb_procs>{}); | |||
}; | |||
} | |||
} // doctest | |||
#define DOCTEST_MPI_GEN_ASSERTION(rank_to_test, assertion, ...) \ | |||
static_assert(rank_to_test<test_nb_procs_as_int_constant.value,"Trying to assert on a rank greater than the number of procs of the test!"); \ | |||
if(rank_to_test == test_rank) assertion(__VA_ARGS__) | |||
#define DOCTEST_MPI_WARN(rank_to_test, ...) DOCTEST_MPI_GEN_ASSERTION(rank_to_test,DOCTEST_WARN,__VA_ARGS__) | |||
#define DOCTEST_MPI_CHECK(rank_to_test, ...) DOCTEST_MPI_GEN_ASSERTION(rank_to_test,DOCTEST_CHECK,__VA_ARGS__) | |||
#define DOCTEST_MPI_REQUIRE(rank_to_test, ...) DOCTEST_MPI_GEN_ASSERTION(rank_to_test,DOCTEST_REQUIRE,__VA_ARGS__) | |||
#define DOCTEST_MPI_WARN_FALSE(rank_to_test, ...) DOCTEST_MPI_GEN_ASSERTION(rank_to_test,DOCTEST_WARN_FALSE,__VA_ARGS__) | |||
#define DOCTEST_MPI_CHECK_FALSE(rank_to_test, ...) DOCTEST_MPI_GEN_ASSERTION(rank_to_test,DOCTEST_CHECK_FALSE,__VA_ARGS__) | |||
#define DOCTEST_MPI_REQUIRE_FALSE(rank_to_test, ...) DOCTEST_MPI_GEN_ASSERTION(rank_to_test,DOCTEST_REQUIRE_FALSE,__VA_ARGS__) | |||
#define DOCTEST_CREATE_MPI_TEST_CASE(name,nb_procs,func) \ | |||
static void func(DOCTEST_UNUSED int test_rank, DOCTEST_UNUSED int test_nb_procs, DOCTEST_UNUSED MPI_Comm test_comm, DOCTEST_UNUSED std::integral_constant<int,nb_procs>); \ | |||
TEST_CASE(name * doctest::description("MPI_TEST_CASE")) { \ | |||
doctest::execute_mpi_test_case<nb_procs>(func); \ | |||
} \ | |||
static void func(DOCTEST_UNUSED int test_rank, DOCTEST_UNUSED int test_nb_procs, DOCTEST_UNUSED MPI_Comm test_comm, DOCTEST_UNUSED std::integral_constant<int,nb_procs> test_nb_procs_as_int_constant) | |||
// DOC: test_rank, test_nb_procs, and test_comm are available UNDER THESE SPECIFIC NAMES in the body of the unit test | |||
// DOC: test_nb_procs_as_int_constant is equal to test_nb_procs, but as a compile time value | |||
// (used in CHECK-like macros to assert the checked rank exists) | |||
#define DOCTEST_MPI_TEST_CASE(name,nb_procs) \ | |||
DOCTEST_CREATE_MPI_TEST_CASE(name,nb_procs,DOCTEST_ANONYMOUS(DOCTEST_MPI_FUNC)) | |||
// == SHORT VERSIONS OF THE MACROS | |||
#if !defined(DOCTEST_CONFIG_NO_SHORT_MACRO_NAMES) | |||
#define MPI_WARN DOCTEST_MPI_WARN | |||
#define MPI_CHECK DOCTEST_MPI_CHECK | |||
#define MPI_REQUIRE DOCTEST_MPI_REQUIRE | |||
#define MPI_WARN_FALSE DOCTEST_MPI_WARN_FALSE | |||
#define MPI_CHECK_FALSE DOCTEST_MPI_CHECK_FALSE | |||
#define MPI_REQUIRE_FALSE DOCTEST_MPI_REQUIRE_FALSE | |||
#define MPI_TEST_CASE DOCTEST_MPI_TEST_CASE | |||
#endif // DOCTEST_CONFIG_NO_SHORT_MACRO_NAMES | |||
#endif // DOCTEST_CONFIG_IMPLEMENT |
@@ -0,0 +1,34 @@ | |||
// | |||
// doctest_util.h - an accompanying extensions header to the main doctest.h header | |||
// | |||
// Copyright (c) 2016-2021 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 | |||
// | |||
#pragma once | |||
#ifndef DOCTEST_LIBRARY_INCLUDED | |||
#include "../doctest.h" | |||
#endif | |||
#include <memory> | |||
#include <vector> | |||
#include <string> | |||
namespace doctest { | |||
inline void applyCommandLine(doctest::Context& ctx, const std::vector<std::string>& args) { | |||
auto doctest_args = std::make_unique<const char*[]>(args.size()); | |||
for (size_t i = 0; i < args.size(); ++i) { | |||
doctest_args[i] = args[i].c_str(); | |||
} | |||
ctx.applyCommandLine(args.size(), doctest_args.get()); | |||
} | |||
} // namespace doctest |
@@ -0,0 +1,236 @@ | |||
#pragma once | |||
// #include <doctest/doctest.h> | |||
#include <fstream> | |||
#include <string> | |||
#include "mpi.h" | |||
#include <vector> | |||
#include <mutex> | |||
namespace doctest { | |||
namespace { | |||
// https://stackoverflow.com/a/11826666/1583122 | |||
struct NullBuffer : std::streambuf { | |||
int overflow(int c) { return c; } | |||
}; | |||
class NullStream : public std::ostream { | |||
public: | |||
NullStream() | |||
: std::ostream(&nullBuff) | |||
{} | |||
private: | |||
NullBuffer nullBuff = {}; | |||
}; | |||
static NullStream nullStream; | |||
/* \brief Extends the ConsoleReporter of doctest | |||
* Each process writes its results to its own file | |||
* Intended to be used when a test assertion fails and the user wants to know exactly what happens on which process | |||
*/ | |||
struct MpiFileReporter : public ConsoleReporter { | |||
std::ofstream logfile_stream = {}; | |||
MpiFileReporter(const ContextOptions& co) | |||
: ConsoleReporter(co,logfile_stream) | |||
{ | |||
int rank = 0; | |||
MPI_Comm_rank(MPI_COMM_WORLD, &rank); | |||
std::string logfile_name = "doctest_" + std::to_string(rank) + ".log"; | |||
logfile_stream = std::ofstream(logfile_name.c_str(), std::fstream::out); | |||
} | |||
}; | |||
/* \brief Extends the ConsoleReporter of doctest | |||
* Allows to manage the execution of tests in a parallel framework | |||
* All results are collected on rank 0 | |||
*/ | |||
struct MpiConsoleReporter : public ConsoleReporter { | |||
private: | |||
static std::ostream& replace_by_null_if_not_rank_0(std::ostream* os) { | |||
int rank = 0; | |||
MPI_Comm_rank(MPI_COMM_WORLD, &rank); | |||
if (rank==0) { | |||
return *os; | |||
} else { | |||
return nullStream; | |||
} | |||
} | |||
public: | |||
MpiConsoleReporter(const ContextOptions& co) | |||
: ConsoleReporter(co,replace_by_null_if_not_rank_0(co.cout)) | |||
{} | |||
std::string file_line_to_string(const char* file, int line, | |||
const char* tail = ""){ | |||
std::stringstream ss; | |||
ss << 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; | |||
return ss.str(); | |||
} | |||
void test_run_end(const TestRunStats& p) override { | |||
ConsoleReporter::test_run_end(p); | |||
const bool anythingFailed = p.numTestCasesFailed > 0 || p.numAssertsFailed > 0; | |||
// ----------------------------------------------------- | |||
// > Gather information in rank 0 | |||
int n_rank, rank; | |||
MPI_Comm_rank(MPI_COMM_WORLD, &rank); | |||
MPI_Comm_size(MPI_COMM_WORLD, &n_rank); | |||
int g_numAsserts = 0; | |||
int g_numAssertsFailed = 0; | |||
int g_numTestCasesFailed = 0; | |||
MPI_Reduce(&p.numAsserts , &g_numAsserts , 1, MPI_INT, MPI_SUM, 0, MPI_COMM_WORLD); | |||
MPI_Reduce(&p.numAssertsFailed , &g_numAssertsFailed , 1, MPI_INT, MPI_SUM, 0, MPI_COMM_WORLD); | |||
MPI_Reduce(&p.numTestCasesFailed, &g_numTestCasesFailed, 1, MPI_INT, MPI_SUM, 0, MPI_COMM_WORLD); | |||
std::vector<int> numAssertsFailedByRank; | |||
if(rank == 0){ | |||
numAssertsFailedByRank.resize(static_cast<std::size_t>(n_rank)); | |||
} | |||
MPI_Gather(&p.numAssertsFailed, 1, MPI_INT, numAssertsFailedByRank.data(), 1, MPI_INT, 0, MPI_COMM_WORLD); | |||
if(rank == 0) { | |||
separator_to_stream(); | |||
s << Color::Cyan << "[doctest] " << Color::None << "glob assertions: " << std::setw(6) | |||
<< g_numAsserts << " | " | |||
<< ((g_numAsserts == 0 || anythingFailed) ? Color::None : Color::Green) | |||
<< std::setw(6) << (g_numAsserts - g_numAssertsFailed) << " passed" << Color::None | |||
<< " | " << (g_numAssertsFailed > 0 ? Color::Red : Color::None) << std::setw(6) | |||
<< g_numAssertsFailed << " failed" << Color::None << " |\n"; | |||
separator_to_stream(); | |||
if(g_numAssertsFailed > 0){ | |||
s << Color::Cyan << "[doctest] " << Color::None << "fail on rank:" << std::setw(6) << "\n"; | |||
for(std::size_t i = 0; i < numAssertsFailedByRank.size(); ++i){ | |||
if( numAssertsFailedByRank[i] > 0 ){ | |||
s << std::setw(16) << " -> On rank [" << i << "] with " << numAssertsFailedByRank[i] << " test failed" << std::endl; | |||
} | |||
} | |||
} | |||
s << Color::Cyan << "[doctest] " << Color::None | |||
<< "Status: " << (g_numTestCasesFailed > 0 ? Color::Red : Color::Green) | |||
<< ((g_numTestCasesFailed > 0) ? "FAILURE!" : "SUCCESS!") << Color::None << std::endl; | |||
} | |||
} | |||
void test_case_end(const CurrentTestCaseStats& st) override { | |||
if (is_mpi_test_case()) { | |||
// function called by every rank at the end of a test | |||
// if failed assertions happended, they have been sent to rank 0 | |||
// here rank zero gathers them and prints them all | |||
int rank; | |||
MPI_Comm_rank(MPI_COMM_WORLD, &rank); | |||
// Compute the number of assert with fail among all procs | |||
int nb_fail_asserts_glob = 0; | |||
MPI_Reduce(&st.numAssertsFailedCurrentTest, &nb_fail_asserts_glob, 1, MPI_INT, MPI_SUM, 0, MPI_COMM_WORLD); | |||
if(rank == 0) { | |||
MPI_Status status; | |||
MPI_Status status_recv; | |||
using id_string = std::pair<int,std::string>; | |||
std::vector<id_string> msgs(static_cast<std::size_t>(nb_fail_asserts_glob)); | |||
for (std::size_t i=0; i<static_cast<std::size_t>(nb_fail_asserts_glob); ++i) { | |||
MPI_Probe(MPI_ANY_SOURCE, MPI_ANY_TAG, MPI_COMM_WORLD, &status); | |||
int count; | |||
MPI_Get_count(&status, MPI_BYTE, &count); | |||
std::string recv_msg(static_cast<std::size_t>(count),'\0'); | |||
void* recv_msg_data = const_cast<char*>(recv_msg.data()); // const_cast needed. Non-const .data() exists in C++11 though... | |||
MPI_Recv(recv_msg_data, count, MPI_BYTE, status.MPI_SOURCE, | |||
status.MPI_TAG, MPI_COMM_WORLD, &status_recv); | |||
msgs[i] = {status.MPI_SOURCE,recv_msg}; | |||
} | |||
std::sort(begin(msgs),end(msgs),[](const id_string& x, const id_string& y){ return x.first < y.first; }); | |||
if (nb_fail_asserts_glob>0) { | |||
separator_to_stream(); | |||
file_line_to_stream(tc->m_file.c_str(), static_cast<int>(tc->m_line), "\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\n"; | |||
for(const auto& msg : msgs) { | |||
s << msg.second; | |||
} | |||
s << "\n"; | |||
} | |||
} | |||
} | |||
ConsoleReporter::test_case_end(st); | |||
} | |||
bool is_mpi_test_case() const { | |||
return tc->m_description != nullptr | |||
&& std::string(tc->m_description) == std::string("MPI_TEST_CASE"); | |||
} | |||
void log_assert(const AssertData& rb) override { | |||
if (!is_mpi_test_case()) { | |||
ConsoleReporter::log_assert(rb); | |||
} else { | |||
int rank; | |||
MPI_Comm_rank(MPI_COMM_WORLD, &rank); | |||
if(!rb.m_failed && !opt.success) | |||
return; | |||
std::lock_guard<std::mutex> lock(mutex); | |||
std::stringstream failure_msg; | |||
failure_msg << Color::Red << "On rank [" << rank << "] : " << Color::None; | |||
failure_msg << file_line_to_string(rb.m_file, rb.m_line, " "); | |||
if((rb.m_at & (assertType::is_throws_as | assertType::is_throws_with)) ==0){ | |||
failure_msg << Color::Cyan | |||
<< assertString(rb.m_at) | |||
<< "( " << rb.m_expr << " ) " | |||
<< Color::None | |||
<< (!rb.m_failed ? "is correct!\n" : "is NOT correct!\n") | |||
<< " values: " | |||
<< assertString(rb.m_at) | |||
<< "( " << rb.m_decomp.c_str() << " )\n"; | |||
} | |||
std::string failure_str = failure_msg.str(); | |||
int failure_msg_size = static_cast<int>(failure_str.size()); | |||
MPI_Send(failure_str.c_str(), failure_msg_size, MPI_BYTE, | |||
0, rb.m_line, MPI_COMM_WORLD); // Tag = file line | |||
} | |||
} | |||
}; // MpiConsoleReporter | |||
// "1" is the priority - used for ordering when multiple reporters/listeners are used | |||
REGISTER_REPORTER("MpiConsoleReporter", 1, MpiConsoleReporter); | |||
REGISTER_REPORTER("MpiFileReporter", 1, MpiFileReporter); | |||
} // anonymous | |||
} // doctest |
@@ -14,7 +14,6 @@ SET(TESTSRC rspamd_mem_pool_test.c | |||
rspamd_test_suite.c) | |||
ADD_EXECUTABLE(rspamd-test EXCLUDE_FROM_ALL ${TESTSRC}) | |||
SET_TARGET_PROPERTIES(rspamd-test PROPERTIES LINKER_LANGUAGE C) | |||
SET_TARGET_PROPERTIES(rspamd-test PROPERTIES COMPILE_FLAGS "-DRSPAMD_TEST") | |||
ADD_DEPENDENCIES(rspamd-test rspamd-server) | |||
IF(USE_CXX_LINKER) |