Compare commits

...

15 Commits

Author SHA1 Message Date
Martin Hořeňovský
c1968b3114 More tsan builds 2025-11-09 23:13:57 +01:00
Martin Hořeňovský
5ed9c45e5f Verbose ctest 2025-11-09 22:53:55 +01:00
Martin Hořeňovský
950ad70f4c Fix 2025-11-09 21:34:41 +01:00
Martin Hořeňovský
d7c67270af Disable werror 2025-11-09 21:32:03 +01:00
Martin Hořeňovský
c748569310 Test tsan on mac 2025-11-09 21:28:25 +01:00
Martin Hořeňovský
cd7e43489e Fewer assertions so that msvc debug build doesn't timeout 2025-11-09 15:18:09 +01:00
Martin Hořeňovský
f6fd079aa3 back to 4 threads 2025-11-09 15:03:54 +01:00
Martin Hořeňovský
22d54b36e0 Just 2 threads for a test 2025-11-09 14:58:10 +01:00
Martin Hořeňovský
a9116c2142 Serial run, CAPTURE back 2025-11-09 13:14:14 +01:00
Martin Hořeňovský
2e3214709a Remove captures 2025-11-09 11:41:27 +01:00
Martin Hořeňovský
41ed8b702a Fix initialization for older standards 2025-11-09 11:19:44 +01:00
Martin Hořeňovský
93ef2b4cb8 Add tests for assertion thread safety 2025-11-09 11:07:32 +01:00
Stefan Haller
a1faad9315 Fix the help text for the --order command line argument
It was changed to rand in v3.9.0.
2025-11-07 21:28:41 +01:00
Martin Hořeňovský
31ee3beb0a Small documentation fixes
This includes 2 small typos I found when working on generator skipping,
and 1 typo found by @sfraczek in #3039.

Closes #3039
2025-10-16 20:45:28 +02:00
Martin Hořeňovský
3b853aa9fb Add lifetime attributes to JSON/XML writers 2025-10-16 20:37:05 +02:00
10 changed files with 113 additions and 17 deletions

36
.github/workflows/mac-other-builds.yml vendored Normal file
View File

@@ -0,0 +1,36 @@
name: Mac Sanitizer Builds
on: [push, pull_request]
env:
CXXFLAGS: -fsanitize=thread,undefined
jobs:
build:
# From macos-14 forward, the baseline "macos-X" image is Arm based,
# and not Intel based.
runs-on: ${{matrix.image}}
strategy:
fail-fast: false
matrix:
image: [macos-13, macos-14]
build_type: [Debug, Release]
std: [14, 17]
steps:
- uses: actions/checkout@v4
- name: Configure
run: |
cmake --preset all-tests -GNinja \
-DCMAKE_BUILD_TYPE=${{matrix.build_type}} \
-DCMAKE_CXX_STANDARD=${{matrix.std}} \
-DCATCH_BUILD_EXTRA_TESTS=ON \
-DCATCH_ENABLE_WERROR=OFF
- name: Build
run: cmake --build build
- name: Test
run: ctest --test-dir build -R ThreadSafetyTests --timeout 21600 --verbose

View File

@@ -10,7 +10,7 @@ in-memory logs if they are not needed (the test case passed).
Unlike reporters, each registered event listener is always active. Event Unlike reporters, each registered event listener is always active. Event
listeners are always notified before reporter(s). listeners are always notified before reporter(s).
To write your own event listener, you should derive from `Catch::TestEventListenerBase`, To write your own event listener, you should derive from `Catch::EventListenerBase`,
as it provides empty stubs for all reporter events, allowing you to as it provides empty stubs for all reporter events, allowing you to
only override events you care for. Afterwards you have to register it only override events you care for. Afterwards you have to register it
with Catch2 using `CATCH_REGISTER_LISTENER` macro, so that Catch2 knows with Catch2 using `CATCH_REGISTER_LISTENER` macro, so that Catch2 knows

View File

@@ -275,7 +275,7 @@ There are two ways to handle this, depending on whether you want this
to be an error or not. to be an error or not.
* If empty generator **is** an error, throw an exception in constructor. * If empty generator **is** an error, throw an exception in constructor.
* If empty generator **is not** an error, use the [`SKIP`](skipping-passing-failing.md#skipping-test-cases-at-runtime) in constructor. * If empty generator **is not** an error, use the [`SKIP` macro](skipping-passing-failing.md#skipping-test-cases-at-runtime) in constructor.

View File

@@ -87,7 +87,7 @@ TEST_CASE("complex test case") {
``` ```
This test case will report 5 passing assertions; one for each of the three This test case will report 5 passing assertions; one for each of the three
values in section `a1`, and then two in section `a2`, from values 2 and 4. values in section `a1`, and then two in section `a2`, from values 2 and 6.
Note that as soon as one section is skipped, the entire test case will Note that as soon as one section is skipped, the entire test case will
be reported as _skipped_ (unless there is a failing assertion, in which be reported as _skipped_ (unless there is a failing assertion, in which

View File

@@ -265,7 +265,7 @@ namespace Catch {
( "list all listeners" ) ( "list all listeners" )
| Opt( setTestOrder, "decl|lex|rand" ) | Opt( setTestOrder, "decl|lex|rand" )
["--order"] ["--order"]
( "test case order (defaults to decl)" ) ( "test case order (defaults to rand)" )
| Opt( setRngSeed, "'time'|'random-device'|number" ) | Opt( setRngSeed, "'time'|'random-device'|number" )
["--rng-seed"] ["--rng-seed"]
( "set a specific seed for random numbers" ) ( "set a specific seed for random numbers" )

View File

@@ -8,6 +8,7 @@
#ifndef CATCH_JSONWRITER_HPP_INCLUDED #ifndef CATCH_JSONWRITER_HPP_INCLUDED
#define CATCH_JSONWRITER_HPP_INCLUDED #define CATCH_JSONWRITER_HPP_INCLUDED
#include <catch2/internal/catch_lifetimebound.hpp>
#include <catch2/internal/catch_reusable_string_stream.hpp> #include <catch2/internal/catch_reusable_string_stream.hpp>
#include <catch2/internal/catch_stringref.hpp> #include <catch2/internal/catch_stringref.hpp>
@@ -27,8 +28,8 @@ namespace Catch {
class JsonValueWriter { class JsonValueWriter {
public: public:
JsonValueWriter( std::ostream& os ); JsonValueWriter( std::ostream& os CATCH_ATTR_LIFETIMEBOUND );
JsonValueWriter( std::ostream& os, std::uint64_t indent_level ); JsonValueWriter( std::ostream& os CATCH_ATTR_LIFETIMEBOUND, std::uint64_t indent_level );
JsonObjectWriter writeObject() &&; JsonObjectWriter writeObject() &&;
JsonArrayWriter writeArray() &&; JsonArrayWriter writeArray() &&;
@@ -62,8 +63,8 @@ namespace Catch {
class JsonObjectWriter { class JsonObjectWriter {
public: public:
JsonObjectWriter( std::ostream& os ); JsonObjectWriter( std::ostream& os CATCH_ATTR_LIFETIMEBOUND );
JsonObjectWriter( std::ostream& os, std::uint64_t indent_level ); JsonObjectWriter( std::ostream& os CATCH_ATTR_LIFETIMEBOUND, std::uint64_t indent_level );
JsonObjectWriter( JsonObjectWriter&& source ) noexcept; JsonObjectWriter( JsonObjectWriter&& source ) noexcept;
JsonObjectWriter& operator=( JsonObjectWriter&& source ) = delete; JsonObjectWriter& operator=( JsonObjectWriter&& source ) = delete;
@@ -81,8 +82,8 @@ namespace Catch {
class JsonArrayWriter { class JsonArrayWriter {
public: public:
JsonArrayWriter( std::ostream& os ); JsonArrayWriter( std::ostream& os CATCH_ATTR_LIFETIMEBOUND );
JsonArrayWriter( std::ostream& os, std::uint64_t indent_level ); JsonArrayWriter( std::ostream& os CATCH_ATTR_LIFETIMEBOUND, std::uint64_t indent_level );
JsonArrayWriter( JsonArrayWriter&& source ) noexcept; JsonArrayWriter( JsonArrayWriter&& source ) noexcept;
JsonArrayWriter& operator=( JsonArrayWriter&& source ) = delete; JsonArrayWriter& operator=( JsonArrayWriter&& source ) = delete;

View File

@@ -23,10 +23,10 @@ namespace Catch {
using Mutex = std::mutex; using Mutex = std::mutex;
using LockGuard = std::lock_guard<std::mutex>; using LockGuard = std::lock_guard<std::mutex>;
struct AtomicCounts { struct AtomicCounts {
std::atomic<std::uint64_t> passed = 0; std::atomic<std::uint64_t> passed{ 0 };
std::atomic<std::uint64_t> failed = 0; std::atomic<std::uint64_t> failed{ 0 };
std::atomic<std::uint64_t> failedButOk = 0; std::atomic<std::uint64_t> failedButOk{ 0 };
std::atomic<std::uint64_t> skipped = 0; std::atomic<std::uint64_t> skipped{ 0 };
}; };
#else // ^^ Use actual mutex, lock and atomics #else // ^^ Use actual mutex, lock and atomics
// vv Dummy implementations for single-thread performance // vv Dummy implementations for single-thread performance

View File

@@ -8,6 +8,7 @@
#ifndef CATCH_XMLWRITER_HPP_INCLUDED #ifndef CATCH_XMLWRITER_HPP_INCLUDED
#define CATCH_XMLWRITER_HPP_INCLUDED #define CATCH_XMLWRITER_HPP_INCLUDED
#include <catch2/internal/catch_lifetimebound.hpp>
#include <catch2/internal/catch_reusable_string_stream.hpp> #include <catch2/internal/catch_reusable_string_stream.hpp>
#include <catch2/internal/catch_stringref.hpp> #include <catch2/internal/catch_stringref.hpp>
@@ -43,7 +44,7 @@ namespace Catch {
public: public:
enum ForWhat { ForTextNodes, ForAttributes }; enum ForWhat { ForTextNodes, ForAttributes };
constexpr XmlEncode( StringRef str, ForWhat forWhat = ForTextNodes ): constexpr XmlEncode( StringRef str CATCH_ATTR_LIFETIMEBOUND, ForWhat forWhat = ForTextNodes ):
m_str( str ), m_forWhat( forWhat ) {} m_str( str ), m_forWhat( forWhat ) {}
@@ -61,7 +62,7 @@ namespace Catch {
class ScopedElement { class ScopedElement {
public: public:
ScopedElement( XmlWriter* writer, XmlFormatting fmt ); ScopedElement( XmlWriter* writer CATCH_ATTR_LIFETIMEBOUND, XmlFormatting fmt );
ScopedElement( ScopedElement&& other ) noexcept; ScopedElement( ScopedElement&& other ) noexcept;
ScopedElement& operator=( ScopedElement&& other ) noexcept; ScopedElement& operator=( ScopedElement&& other ) noexcept;
@@ -93,7 +94,7 @@ namespace Catch {
XmlFormatting m_fmt; XmlFormatting m_fmt;
}; };
XmlWriter( std::ostream& os ); XmlWriter( std::ostream& os CATCH_ATTR_LIFETIMEBOUND );
~XmlWriter(); ~XmlWriter();
XmlWriter( XmlWriter const& ) = delete; XmlWriter( XmlWriter const& ) = delete;

View File

@@ -553,3 +553,17 @@ set_tests_properties(AmalgamatedFileTest
PROPERTIES PROPERTIES
PASS_REGULAR_EXPRESSION "All tests passed \\(14 assertions in 3 test cases\\)" PASS_REGULAR_EXPRESSION "All tests passed \\(14 assertions in 3 test cases\\)"
) )
add_executable(ThreadSafetyTests
${TESTS_DIR}/X94-ThreadSafetyTests.cpp
)
target_link_libraries(ThreadSafetyTests Catch2_buildall_interface)
target_compile_definitions(ThreadSafetyTests PUBLIC CATCH_CONFIG_EXPERIMENTAL_THREAD_SAFE_ASSERTIONS)
add_test(NAME ThreadSafetyTests
COMMAND ThreadSafetyTests -r compact
)
set_tests_properties(ThreadSafetyTests
PROPERTIES
PASS_REGULAR_EXPRESSION "assertions: 801 | 400 passed | 401 failed"
RUN_SERIAL ON
)

View File

@@ -0,0 +1,44 @@
// Copyright Catch2 Authors
// Distributed under the Boost Software License, Version 1.0.
// (See accompanying file LICENSE.txt or copy at
// https://www.boost.org/LICENSE_1_0.txt)
// SPDX-License-Identifier: BSL-1.0
/**\file
* Test that assertions and messages are thread-safe.
*
* This is done by spamming assertions and messages on multiple subthreads.
* In manual, this reliably causes segfaults if the test is linked against
* a non-thread-safe version of Catch2.
*
* The CTest test definition should also verify that the final assertion
* count is correct.
*/
#include <catch2/catch_test_macros.hpp>
#include <atomic>
#include <thread>
#include <vector>
TEST_CASE( "Failed REQUIRE in the main thread is fine" ) {
std::vector<std::thread> threads;
for ( size_t t = 0; t < 4; ++t) {
threads.emplace_back( [t]() {
CAPTURE(t);
for (size_t i = 0; i < 100; ++i) {
CAPTURE(i);
CHECK( false );
CHECK( true );
}
} );
}
for (auto& t : threads) {
t.join();
}
REQUIRE( false );
}