From 572a5e423037566fe5ed8817887953c05830ac8b Mon Sep 17 00:00:00 2001 From: Alexander Grund Date: Fri, 10 Jul 2026 20:05:17 +0200 Subject: [PATCH 01/14] Show output in log matcher tests on failure --- test/framework-ts/logger-for-tests.hpp | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/test/framework-ts/logger-for-tests.hpp b/test/framework-ts/logger-for-tests.hpp index 4a59e5f6dc..2eda7aa8ae 100644 --- a/test/framework-ts/logger-for-tests.hpp +++ b/test/framework-ts/logger-for-tests.hpp @@ -54,6 +54,15 @@ class output_test_stream_for_loggers : public output_test_stream { return basename; } + virtual boost::test_tools::assertion_result match_pattern( bool flush_stream = true ) { + boost::test_tools::assertion_result result = output_test_stream::match_pattern(false); + if(!result) + result.message() << "[Full content]:\n" << get_stream_string_representation() << "\n[End Full content]"; + if(flush_stream) + flush(); + return result; + } + virtual std::string get_stream_string_representation() const { std::string current_string = output_test_stream::get_stream_string_representation(); From a49c4cd477429627f12eac9d6887c6edd5928463 Mon Sep 17 00:00:00 2001 From: Alexander Grund Date: Fri, 17 Jul 2026 08:41:58 +0200 Subject: [PATCH 02/14] Make `test_unit-order-shuffled-test` more reliable Random shuffling (based on current time) might yield the same exact order with a probability proportional to the number of tests/suites. Make it less likely by increasing the number of tests. --- test/test-organization-ts/test_unit-order-shuffled-test.cpp | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/test/test-organization-ts/test_unit-order-shuffled-test.cpp b/test/test-organization-ts/test_unit-order-shuffled-test.cpp index 0588656538..65d76e42d9 100644 --- a/test/test-organization-ts/test_unit-order-shuffled-test.cpp +++ b/test/test-organization-ts/test_unit-order-shuffled-test.cpp @@ -74,7 +74,8 @@ struct test_tree { test_tree() { master = BOOST_TEST_SUITE( "master" ); - std::size_t nb_ts = (std::max)(3, std::rand() % 17); + // Need sufficiently high number of tests so random shuffling is unlikely to yield the same order + std::size_t nb_ts = 7 + (std::rand() % 17); std::vector tsuites(1, master); // master is in there for(std::size_t s = 0; s < nb_ts; s++) From 9f5cbe12ecfca4edf7cd9c0575b8c0142e08fc08 Mon Sep 17 00:00:00 2001 From: Alexander Grund Date: Tue, 23 Jun 2026 08:49:38 +0200 Subject: [PATCH 03/14] GHA: Use the shared workflow from Boost.CI for tests --- .github/workflows/ci.yml | 614 ++------------------------------------- 1 file changed, 30 insertions(+), 584 deletions(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 24a0b1466b..281e084596 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -1,12 +1,23 @@ +# # Copyright 2020-2021 Peter Dimov # Copyright 2021 Andrey Semashev -# Copyright 2021 Alexander Grund -# Copyright 2022 James E. King III +# Copyright 2021-2026 Alexander Grund +# Copyright 2022-2025 James E. King III # # Distributed under the Boost Software License, Version 1.0. # (See accompanying file LICENSE_1_0.txt or copy at http://boost.org/LICENSE_1_0.txt) +# +# This workflow uses the Boost.CI reusable workflow which builds a variety of +# configurations of your project, runs tests, and generates code coverage reports. +# +# To use it, copy this file into your repository as `.github/workflows/ci.yml` and +# customize it appropriately. +# --- -name: CI +name: Boost.CI + +permissions: + contents: read on: pull_request: @@ -17,587 +28,22 @@ on: - bugfix/** - feature/** - fix/** + - github/** - pr/** - -concurrency: - group: ${{format('{0}:{1}', github.repository, github.ref)}} - cancel-in-progress: true - -env: - GIT_FETCH_JOBS: 8 - NET_RETRY_COUNT: 5 - DEFAULT_BUILD_VARIANT: release + paths-ignore: + - LICENSE + - meta/** + - README.md jobs: - posix: - defaults: - run: - shell: bash - - strategy: - fail-fast: false - matrix: - include: - - toolset: gcc-5 - cxxstd: "11" - address_model: 64 - os: ubuntu-latest - container: ubuntu:16.04 - install: - - g++-5-multilib - - toolset: gcc-5 - cxxstd: "11-gnu" - address_model: 64 - os: ubuntu-latest - container: ubuntu:16.04 - install: - - g++-5-multilib - - toolset: gcc-6 - cxxstd: "11,14" - address_model: 64 - os: ubuntu-latest - container: ubuntu:18.04 - install: - - g++-6-multilib - - toolset: gcc-7 - cxxstd: "14,17" - address_model: 64 - os: ubuntu-latest - container: ubuntu:18.04 - install: - - g++-7-multilib - - toolset: gcc-8 - cxxstd: "17,2a" - address_model: 64 - os: ubuntu-latest - container: ubuntu:18.04 - install: - - g++-8-multilib - - toolset: gcc-9 - cxxstd: "17,2a" - address_model: 64 - os: ubuntu-latest - container: ubuntu:20.04 - install: - - g++-9-multilib - - toolset: gcc-9 - cxxstd: "17-gnu,2a-gnu" - address_model: 64 - os: ubuntu-latest - container: ubuntu:20.04 - install: - - g++-9-multilib - - toolset: gcc-10 - cxxstd: "17,20" - address_model: 64 - os: ubuntu-latest - container: ubuntu:20.04 - install: - - g++-10-multilib - - toolset: gcc-11 - cxxstd: "20,23" - address_model: 64 - os: ubuntu-22.04 - install: - - g++-11-multilib - - toolset: gcc-12 - cxxstd: "20,23" - address_model: 64 - os: ubuntu-22.04 - install: - - g++-12-multilib - - toolset: gcc-12 - cxxstd: "20-gnu,23-gnu" - address_model: 64 - os: ubuntu-22.04 - install: - - g++-12-multilib - - name: UBSAN - toolset: gcc-13 - cxxstd: "11" - address_model: 64 - ubsan: 1 - os: ubuntu-24.04 - install: - - g++-13-multilib - - # Linux, clang - - toolset: clang - compiler: clang++-3.8 - cxxstd: "11" - os: ubuntu-latest - container: ubuntu:16.04 - install: - - clang-3.8 - - toolset: clang - compiler: clang++-3.9 - cxxstd: "11,14" - os: ubuntu-latest - container: ubuntu:18.04 - install: - - clang-3.9 - - toolset: clang - compiler: clang++-4.0 - cxxstd: "11" - os: ubuntu-latest - container: ubuntu:18.04 - install: - - clang-4.0 - - toolset: clang - compiler: clang++-5.0 - cxxstd: "11,14" - os: ubuntu-latest - container: ubuntu:18.04 - install: - - clang-5.0 - - toolset: clang - compiler: clang++-6.0 - cxxstd: "14,1z" - os: ubuntu-latest - container: ubuntu:18.04 - install: - - clang-6.0 - - toolset: clang - compiler: clang++-7 - cxxstd: "14,17" - os: ubuntu-latest - container: ubuntu:18.04 - install: - - clang-7 - # Note: clang-8 does not fully support C++20, so it is not compatible with libstdc++-8 in this mode - - toolset: clang - compiler: clang++-8 - cxxstd: "14,17" - os: ubuntu-latest - container: ubuntu:18.04 - install: - - clang-8 - - g++-7 - gcc_toolchain: 7 - - toolset: clang - compiler: clang++-9 - cxxstd: "17,2a" - os: ubuntu-latest - container: ubuntu:20.04 - install: - - clang-9 - - toolset: clang - compiler: clang++-10 - cxxstd: "17,20" - os: ubuntu-latest - container: ubuntu:20.04 - install: - - clang-10 - - toolset: clang - compiler: clang++-11 - cxxstd: "17" - os: ubuntu-22.04 - install: - - clang-11 - - toolset: clang - compiler: clang++-12 - cxxstd: "17" - os: ubuntu-22.04 - install: - - clang-12 - - toolset: clang - compiler: clang++-13 - cxxstd: "17" - os: ubuntu-22.04 - install: - - clang-13 - - toolset: clang - compiler: clang++-14 - cxxstd: "17" - os: ubuntu-22.04 - install: - - clang-14 - - toolset: clang - compiler: clang++-14 - cxxstd: "17-gnu" - os: ubuntu-22.04 - install: - - clang-14 - - toolset: clang - compiler: clang++-15 - cxxstd: "20" - os: ubuntu-22.04 - install: - - clang-15 - sources: - - "deb http://apt.llvm.org/jammy/ llvm-toolchain-jammy-15 main" - source_keys: - - "https://apt.llvm.org/llvm-snapshot.gpg.key" - - toolset: clang - compiler: clang++-15 - cxxstd: "20" - os: ubuntu-22.04 - install: - - clang-15 - - libc++-15-dev - - libc++abi-15-dev - sources: - - "deb http://apt.llvm.org/jammy/ llvm-toolchain-jammy-15 main" - source_keys: - - "https://apt.llvm.org/llvm-snapshot.gpg.key" - cxxflags: -stdlib=libc++ - linkflags: -stdlib=libc++ - - name: UBSAN - toolset: clang - compiler: clang++-14 - cxxstd: "17" - cxxflags: -stdlib=libc++ - linkflags: -stdlib=libc++ - ubsan: 1 - os: ubuntu-22.04 - install: - - clang-14 - - libc++-14-dev - - libc++abi-14-dev - - - toolset: clang - cxxstd: "20" - os: macos-14 - - toolset: clang - cxxstd: "20" - os: macos-15 - - runs-on: ${{matrix.os}} - container: - image: ${{matrix.container}} - volumes: - - /node20217:/node20217:rw,rshared - - ${{ startsWith(matrix.container, 'ubuntu:1') && '/node20217:/__e/node20:ro,rshared' || ' ' }} - - steps: - - name: Setup environment - run: | - if [ -f "/etc/debian_version" ] - then - echo "DEBIAN_FRONTEND=noninteractive" >> $GITHUB_ENV - export DEBIAN_FRONTEND=noninteractive - fi - if [ -n "${{matrix.container}}" ] - then - echo "GHA_CONTAINER=${{matrix.container}}" >> $GITHUB_ENV - if [ -f "/etc/debian_version" ] - then - apt-get -o Acquire::Retries=$NET_RETRY_COUNT update - if [ "$(apt-cache search "^python-is-python3$" | wc -l)" -ne 0 ] - then - PYTHON_PACKAGE="python-is-python3" - else - PYTHON_PACKAGE="python" - fi - apt-get -o Acquire::Retries=$NET_RETRY_COUNT install -y sudo software-properties-common tzdata wget curl apt-transport-https ca-certificates make build-essential g++ $PYTHON_PACKAGE python3 perl git cmake - fi - if [[ "${{matrix.container}}" == "ubuntu:1"* ]]; then - # Node 20 doesn't work with Ubuntu 16/18 glibc: https://github.com/actions/checkout/issues/1590 - curl -sL https://archives.boost.io/misc/node/node-v20.9.0-linux-x64-glibc-217.tar.xz | tar -xJ --strip-components 1 -C /node20217 - fi - fi - git config --global pack.threads 0 - - uses: actions/checkout@v4 - - - name: Install packages - if: matrix.install - run: | - declare -a SOURCE_KEYS SOURCES - if [ -n "${{join(matrix.source_keys, ' ')}}" ] - then - SOURCE_KEYS=("${{join(matrix.source_keys, '" "')}}") - fi - if [ -n "${{join(matrix.sources, ' ')}}" ] - then - SOURCES=("${{join(matrix.sources, '" "')}}") - fi - for key in "${SOURCE_KEYS[@]}" - do - for i in {1..$NET_RETRY_COUNT} - do - echo "Adding key: $key" - wget -O - "$key" | sudo apt-key add - && break || sleep 2 - done - done - if [ ${#SOURCES[@]} -gt 0 ] - then - APT_ADD_REPO_COMMON_ARGS=("-y") - APT_ADD_REPO_SUPPORTED_ARGS="$(apt-add-repository --help | perl -ne 'if (/^\s*-n/) { print "n"; } elsif (/^\s*-P/) { print "P"; } elsif (/^\s*-S/) { print "S"; } elsif (/^\s*-U/) { print "U"; }')" - if [ -n "$APT_ADD_REPO_SUPPORTED_ARGS" -a -z "${APT_ADD_REPO_SUPPORTED_ARGS##*n*}" ] - then - APT_ADD_REPO_COMMON_ARGS+=("-n") - fi - APT_ADD_REPO_HAS_SOURCE_ARGS="$([ -n "$APT_ADD_REPO_SUPPORTED_ARGS" -a -z "${APT_ADD_REPO_SUPPORTED_ARGS##*P*}" -a -z "${APT_ADD_REPO_SUPPORTED_ARGS##*S*}" -a -z "${APT_ADD_REPO_SUPPORTED_ARGS##*U*}" ] && echo 1 || echo 0)" - for source in "${SOURCES[@]}" - do - for i in {1..$NET_RETRY_COUNT} - do - APT_ADD_REPO_ARGS=("${APT_ADD_REPO_COMMON_ARGS[@]}") - if [ $APT_ADD_REPO_HAS_SOURCE_ARGS -ne 0 ] - then - case "$source" in - "ppa:"*) - APT_ADD_REPO_ARGS+=("-P") - ;; - "deb "*) - APT_ADD_REPO_ARGS+=("-S") - ;; - *) - APT_ADD_REPO_ARGS+=("-U") - ;; - esac - fi - APT_ADD_REPO_ARGS+=("$source") - echo "apt-add-repository ${APT_ADD_REPO_ARGS[@]}" - sudo -E apt-add-repository "${APT_ADD_REPO_ARGS[@]}" && break || sleep 2 - done - done - fi - sudo apt-get -o Acquire::Retries=$NET_RETRY_COUNT update - sudo apt-get -o Acquire::Retries=$NET_RETRY_COUNT install -y ${{join(matrix.install, ' ')}} - - name: Setup GCC Toolchain - if: matrix.gcc_toolchain - run: | - GCC_TOOLCHAIN_ROOT="$HOME/gcc-toolchain" - echo "GCC_TOOLCHAIN_ROOT=\"$GCC_TOOLCHAIN_ROOT\"" >> $GITHUB_ENV - MULTIARCH_TRIPLET="$(dpkg-architecture -qDEB_HOST_MULTIARCH)" - mkdir -p "$GCC_TOOLCHAIN_ROOT" - ln -s /usr/include "$GCC_TOOLCHAIN_ROOT/include" - ln -s /usr/bin "$GCC_TOOLCHAIN_ROOT/bin" - mkdir -p "$GCC_TOOLCHAIN_ROOT/lib/gcc/$MULTIARCH_TRIPLET" - ln -s "/usr/lib/gcc/$MULTIARCH_TRIPLET/${{matrix.gcc_toolchain}}" "$GCC_TOOLCHAIN_ROOT/lib/gcc/$MULTIARCH_TRIPLET/${{matrix.gcc_toolchain}}" - - name: Setup Boost - run: | - echo GITHUB_REPOSITORY: $GITHUB_REPOSITORY - LIBRARY=${GITHUB_REPOSITORY#*/} - echo LIBRARY: $LIBRARY - echo "LIBRARY=$LIBRARY" >> $GITHUB_ENV - echo GITHUB_BASE_REF: $GITHUB_BASE_REF - echo GITHUB_REF: $GITHUB_REF - REF=${GITHUB_BASE_REF:-$GITHUB_REF} - REF=${REF#refs/heads/} - echo REF: $REF - BOOST_BRANCH=develop && [ "$REF" = "master" ] && BOOST_BRANCH=master || true - echo BOOST_BRANCH: $BOOST_BRANCH - BUILD_JOBS=$((nproc || sysctl -n hw.ncpu) 2> /dev/null) - echo "BUILD_JOBS=$BUILD_JOBS" >> $GITHUB_ENV - echo "CMAKE_BUILD_PARALLEL_LEVEL=$BUILD_JOBS" >> $GITHUB_ENV - DEPINST_ARGS=() - GIT_VERSION="$(git --version | sed -e 's/git version //')" - GIT_HAS_JOBS=1 - if [ -f "/etc/debian_version" ] - then - if $(dpkg --compare-versions "$GIT_VERSION" lt 2.8.0) - then - GIT_HAS_JOBS=0 - fi - else - declare -a GIT_VER=(${GIT_VERSION//./ }) - declare -a GIT_MIN_VER=(2 8 0) - for ((i=0; i<${#GIT_VER[@]}; i++)) - do - if [ -z "${GIT_MIN_VER[i]}" ] - then - GIT_MIN_VER[i]=0 - fi - if [ "${GIT_VER[i]}" -lt "${GIT_MIN_VER[i]}" ] - then - GIT_HAS_JOBS=0 - break - fi - done - fi - if [ "$GIT_HAS_JOBS" -ne 0 ] - then - DEPINST_ARGS+=("--git_args" "--jobs $GIT_FETCH_JOBS") - fi - cd .. - git clone -b "$BOOST_BRANCH" --depth 1 "https://github.com/boostorg/boost.git" "boost-root" - cd boost-root - mkdir -p libs/$LIBRARY - cp -r $GITHUB_WORKSPACE/* libs/$LIBRARY - git submodule update --init tools/boostdep - DEPINST_ARGS+=("$LIBRARY") - python tools/boostdep/depinst/depinst.py -I doc/examples "${DEPINST_ARGS[@]}" - if [ -z "${{matrix.cmake_tests}}" ] - then - ./bootstrap.sh - ./b2 headers - if [ -n "${{matrix.compiler}}" -o -n "$GCC_TOOLCHAIN_ROOT" ] - then - echo -n "using ${{matrix.toolset}} : : ${{matrix.compiler}}" > ~/user-config.jam - if [ -n "$GCC_TOOLCHAIN_ROOT" ] - then - echo -n " : \"--gcc-toolchain=$GCC_TOOLCHAIN_ROOT\" \"--gcc-toolchain=$GCC_TOOLCHAIN_ROOT\"" >> ~/user-config.jam - fi - echo " ;" >> ~/user-config.jam - fi - fi - - name: Run tests - if: matrix.cmake_tests == '' - run: | - cd ../boost-root - B2_ARGS=("-j" "$BUILD_JOBS" "toolset=${{matrix.toolset}}" "cxxstd=${{matrix.cxxstd}}" "link=static,shared") - if [ -n "${{matrix.build_variant}}" ] - then - B2_ARGS+=("variant=${{matrix.build_variant}}") - else - B2_ARGS+=("variant=$DEFAULT_BUILD_VARIANT") - fi - if [ -n "${{matrix.threading}}" ] - then - B2_ARGS+=("threading=${{matrix.threading}}") - fi - if [ -n "${{matrix.ubsan}}" ] - then - export UBSAN_OPTIONS="print_stacktrace=1" - B2_ARGS+=("cxxflags=-fsanitize=undefined -fno-sanitize-recover=undefined" "linkflags=-fsanitize=undefined -fuse-ld=gold" "define=UBSAN=1" "debug-symbols=on" "visibility=global") - fi - if [ -n "${{matrix.cxxflags}}" ] - then - B2_ARGS+=("cxxflags=${{matrix.cxxflags}}") - fi - if [ -n "${{matrix.linkflags}}" ] - then - B2_ARGS+=("linkflags=${{matrix.linkflags}}") - fi - if [ -n "${{matrix.address_model}}" ] - then - B2_ARGS+=("address-model=${{matrix.address_model}}") - fi - B2_ARGS+=("libs/$LIBRARY/test") - ./b2 "${B2_ARGS[@]}" - - windows: - strategy: - fail-fast: false - matrix: - include: - - toolset: msvc-14.3 - cxxstd: "20" - addrmd: 64 - os: windows-2022 - - toolset: clang-win - cxxstd: "17" - addrmd: 64 - os: windows-2022 - - toolset: gcc - cxxstd: "17" - addrmd: 64 - os: windows-2022 - - runs-on: ${{matrix.os}} - - steps: - - uses: actions/checkout@v3 - - - name: Setup Boost - shell: cmd - run: | - echo GITHUB_REPOSITORY: %GITHUB_REPOSITORY% - for /f %%i in ("%GITHUB_REPOSITORY%") do set LIBRARY=%%~nxi - echo LIBRARY: %LIBRARY% - echo LIBRARY=%LIBRARY%>>%GITHUB_ENV% - echo GITHUB_BASE_REF: %GITHUB_BASE_REF% - echo GITHUB_REF: %GITHUB_REF% - if "%GITHUB_BASE_REF%" == "" set GITHUB_BASE_REF=%GITHUB_REF% - set BOOST_BRANCH=develop - for /f %%i in ("%GITHUB_BASE_REF%") do if "%%~nxi" == "master" set BOOST_BRANCH=master - echo BOOST_BRANCH: %BOOST_BRANCH% - cd .. - git clone -b %BOOST_BRANCH% --depth 1 https://github.com/boostorg/boost.git boost-root - cd boost-root - xcopy /s /e /q %GITHUB_WORKSPACE% libs\%LIBRARY%\ - git submodule update --init tools/boostdep - python tools/boostdep/depinst/depinst.py -I doc/examples --git_args "--jobs 3" %LIBRARY% - cmd /c bootstrap - b2 -d0 headers - - - name: Run tests - shell: cmd - run: | - cd ../boost-root - b2 -j3 libs/%LIBRARY%/test toolset=${{matrix.toolset}} cxxstd=${{matrix.cxxstd}} address-model=${{matrix.addrmd}} variant=release link=static,shared embed-manifest-via=linker - - CMake: - defaults: - run: - shell: bash - - strategy: - fail-fast: false - matrix: - include: - - { os: ubuntu-22.04, build_shared: ON, build_type: Debug, generator: 'Unix Makefiles' } - - { os: ubuntu-22.04, build_shared: OFF, build_type: Debug, generator: 'Unix Makefiles' } - - { os: windows-2025, build_shared: ON, build_type: Debug, generator: 'Visual Studio 17 2022' } - - { os: windows-2025, build_shared: OFF, build_type: Debug, generator: 'Visual Studio 17 2022' } - - timeout-minutes: 120 - runs-on: ${{matrix.os}} - - steps: - - uses: actions/checkout@v3 - - name: Fetch Boost.CI - uses: actions/checkout@v3 - with: - repository: boostorg/boost-ci - ref: master - path: boost-ci-cloned - - name: Get CI scripts folder - run: | - # Copy ci folder if not testing Boost.CI - [[ "$GITHUB_REPOSITORY" =~ "boost-ci" ]] || cp -r boost-ci-cloned/ci . - rm -rf boost-ci-cloned - - name: Setup Boost - env: {B2_DONT_BOOTSTRAP: 1} - run: source ci/github/install.sh - - - name: Run CMake tests - run: | - cd "$BOOST_ROOT" - mkdir __build_cmake_test__ && cd __build_cmake_test__ - cmake -G "${{matrix.generator}}" -DCMAKE_BUILD_TYPE=${{matrix.build_type}} -DBOOST_INCLUDE_LIBRARIES=$SELF -DBUILD_SHARED_LIBS=${{matrix.build_shared}} -DBUILD_TESTING=ON -DBoost_VERBOSE=ON .. - cmake --build . --target tests --config ${{matrix.build_type}} -j$B2_JOBS - ctest --output-on-failure --build-config ${{matrix.build_type}} - - - name: Run CMake subdir tests - run: | - cmake_test_folder="$BOOST_ROOT/libs/$SELF/test/cmake_test" # New unified folder - [ -d "$cmake_test_folder" ] || cmake_test_folder="$BOOST_ROOT/libs/$SELF/test/cmake_subdir_test" - cd "$cmake_test_folder" - mkdir __build_cmake_subdir_test__ && cd __build_cmake_subdir_test__ - extra_args="" - # On Windows DLLs need to be either in PATH or in the same folder as the executable, so put all binaries into the same folder - if [[ "$RUNNER_OS" == "Windows" ]] && [[ "${{matrix.build_shared}}" == "ON" ]]; then - extra_args="-DCMAKE_RUNTIME_OUTPUT_DIRECTORY='$(pwd)/bin'" - fi - cmake -G "${{matrix.generator}}" -DBOOST_CI_INSTALL_TEST=OFF -DCMAKE_BUILD_TYPE=${{matrix.build_type}} -DBUILD_SHARED_LIBS=${{matrix.build_shared}} $extra_args .. - cmake --build . --config ${{matrix.build_type}} -j$B2_JOBS - ctest --output-on-failure --build-config ${{matrix.build_type}} - - - name: Install Library - run: | - BCM_INSTALL_PATH=/tmp/boost_install - echo "BCM_INSTALL_PATH=$BCM_INSTALL_PATH" >> $GITHUB_ENV - cd "$BOOST_ROOT" - mkdir __build_cmake_install_test__ && cd __build_cmake_install_test__ - cmake -G "${{matrix.generator}}" -DCMAKE_BUILD_TYPE=${{matrix.build_type}} -DBOOST_INCLUDE_LIBRARIES=$SELF -DBUILD_SHARED_LIBS=${{matrix.build_shared}} -DCMAKE_INSTALL_PREFIX="$BCM_INSTALL_PATH" -DBoost_VERBOSE=ON -DBoost_DEBUG=ON .. - cmake --build . --target install --config ${{matrix.build_type}} -j$B2_JOBS - - name: Run CMake install tests - run: | - cmake_test_folder="$BOOST_ROOT/libs/$SELF/test/cmake_test" # New unified folder - [ -d "$cmake_test_folder" ] || cmake_test_folder="$BOOST_ROOT/libs/$SELF/test/cmake_install_test" - cd "$cmake_test_folder" - mkdir __build_cmake_install_test__ && cd __build_cmake_install_test__ - cmake -G "${{matrix.generator}}" -DBOOST_CI_INSTALL_TEST=ON -DCMAKE_BUILD_TYPE=${{matrix.build_type}} -DBUILD_SHARED_LIBS=${{matrix.build_shared}} -DCMAKE_PREFIX_PATH="$BCM_INSTALL_PATH" .. - cmake --build . --config ${{matrix.build_type}} -j$B2_JOBS - if [[ "${{matrix.build_shared}}" == "ON" ]]; then - # Make sure shared libs can be found at runtime - if [ "$RUNNER_OS" == "Windows" ]; then - export PATH="$BCM_INSTALL_PATH/bin:$PATH" - else - export LD_LIBRARY_PATH="$BCM_INSTALL_PATH/lib:$LD_LIBRARY_PATH" - fi - fi - ctest --output-on-failure --build-config ${{matrix.build_type}} + call-boost-ci: + name: Run Boost.CI + uses: boostorg/boost-ci/.github/workflows/reusable.yml@master + # Customization: + with: + enable_reflection: false + depinst_args: --include doc/examples + secrets: + CODECOV_TOKEN: ${{ secrets.CODECOV_TOKEN }} + COVERITY_SCAN_NOTIFICATION_EMAIL: ${{ secrets.COVERITY_SCAN_NOTIFICATION_EMAIL }} + COVERITY_SCAN_TOKEN: ${{ secrets.COVERITY_SCAN_TOKEN }} From 2f2cb0b18f190ff4d9850125ea18c71330bcb366 Mon Sep 17 00:00:00 2001 From: Alexander Grund Date: Thu, 25 Jun 2026 15:08:08 +0200 Subject: [PATCH 04/14] Disable Cygwin CI --- .github/workflows/ci.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 281e084596..0665e54a3e 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -41,7 +41,7 @@ jobs: uses: boostorg/boost-ci/.github/workflows/reusable.yml@master # Customization: with: - enable_reflection: false + enable_cygwin: false depinst_args: --include doc/examples secrets: CODECOV_TOKEN: ${{ secrets.CODECOV_TOKEN }} From ea427992788cabf7f78a68b550bff07de5a45a90 Mon Sep 17 00:00:00 2001 From: Alexander Grund Date: Thu, 16 Jul 2026 15:59:55 +0200 Subject: [PATCH 05/14] Disable coverage LCov has too many issues matching collected lines to the source when the `BOOST_TEST_*` macros are used. E.g. it reports that it found covered lines after the end of the file. --- .github/workflows/ci.yml | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 0665e54a3e..df54b0a806 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -41,9 +41,10 @@ jobs: uses: boostorg/boost-ci/.github/workflows/reusable.yml@master # Customization: with: + enable_pr_coverage: false # Disabled: Too many issues with lcov enable_cygwin: false depinst_args: --include doc/examples secrets: - CODECOV_TOKEN: ${{ secrets.CODECOV_TOKEN }} + # CODECOV_TOKEN: ${{ secrets.CODECOV_TOKEN }} Disabled: Too many issues with lcov COVERITY_SCAN_NOTIFICATION_EMAIL: ${{ secrets.COVERITY_SCAN_NOTIFICATION_EMAIL }} COVERITY_SCAN_TOKEN: ${{ secrets.COVERITY_SCAN_TOKEN }} From 2f83a1ce9abe2769c257aa6a65ad64111fd474ba Mon Sep 17 00:00:00 2001 From: Alexander Grund Date: Mon, 6 Jul 2026 08:43:20 +0200 Subject: [PATCH 06/14] Split jobs to 2 C++ standards per run GHA CI runners run out of memory with the amount of tests and combinations. --- .github/workflows/ci.yml | 1 + 1 file changed, 1 insertion(+) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index df54b0a806..56434fc55a 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -44,6 +44,7 @@ jobs: enable_pr_coverage: false # Disabled: Too many issues with lcov enable_cygwin: false depinst_args: --include doc/examples + max_cxx_stds_per_job: 2 secrets: # CODECOV_TOKEN: ${{ secrets.CODECOV_TOKEN }} Disabled: Too many issues with lcov COVERITY_SCAN_NOTIFICATION_EMAIL: ${{ secrets.COVERITY_SCAN_NOTIFICATION_EMAIL }} From e97da5ac9df3eb27a72a91b736e5a368334bc860 Mon Sep 17 00:00:00 2001 From: Alexander Grund Date: Thu, 25 Jun 2026 11:47:12 +0200 Subject: [PATCH 07/14] Exclude GCC 4.8 & 4.9 --- .github/workflows/ci.yml | 1 + 1 file changed, 1 insertion(+) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 56434fc55a..51939d6108 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -44,6 +44,7 @@ jobs: enable_pr_coverage: false # Disabled: Too many issues with lcov enable_cygwin: false depinst_args: --include doc/examples + exclude_compiler: 'gcc-4.7,gcc-4.8,gcc-4.9' max_cxx_stds_per_job: 2 secrets: # CODECOV_TOKEN: ${{ secrets.CODECOV_TOKEN }} Disabled: Too many issues with lcov From b67fdd589fbcd4ccdfc5759a0427935f73eeaa88 Mon Sep 17 00:00:00 2001 From: Alexander Grund Date: Thu, 25 Jun 2026 19:58:58 +0200 Subject: [PATCH 08/14] Disable clang-3.6 CI --- .github/workflows/ci.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 51939d6108..087d99e507 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -44,7 +44,7 @@ jobs: enable_pr_coverage: false # Disabled: Too many issues with lcov enable_cygwin: false depinst_args: --include doc/examples - exclude_compiler: 'gcc-4.7,gcc-4.8,gcc-4.9' + exclude_compiler: 'gcc-4.7,gcc-4.8,gcc-4.9,clang-3.6' max_cxx_stds_per_job: 2 secrets: # CODECOV_TOKEN: ${{ secrets.CODECOV_TOKEN }} Disabled: Too many issues with lcov From 74fb23de499dc5bfe69c618d9c1280b1575872db Mon Sep 17 00:00:00 2001 From: Alexander Grund Date: Mon, 6 Jul 2026 13:21:16 +0200 Subject: [PATCH 09/14] Exclude failing jobs --- .github/workflows/ci.yml | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 087d99e507..ee3d36d492 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -44,7 +44,11 @@ jobs: enable_pr_coverage: false # Disabled: Too many issues with lcov enable_cygwin: false depinst_args: --include doc/examples - exclude_compiler: 'gcc-4.7,gcc-4.8,gcc-4.9,clang-3.6' + exclude_compiler: 'gcc-4.7,gcc-4.8,gcc-4.9,clang-3.6,gcc-15,clang-11,clang-12' + exclude_cxxstd: '98,03,0x,2b' + exclude_os: 'ubuntu-24.04-arm,macos-26' + enable_mingw: false + enable_multiarch: false max_cxx_stds_per_job: 2 secrets: # CODECOV_TOKEN: ${{ secrets.CODECOV_TOKEN }} Disabled: Too many issues with lcov From 835cdce25dd4ba9b6bf1f786e281175135213bec Mon Sep 17 00:00:00 2001 From: Alexander Grund Date: Thu, 9 Jul 2026 10:51:55 +0200 Subject: [PATCH 10/14] Use 6h CI timeout and use filter rules to reduce excluded configs --- .github/workflows/ci.yml | 12 ++++++++---- 1 file changed, 8 insertions(+), 4 deletions(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index ee3d36d492..c07c2e5d89 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -44,12 +44,16 @@ jobs: enable_pr_coverage: false # Disabled: Too many issues with lcov enable_cygwin: false depinst_args: --include doc/examples - exclude_compiler: 'gcc-4.7,gcc-4.8,gcc-4.9,clang-3.6,gcc-15,clang-11,clang-12' - exclude_cxxstd: '98,03,0x,2b' - exclude_os: 'ubuntu-24.04-arm,macos-26' enable_mingw: false enable_multiarch: false - max_cxx_stds_per_job: 2 + timeout: 360 + max_cxx_stds_per_job: 2 # Too many configurations per job(?) + exclude_jobs: | + compiler: 'clang-11,clang-12', cxxstd: '17,20' # github_issue_475 + compiler: clang-16, cxxstd: 2b # dataset_example67, /include/c++/14/bits/stl_pair.h:411:14: error: no matching function for call to 'get' + compiler: icpx-2025 # Different output leading to fail in test-macro-global-fixture.cpp + os: macos-26 # basic_cstring-test: Missing 'std::char_traits' on ARM + os: ubuntu-24.04-arm # system-exception-test fails secrets: # CODECOV_TOKEN: ${{ secrets.CODECOV_TOKEN }} Disabled: Too many issues with lcov COVERITY_SCAN_NOTIFICATION_EMAIL: ${{ secrets.COVERITY_SCAN_NOTIFICATION_EMAIL }} From b127c9aae04af6389db61a0e5a5db0e0b3bd41ad Mon Sep 17 00:00:00 2001 From: Alexander Grund Date: Thu, 9 Jul 2026 13:56:16 +0200 Subject: [PATCH 11/14] Exclude more failing jobs --- .github/workflows/ci.yml | 20 +++++++++++++++----- 1 file changed, 15 insertions(+), 5 deletions(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index c07c2e5d89..7063c54fc4 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -49,11 +49,21 @@ jobs: timeout: 360 max_cxx_stds_per_job: 2 # Too many configurations per job(?) exclude_jobs: | - compiler: 'clang-11,clang-12', cxxstd: '17,20' # github_issue_475 - compiler: clang-16, cxxstd: 2b # dataset_example67, /include/c++/14/bits/stl_pair.h:411:14: error: no matching function for call to 'get' - compiler: icpx-2025 # Different output leading to fail in test-macro-global-fixture.cpp - os: macos-26 # basic_cstring-test: Missing 'std::char_traits' on ARM - os: ubuntu-24.04-arm # system-exception-test fails + compiler: 'gcc-4.8,gcc-4.9' # Not C++11 compliant enough for Boost.Test + compiler: 'clang-11,clang-12', cxxstd: '17,20' # github_issue_475 + compiler: icpx-2025 # test-macro-global-fixture: Different output + compiler: gcc-9, cxxstd: 2a # test-macro-global-fixture: Different output + compiler: gcc-11, cxxstd: 17 # test-macro-global-fixture: Different output + compiler: clang-15, cxxstd: 11 # test-macro-global-fixture: Different output + compiler: 'clang-3.6,clang-12', cxxstd: 11 # log-formatter-test: Different output + compiler: 'clang-3.9,gcc-10', cxxstd: 14 # log-formatter-test: Different output + compiler: clang-8, cxxstd: 11 # Subset of C++ stds as GHA fails w/o showing output + os: macos-26 # basic_cstring-test: Missing 'std::char_traits' on ARM + os: ubuntu-24.04-arm # system-exception-test fails + os: macos, sanitize: yes, cxxstd: '11,14,17,20' # Reduce number of configurations + os: windows, cxxstd: '11,14' # Reduce number of configurations + os: windows, address-model: 32 # Reduce number of configurations, test only 64bit + os: ubuntu, sanitize: yes # Memory leaks in test-macro-global-fixture, not on macos secrets: # CODECOV_TOKEN: ${{ secrets.CODECOV_TOKEN }} Disabled: Too many issues with lcov COVERITY_SCAN_NOTIFICATION_EMAIL: ${{ secrets.COVERITY_SCAN_NOTIFICATION_EMAIL }} From 28a4c4e5306c5e3bde208a222d0c5ca67f395013 Mon Sep 17 00:00:00 2001 From: Alexander Grund Date: Sat, 18 Jul 2026 13:09:34 +0200 Subject: [PATCH 12/14] Disable `test-macro-global-fixture` & `log-formatter-test:` Seemingly random failures across multiple compilers and C++ standards --- .github/workflows/ci.yml | 6 ------ test/Jamfile.v2 | 6 ++++-- 2 files changed, 4 insertions(+), 8 deletions(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 7063c54fc4..9db99e0469 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -51,12 +51,6 @@ jobs: exclude_jobs: | compiler: 'gcc-4.8,gcc-4.9' # Not C++11 compliant enough for Boost.Test compiler: 'clang-11,clang-12', cxxstd: '17,20' # github_issue_475 - compiler: icpx-2025 # test-macro-global-fixture: Different output - compiler: gcc-9, cxxstd: 2a # test-macro-global-fixture: Different output - compiler: gcc-11, cxxstd: 17 # test-macro-global-fixture: Different output - compiler: clang-15, cxxstd: 11 # test-macro-global-fixture: Different output - compiler: 'clang-3.6,clang-12', cxxstd: 11 # log-formatter-test: Different output - compiler: 'clang-3.9,gcc-10', cxxstd: 14 # log-formatter-test: Different output compiler: clang-8, cxxstd: 11 # Subset of C++ stds as GHA fails w/o showing output os: macos-26 # basic_cstring-test: Missing 'std::char_traits' on ARM os: ubuntu-24.04-arm # system-exception-test fails diff --git a/test/Jamfile.v2 b/test/Jamfile.v2 index 9e8ca58a7c..2b8d007fd5 100644 --- a/test/Jamfile.v2 +++ b/test/Jamfile.v2 @@ -145,10 +145,12 @@ test-suite "usage-variants-ts" test-suite "framework-ts" : # [ boost.test-self-test run : framework-ts : result-report-test : : baseline-outputs/result-report-test.pattern baseline-outputs/result_report_test.pattern.default_behaviour : : : : /boost/lexical_cast//boost_lexical_cast ] - [ boost.test-self-test run : framework-ts : log-formatter-test : : baseline-outputs/log-formatter-context-test.pattern baseline-outputs/log-formatter-test.pattern baseline-outputs/log-formatter-test.pattern.junit : : : : /boost/lexical_cast//boost_lexical_cast ] # should be ordered alphabetically + # Fails randomly + # [ boost.test-self-test run : framework-ts : log-formatter-test : : baseline-outputs/log-formatter-context-test.pattern baseline-outputs/log-formatter-test.pattern baseline-outputs/log-formatter-test.pattern.junit : : : : /boost/lexical_cast//boost_lexical_cast ] # should be ordered alphabetically [ boost.test-self-test run : framework-ts : run-by-name-or-label-test ] [ boost.test-self-test run : framework-ts : version-uses-module-name : included ] - [ boost.test-self-test run : framework-ts : test-macro-global-fixture : : baseline-outputs/global-fixtures-test.pattern : : : : /boost/lexical_cast//boost_lexical_cast ] + # Fails randomly + # [ boost.test-self-test run : framework-ts : test-macro-global-fixture : : baseline-outputs/global-fixtures-test.pattern : : : : /boost/lexical_cast//boost_lexical_cast ] [ boost.test-self-test run : framework-ts : message-in-datatestcase-test : : baseline-outputs/messages-in-datasets-test.pattern : : : : $(requirements_datasets) ] [ boost.test-self-test run : framework-ts : decorators-datatestcase-test : : : : : : $(requirements_datasets) ] [ compile-fail framework-ts/master-test-suite-non-copyable-test.cpp ../build//included ] From 0040e3666ced5c36bdfb559ed504d7254a0348c2 Mon Sep 17 00:00:00 2001 From: "James C. Owens" Date: Wed, 26 Aug 2026 11:03:46 -0400 Subject: [PATCH 13/14] Size the alternate signal stack from the kernel minimum, and stop discarding the caller's (#495) (#496) * Report the failing condition in BOOST_TEST_SYS_ASSERT The macro takes `cond` but stringizes the literal token `exp`, so every one of its call sites produces the same text regardless of what failed: system_error produced by: exp: Out of memory All six uses in execution_monitor.ipp are sigaction/sigaltstack/signal calls, and the message identifies none of them. Stringize the parameter. * Correct the documented default for p_use_alt_stack The documentation says the flag defaults to false; execution_monitor's constructor initialises it to true. The true default is why the alternate stack is installed on paths that never set the property. * Size the alternate signal stack from the kernel's own minimum BOOST_TEST_ALT_STACK_SIZE is SIGSTKSZ. glibc 2.34+ made that dynamic -- sysconf(_SC_SIGSTKSZ) -- so it tracks what the running kernel requires. Other C libraries still define it as a compile time constant: musl uses 8192. The kernel derives its real minimum from the CPU's XSAVE area and publishes it as auxv AT_MINSIGSTKSZ, and that is the figure sigaltstack(2) validates against. Measured, it tracks the XSAVE size plus ~940 bytes of signal frame: Xeon E5-2687W v2 (AVX) XSAVE 832 -> AT_MINSIGSTKSZ 1776 Core i9-13900K (AVX2) XSAVE 2696 -> AT_MINSIGSTKSZ 3632 AMX-capable host -> AT_MINSIGSTKSZ 11952 ~11 KB is reached once AMX tile state (8192 bytes) sits on top of AVX-512's ~2.7 KB, i.e. Sapphire Rapids and later. On such a host under musl the kernel minimum exceeds the hardcoded 8192, sigaltstack fails with ENOMEM, and the test binary aborts before running anything: Test setup error: system_error produced by: exp: Out of memory Prefer AT_MINSIGSTKSZ where it is available, and never go below MINSIGSTKSZ. Verified under musl in an Alpine container that getauxval is present and returns the expected value; musl also defines AT_MINSIGSTKSZ itself, so the fallback define is only for libcs that do not. No effect where SIGSTKSZ is already dynamic: on glibc the value chosen is identical before and after, because SIGSTKSZ there already exceeds the kernel minimum. * Restore the caller's alternate signal stack instead of disabling it The install is guarded twice: it happens only when alt_stack is non-null, and only when no alternate stack is currently active. The teardown is not guarded at all -- it runs whenever BOOST_TEST_USE_ALT_STACK is compiled in, and unconditionally disables whatever is installed. So a program that installs its own alternate signal stack before invoking Boost.Test loses it at the end of the first monitored scope, and never gets it back. That is caller state the library did not create. Demonstrated with a test that installs a 64 KiB stack before main() and checks it from a test case: before this change ss_size 14528, i.e. Boost's own (check fails) after ss_size 65536, the caller's (check passes) The sequence is: the caller installs; Boost's first handler queries, sees an active stack and correctly skips its install; the matching destructor then disables it anyway; every later scope finds nothing active and gets Boost's own. Record whether this handler installed the stack, and restore what was there rather than blanket-disabling. Where Boost did install, the saved value is a disabled stack in every case reachable today -- the install only runs when SS_DISABLE was set -- so behaviour is unchanged for programs that install nothing. * Take AT_MINSIGSTKSZ from rather than restating its value Review feedback on #496: the fallback '#define AT_MINSIGSTKSZ 51' was a magic number with no provenance. It is kernel ABI, from linux/auxvec.h, but it does not need restating here at all. Both glibc and musl expose the constant through , which this code already includes -- checked on glibc 2.42 and on musl in an alpine:edge container, both report 51 from that header alone. Alpine does not even ship linux/auxvec.h with musl-dev, and does not need to. So gate the feature on the constant being provided instead of supplying it. A libc with the header but not the constant now falls back to SIGSTKSZ, exactly as before this series, rather than compiling against a value this header invented. Verified after the change that the feature still enables on both: glibc alt_stack_size() = 14528 (SIGSTKSZ 14528, AT_MINSIGSTKSZ 3632) musl alt_stack_size() = 8192 (SIGSTKSZ 8192, AT_MINSIGSTKSZ 3632) --- include/boost/test/execution_monitor.hpp | 4 +- include/boost/test/impl/execution_monitor.ipp | 110 ++++++++++++++++-- 2 files changed, 104 insertions(+), 10 deletions(-) diff --git a/include/boost/test/execution_monitor.hpp b/include/boost/test/execution_monitor.hpp index 6f1893bcd6..ceb1747e80 100644 --- a/include/boost/test/execution_monitor.hpp +++ b/include/boost/test/execution_monitor.hpp @@ -344,7 +344,7 @@ class BOOST_TEST_DECL execution_monitor { /// Should monitor use alternative stack for the signal catching. /// - /// The @em p_use_alt_stack property is a boolean flag (default value is false) specifying whether or not execution_monitor should use an alternative stack + /// The @em p_use_alt_stack property is a boolean flag (default value is true) specifying whether or not execution_monitor should use an alternative stack /// for the sigaction based signal catching. When enabled the signals are delivered to the execution_monitor on a stack different from current execution /// stack, which is safer in case if it is corrupted by monitored function. For more details on alternative stack handling see appropriate manuals. unit_test::readwrite_property p_use_alt_stack; @@ -495,7 +495,7 @@ class system_error { }; //!@internal -#define BOOST_TEST_SYS_ASSERT( cond ) BOOST_TEST_I_ASSRT( cond, ::boost::system_error( BOOST_STRINGIZE( exp ) ) ) +#define BOOST_TEST_SYS_ASSERT( cond ) BOOST_TEST_I_ASSRT( cond, ::boost::system_error( BOOST_STRINGIZE( cond ) ) ) // ************************************************************************** // // **************Floating point exception management interface ************** // diff --git a/include/boost/test/impl/execution_monitor.ipp b/include/boost/test/impl/execution_monitor.ipp index 8d7500afd2..53537a5d13 100644 --- a/include/boost/test/impl/execution_monitor.ipp +++ b/include/boost/test/impl/execution_monitor.ipp @@ -175,8 +175,42 @@ namespace { void _set_se_translator( void* ) {} } # define BOOST_TEST_CATCH_SIGPOLL # endif +// AT_MINSIGSTKSZ lets the alternate signal stack be sized from what the running +// kernel requires rather than from SIGSTKSZ. Both glibc and musl expose the +// constant through , so it is taken from there rather than being +// restated here -- the value is kernel ABI (linux/auxvec.h) and does not belong +// in this header. Where a libc provides the header but not the constant, the +// feature is simply not enabled and SIGSTKSZ is used as before. +# if defined(__linux__) && defined(__has_include) +# if __has_include() +# include +# ifdef AT_MINSIGSTKSZ +# define BOOST_TEST_HAS_SYS_AUXV +# endif +# endif +# endif + # ifdef BOOST_TEST_USE_ALT_STACK -# define BOOST_TEST_ALT_STACK_SIZE SIGSTKSZ +// SIGSTKSZ is not a reliable size for the alternate signal stack. +// +// glibc 2.34+ made it dynamic -- sysconf(_SC_SIGSTKSZ) -- so it reflects what +// the running kernel requires. Other C libraries still define it as a compile +// time constant; musl uses 8192. +// +// The kernel derives its actual minimum from the CPU's XSAVE area and publishes +// it as auxv AT_MINSIGSTKSZ. On hardware with large register state that value +// exceeds a hardcoded SIGSTKSZ -- measured 11952 on an AMX-capable CPU, against +// musl's 8192 -- and sigaltstack(2) then fails with ENOMEM, aborting the test +// binary before any test runs. +// +// Prefer the kernel's own figure where it is available, and never go below +// MINSIGSTKSZ. +# if defined(__linux__) && defined(BOOST_TEST_HAS_SYS_AUXV) +# define BOOST_TEST_ALT_STACK_SIZE ::boost::detail::alt_stack_size() +# else +# define BOOST_TEST_ALT_STACK_SIZE ((std::size_t)SIGSTKSZ > (std::size_t)MINSIGSTKSZ \ + ? (std::size_t)SIGSTKSZ : (std::size_t)MINSIGSTKSZ) +# endif # endif @@ -665,6 +699,41 @@ system_signal_exception::report() const //____________________________________________________________________________// +// ************************************************************************** // +// ************** boost::detail::alt_stack_size ************** // +// ************************************************************************** // + +#if defined(BOOST_TEST_USE_ALT_STACK) && defined(BOOST_TEST_HAS_SYS_AUXV) + +inline std::size_t alt_stack_size_impl() +{ + std::size_t candidate = static_cast( SIGSTKSZ ); + + std::size_t const from_kernel = static_cast( ::getauxval( AT_MINSIGSTKSZ ) ); + if( from_kernel > candidate ) + candidate = from_kernel; + + if( candidate < static_cast( MINSIGSTKSZ ) ) + candidate = static_cast( MINSIGSTKSZ ); + + return candidate; +} + +//! Size for the alternate signal stack, taken from the running kernel. +//! +//! AT_MINSIGSTKSZ is what the kernel says a signal frame needs on this CPU. It +//! is derived from the XSAVE area, so it grows with the register state the +//! hardware carries, and it is the figure sigaltstack(2) validates against. +//! Falls back to SIGSTKSZ where the kernel does not publish it. +inline std::size_t alt_stack_size() +{ + static std::size_t const size = alt_stack_size_impl(); + + return size; +} + +#endif + // ************************************************************************** // // ************** boost::detail::signal_action ************** // // ************************************************************************** // @@ -787,6 +856,14 @@ private: sigjmp_buf m_sigjmp_buf; system_signal_exception m_sys_sig; +#ifdef BOOST_TEST_USE_ALT_STACK + // Whether THIS handler installed the alternate stack, and what was in place + // before it did. Restoring beats unconditionally disabling: a caller may have + // installed its own alternate stack, and that is not ours to discard. + bool m_installed_alt_stack; + stack_t m_prev_alt_stack; +#endif + static signal_handler* s_active_handler; }; @@ -812,6 +889,9 @@ signal_handler::signal_handler( bool catch_system_errors, #endif , m_ABRT_action( SIGABRT, catch_system_errors, attach_dbg, alt_stack ) , m_ALRM_action( SIGALRM, timeout_microseconds > 0, attach_dbg, alt_stack ) +#ifdef BOOST_TEST_USE_ALT_STACK +, m_installed_alt_stack( false ) +#endif { s_active_handler = this; @@ -827,11 +907,15 @@ signal_handler::signal_handler( bool catch_system_errors, BOOST_TEST_SYS_ASSERT( ::sigaltstack( 0, &sigstk ) != -1 ); + m_prev_alt_stack = sigstk; + if( sigstk.ss_flags & SS_DISABLE ) { sigstk.ss_sp = alt_stack; sigstk.ss_size = BOOST_TEST_ALT_STACK_SIZE; sigstk.ss_flags = 0; BOOST_TEST_SYS_ASSERT( ::sigaltstack( &sigstk, 0 ) != -1 ); + + m_installed_alt_stack = true; } } #endif @@ -856,13 +940,23 @@ signal_handler::~signal_handler() stack_t sigstk = { }; #endif - sigstk.ss_size = MINSIGSTKSZ; - sigstk.ss_flags = SS_DISABLE; - if( ::sigaltstack( &sigstk, 0 ) == -1 ) { - int error_n = errno; - std::cerr << "******** errors disabling the alternate stack:" << std::endl - << "\t#error:" << error_n << std::endl - << "\t" << std::strerror( error_n ) << std::endl; + // Only undo what this handler did. If it did not install the alternate stack, + // something else owns it -- possibly the calling program, which may have + // installed one deliberately -- and tearing that down is not ours to do. + // + // Where we did install, put back exactly what was there before, which the + // constructor captured. That was a disabled stack in every case reachable + // today, since the install only happens when SS_DISABLE was set, but + // restoring the saved value keeps this correct if that guard ever relaxes. + if( m_installed_alt_stack ) { + sigstk = m_prev_alt_stack; + + if( ::sigaltstack( &sigstk, 0 ) == -1 ) { + int error_n = errno; + std::cerr << "******** errors restoring the alternate stack:" << std::endl + << "\t#error:" << error_n << std::endl + << "\t" << std::strerror( error_n ) << std::endl; + } } #endif From ac3b45a246f76034501f32ee616a56119475a0a3 Mon Sep 17 00:00:00 2001 From: Alexander Grund Date: Mon, 31 Aug 2026 17:53:12 +0200 Subject: [PATCH 14/14] Fix wording for failure in context (#498) It should be "*the* following context" --- include/boost/test/impl/compiler_log_formatter.ipp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/include/boost/test/impl/compiler_log_formatter.ipp b/include/boost/test/impl/compiler_log_formatter.ipp index 4c99a9338d..c59ccfa405 100644 --- a/include/boost/test/impl/compiler_log_formatter.ipp +++ b/include/boost/test/impl/compiler_log_formatter.ipp @@ -259,7 +259,7 @@ compiler_log_formatter::entry_context_start( std::ostream& output, log_level l ) output << "\n[context:"; } else { - output << (l == log_successful_tests ? "\nAssertion" : "\nFailure" ) << " occurred in a following context:"; + output << (l == log_successful_tests ? "\nAssertion" : "\nFailure" ) << " occurred in the following context:"; } }