From 1a57dafd043814a29c98d0b8c79cdc9a62c7acf5 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Anarthal=20=28Rub=C3=A9n=20P=C3=A9rez=29?= <34971811+anarthal@users.noreply.github.com> Date: Thu, 30 Apr 2026 13:04:57 +0200 Subject: [PATCH 01/14] Removes the standalone CMake workflow (#409) All integration testing now happens via the superproject, gated by BOOST_REDIS_INTEGRATION_TESTS Added namespacing to CMake functions and targets to avoid collisions Increases the maximum CMake supported version to 4.2 --- .github/workflows/ci.yml | 38 ++----- CMakeLists.txt | 162 +++++++---------------------- example/CMakeLists.txt | 55 +++++----- test/CMakeLists.txt | 132 ++++++++++------------- test/test_sentinel_resolve_fsm.cpp | 17 ++- tools/ci.py | 54 ++-------- 6 files changed, 150 insertions(+), 308 deletions(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 14d5b421..f165eb16 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -39,29 +39,17 @@ jobs: python3 tools/ci.py build-b2-distro \ --toolset ${{ matrix.toolset }} - - name: Build a Boost distribution using CMake + # No Redis server is available on this job, so integration tests are skipped. + # Unit tests are built and run via the CMake superproject build. + - name: Build a Boost distribution and run the tests using CMake run: | python3 tools/ci.py build-cmake-distro \ --build-type ${{ matrix.build-type }} \ --cxxstd ${{ matrix.cxxstd }} \ --toolset ${{ matrix.toolset }} \ --generator "${{ matrix.generator }}" \ - --build-shared-libs ${{ matrix.build-shared-libs }} - - - name: Build the project tests - run: | - python3 tools/ci.py build-cmake-standalone-tests \ - --build-type ${{ matrix.build-type }} \ - --cxxstd ${{ matrix.cxxstd }} \ - --toolset ${{ matrix.toolset }} \ - --generator "${{ matrix.generator }}" \ - --build-shared-libs ${{ matrix.build-shared-libs }} - - # # TODO: re-enable this when a Redis server is available for this job - # - name: Run the project tests - # run: | - # python3 tools/ci.py run-cmake-standalone-tests \ - # --build-type ${{ matrix.build-type }} + --build-shared-libs ${{ matrix.build-shared-libs }} \ + --integration-tests 0 - name: Run add_subdirectory tests run: | @@ -263,25 +251,15 @@ jobs: docker exec builder /boost-redis/tools/ci.py build-b2-distro \ --toolset ${{ matrix.toolset }} - - name: Build a Boost distribution using CMake + # The CMake superproject build also drives the project tests, including + # integration tests against the Redis server set up above. + - name: Build a Boost distribution and run the tests using CMake run: | docker exec builder /boost-redis/tools/ci.py build-cmake-distro \ --build-type ${{ matrix.build-type }} \ --cxxstd ${{ matrix.cxxstd }} \ --toolset ${{ matrix.toolset }} - - name: Build the project tests - run: | - docker exec builder /boost-redis/tools/ci.py build-cmake-standalone-tests \ - --build-type ${{ matrix.build-type }} \ - --cxxstd ${{ matrix.cxxstd }} \ - --toolset ${{ matrix.toolset }} - - - name: Run the project tests - run: | - docker exec builder /boost-redis/tools/ci.py run-cmake-standalone-tests \ - --build-type ${{ matrix.build-type }} - - name: Run add_subdirectory tests run: | docker exec builder /boost-redis/tools/ci.py run-cmake-add-subdirectory-tests \ diff --git a/CMakeLists.txt b/CMakeLists.txt index 967f1ccd..e16073ac 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -1,13 +1,4 @@ -cmake_minimum_required(VERSION 3.8...3.20) - -# determine whether it's main/root project -# or being built under another project. -if (NOT DEFINED BOOST_REDIS_MAIN_PROJECT) - set(BOOST_REDIS_MAIN_PROJECT OFF) - if (CMAKE_CURRENT_SOURCE_DIR STREQUAL CMAKE_SOURCE_DIR) - set(BOOST_REDIS_MAIN_PROJECT ON) - endif() -endif() +cmake_minimum_required(VERSION 3.8...4.2) project(boost_redis VERSION "${BOOST_SUPERPROJECT_VERSION}" LANGUAGES CXX) @@ -18,125 +9,52 @@ target_include_directories(boost_redis INTERFACE include) target_compile_features(boost_redis INTERFACE cxx_std_17) # Dependencies -if (BOOST_REDIS_MAIN_PROJECT) - # TODO: Understand why we have to list all dependencies below - # instead of - #set(BOOST_INCLUDE_LIBRARIES redis) - #set(BOOST_EXCLUDE_LIBRARIES redis) - #add_subdirectory(../.. boostorg/boost EXCLUDE_FROM_ALL) - - set(deps - system - assert - config - throw_exception - asio - variant2 - mp11 - winapi - predef - align - context - core - static_assert - pool - date_time - smart_ptr - exception - integer - move - type_traits - algorithm - utility - io - lexical_cast - numeric/conversion - mpl - range - tokenizer - tuple - array - bind - concept_check - function - iterator - regex - unordered - preprocessor - container - conversion - container_hash - detail - optional - function_types - fusion - intrusive - describe - typeof - functional - test - json - endian - compat - ) - - foreach(dep IN LISTS deps) - add_subdirectory(../${dep} boostorg/${dep}) - endforeach() - - find_package(Threads REQUIRED) - find_package(OpenSSL REQUIRED) - target_link_libraries(boost_redis - INTERFACE - Boost::system - Boost::asio - Threads::Threads - OpenSSL::Crypto - OpenSSL::SSL - ) -else() - # If we're in the superproject or called from add_subdirectory, - # Boost dependencies should be already available. - # If other dependencies are not found, we bail out - find_package(Threads) - if(NOT Threads_FOUND) - message(STATUS "Boost.Redis has been disabled, because the required package Threads hasn't been found") - return() - endif() - find_package(OpenSSL) - if(NOT OpenSSL_FOUND) - message(STATUS "Boost.Redis has been disabled, because the required package OpenSSL hasn't been found") - return() - endif() - - # This is generated by boostdep - target_link_libraries(boost_redis - INTERFACE - Boost::asio - Boost::assert - Boost::core - Boost::mp11 - Boost::system - Boost::throw_exception - Threads::Threads - OpenSSL::Crypto - OpenSSL::SSL - ) +# Boost dependencies should be already available. +# If other dependencies are not found, we bail out +find_package(Threads) +if(NOT Threads_FOUND) + message(STATUS "Boost.Redis has been disabled, because the required package Threads hasn't been found") + return() endif() - -# Enable testing. If we're being called from the superproject, this has already been done -if (BOOST_REDIS_MAIN_PROJECT) - include(CTest) +find_package(OpenSSL) +if(NOT OpenSSL_FOUND) + message(STATUS "Boost.Redis has been disabled, because the required package OpenSSL hasn't been found") + return() endif() -# Most tests require a running Redis server, so we only run them if we're the main project -if(BOOST_REDIS_MAIN_PROJECT AND BUILD_TESTING) +# This is generated by boostdep +target_link_libraries(boost_redis + INTERFACE + Boost::asio + Boost::assert + Boost::core + Boost::mp11 + Boost::system + Boost::throw_exception + Threads::Threads + OpenSSL::Crypto + OpenSSL::SSL +) + +# Don't run integration testing unless explicitly requested, since these require a running server +option(BOOST_REDIS_INTEGRATION_TESTS OFF "Whether to build and run integration tests or not") +mark_as_advanced(BOOST_REDIS_INTEGRATION_TESTS) + +# Examples and tests +if(BUILD_TESTING) + # Custom target tests; required by the Boost superproject + if(NOT TARGET tests) + add_custom_target(tests) + endif() + # Tests and common utilities add_subdirectory(test) # Benchmarks. Build them with tests to prevent code rotting add_subdirectory(benchmarks) - # Examples - add_subdirectory(example) + # Examples. All of them require a real server running + if (BOOST_REDIS_INTEGRATION_TESTS) + add_subdirectory(example) + endif() endif() diff --git a/example/CMakeLists.txt b/example/CMakeLists.txt index 55fde857..eafbf7bc 100644 --- a/example/CMakeLists.txt +++ b/example/CMakeLists.txt @@ -1,54 +1,57 @@ -add_library(examples_main STATIC main.cpp) -target_link_libraries(examples_main PRIVATE boost_redis_project_options) - -function(make_example EXAMPLE_NAME) - add_executable(${EXAMPLE_NAME} ${EXAMPLE_NAME}.cpp) - target_link_libraries(${EXAMPLE_NAME} PRIVATE boost_redis_src) - target_link_libraries(${EXAMPLE_NAME} PRIVATE boost_redis_project_options) +add_library(boost_redis_examples_main STATIC main.cpp) +target_link_libraries(boost_redis_examples_main PRIVATE boost_redis_project_options) + +function(boost_redis_make_example EXAMPLE_NAME) + set(EXE_NAME "boost_redis_${EXAMPLE_NAME}") + add_executable(${EXE_NAME} ${EXAMPLE_NAME}.cpp) + target_link_libraries(${EXE_NAME} PRIVATE boost_redis_src) + target_link_libraries(${EXE_NAME} PRIVATE boost_redis_project_options) if (ARGN) - target_link_libraries(${EXAMPLE_NAME} PRIVATE ${ARGN}) + target_link_libraries(${EXE_NAME} PRIVATE ${ARGN}) endif() endfunction() -function(make_testable_example EXAMPLE_NAME) - make_example(${EXAMPLE_NAME} ${ARGN}) - add_test(${EXAMPLE_NAME} ${EXAMPLE_NAME} $ENV{BOOST_REDIS_TEST_SERVER} 6379) +function(boost_redis_make_testable_example EXAMPLE_NAME) + set(EXE_NAME "boost_redis_${EXAMPLE_NAME}") + boost_redis_make_example(${EXAMPLE_NAME} ${ARGN}) + add_test(NAME ${EXE_NAME} COMMAND ${EXE_NAME} $ENV{BOOST_REDIS_TEST_SERVER} 6379) + add_dependencies(tests ${EXE_NAME}) endfunction() -make_testable_example(cpp17_intro) -make_testable_example(cpp17_intro_sync) +boost_redis_make_testable_example(cpp17_intro) +boost_redis_make_testable_example(cpp17_intro_sync) -make_testable_example(cpp20_intro examples_main) -make_testable_example(cpp20_containers examples_main) -make_testable_example(cpp20_json examples_main Boost::json Boost::container_hash) -make_testable_example(cpp20_unix_sockets examples_main) -make_testable_example(cpp20_timeouts examples_main) -make_testable_example(cpp20_sentinel examples_main) +boost_redis_make_testable_example(cpp20_intro boost_redis_examples_main) +boost_redis_make_testable_example(cpp20_containers boost_redis_examples_main) +boost_redis_make_testable_example(cpp20_json boost_redis_examples_main Boost::json Boost::container_hash) +boost_redis_make_testable_example(cpp20_unix_sockets boost_redis_examples_main) +boost_redis_make_testable_example(cpp20_timeouts boost_redis_examples_main) +boost_redis_make_testable_example(cpp20_sentinel boost_redis_examples_main) -make_example(cpp20_subscriber examples_main) -make_example(cpp20_streams examples_main) -make_example(cpp20_echo_server examples_main) -make_example(cpp20_intro_tls examples_main) +boost_redis_make_example(cpp20_subscriber boost_redis_examples_main) +boost_redis_make_example(cpp20_streams boost_redis_examples_main) +boost_redis_make_example(cpp20_echo_server boost_redis_examples_main) +boost_redis_make_example(cpp20_intro_tls boost_redis_examples_main) # We test the protobuf example only on gcc. if (CMAKE_CXX_COMPILER_ID STREQUAL "GNU") find_package(Protobuf) if (Protobuf_FOUND) protobuf_generate_cpp(PROTO_SRCS PROTO_HDRS person.proto) - make_testable_example(cpp20_protobuf examples_main ${Protobuf_LIBRARIES}) + boost_redis_make_testable_example(cpp20_protobuf boost_redis_examples_main ${Protobuf_LIBRARIES}) target_sources(cpp20_protobuf PUBLIC ${PROTO_SRCS} ${PROTO_HDRS}) target_include_directories(cpp20_protobuf PUBLIC ${Protobuf_INCLUDE_DIRS} ${CMAKE_CURRENT_BINARY_DIR}) endif() endif() if (NOT MSVC) - make_example(cpp20_chat_room examples_main) + boost_redis_make_example(cpp20_chat_room boost_redis_examples_main) endif() # We build and test the spdlog integration example only if the library is found find_package(spdlog) if (spdlog_FOUND) - make_testable_example(cpp17_spdlog spdlog::spdlog) + boost_redis_make_testable_example(cpp17_spdlog spdlog::spdlog) else() message(STATUS "Skipping the spdlog example because the spdlog package couldn't be found") endif() diff --git a/test/CMakeLists.txt b/test/CMakeLists.txt index aabf8b7b..75c3789d 100644 --- a/test/CMakeLists.txt +++ b/test/CMakeLists.txt @@ -15,92 +15,68 @@ target_link_libraries(boost_redis_src PRIVATE boost_redis_project_options) # Test utils add_library(boost_redis_tests_common STATIC common.cpp sansio_utils.cpp) -target_link_libraries(boost_redis_tests_common PRIVATE boost_redis_project_options) +target_link_libraries(boost_redis_tests_common PUBLIC boost_redis_project_options boost_redis_src) +target_compile_definitions(boost_redis_tests_common INTERFACE BOOST_ALLOW_DEPRECATED=1) # we need to still test deprecated fns -macro(make_test TEST_NAME) +function(boost_redis_make_test TEST_NAME) set(EXE_NAME "boost_redis_${TEST_NAME}") add_executable(${EXE_NAME} ${TEST_NAME}.cpp) target_link_libraries(${EXE_NAME} PRIVATE - boost_redis_src boost_redis_tests_common - boost_redis_project_options Boost::unit_test_framework ) - target_compile_definitions(${EXE_NAME} PRIVATE BOOST_ALLOW_DEPRECATED=1) # we need to still test deprecated fns - add_test(${EXE_NAME} ${EXE_NAME}) -endmacro() + add_test(NAME ${EXE_NAME} COMMAND ${EXE_NAME}) + add_dependencies(tests ${EXE_NAME}) +endfunction() # Unit tests -make_test(test_low_level) -make_test(test_request) -make_test(test_serialization) -make_test(test_low_level_sync_sans_io) -make_test(test_any_adapter) -make_test(test_log_to_file) -make_test(test_conn_logging) -make_test(test_exec_fsm) -make_test(test_exec_one_fsm) -make_test(test_writer_fsm) -make_test(test_reader_fsm) -make_test(test_connect_fsm) -make_test(test_sentinel_resolve_fsm) -make_test(test_receive_fsm) -make_test(test_run_fsm) -make_test(test_compose_setup_request) -make_test(test_setup_adapter) -make_test(test_multiplexer) -make_test(test_parse_sentinel_response) -make_test(test_update_sentinel_list) -make_test(test_flat_tree) -make_test(test_generic_flat_response) -make_test(test_read_buffer) -make_test(test_subscription_tracker) -make_test(test_push_parser) +boost_redis_make_test(test_low_level) +boost_redis_make_test(test_request) +boost_redis_make_test(test_serialization) +boost_redis_make_test(test_low_level_sync_sans_io) +boost_redis_make_test(test_any_adapter) +boost_redis_make_test(test_log_to_file) +boost_redis_make_test(test_conn_logging) +boost_redis_make_test(test_exec_fsm) +boost_redis_make_test(test_exec_one_fsm) +boost_redis_make_test(test_writer_fsm) +boost_redis_make_test(test_reader_fsm) +boost_redis_make_test(test_connect_fsm) +boost_redis_make_test(test_sentinel_resolve_fsm) +boost_redis_make_test(test_receive_fsm) +boost_redis_make_test(test_run_fsm) +boost_redis_make_test(test_compose_setup_request) +boost_redis_make_test(test_setup_adapter) +boost_redis_make_test(test_multiplexer) +boost_redis_make_test(test_parse_sentinel_response) +boost_redis_make_test(test_update_sentinel_list) +boost_redis_make_test(test_flat_tree) +boost_redis_make_test(test_generic_flat_response) +boost_redis_make_test(test_read_buffer) +boost_redis_make_test(test_subscription_tracker) +boost_redis_make_test(test_push_parser) # Tests that require a real Redis server -make_test(test_conn_quit) -make_test(test_conn_exec_retry) -make_test(test_conn_exec_error) -make_test(test_run) -make_test(test_conn_run_cancel) -make_test(test_conn_check_health) -make_test(test_conn_exec) -make_test(test_conn_push) -make_test(test_conn_push2) -make_test(test_conn_monitor) -make_test(test_conn_reconnect) -make_test(test_conn_exec_cancel) -make_test(test_conn_echo_stress) -make_test(test_conn_move) -make_test(test_conn_setup) -make_test(test_issue_50) -make_test(test_conversions) -make_test(test_conn_tls) -make_test(test_unix_sockets) -make_test(test_conn_cancel_after) -make_test(test_conn_sentinel) - -# Coverage -set( - COVERAGE_TRACE_COMMAND - lcov --capture - -output-file "${PROJECT_BINARY_DIR}/coverage.info" - --directory "${PROJECT_BINARY_DIR}" - --include "${PROJECT_SOURCE_DIR}/include/*" -) - -set( - COVERAGE_HTML_COMMAND - genhtml --legend -f -q - "${PROJECT_BINARY_DIR}/coverage.info" - --prefix "${PROJECT_SOURCE_DIR}" - --output-directory "${PROJECT_BINARY_DIR}/coverage_html" -) - -add_custom_target( - coverage - COMMAND ${COVERAGE_TRACE_COMMAND} - COMMAND ${COVERAGE_HTML_COMMAND} - COMMENT "Generating coverage report" - VERBATIM -) +if (BOOST_REDIS_INTEGRATION_TESTS) + boost_redis_make_test(test_conn_quit) + boost_redis_make_test(test_conn_exec_retry) + boost_redis_make_test(test_conn_exec_error) + boost_redis_make_test(test_run) + boost_redis_make_test(test_conn_run_cancel) + boost_redis_make_test(test_conn_check_health) + boost_redis_make_test(test_conn_exec) + boost_redis_make_test(test_conn_push) + boost_redis_make_test(test_conn_push2) + boost_redis_make_test(test_conn_monitor) + boost_redis_make_test(test_conn_reconnect) + boost_redis_make_test(test_conn_exec_cancel) + boost_redis_make_test(test_conn_echo_stress) + boost_redis_make_test(test_conn_move) + boost_redis_make_test(test_conn_setup) + boost_redis_make_test(test_issue_50) + boost_redis_make_test(test_conversions) + boost_redis_make_test(test_conn_tls) + boost_redis_make_test(test_unix_sockets) + boost_redis_make_test(test_conn_cancel_after) + boost_redis_make_test(test_conn_sentinel) +endif() \ No newline at end of file diff --git a/test/test_sentinel_resolve_fsm.cpp b/test/test_sentinel_resolve_fsm.cpp index d3046819..507b2f4a 100644 --- a/test/test_sentinel_resolve_fsm.cpp +++ b/test/test_sentinel_resolve_fsm.cpp @@ -20,6 +20,7 @@ #include "sansio_utils.hpp" #include +#include using namespace boost::redis; namespace asio = boost::asio; @@ -184,16 +185,24 @@ void test_success_replica() act = fix.fsm.resume(fix.st, error_code(), cancellation_type_t::none); BOOST_TEST_EQ(act, error_code()); - // The address of one of the replicas is stored - BOOST_TEST_EQ(fix.st.cfg.addr, (address{"replica.two", "6379"})); + // The address of one of the replicas is stored. + // Which one is implementation-defined. + BOOST_TEST_WITH(fix.st.cfg.addr.host, 0, [](std::string_view value, int) { + return value == "replica.two" || value == "replica.thr"; + }); + BOOST_TEST_EQ(fix.st.cfg.addr.port, "6379"); - // Logs + // Logs. The final message depends on the chosen replica + const char* const + replica_msg = fix.st.cfg.addr.host == "replica.two" + ? "Sentinel at host1:1000 resolved the server address to replica.two:6379" + : "Sentinel at host1:1000 resolved the server address to replica.thr:6379"; fix.check_log({ // clang-format off {logger::level::info, "Trying to resolve the address of a replica of master 'mymaster' using Sentinel" }, {logger::level::debug, "Trying to contact Sentinel at host1:1000" }, {logger::level::debug, "Executing Sentinel request at host1:1000" }, - {logger::level::info, "Sentinel at host1:1000 resolved the server address to replica.two:6379" }, + {logger::level::info, replica_msg }, // clang-format on }); } diff --git a/tools/ci.py b/tools/ci.py index 6e1ba13e..edd6ace4 100755 --- a/tools/ci.py +++ b/tools/ci.py @@ -143,12 +143,15 @@ def _build_b2_distro( # Builds a Boost distribution using cmake, and places it into _cmake_distro. # It includes only our library and any dependency. +# When integration_tests is True, tests requiring a live Redis server are also +# built and run; otherwise only the unit tests are. def _build_cmake_distro( generator: str, build_type: str, cxxstd: str, toolset: str, - build_shared_libs: bool = False + build_shared_libs: bool = False, + integration_tests: bool = False ): _mkdir_and_cd(_boost_root.joinpath('__build_cmake_test__')) _run([ @@ -162,7 +165,7 @@ def _build_cmake_distro( '-DBOOST_INCLUDE_LIBRARIES=redis', '-DBUILD_SHARED_LIBS={}'.format(_cmake_bool(build_shared_libs)), '-DCMAKE_INSTALL_PREFIX={}'.format(_cmake_distro), - '-DBUILD_TESTING=ON', + '-DBOOST_REDIS_INTEGRATION_TESTS={}'.format(_cmake_bool(integration_tests)), '-DBoost_VERBOSE=ON', '-DCMAKE_INSTALL_MESSAGE=NEVER', '..' @@ -172,40 +175,6 @@ def _build_cmake_distro( _run(['cmake', '--build', '.', '--target', 'install', '--config', build_type]) -# Builds our CMake tests as a standalone project -# (BOOST_REDIS_MAIN_PROJECT is ON) and we find_package Boost. -# This ensures that all our test suite is run. -def _build_cmake_standalone_tests( - generator: str, - build_type: str, - cxxstd: str, - toolset: str, - build_shared_libs: bool = False -): - _mkdir_and_cd(_boost_root.joinpath('libs', 'redis', '__build_standalone__')) - _run([ - 'cmake', - '-DBUILD_TESTING=ON', - '-DCMAKE_CXX_COMPILER={}'.format(_compiler_from_toolset(toolset)), - '-DCMAKE_PREFIX_PATH={}'.format(_b2_distro), - '-DCMAKE_BUILD_TYPE={}'.format(build_type), - '-DBUILD_SHARED_LIBS={}'.format(_cmake_bool(build_shared_libs)), - '-DCMAKE_CXX_STANDARD={}'.format(cxxstd), - '-G', - generator, - '..' - ]) - _run(['cmake', '--build', '.']) - - -# Runs the tests built in the previous step -def _run_cmake_standalone_tests( - build_type: str -): - os.chdir(str(_boost_root.joinpath('libs', 'redis', '__build_standalone__'))) - _run(['ctest', '--output-on-failure', '--build-config', build_type, '--no-tests=error']) - - # Tests that the library can be consumed using add_subdirectory() def _run_cmake_add_subdirectory_tests( generator: str, @@ -321,20 +290,9 @@ def main(): subp.add_argument('--cxxstd', default='20') subp.add_argument('--toolset', default='gcc') subp.add_argument('--build-shared-libs', type=_str2bool, default=False) + subp.add_argument('--integration-tests', type=_str2bool, default=True) subp.set_defaults(func=_build_cmake_distro) - subp = subparsers.add_parser('build-cmake-standalone-tests') - subp.add_argument('--generator', default='Unix Makefiles') - subp.add_argument('--build-type', default='Debug') - subp.add_argument('--cxxstd', default='20') - subp.add_argument('--toolset', default='gcc') - subp.add_argument('--build-shared-libs', type=_str2bool, default=False) - subp.set_defaults(func=_build_cmake_standalone_tests) - - subp = subparsers.add_parser('run-cmake-standalone-tests') - subp.add_argument('--build-type', default='Debug') - subp.set_defaults(func=_run_cmake_standalone_tests) - subp = subparsers.add_parser('run-cmake-add-subdirectory-tests') subp.add_argument('--generator', default='Unix Makefiles') subp.add_argument('--build-type', default='Debug') From 13c4b82e477e9e076094ecc35163038984dff8c6 Mon Sep 17 00:00:00 2001 From: Sam Darwin Date: Thu, 30 Apr 2026 05:06:23 -0600 Subject: [PATCH 02/14] docs: retry ui-bundle downloads (#401) --- doc/package-lock.json | 7 +++++++ doc/package.json | 1 + doc/redis-playbook.yml | 1 + 3 files changed, 9 insertions(+) diff --git a/doc/package-lock.json b/doc/package-lock.json index e4d51631..4f8ee17a 100644 --- a/doc/package-lock.json +++ b/doc/package-lock.json @@ -5,6 +5,7 @@ "packages": { "": { "dependencies": { + "@cppalliance/antora-downloads-extension": "^0.0.2", "@cppalliance/antora-cpp-reference-extension": "^0.1.0", "antora": "^3.1.10" } @@ -325,6 +326,12 @@ "semver": "^7.7.3" } }, + "node_modules/@cppalliance/antora-downloads-extension": { + "version": "0.0.2", + "resolved": "https://registry.npmjs.org/@cppalliance/antora-downloads-extension/-/antora-downloads-extension-0.0.2.tgz", + "integrity": "sha512-2wXahlvRz9J75ZSfzDeP4XpIZiqIm+w/YjmCWJxFPp6oWgP7e8f6ps7HqdtHNGxnK5mG38OjiCFdHjmHYfgbDA==", + "license": "BSL-1.0" + }, "node_modules/@iarna/toml": { "version": "2.2.5", "resolved": "https://registry.npmjs.org/@iarna/toml/-/toml-2.2.5.tgz", diff --git a/doc/package.json b/doc/package.json index 4384079f..bb5bd703 100644 --- a/doc/package.json +++ b/doc/package.json @@ -1,5 +1,6 @@ { "dependencies": { + "@cppalliance/antora-downloads-extension": "^0.0.2", "@cppalliance/antora-cpp-reference-extension": "^0.1.0", "antora": "^3.1.10" } diff --git a/doc/redis-playbook.yml b/doc/redis-playbook.yml index 54a901e8..d3592a29 100644 --- a/doc/redis-playbook.yml +++ b/doc/redis-playbook.yml @@ -20,6 +20,7 @@ antora: tag: 'develop' variable: 'BOOST_SRC_DIR' system-env: 'BOOST_SRC_DIR' + - require: '@cppalliance/antora-downloads-extension' asciidoc: attributes: From 3d212642b3d7fa60645b49d6b71ed65aa24dd1ed Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Anarthal=20=28Rub=C3=A9n=20P=C3=A9rez=29?= <34971811+anarthal@users.noreply.github.com> Date: Fri, 1 May 2026 17:11:25 +0200 Subject: [PATCH 03/14] Removes -Werror from CMake tests (#412) --- test/CMakeLists.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/test/CMakeLists.txt b/test/CMakeLists.txt index 75c3789d..3c40b8b7 100644 --- a/test/CMakeLists.txt +++ b/test/CMakeLists.txt @@ -7,7 +7,7 @@ if (MSVC) target_compile_options(boost_redis_project_options INTERFACE /bigobj /W4 /WX /wd4459) target_compile_definitions(boost_redis_project_options INTERFACE _WIN32_WINNT=0x0601 _CRT_SECURE_NO_WARNINGS=1) elseif(CMAKE_CXX_COMPILER_ID STREQUAL "Clang" OR CMAKE_CXX_COMPILER_ID STREQUAL "GNU") - target_compile_options(boost_redis_project_options INTERFACE -Wall -Wextra -Werror) + target_compile_options(boost_redis_project_options INTERFACE -Wall -Wextra) endif() add_library(boost_redis_src STATIC boost_redis.cpp) From 8a8fc18726ba10ef03e26748e0affb27b125a4da Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Anarthal=20=28Rub=C3=A9n=20P=C3=A9rez=29?= <34971811+anarthal@users.noreply.github.com> Date: Sun, 3 May 2026 18:02:06 +0200 Subject: [PATCH 04/14] Guards benchmarks behind BOOST_REDIS_INTEGRATION_TESTS (#411) Namespaces CMake target names in benchmarks Guards benchmarks in the main CMake by BOOST_REDIS_INTEGRATION_TESTS. They are only built to prevent code rotting, and shouldn't be built by superproject builds except in our CIs --- CMakeLists.txt | 11 ++++++----- benchmarks/CMakeLists.txt | 25 ++++++++----------------- 2 files changed, 14 insertions(+), 22 deletions(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index e16073ac..6db4a672 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -49,12 +49,13 @@ if(BUILD_TESTING) # Tests and common utilities add_subdirectory(test) - - # Benchmarks. Build them with tests to prevent code rotting - add_subdirectory(benchmarks) - - # Examples. All of them require a real server running + if (BOOST_REDIS_INTEGRATION_TESTS) + # Benchmarks. Build them with tests to prevent code rotting. + # Guarded to prevent them from building in normal builds + add_subdirectory(benchmarks) + + # Examples. All of them require a real server running add_subdirectory(example) endif() endif() diff --git a/benchmarks/CMakeLists.txt b/benchmarks/CMakeLists.txt index e40a7dae..d3f35cbc 100644 --- a/benchmarks/CMakeLists.txt +++ b/benchmarks/CMakeLists.txt @@ -1,20 +1,11 @@ -add_library(benchmarks_options INTERFACE) -target_link_libraries(benchmarks_options INTERFACE boost_redis_src) -target_link_libraries(benchmarks_options INTERFACE boost_redis_project_options) -target_compile_features(benchmarks_options INTERFACE cxx_std_20) +add_library(boost_redis_benchmarks_options INTERFACE) +target_link_libraries(boost_redis_benchmarks_options INTERFACE boost_redis_src) +target_link_libraries(boost_redis_benchmarks_options INTERFACE boost_redis_project_options) +target_compile_features(boost_redis_benchmarks_options INTERFACE cxx_std_20) -add_executable(echo_server_client cpp/asio/echo_server_client.cpp) -target_link_libraries(echo_server_client PRIVATE benchmarks_options) +add_executable(boost_redis_echo_server_client cpp/asio/echo_server_client.cpp) +target_link_libraries(boost_redis_echo_server_client PRIVATE boost_redis_benchmarks_options) -add_executable(echo_server_direct cpp/asio/echo_server_direct.cpp) -target_link_libraries(echo_server_direct PRIVATE benchmarks_options) - -# TODO -#======================================================================= - -#.PHONY: bench -#bench: -# pdflatex --jobname=echo-f0 benchmarks/benchmarks.tex -# pdflatex --jobname=echo-f1 benchmarks/benchmarks.tex -# pdftoppm {input.pdf} {output.file} -png \ No newline at end of file +add_executable(boost_redis_echo_server_direct cpp/asio/echo_server_direct.cpp) +target_link_libraries(boost_redis_echo_server_direct PRIVATE boost_redis_benchmarks_options) From 911d145d2d719ca8c1a626eca54180ef422a7cea Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Anarthal=20=28Rub=C3=A9n=20P=C3=A9rez=29?= <34971811+anarthal@users.noreply.github.com> Date: Mon, 4 May 2026 17:30:51 +0200 Subject: [PATCH 05/14] Replaces Boost.Test by boost::core::lightweight_test (#416) --- .../boost/redis/adapter/detail/adapters.hpp | 6 +- test/CMakeLists.txt | 5 +- test/Jamfile | 1 - test/common.hpp | 5 - test/test_any_adapter.cpp | 43 ++++--- test/test_conn_echo_stress.cpp | 33 +++-- test/test_conn_exec.cpp | 53 ++++---- test/test_conn_exec_error.cpp | 70 ++++++----- test/test_conn_exec_retry.cpp | 39 +++--- test/test_conn_quit.cpp | 21 ++-- test/test_conn_reconnect.cpp | 88 ++++++------- test/test_conn_tls.cpp | 42 ++++--- test/test_conversions.cpp | 53 ++++---- test/test_issue_50.cpp | 33 +++-- test/test_low_level.cpp | 88 ++++++++----- test/test_low_level_sync_sans_io.cpp | 119 ++++++++++-------- test/test_run.cpp | 48 +++---- 17 files changed, 418 insertions(+), 329 deletions(-) diff --git a/include/boost/redis/adapter/detail/adapters.hpp b/include/boost/redis/adapter/detail/adapters.hpp index 8419e220..6ddd5602 100644 --- a/include/boost/redis/adapter/detail/adapters.hpp +++ b/include/boost/redis/adapter/detail/adapters.hpp @@ -352,7 +352,7 @@ class set_impl { return; } - typename Result::key_type obj; + typename Result::key_type obj{}; boost_redis_from_bulk(obj, nd, ec); hint_ = result.insert(hint_, std::move(obj)); } @@ -387,11 +387,11 @@ class map_impl { } if (on_key_) { - typename Result::key_type obj; + typename Result::key_type obj{}; boost_redis_from_bulk(obj, nd, ec); current_ = result.insert(current_, {std::move(obj), {}}); } else { - typename Result::mapped_type obj; + typename Result::mapped_type obj{}; boost_redis_from_bulk(obj, nd, ec); current_->second = std::move(obj); } diff --git a/test/CMakeLists.txt b/test/CMakeLists.txt index 3c40b8b7..338f3c88 100644 --- a/test/CMakeLists.txt +++ b/test/CMakeLists.txt @@ -21,10 +21,7 @@ target_compile_definitions(boost_redis_tests_common INTERFACE BOOST_ALLOW_DEPREC function(boost_redis_make_test TEST_NAME) set(EXE_NAME "boost_redis_${TEST_NAME}") add_executable(${EXE_NAME} ${TEST_NAME}.cpp) - target_link_libraries(${EXE_NAME} PRIVATE - boost_redis_tests_common - Boost::unit_test_framework - ) + target_link_libraries(${EXE_NAME} PRIVATE boost_redis_tests_common) add_test(NAME ${EXE_NAME} COMMAND ${EXE_NAME}) add_dependencies(tests ${EXE_NAME}) endfunction() diff --git a/test/Jamfile b/test/Jamfile index adaa257f..68d517da 100644 --- a/test/Jamfile +++ b/test/Jamfile @@ -33,7 +33,6 @@ local requirements = ] [ ac.check-library /openssl//ssl : /openssl//ssl/shared : no ] [ ac.check-library /openssl//crypto : /openssl//crypto/shared : no ] - /boost/test//boost_unit_test_framework/off ; diff --git a/test/common.hpp b/test/common.hpp index 1dafbe26..6c7547ba 100644 --- a/test/common.hpp +++ b/test/common.hpp @@ -6,7 +6,6 @@ #include #include -#include #include #include @@ -21,10 +20,6 @@ inline constexpr std::chrono::seconds test_timeout{30}; #ifdef BOOST_ASIO_HAS_CO_AWAIT -inline auto redir(boost::system::error_code& ec) -{ - return boost::asio::redirect_error(boost::asio::use_awaitable, ec); -} void run_coroutine_test( boost::asio::awaitable, std::chrono::steady_clock::duration timeout = test_timeout); diff --git a/test/test_any_adapter.cpp b/test/test_any_adapter.cpp index ca123a49..88308ed7 100644 --- a/test/test_any_adapter.cpp +++ b/test/test_any_adapter.cpp @@ -8,33 +8,30 @@ #include #include +#include + #include -#define BOOST_TEST_MODULE any_adapter -#include -using boost::redis::generic_response; -using boost::redis::resp3::flat_tree; -using boost::redis::response; -using boost::redis::ignore; -using boost::redis::any_adapter; -using boost::redis::any_adapter; +using namespace boost::redis; + +namespace { -BOOST_AUTO_TEST_CASE(any_adapter_response_types) +void test_response_types() { // any_adapter can be used with any supported responses response r1; response r2; generic_response r3; - flat_tree r4; + resp3::flat_tree r4; - BOOST_CHECK_NO_THROW(any_adapter{r1}); - BOOST_CHECK_NO_THROW(any_adapter{r2}); - BOOST_CHECK_NO_THROW(any_adapter{r3}); - BOOST_CHECK_NO_THROW(any_adapter{r4}); - BOOST_CHECK_NO_THROW(any_adapter{ignore}); + BOOST_TEST_NO_THROW(any_adapter{r1}); + BOOST_TEST_NO_THROW(any_adapter{r2}); + BOOST_TEST_NO_THROW(any_adapter{r3}); + BOOST_TEST_NO_THROW(any_adapter{r4}); + BOOST_TEST_NO_THROW(any_adapter{ignore}); } -BOOST_AUTO_TEST_CASE(any_adapter_copy_move) +void test_copy_move() { // any_adapter can be copied/moved response r; @@ -47,8 +44,18 @@ BOOST_AUTO_TEST_CASE(any_adapter_copy_move) auto ad3 = any_adapter(std::move(ad2)); // copy assignment - BOOST_CHECK_NO_THROW(ad2 = ad1); + BOOST_TEST_NO_THROW(ad2 = ad1); // move assignment - BOOST_CHECK_NO_THROW(ad2 = std::move(ad1)); + BOOST_TEST_NO_THROW(ad2 = std::move(ad1)); } + +} // namespace + +int main() +{ + test_response_types(); + test_copy_move(); + + return boost::report_errors(); +} \ No newline at end of file diff --git a/test/test_conn_echo_stress.cpp b/test/test_conn_echo_stress.cpp index e1455b2a..06e79cdd 100644 --- a/test/test_conn_echo_stress.cpp +++ b/test/test_conn_echo_stress.cpp @@ -4,24 +4,33 @@ * accompanying file LICENSE.txt) */ +#include + +#ifndef BOOST_ASIO_HAS_CO_AWAIT + +#include + +BOOST_PRAGMA_MESSAGE( + "test_conn_echo_stress skipped because BOOST_ASIO_HAS_CO_AWAIT is not defined"); + +int main() { } + +#else + #include #include #include #include #include - -#include -#include -#define BOOST_TEST_MODULE echo_stress -#include +#include #include "common.hpp" +#include +#include #include -#ifdef BOOST_ASIO_HAS_CO_AWAIT - namespace net = boost::asio; using error_code = boost::system::error_code; using boost::redis::operation; @@ -91,7 +100,9 @@ request make_pub_req(std::size_t n_pubs) return req; } -BOOST_AUTO_TEST_CASE(echo_stress) +} // namespace + +int main() { // Setup net::io_context ctx; @@ -151,10 +162,8 @@ BOOST_AUTO_TEST_CASE(echo_stress) << conn.get_usage() << "\n" << "-------------------\n" << "Reallocations: " << resp.get_reallocs() << std::endl; -} -} // namespace + return boost::report_errors(); +} -#else -BOOST_AUTO_TEST_CASE(dummy) { } #endif diff --git a/test/test_conn_exec.cpp b/test/test_conn_exec.cpp index 715d844f..b7712cae 100644 --- a/test/test_conn_exec.cpp +++ b/test/test_conn_exec.cpp @@ -9,15 +9,13 @@ #include #include - -#include -#include -#define BOOST_TEST_MODULE conn_exec -#include +#include #include "common.hpp" +#include #include +#include // TODO: Test whether HELLO won't be inserted past commands that have // been already written. @@ -40,7 +38,7 @@ namespace { // Sends three requests where one of them has a hello with a priority // set, which means it should be executed first. -BOOST_AUTO_TEST_CASE(hello_priority) +void test_hello_priority() { request req1; req1.push("PING", "req1"); @@ -66,7 +64,7 @@ BOOST_AUTO_TEST_CASE(hello_priority) conn->async_exec(req1, ignore, [&](error_code ec, std::size_t) { // Second callback to the called. std::cout << "req1" << std::endl; - BOOST_TEST(ec == error_code()); + BOOST_TEST_EQ(ec, error_code()); BOOST_TEST(!seen2); BOOST_TEST(seen3); seen1 = true; @@ -75,7 +73,7 @@ BOOST_AUTO_TEST_CASE(hello_priority) conn->async_exec(req2, ignore, [&](error_code ec, std::size_t) { // Last callback to the called. std::cout << "req2" << std::endl; - BOOST_TEST(ec == error_code()); + BOOST_TEST_EQ(ec, error_code()); BOOST_TEST(seen1); BOOST_TEST(seen3); seen2 = true; @@ -86,7 +84,7 @@ BOOST_AUTO_TEST_CASE(hello_priority) conn->async_exec(req3, ignore, [&](error_code ec, std::size_t) { // Callback that will be called first. std::cout << "req3" << std::endl; - BOOST_TEST(ec == error_code()); + BOOST_TEST_EQ(ec, error_code()); BOOST_TEST(!seen1); BOOST_TEST(!seen2); seen3 = true; @@ -100,7 +98,7 @@ BOOST_AUTO_TEST_CASE(hello_priority) } // Tries to receive a string in an int and gets an error. -BOOST_AUTO_TEST_CASE(wrong_response_data_type) +void test_wrong_response_data_type() { request req; req.push("PING"); @@ -113,7 +111,7 @@ BOOST_AUTO_TEST_CASE(wrong_response_data_type) bool finished = false; conn->async_exec(req, resp, [conn, &finished](error_code ec, std::size_t) { - BOOST_TEST(ec == boost::redis::error::not_a_number); + BOOST_TEST_EQ(ec, boost::redis::error::not_a_number); conn->cancel(operation::reconnection); finished = true; }); @@ -123,7 +121,7 @@ BOOST_AUTO_TEST_CASE(wrong_response_data_type) BOOST_TEST(finished); } -BOOST_AUTO_TEST_CASE(large_number_of_concurrent_requests_issue_170) +void test_large_number_of_concurrent_requests_issue_170() { // See https://github.com/boostorg/redis/issues/170 @@ -144,7 +142,7 @@ BOOST_AUTO_TEST_CASE(large_number_of_concurrent_requests_issue_170) auto req = std::make_shared(); req->push("PING", payload); conn->async_exec(*req, ignore, [req, &remaining, conn](error_code ec, std::size_t) { - BOOST_TEST(ec == error_code()); + BOOST_TEST_EQ(ec, error_code()); if (--remaining == 0) conn->cancel(); }); @@ -152,10 +150,10 @@ BOOST_AUTO_TEST_CASE(large_number_of_concurrent_requests_issue_170) ioc.run_for(test_timeout); - BOOST_TEST(remaining == 0); + BOOST_TEST_EQ(remaining, 0); } -BOOST_AUTO_TEST_CASE(exec_any_adapter) +void test_exec_any_adapter() { // Executing an any_adapter object works request req; @@ -169,19 +167,19 @@ BOOST_AUTO_TEST_CASE(exec_any_adapter) bool finished = false; conn->async_exec(req, res, [&](error_code ec, std::size_t) { - BOOST_TEST(ec == error_code()); + BOOST_TEST_EQ(ec, error_code()); conn->cancel(); finished = true; }); run(conn); ioc.run_for(test_timeout); - BOOST_TEST_REQUIRE(finished); + BOOST_TEST(finished); - BOOST_TEST(std::get<0>(res).value() == "PONG"); + BOOST_TEST_EQ(std::get<0>(res).value(), "PONG"); } -BOOST_AUTO_TEST_CASE(exec_generic_flat_response) +void test_exec_generic_flat_response() { // Executing with a generic_flat_response works request req; @@ -195,17 +193,28 @@ BOOST_AUTO_TEST_CASE(exec_generic_flat_response) bool finished = false; conn->async_exec(req, resp, [&](error_code ec, std::size_t) { - BOOST_TEST(ec == error_code()); + BOOST_TEST_EQ(ec, error_code()); conn->cancel(); finished = true; }); run(conn); ioc.run_for(test_timeout); - BOOST_TEST_REQUIRE(finished); + BOOST_TEST(finished); BOOST_TEST(resp.has_value()); - BOOST_TEST(resp.value().front().value == "PONG"); + BOOST_TEST_EQ(resp.value().front().value, "PONG"); } } // namespace + +int main() +{ + test_hello_priority(); + test_wrong_response_data_type(); + test_large_number_of_concurrent_requests_issue_170(); + test_exec_any_adapter(); + test_exec_generic_flat_response(); + + return boost::report_errors(); +} \ No newline at end of file diff --git a/test/test_conn_exec_error.cpp b/test/test_conn_exec_error.cpp index b77f1299..ae4719e6 100644 --- a/test/test_conn_exec_error.cpp +++ b/test/test_conn_exec_error.cpp @@ -6,8 +6,7 @@ #include -#define BOOST_TEST_MODULE conn_exec_error -#include +#include #include "common.hpp" @@ -30,7 +29,7 @@ using namespace std::chrono_literals; namespace { -BOOST_AUTO_TEST_CASE(no_ignore_error) +void test_no_ignore_error() { request req; @@ -45,7 +44,7 @@ BOOST_AUTO_TEST_CASE(no_ignore_error) conn->async_exec(req, ignore, [&](error_code ec, std::size_t) { exec_finished = true; - BOOST_TEST(ec == error::resp3_simple_error); + BOOST_TEST_EQ(ec, error::resp3_simple_error); conn->cancel(operation::run); conn->cancel(operation::reconnection); }); @@ -57,7 +56,7 @@ BOOST_AUTO_TEST_CASE(no_ignore_error) BOOST_TEST(exec_finished); } -BOOST_AUTO_TEST_CASE(has_diagnostic) +void test_has_diagnostic() { request req; @@ -77,18 +76,18 @@ BOOST_AUTO_TEST_CASE(has_diagnostic) conn->async_exec(req, resp, [&](error_code ec, std::size_t) { exec_finished = true; - BOOST_TEST(ec == error_code()); + BOOST_TEST_EQ(ec, error_code()); // HELLO BOOST_TEST(std::get<0>(resp).has_error()); - BOOST_TEST(std::get<0>(resp).error().data_type == resp3::type::simple_error); + BOOST_TEST_EQ(std::get<0>(resp).error().data_type, resp3::type::simple_error); auto const diag = std::get<0>(resp).error().diagnostic; BOOST_TEST(!std::empty(diag)); std::cout << "has_diagnostic: " << diag << std::endl; // PING BOOST_TEST(std::get<1>(resp).has_value()); - BOOST_TEST(std::get<1>(resp).value() == "Barra do Una"); + BOOST_TEST_EQ(std::get<1>(resp).value(), "Barra do Una"); conn->cancel(operation::run); conn->cancel(operation::reconnection); @@ -101,7 +100,7 @@ BOOST_AUTO_TEST_CASE(has_diagnostic) BOOST_TEST(exec_finished); } -BOOST_AUTO_TEST_CASE(resp3_error_in_cmd_pipeline) +void test_resp3_error_in_cmd_pipeline() { request req1; req1.push("HELLO", "3"); @@ -123,24 +122,24 @@ BOOST_AUTO_TEST_CASE(resp3_error_in_cmd_pipeline) auto c2 = [&](error_code ec, std::size_t) { c2_called = true; - BOOST_TEST(ec == error_code()); + BOOST_TEST_EQ(ec, error_code()); BOOST_TEST(std::get<0>(resp2).has_value()); - BOOST_TEST(std::get<0>(resp2).value() == "req2-msg1"); + BOOST_TEST_EQ(std::get<0>(resp2).value(), "req2-msg1"); conn->cancel(operation::run); conn->cancel(operation::reconnection); }; auto c1 = [&](error_code ec, std::size_t) { c1_called = true; - BOOST_TEST(ec == error_code()); + BOOST_TEST_EQ(ec, error_code()); BOOST_TEST(std::get<2>(resp1).has_error()); - BOOST_TEST(std::get<2>(resp1).error().data_type == resp3::type::simple_error); + BOOST_TEST_EQ(std::get<2>(resp1).error().data_type, resp3::type::simple_error); auto const diag = std::get<2>(resp1).error().diagnostic; BOOST_TEST(!std::empty(diag)); std::cout << "resp3_error_in_cmd_pipeline: " << diag << std::endl; BOOST_TEST(std::get<3>(resp1).has_value()); - BOOST_TEST(std::get<3>(resp1).value() == "req1-msg3"); + BOOST_TEST_EQ(std::get<3>(resp1).value(), "req1-msg3"); conn->async_exec(req2, resp2, c2); }; @@ -154,7 +153,7 @@ BOOST_AUTO_TEST_CASE(resp3_error_in_cmd_pipeline) BOOST_TEST(c2_called); } -BOOST_AUTO_TEST_CASE(error_in_transaction) +void test_error_in_transaction() { request req; req.push("HELLO", 3); @@ -184,7 +183,7 @@ BOOST_AUTO_TEST_CASE(error_in_transaction) conn->async_exec(req, resp, [&](error_code ec, std::size_t) { finished = true; - BOOST_TEST(ec == error_code()); + BOOST_TEST_EQ(ec, error_code()); BOOST_TEST(std::get<0>(resp).has_value()); BOOST_TEST(std::get<1>(resp).has_value()); @@ -195,22 +194,23 @@ BOOST_AUTO_TEST_CASE(error_in_transaction) // Test errors in the pipeline commands. BOOST_TEST(std::get<0>(std::get<5>(resp).value()).has_value()); - BOOST_TEST(std::get<0>(std::get<5>(resp).value()).value() == "PONG"); + BOOST_TEST_EQ(std::get<0>(std::get<5>(resp).value()).value(), "PONG"); // The ping in the transaction that should be an error. BOOST_TEST(std::get<1>(std::get<5>(resp).value()).has_error()); - BOOST_TEST( - std::get<1>(std::get<5>(resp).value()).error().data_type == resp3::type::simple_error); + BOOST_TEST_EQ( + std::get<1>(std::get<5>(resp).value()).error().data_type, + resp3::type::simple_error); auto const diag = std::get<1>(std::get<5>(resp).value()).error().diagnostic; BOOST_TEST(!std::empty(diag)); // The ping thereafter in the transaction should not be an error. BOOST_TEST(std::get<2>(std::get<5>(resp).value()).has_value()); - BOOST_TEST(std::get<2>(std::get<5>(resp).value()).value() == "PONG"); + BOOST_TEST_EQ(std::get<2>(std::get<5>(resp).value()).value(), "PONG"); // The command right after the pipeline should be successful. BOOST_TEST(std::get<6>(resp).has_value()); - BOOST_TEST(std::get<6>(resp).value() == "PONG"); + BOOST_TEST_EQ(std::get<6>(resp).value(), "PONG"); conn->cancel(operation::run); conn->cancel(operation::reconnection); @@ -238,7 +238,7 @@ BOOST_AUTO_TEST_CASE(error_in_transaction) // response to the PING command that comes thereafter and won't be // forwarded to the receive_op, resulting in a difficult to handle // error. -BOOST_AUTO_TEST_CASE(subscriber_wrong_syntax) +void test_subscriber_wrong_syntax() { request req1; req1.push("PING"); @@ -254,13 +254,13 @@ BOOST_AUTO_TEST_CASE(subscriber_wrong_syntax) auto c2 = [&](error_code ec, std::size_t) { c2_called = true; std::cout << "async_exec: subscribe" << std::endl; - BOOST_TEST(ec == error_code()); + BOOST_TEST_EQ(ec, error_code()); }; auto c1 = [&](error_code ec, std::size_t) { c1_called = true; std::cout << "async_exec: hello" << std::endl; - BOOST_TEST(ec == error_code()); + BOOST_TEST_EQ(ec, error_code()); conn->async_exec(req2, ignore, c2); }; @@ -274,7 +274,7 @@ BOOST_AUTO_TEST_CASE(subscriber_wrong_syntax) std::cout << "async_receive2" << std::endl; BOOST_TEST(!ec); BOOST_TEST(gresp.has_error()); - BOOST_CHECK_EQUAL(gresp.error().data_type, resp3::type::simple_error); + BOOST_TEST_EQ(gresp.error().data_type, resp3::type::simple_error); BOOST_TEST(!std::empty(gresp.error().diagnostic)); std::cout << gresp.error().diagnostic << std::endl; conn->cancel(operation::run); @@ -292,7 +292,7 @@ BOOST_AUTO_TEST_CASE(subscriber_wrong_syntax) BOOST_TEST(c3_called); } -BOOST_AUTO_TEST_CASE(issue_287_generic_response_error_then_success) +void test_issue_287_generic_response_error_then_success() { // Setup auto cfg = make_test_config(); @@ -308,12 +308,12 @@ BOOST_AUTO_TEST_CASE(issue_287_generic_response_error_then_success) bool run_finished = false, exec_finished = false; conn.async_run(cfg, [&](error_code ec) { - BOOST_TEST(ec == net::error::operation_aborted); + BOOST_TEST_EQ(ec, net::error::operation_aborted); run_finished = true; }); conn.async_exec(req, resp, [&](error_code ec, std::size_t) { - BOOST_TEST(ec == error_code()); + BOOST_TEST_EQ(ec, error_code()); exec_finished = true; conn.cancel(); }); @@ -323,7 +323,19 @@ BOOST_AUTO_TEST_CASE(issue_287_generic_response_error_then_success) BOOST_TEST(run_finished); BOOST_TEST(exec_finished); BOOST_TEST(resp.has_error()); - BOOST_TEST(resp.error().diagnostic == "ERR wrong number of arguments for 'set' command"); + BOOST_TEST_EQ(resp.error().diagnostic, "ERR wrong number of arguments for 'set' command"); } } // namespace + +int main() +{ + test_no_ignore_error(); + test_has_diagnostic(); + test_resp3_error_in_cmd_pipeline(); + test_error_in_transaction(); + test_subscriber_wrong_syntax(); + test_issue_287_generic_response_error_then_success(); + + return boost::report_errors(); +} diff --git a/test/test_conn_exec_retry.cpp b/test/test_conn_exec_retry.cpp index e9d941c5..4c86a61a 100644 --- a/test/test_conn_exec_retry.cpp +++ b/test/test_conn_exec_retry.cpp @@ -6,15 +6,12 @@ #include +#include #include -#include - -#define BOOST_TEST_MODULE conn_exec_retry -#include - #include "common.hpp" +#include #include namespace net = boost::asio; @@ -30,7 +27,7 @@ using namespace std::chrono_literals; namespace { -BOOST_AUTO_TEST_CASE(request_cancel_if_unresponded_true) +void test_request_cancel_if_unresponded_true() { request req0; req0.get_config().cancel_on_connection_lost = true; @@ -61,7 +58,7 @@ BOOST_AUTO_TEST_CASE(request_cancel_if_unresponded_true) // being it has already been written so // cancel_on_connection_lost does not apply. timer_finished = true; - BOOST_TEST(ec == error_code()); + BOOST_TEST_EQ(ec, error_code()); conn->cancel(operation::run); conn->cancel(operation::reconnection); std::cout << "async_wait" << std::endl; @@ -70,19 +67,19 @@ BOOST_AUTO_TEST_CASE(request_cancel_if_unresponded_true) auto c2 = [&](error_code ec, std::size_t) { c2_called = true; std::cout << "c2" << std::endl; - BOOST_TEST(ec == net::error::operation_aborted); + BOOST_TEST_EQ(ec, net::error::operation_aborted); }; auto c1 = [&](error_code ec, std::size_t) { c1_called = true; std::cout << "c1" << std::endl; - BOOST_TEST(ec == net::error::operation_aborted); + BOOST_TEST_EQ(ec, net::error::operation_aborted); }; auto c0 = [&](error_code ec, std::size_t) { c0_called = true; std::cout << "c0" << std::endl; - BOOST_TEST(ec == error_code()); + BOOST_TEST_EQ(ec, error_code()); conn->async_exec(req1, ignore, c1); conn->async_exec(req2, ignore, c2); }; @@ -105,7 +102,7 @@ BOOST_AUTO_TEST_CASE(request_cancel_if_unresponded_true) BOOST_TEST(run_finished); } -BOOST_AUTO_TEST_CASE(request_cancel_if_unresponded_false) +void test_request_cancel_if_unresponded_false() { // The BLPOP request will block forever, causing the health checker // to trigger a reconnection. Although req2 has been written, @@ -138,24 +135,24 @@ BOOST_AUTO_TEST_CASE(request_cancel_if_unresponded_false) auto c3 = [&](error_code ec, std::size_t) { c3_called = true; std::cout << "c3: " << ec.message() << std::endl; - BOOST_TEST(ec == error_code()); + BOOST_TEST_EQ(ec, error_code()); conn->cancel(); }; auto c2 = [&](error_code ec, std::size_t) { c2_called = true; - BOOST_TEST(ec == error_code()); + BOOST_TEST_EQ(ec, error_code()); conn->async_exec(req3, ignore, c3); }; auto c1 = [&](error_code ec, std::size_t) { c1_called = true; - BOOST_TEST(ec == net::error::operation_aborted); + BOOST_TEST_EQ(ec, net::error::operation_aborted); }; auto c0 = [&](error_code ec, std::size_t) { c0_called = true; - BOOST_TEST(ec == error_code()); + BOOST_TEST_EQ(ec, error_code()); conn->async_exec(req1, ignore, c1); conn->async_exec(req2, ignore, c2); }; @@ -167,7 +164,7 @@ BOOST_AUTO_TEST_CASE(request_cancel_if_unresponded_false) conn->async_run(cfg, [&](error_code ec) { run_finished = true; std::cout << ec.message() << std::endl; - BOOST_TEST(ec != error_code()); + BOOST_TEST_NE(ec, error_code()); }); ioc.run_for(test_timeout); @@ -179,4 +176,12 @@ BOOST_AUTO_TEST_CASE(request_cancel_if_unresponded_false) BOOST_TEST(run_finished); } -} // namespace \ No newline at end of file +} // namespace + +int main() +{ + test_request_cancel_if_unresponded_true(); + test_request_cancel_if_unresponded_false(); + + return boost::report_errors(); +} \ No newline at end of file diff --git a/test/test_conn_quit.cpp b/test/test_conn_quit.cpp index 5a614a37..d4cf2a0b 100644 --- a/test/test_conn_quit.cpp +++ b/test/test_conn_quit.cpp @@ -6,14 +6,12 @@ #include +#include #include -#include -#define BOOST_TEST_MODULE conn_quit -#include - #include "common.hpp" +#include #include namespace net = boost::asio; @@ -26,7 +24,7 @@ using boost::redis::ignore; using namespace std::chrono_literals; // Test if quit causes async_run to exit. -BOOST_AUTO_TEST_CASE(test_async_run_exits) +void test_async_run_exits() { net::io_context ioc; @@ -51,20 +49,20 @@ BOOST_AUTO_TEST_CASE(test_async_run_exits) auto c3 = [&](error_code ec, std::size_t) { c3_called = true; std::clog << "c3: " << ec.message() << std::endl; - BOOST_TEST(ec == net::error::operation_aborted); + BOOST_TEST_EQ(ec, net::error::operation_aborted); }; auto c2 = [&](error_code ec, std::size_t) { c2_called = true; std::clog << "c2: " << ec.message() << std::endl; - BOOST_TEST(ec == error_code()); + BOOST_TEST_EQ(ec, error_code()); conn->async_exec(req3, ignore, c3); }; auto c1 = [&](error_code ec, std::size_t) { c1_called = true; std::cout << "c1: " << ec.message() << std::endl; - BOOST_TEST(ec == error_code()); + BOOST_TEST_EQ(ec, error_code()); conn->async_exec(req2, ignore, c2); }; @@ -83,3 +81,10 @@ BOOST_AUTO_TEST_CASE(test_async_run_exits) BOOST_TEST(c2_called); BOOST_TEST(c3_called); } + +int main() +{ + test_async_run_exits(); + + return boost::report_errors(); +} \ No newline at end of file diff --git a/test/test_conn_reconnect.cpp b/test/test_conn_reconnect.cpp index 7740c9b5..f91ca2d9 100644 --- a/test/test_conn_reconnect.cpp +++ b/test/test_conn_reconnect.cpp @@ -4,21 +4,29 @@ * accompanying file LICENSE.txt) */ +#include + +#ifndef BOOST_ASIO_HAS_CO_AWAIT + +#include + +BOOST_PRAGMA_MESSAGE("test_conn_reconnect skipped because BOOST_ASIO_HAS_CO_AWAIT is not defined"); + +int main() { } + +#else + #include #include +#include +#include #include -#define BOOST_TEST_MODULE conn_reconnect -#include - #include "common.hpp" #include -#ifdef BOOST_ASIO_HAS_CO_AWAIT -#include - namespace net = boost::asio; using boost::system::error_code; using boost::redis::request; @@ -28,11 +36,11 @@ using boost::redis::logger; using boost::redis::operation; using boost::redis::connection; using namespace std::chrono_literals; -using namespace boost::asio::experimental::awaitable_operators; namespace { -net::awaitable test_reconnect_impl() +// Test whether the client works after a reconnect. +net::awaitable test_reconnect() { auto ex = co_await net::this_coro::executor; @@ -50,37 +58,29 @@ net::awaitable test_reconnect_impl() run(conn, make_test_config()); for (int i = 0; i < 3; ++i) { - BOOST_TEST_CONTEXT("i=" << i) - { - // Issue a quit request, which will cause the server to close the connection. - // This request will succeed, since this happens before the connection is lost. - error_code ec; - co_await conn->async_exec(quit_req, ignore, net::redirect_error(ec)); - BOOST_TEST(ec == error_code()); - - // Reconnection will happen, and this request will succeed, too. - co_await conn->async_exec(regular_req, ignore, net::redirect_error(ec)); - BOOST_TEST(ec == error_code()); - } + // Issue a quit request, which will cause the server to close the connection. + // This request will succeed, since this happens before the connection is lost. + error_code ec; + co_await conn->async_exec(quit_req, ignore, net::redirect_error(ec)); + if (!BOOST_TEST_EQ(ec, error_code())) + std::cerr << " With i = " << i << std::endl; + + // Reconnection will happen, and this request will succeed, too. + co_await conn->async_exec(regular_req, ignore, net::redirect_error(ec)); + if (!BOOST_TEST_EQ(ec, error_code())) + std::cerr << " With i = " << i << std::endl; } conn->cancel(); } -// Test whether the client works after a reconnect. -BOOST_AUTO_TEST_CASE(test_reconnect) -{ - run_coroutine_test(test_reconnect_impl(), 5 * test_timeout); -} - -auto async_test_reconnect_timeout() -> net::awaitable +// The connection is usable after a timeout +auto test_after_timeout() -> net::awaitable { auto ex = co_await net::this_coro::executor; - net::steady_timer st{ex}; - auto conn = std::make_shared(ex); - error_code ec1, ec3; + error_code ec1; request req1; req1.get_config().cancel_if_not_connected = false; @@ -88,12 +88,8 @@ auto async_test_reconnect_timeout() -> net::awaitable req1.get_config().cancel_if_unresponded = true; req1.push("BLPOP", "any", 0); - st.expires_after(std::chrono::seconds{1}); - auto cfg = make_test_config(); - co_await (conn->async_exec(req1, ignore, redir(ec1)) || st.async_wait(redir(ec3))); - - //BOOST_TEST(!ec1); - //BOOST_TEST(!ec3); + co_await conn->async_exec(req1, ignore, net::cancel_after(1s, net::redirect_error(ec1))); + BOOST_TEST_EQ(ec1, net::error::operation_aborted); request req2; req2.get_config().cancel_if_not_connected = false; @@ -101,24 +97,22 @@ auto async_test_reconnect_timeout() -> net::awaitable req2.get_config().cancel_if_unresponded = true; req2.push("QUIT"); - st.expires_after(std::chrono::seconds{1}); - co_await ( - conn->async_exec(req1, ignore, net::redirect_error(net::use_awaitable, ec1)) || - st.async_wait(net::redirect_error(net::use_awaitable, ec3))); + co_await conn->async_exec(req1, ignore, net::cancel_after(1s, net::redirect_error(ec1))); conn->cancel(); std::cout << "ccc" << std::endl; - BOOST_CHECK_EQUAL(ec1, boost::asio::error::operation_aborted); + BOOST_TEST_EQ(ec1, net::error::operation_aborted); } -BOOST_AUTO_TEST_CASE(test_reconnect_and_idle) +} // namespace + +int main() { - run_coroutine_test(async_test_reconnect_timeout()); -} + run_coroutine_test(test_reconnect(), 5 * test_timeout); + run_coroutine_test(test_after_timeout()); -} // namespace + return boost::report_errors(); +} -#else -BOOST_AUTO_TEST_CASE(dummy) { } #endif diff --git a/test/test_conn_tls.cpp b/test/test_conn_tls.cpp index 9e588884..4359f209 100644 --- a/test/test_conn_tls.cpp +++ b/test/test_conn_tls.cpp @@ -9,18 +9,17 @@ #include #include +#include #include #include +#include "common.hpp" + #include #include #include #include #include -#define BOOST_TEST_MODULE conn_tls -#include - -#include "common.hpp" namespace net = boost::asio; using namespace boost::redis; @@ -55,7 +54,7 @@ config make_tls_config() } // Using the default TLS context allows establishing TLS connections and execute requests -BOOST_AUTO_TEST_CASE(exec_default_ssl_context) +void test_exec_default_ssl_context() { auto const cfg = make_tls_config(); constexpr std::string_view ping_value = "Kabuf"; @@ -76,24 +75,24 @@ BOOST_AUTO_TEST_CASE(exec_default_ssl_context) conn.async_exec(req, resp, [&](error_code ec, std::size_t) { exec_finished = true; - BOOST_TEST(ec == error_code()); + BOOST_TEST_EQ(ec, error_code()); conn.cancel(); }); conn.async_run(cfg, {}, [&](error_code ec) { run_finished = true; - BOOST_TEST(ec == net::error::operation_aborted); + BOOST_TEST_EQ(ec, net::error::operation_aborted); }); ioc.run_for(test_timeout); BOOST_TEST(exec_finished); BOOST_TEST(run_finished); - BOOST_TEST(std::get<0>(resp).value() == ping_value); + BOOST_TEST_EQ(std::get<0>(resp).value(), ping_value); } // Users can pass a custom context with TLS config -BOOST_AUTO_TEST_CASE(exec_custom_ssl_context) +void test_exec_custom_ssl_context() { std::string ca_pem = load_ca_certificate(); auto const cfg = make_tls_config(); @@ -119,25 +118,25 @@ BOOST_AUTO_TEST_CASE(exec_custom_ssl_context) conn.async_exec(req, resp, [&](error_code ec, std::size_t) { exec_finished = true; - BOOST_TEST(ec == error_code()); + BOOST_TEST_EQ(ec, error_code()); conn.cancel(); }); conn.async_run(cfg, {}, [&](error_code ec) { run_finished = true; - BOOST_TEST(ec == net::error::operation_aborted); + BOOST_TEST_EQ(ec, net::error::operation_aborted); }); ioc.run_for(test_timeout); BOOST_TEST(exec_finished); BOOST_TEST(run_finished); - BOOST_TEST(std::get<0>(resp).value() == ping_value); + BOOST_TEST_EQ(std::get<0>(resp).value(), ping_value); } // After an error, a TLS connection can recover. // Force an error using QUIT, then issue a regular request to verify that we could reconnect -BOOST_AUTO_TEST_CASE(reconnection) +void test_reconnection() { // Setup net::io_context ioc; @@ -157,18 +156,18 @@ BOOST_AUTO_TEST_CASE(reconnection) // Run the connection conn.async_run(make_test_config(), [&](error_code ec) { run_finished = true; - BOOST_TEST(ec == net::error::operation_aborted); + BOOST_TEST_EQ(ec, net::error::operation_aborted); }); // The PING is the end of the callback chain auto ping_callback = [&](error_code ec, std::size_t) { exec_finished = true; - BOOST_TEST(ec == error_code()); + BOOST_TEST_EQ(ec, error_code()); conn.cancel(); }; auto quit_callback = [&](error_code ec, std::size_t) { - BOOST_TEST(ec == error_code()); + BOOST_TEST_EQ(ec, error_code()); conn.async_exec(ping_request, ignore, ping_callback); }; @@ -180,4 +179,13 @@ BOOST_AUTO_TEST_CASE(reconnection) BOOST_TEST(run_finished); } -} // namespace \ No newline at end of file +} // namespace + +int main() +{ + test_exec_default_ssl_context(); + test_exec_custom_ssl_context(); + test_reconnection(); + + return boost::report_errors(); +} \ No newline at end of file diff --git a/test/test_conversions.cpp b/test/test_conversions.cpp index ac684506..1fb3a5db 100644 --- a/test/test_conversions.cpp +++ b/test/test_conversions.cpp @@ -7,11 +7,9 @@ #include #include +#include #include -#define BOOST_TEST_MODULE conversions -#include - #include "common.hpp" namespace net = boost::asio; @@ -23,7 +21,7 @@ using boost::system::error_code; namespace { -BOOST_AUTO_TEST_CASE(ints) +void test_ints() { // Setup net::io_context ioc; @@ -54,7 +52,7 @@ BOOST_AUTO_TEST_CASE(ints) conn->async_exec(req, resp, [conn, &finished](error_code ec, std::size_t) { finished = true; - BOOST_TEST(ec == error_code()); + BOOST_TEST_EQ(ec, error_code()); conn->cancel(); }); @@ -63,19 +61,19 @@ BOOST_AUTO_TEST_CASE(ints) BOOST_TEST(finished); // Check - BOOST_TEST(std::get<1>(resp).value() == 42); - BOOST_TEST(std::get<2>(resp).value() == 42u); - BOOST_TEST(std::get<3>(resp).value() == 42); - BOOST_TEST(std::get<4>(resp).value() == 42u); - BOOST_TEST(std::get<5>(resp).value() == 42); - BOOST_TEST(std::get<6>(resp).value() == 42u); - BOOST_TEST(std::get<7>(resp).value() == 42); - BOOST_TEST(std::get<8>(resp).value() == 42u); - BOOST_TEST(std::get<9>(resp).value() == 42); - BOOST_TEST(std::get<10>(resp).value() == 42u); + BOOST_TEST_EQ(std::get<1>(resp).value(), 42); + BOOST_TEST_EQ(std::get<2>(resp).value(), 42u); + BOOST_TEST_EQ(std::get<3>(resp).value(), 42); + BOOST_TEST_EQ(std::get<4>(resp).value(), 42u); + BOOST_TEST_EQ(std::get<5>(resp).value(), 42); + BOOST_TEST_EQ(std::get<6>(resp).value(), 42u); + BOOST_TEST_EQ(std::get<7>(resp).value(), 42); + BOOST_TEST_EQ(std::get<8>(resp).value(), 42u); + BOOST_TEST_EQ(std::get<9>(resp).value(), 42); + BOOST_TEST_EQ(std::get<10>(resp).value(), 42u); } -BOOST_AUTO_TEST_CASE(bools) +void test_bools() { // Setup net::io_context ioc; @@ -95,7 +93,7 @@ BOOST_AUTO_TEST_CASE(bools) conn->async_exec(req, resp, [conn, &finished](error_code ec, std::size_t) { finished = true; - BOOST_TEST(ec == error_code()); + BOOST_TEST_EQ(ec, error_code()); conn->cancel(); }); @@ -103,11 +101,11 @@ BOOST_AUTO_TEST_CASE(bools) ioc.run_for(test_timeout); // Check - BOOST_TEST(std::get<2>(resp).value() == true); - BOOST_TEST(std::get<3>(resp).value() == false); + BOOST_TEST(std::get<2>(resp).value()); + BOOST_TEST_NOT(std::get<3>(resp).value()); } -BOOST_AUTO_TEST_CASE(floating_points) +void test_floating_points() { // Setup net::io_context ioc; @@ -125,7 +123,7 @@ BOOST_AUTO_TEST_CASE(floating_points) conn->async_exec(req, resp, [conn, &finished](error_code ec, std::size_t) { finished = true; - BOOST_TEST(ec == error_code()); + BOOST_TEST_EQ(ec, error_code()); conn->cancel(); }); @@ -134,7 +132,16 @@ BOOST_AUTO_TEST_CASE(floating_points) BOOST_TEST(finished); // Check - BOOST_TEST(std::get<1>(resp).value() == 4.12); + BOOST_TEST_EQ(std::get<1>(resp).value(), 4.12); } -} // namespace \ No newline at end of file +} // namespace + +int main() +{ + test_ints(); + test_bools(); + test_floating_points(); + + return boost::report_errors(); +} \ No newline at end of file diff --git a/test/test_issue_50.cpp b/test/test_issue_50.cpp index 00c04cad..8c7f0b1a 100644 --- a/test/test_issue_50.cpp +++ b/test/test_issue_50.cpp @@ -4,7 +4,17 @@ * accompanying file LICENSE.txt) */ -// Must come before any asio header, otherwise build fails on msvc. +#include + +#ifndef BOOST_ASIO_HAS_CO_AWAIT + +#include + +BOOST_PRAGMA_MESSAGE("test_issue_50 skipped because BOOST_ASIO_HAS_CO_AWAIT is not defined"); + +int main() { } + +#else #include #include @@ -14,18 +24,14 @@ #include #include #include +#include #include -#include -#define BOOST_TEST_MODULE issue50 -#include - #include "common.hpp" +#include #include -#if defined(BOOST_ASIO_HAS_CO_AWAIT) - namespace net = boost::asio; using boost::redis::request; using boost::redis::response; @@ -86,7 +92,7 @@ auto periodic_task(std::shared_ptr conn) -> net::awaitable conn->cancel(operation::reconnection); } -BOOST_AUTO_TEST_CASE(issue_50) +void test_issue_50() { bool receiver_finished = false, periodic_finished = false, run_finished = false; @@ -121,6 +127,11 @@ BOOST_AUTO_TEST_CASE(issue_50) } // namespace -#else -BOOST_AUTO_TEST_CASE(dummy) { } -#endif // defined(BOOST_ASIO_HAS_CO_AWAIT) +int main() +{ + test_issue_50(); + + return boost::report_errors(); +} + +#endif diff --git a/test/test_low_level.cpp b/test/test_low_level.cpp index 0b208d9d..f491e18c 100644 --- a/test/test_low_level.cpp +++ b/test/test_low_level.cpp @@ -4,13 +4,14 @@ * accompanying file LICENSE.txt) */ +// clang-format off + #include #include #include #include -#define BOOST_TEST_MODULE low_level -#include +#include #include #include @@ -23,6 +24,8 @@ auto operator==(boost::redis::ignore_t, boost::redis::ignore_t) noexcept { retur auto operator!=(boost::redis::ignore_t, boost::redis::ignore_t) noexcept { return false; } } // namespace std +namespace { + namespace redis = boost::redis; namespace resp3 = boost::redis::resp3; using boost::system::error_code; @@ -112,15 +115,15 @@ void test_sync(expect e) BOOST_TEST(res); // None of these tests need more data. if (ec) { - BOOST_CHECK_EQUAL(ec, e.ec); + BOOST_TEST_EQ(ec, e.ec); return; } if (result.has_value()) { BOOST_TEST(bool(result == e.expected)); - BOOST_CHECK_EQUAL(e.in.size(), p.get_consumed()); + BOOST_TEST_EQ(e.in.size(), p.get_consumed()); } else { - BOOST_CHECK_EQUAL(result.error().data_type, e.error_type); + BOOST_TEST_EQ(result.error().data_type, e.error_type); } } @@ -134,7 +137,7 @@ void test_sync2(expect e) auto const res = parse(p, e.in, adapter, ec); BOOST_TEST(res); // None of these tests need more data. - BOOST_CHECK_EQUAL(ec, e.ec); + BOOST_TEST_EQ(ec, e.ec); } auto make_blob() @@ -473,29 +476,28 @@ generic_response const attr_e1b test(make_expected(S12b, node_type{{resp3::type::blob_error, 1UL, 0UL, {}}}, {}, resp3::type::blob_error));\ test(make_expected(S12c, result{}, boost::redis::error::resp3_blob_error)); -// clang-format on -BOOST_AUTO_TEST_CASE(sansio){NUMBER_TEST_CONDITIONS(test_sync)} +void test_sansio(){NUMBER_TEST_CONDITIONS(test_sync)} -BOOST_AUTO_TEST_CASE(ignore_adapter_simple_error) +void test_ignore_adapter_simple_error() { test_sync2(make_expected(S10a, ignore, boost::redis::error::resp3_simple_error)); } -BOOST_AUTO_TEST_CASE(ignore_adapter_blob_error) +void test_ignore_adapter_blob_error() { test_sync2(make_expected(S12a, ignore, boost::redis::error::resp3_blob_error)); } -BOOST_AUTO_TEST_CASE(ignore_adapter_no_error) { test_sync2(make_expected(S05b, ignore)); } +void test_ignore_adapter_no_error() { test_sync2(make_expected(S05b, ignore)); } //----------------------------------------------------------------------------------- void check_error(char const* name, boost::redis::error ev) { auto const ec = boost::redis::make_error_code(ev); auto const& cat = ec.category(); - BOOST_TEST(std::string(ec.category().name()) == name); - BOOST_TEST(!ec.message().empty()); + BOOST_TEST_EQ(std::string(ec.category().name()), name); + BOOST_TEST_NOT(ec.message().empty()); BOOST_TEST(cat.equivalent( static_cast::type>(ev), ec.category().default_error_condition( @@ -503,7 +505,7 @@ void check_error(char const* name, boost::redis::error ev) BOOST_TEST(cat.equivalent(ec, static_cast::type>(ev))); } -BOOST_AUTO_TEST_CASE(cover_error) +void test_cover_error() { check_error("boost.redis", boost::redis::error::invalid_data_type); check_error("boost.redis", boost::redis::error::not_a_number); @@ -544,7 +546,7 @@ std::string get_type_as_str(boost::redis::resp3::type t) return ss.str(); } -BOOST_AUTO_TEST_CASE(type_string) +void test_type_string() { BOOST_TEST(!get_type_as_str(boost::redis::resp3::type::array).empty()); BOOST_TEST(!get_type_as_str(boost::redis::resp3::type::push).empty()); @@ -565,13 +567,13 @@ BOOST_AUTO_TEST_CASE(type_string) BOOST_TEST(!get_type_as_str(boost::redis::resp3::type::invalid).empty()); } -BOOST_AUTO_TEST_CASE(type_convert) +void test_type_convert() { using boost::redis::resp3::to_code; using boost::redis::resp3::to_type; using boost::redis::resp3::type; -#define CHECK_CASE(A) BOOST_CHECK_EQUAL(to_type(to_code(type::A)), type::A); +#define CHECK_CASE(A) BOOST_TEST_EQ(to_type(to_code(type::A)), type::A); CHECK_CASE(array); CHECK_CASE(push); CHECK_CASE(set); @@ -591,7 +593,7 @@ BOOST_AUTO_TEST_CASE(type_convert) #undef CHECK_CASE } -BOOST_AUTO_TEST_CASE(adapter) +void test_adapter() { using boost::redis::adapter::boost_redis_adapt; using resp3::type; @@ -608,16 +610,16 @@ BOOST_AUTO_TEST_CASE(adapter) f.on_node(resp3::node_view{type::number, 1, 0, "42"}, ec); f.on_done(); - BOOST_CHECK_EQUAL(std::get<0>(resp).value(), "Hello"); - BOOST_TEST(!ec); + BOOST_TEST_EQ(std::get<0>(resp).value(), "Hello"); + BOOST_TEST_NOT(ec); - BOOST_CHECK_EQUAL(std::get<1>(resp).value(), 42); - BOOST_TEST(!ec); + BOOST_TEST_EQ(std::get<1>(resp).value(), 42); + BOOST_TEST_NOT(ec); } // TODO: This was an experiment, I will resume implementing this // later. -BOOST_AUTO_TEST_CASE(adapter_as) +void test_adapter_as() { result> set; auto adapter = adapt2(set); @@ -628,7 +630,7 @@ BOOST_AUTO_TEST_CASE(adapter_as) } } -BOOST_AUTO_TEST_CASE(cancel_one_1) +void test_cancel_one_1() { auto resp = push_e1a; BOOST_TEST(resp.has_value()); @@ -637,7 +639,7 @@ BOOST_AUTO_TEST_CASE(cancel_one_1) BOOST_TEST(resp.value().empty()); } -BOOST_AUTO_TEST_CASE(cancel_one_empty) +void test_cancel_one_empty() { generic_response resp; BOOST_TEST(resp.has_value()); @@ -646,7 +648,7 @@ BOOST_AUTO_TEST_CASE(cancel_one_empty) BOOST_TEST(resp.value().empty()); } -BOOST_AUTO_TEST_CASE(cancel_one_has_error) +void test_cancel_one_has_error() { generic_response resp = boost::redis::adapter::error{resp3::type::simple_string, {}}; BOOST_TEST(resp.has_error()); @@ -655,7 +657,7 @@ BOOST_AUTO_TEST_CASE(cancel_one_has_error) BOOST_TEST(resp.has_error()); } -BOOST_AUTO_TEST_CASE(cancel_one_has_does_not_consume_past_the_end) +void test_cancel_one_has_does_not_consume_past_the_end() { auto resp = push_e1a; BOOST_TEST(resp.has_value()); @@ -666,10 +668,10 @@ BOOST_AUTO_TEST_CASE(cancel_one_has_does_not_consume_past_the_end) consume_one(resp); - BOOST_CHECK_EQUAL(resp.value().size(), push_e1a.value().size()); + BOOST_TEST_EQ(resp.value().size(), push_e1a.value().size()); } -BOOST_AUTO_TEST_CASE(cancel_one_incompatible_depth) +void test_cancel_one_incompatible_depth() { auto resp = streamed_string_e1; BOOST_TEST(resp.has_value()); @@ -678,7 +680,31 @@ BOOST_AUTO_TEST_CASE(cancel_one_incompatible_depth) consume_one(resp, ec); error_code expected = error::incompatible_node_depth; - BOOST_CHECK_EQUAL(ec, expected); + BOOST_TEST_EQ(ec, expected); + + BOOST_TEST_EQ(resp.value().size(), push_e1a.value().size()); +} - BOOST_CHECK_EQUAL(resp.value().size(), push_e1a.value().size()); } + +int main() +{ + test_sansio(); + test_ignore_adapter_simple_error(); + test_ignore_adapter_blob_error(); + test_ignore_adapter_no_error(); + test_cover_error(); + test_type_string(); + test_type_convert(); + test_adapter(); + test_adapter_as(); + test_cancel_one_1(); + test_cancel_one_empty(); + test_cancel_one_has_error(); + test_cancel_one_has_does_not_consume_past_the_end(); + test_cancel_one_incompatible_depth(); + + return boost::report_errors(); +} + +// clang-format on diff --git a/test/test_low_level_sync_sans_io.cpp b/test/test_low_level_sync_sans_io.cpp index 9628dfdb..cc8b2288 100644 --- a/test/test_low_level_sync_sans_io.cpp +++ b/test/test_low_level_sync_sans_io.cpp @@ -13,42 +13,39 @@ #include #include -#define BOOST_TEST_MODULE low_level_sync_sans_io -#include +#include #include #include -using boost::redis::request; -using boost::redis::adapter::adapt2; -using boost::redis::adapter::result; -using boost::redis::resp3::tree; -using boost::redis::generic_flat_response; -using boost::redis::ignore_t; -using boost::redis::resp3::detail::deserialize; -using boost::redis::resp3::node; -using boost::redis::resp3::node_view; -using boost::redis::resp3::to_string; -using boost::redis::response; -using boost::redis::any_adapter; +using namespace boost::redis; +using adapter::adapt2; +using adapter::result; +using resp3::tree; +using resp3::detail::deserialize; +using resp3::node; +using resp3::node_view; +using resp3::to_string; using boost::system::error_code; namespace resp3 = boost::redis::resp3; +namespace { + #define RESP3_SET_PART1 "~6\r\n+orange\r" #define RESP3_SET_PART2 "\n+apple\r\n+one" #define RESP3_SET_PART3 "\r\n+two\r" #define RESP3_SET_PART4 "\n+three\r\n+orange\r\n" char const* resp3_set = RESP3_SET_PART1 RESP3_SET_PART2 RESP3_SET_PART3 RESP3_SET_PART4; -BOOST_AUTO_TEST_CASE(low_level_sync_sans_io) +void test_low_level_sync_sans_io() { try { result> resp; error_code ec; deserialize(resp3_set, adapt2(resp), ec); - BOOST_CHECK_EQUAL(ec, error_code{}); + BOOST_TEST_EQ(ec, error_code{}); for (auto const& e : resp.value()) std::cout << e << std::endl; @@ -59,7 +56,7 @@ BOOST_AUTO_TEST_CASE(low_level_sync_sans_io) } } -BOOST_AUTO_TEST_CASE(issue_210_empty_set) +void test_issue_210_empty_set() { try { result(resp.value()).value(), 1); - BOOST_CHECK(std::get<1>(resp.value()).value().empty()); - BOOST_CHECK_EQUAL(std::get<2>(resp.value()).value(), "this_should_not_be_in_set"); - BOOST_CHECK_EQUAL(std::get<3>(resp.value()).value(), 2); + BOOST_TEST_EQ(std::get<0>(resp.value()).value(), 1); + BOOST_TEST(std::get<1>(resp.value()).value().empty()); + BOOST_TEST_EQ(std::get<2>(resp.value()).value(), "this_should_not_be_in_set"); + BOOST_TEST_EQ(std::get<3>(resp.value()).value(), 2); } catch (std::exception const& e) { std::cerr << e.what() << std::endl; @@ -86,7 +83,7 @@ BOOST_AUTO_TEST_CASE(issue_210_empty_set) } } -BOOST_AUTO_TEST_CASE(issue_210_non_empty_set_size_one) +void test_issue_210_non_empty_set_size_one() { try { result(resp.value()).value(), 1); - BOOST_CHECK_EQUAL(std::get<1>(resp.value()).value().size(), 1u); - BOOST_CHECK_EQUAL(std::get<1>(resp.value()).value().at(0), std::string{"foo"}); - BOOST_CHECK_EQUAL(std::get<2>(resp.value()).value(), "this_should_not_be_in_set"); - BOOST_CHECK_EQUAL(std::get<3>(resp.value()).value(), 2); + BOOST_TEST_EQ(std::get<0>(resp.value()).value(), 1); + BOOST_TEST_EQ(std::get<1>(resp.value()).value().size(), 1u); + BOOST_TEST_EQ(std::get<1>(resp.value()).value().at(0), std::string{"foo"}); + BOOST_TEST_EQ(std::get<2>(resp.value()).value(), "this_should_not_be_in_set"); + BOOST_TEST_EQ(std::get<3>(resp.value()).value(), 2); } catch (std::exception const& e) { std::cerr << e.what() << std::endl; @@ -115,7 +112,7 @@ BOOST_AUTO_TEST_CASE(issue_210_non_empty_set_size_one) } } -BOOST_AUTO_TEST_CASE(issue_210_non_empty_set_size_two) +void test_issue_210_non_empty_set_size_two() { try { result(resp.value()).value(), 1); - BOOST_CHECK_EQUAL(std::get<1>(resp.value()).value().at(0), std::string{"foo"}); - BOOST_CHECK_EQUAL(std::get<1>(resp.value()).value().at(1), std::string{"bar"}); - BOOST_CHECK_EQUAL(std::get<2>(resp.value()).value(), "this_should_not_be_in_set"); + BOOST_TEST_EQ(std::get<0>(resp.value()).value(), 1); + BOOST_TEST_EQ(std::get<1>(resp.value()).value().at(0), std::string{"foo"}); + BOOST_TEST_EQ(std::get<1>(resp.value()).value().at(1), std::string{"bar"}); + BOOST_TEST_EQ(std::get<2>(resp.value()).value(), "this_should_not_be_in_set"); } catch (std::exception const& e) { std::cerr << e.what() << std::endl; @@ -143,7 +140,7 @@ BOOST_AUTO_TEST_CASE(issue_210_non_empty_set_size_two) } } -BOOST_AUTO_TEST_CASE(issue_210_no_nested) +void test_issue_210_no_nested() { try { result, result, result, result>> @@ -154,12 +151,12 @@ BOOST_AUTO_TEST_CASE(issue_210_no_nested) error_code ec; deserialize(wire, adapt2(resp), ec); - BOOST_CHECK_EQUAL(ec, error_code{}); + BOOST_TEST_EQ(ec, error_code{}); - BOOST_CHECK_EQUAL(std::get<0>(resp.value()).value(), 1); - BOOST_CHECK_EQUAL(std::get<1>(resp.value()).value(), std::string{"foo"}); - BOOST_CHECK_EQUAL(std::get<2>(resp.value()).value(), std::string{"bar"}); - BOOST_CHECK_EQUAL(std::get<3>(resp.value()).value(), "this_should_not_be_in_set"); + BOOST_TEST_EQ(std::get<0>(resp.value()).value(), 1); + BOOST_TEST_EQ(std::get<1>(resp.value()).value(), std::string{"foo"}); + BOOST_TEST_EQ(std::get<2>(resp.value()).value(), std::string{"bar"}); + BOOST_TEST_EQ(std::get<3>(resp.value()).value(), "this_should_not_be_in_set"); } catch (std::exception const& e) { std::cerr << e.what() << std::endl; @@ -167,7 +164,7 @@ BOOST_AUTO_TEST_CASE(issue_210_no_nested) } } -BOOST_AUTO_TEST_CASE(issue_233_array_with_null) +void test_issue_233_array_with_null() { try { result>> resp; @@ -176,11 +173,11 @@ BOOST_AUTO_TEST_CASE(issue_233_array_with_null) error_code ec; deserialize(wire, adapt2(resp), ec); - BOOST_CHECK_EQUAL(ec, error_code{}); + BOOST_TEST_EQ(ec, error_code{}); - BOOST_CHECK_EQUAL(resp.value().at(0).value(), "one"); + BOOST_TEST_EQ(resp.value().at(0).value(), "one"); BOOST_TEST(!resp.value().at(1).has_value()); - BOOST_CHECK_EQUAL(resp.value().at(2).value(), "two"); + BOOST_TEST_EQ(resp.value().at(2).value(), "two"); } catch (std::exception const& e) { std::cerr << e.what() << std::endl; @@ -188,7 +185,7 @@ BOOST_AUTO_TEST_CASE(issue_233_array_with_null) } } -BOOST_AUTO_TEST_CASE(issue_233_optional_array_with_null) +void test_issue_233_optional_array_with_null() { try { result>>> resp; @@ -197,11 +194,11 @@ BOOST_AUTO_TEST_CASE(issue_233_optional_array_with_null) error_code ec; deserialize(wire, adapt2(resp), ec); - BOOST_CHECK_EQUAL(ec, error_code{}); + BOOST_TEST_EQ(ec, error_code{}); - BOOST_CHECK_EQUAL(resp.value().value().at(0).value(), "one"); + BOOST_TEST_EQ(resp.value().value().at(0).value(), "one"); BOOST_TEST(!resp.value().value().at(1).has_value()); - BOOST_CHECK_EQUAL(resp.value().value().at(2).value(), "two"); + BOOST_TEST_EQ(resp.value().value().at(2).value(), "two"); } catch (std::exception const& e) { std::cerr << e.what() << std::endl; @@ -209,7 +206,7 @@ BOOST_AUTO_TEST_CASE(issue_233_optional_array_with_null) } } -BOOST_AUTO_TEST_CASE(check_counter_adapter) +void test_check_counter_adapter() { using boost::redis::any_adapter; using boost::redis::resp3::parse; @@ -248,7 +245,23 @@ BOOST_AUTO_TEST_CASE(check_counter_adapter) BOOST_TEST(!ret3); BOOST_TEST(ret4); - BOOST_CHECK_EQUAL(init, 1); - BOOST_CHECK_EQUAL(node, 7); - BOOST_CHECK_EQUAL(done, 1); + BOOST_TEST_EQ(init, 1); + BOOST_TEST_EQ(node, 7); + BOOST_TEST_EQ(done, 1); } + +} // namespace + +int main() +{ + test_low_level_sync_sans_io(); + test_issue_210_empty_set(); + test_issue_210_non_empty_set_size_one(); + test_issue_210_non_empty_set_size_two(); + test_issue_210_no_nested(); + test_issue_233_array_with_null(); + test_issue_233_optional_array_with_null(); + test_check_counter_adapter(); + + return boost::report_errors(); +} \ No newline at end of file diff --git a/test/test_run.cpp b/test/test_run.cpp index 7540c46e..ee996606 100644 --- a/test/test_run.cpp +++ b/test/test_run.cpp @@ -6,9 +6,8 @@ #include +#include #include -#define BOOST_TEST_MODULE run -#include #include "common.hpp" @@ -33,7 +32,7 @@ bool is_host_not_found(error_code ec) return false; } -BOOST_AUTO_TEST_CASE(resolve_bad_host) +void test_resolve_bad_host() { net::io_context ioc; connection conn{ioc}; @@ -47,16 +46,17 @@ BOOST_AUTO_TEST_CASE(resolve_bad_host) cfg.reconnect_wait_interval = 0s; bool run_finished = true; - conn.async_run(cfg, {}, [&run_finished](error_code ec) { + conn.async_run(cfg, [&run_finished](error_code ec) { run_finished = true; - BOOST_TEST(is_host_not_found(ec), "is_host_not_found(ec) is false, with ec = " << ec); + if (!BOOST_TEST(is_host_not_found(ec))) + std::cerr << " ec = " << ec; }); ioc.run_for(4 * test_timeout); BOOST_TEST(run_finished); } -BOOST_AUTO_TEST_CASE(resolve_with_timeout) +void test_resolve_with_timeout() { net::io_context ioc; connection conn{ioc}; @@ -70,16 +70,16 @@ BOOST_AUTO_TEST_CASE(resolve_with_timeout) cfg.reconnect_wait_interval = 0s; bool run_finished = true; - conn.async_run(cfg, {}, [&run_finished](error_code ec) { + conn.async_run(cfg, [&run_finished](error_code ec) { run_finished = true; - BOOST_TEST(ec != error_code()); + BOOST_TEST_NE(ec, error_code()); }); ioc.run_for(4 * test_timeout); BOOST_TEST(run_finished); } -BOOST_AUTO_TEST_CASE(connect_bad_port) +void test_connect_bad_port() { net::io_context ioc; connection conn{ioc}; @@ -93,30 +93,22 @@ BOOST_AUTO_TEST_CASE(connect_bad_port) cfg.reconnect_wait_interval = 0s; bool run_finished = true; - conn.async_run(cfg, {}, [&run_finished](error_code ec) { + conn.async_run(cfg, [&run_finished](error_code ec) { run_finished = true; - BOOST_TEST(ec != error_code()); + BOOST_TEST_NE(ec, error_code()); }); ioc.run_for(4 * test_timeout); BOOST_TEST(run_finished); } -// Hard to test. -//BOOST_AUTO_TEST_CASE(connect_with_timeout) -//{ -// net::io_context ioc; -// -// config cfg; -// cfg.addr.host = "example.com"; -// cfg.addr.port = "80"; -// cfg.resolve_timeout = 10s; -// cfg.connect_timeout = 1ns; -// cfg.health_check_interval = 10h; -// -// auto conn = std::make_shared(ioc); -// run(conn, cfg, boost::redis::error::connect_timeout); -// ioc.run(); -//} - } // namespace + +int main() +{ + test_resolve_bad_host(); + test_resolve_with_timeout(); + test_connect_bad_port(); + + return boost::report_errors(); +} From 51ec420141cb6ab107def66750ec46dff7aff1c8 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Anarthal=20=28Rub=C3=A9n=20P=C3=A9rez=29?= <34971811+anarthal@users.noreply.github.com> Date: Thu, 7 May 2026 11:39:16 +0200 Subject: [PATCH 06/14] Improves CI coverage for POSIX compilers (#418) Properly covers gcc 11 to 15 and clang 11 to 22 Recovers -Werror close #413 --- .github/workflows/ci.yml | 273 ++++++++++++++++---- example/CMakeLists.txt | 2 +- include/boost/redis/impl/sentinel_utils.hpp | 1 + test/CMakeLists.txt | 2 +- tools/ci.py | 44 +++- 5 files changed, 260 insertions(+), 62 deletions(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index f165eb16..35add30d 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -25,8 +25,6 @@ jobs: #- { toolset: msvc-14.2, os: windows-2019, generator: "Visual Studio 16 2019", cxxstd: '17', build-type: 'Release', build-shared-libs: 0 } - { toolset: msvc-14.3, os: windows-2022, generator: "Visual Studio 17 2022", cxxstd: '20', build-type: 'Debug', build-shared-libs: 0 } - { toolset: msvc-14.3, os: windows-2022, generator: "Visual Studio 17 2022", cxxstd: '20', build-type: 'Release', build-shared-libs: 1 } - env: - CMAKE_BUILD_PARALLEL_LEVEL: 4 steps: - name: Checkout uses: actions/checkout@v4 @@ -41,6 +39,7 @@ jobs: # No Redis server is available on this job, so integration tests are skipped. # Unit tests are built and run via the CMake superproject build. + # Double backslash '//' prevents MSYS from interpreting flags as paths - name: Build a Boost distribution and run the tests using CMake run: | python3 tools/ci.py build-cmake-distro \ @@ -49,7 +48,8 @@ jobs: --toolset ${{ matrix.toolset }} \ --generator "${{ matrix.generator }}" \ --build-shared-libs ${{ matrix.build-shared-libs }} \ - --integration-tests 0 + --integration-tests 0 \ + "--cxxflags=//WX" - name: Run add_subdirectory tests run: | @@ -58,7 +58,8 @@ jobs: --cxxstd ${{ matrix.cxxstd }} \ --toolset ${{ matrix.toolset }} \ --generator "${{ matrix.generator }}" \ - --build-shared-libs ${{ matrix.build-shared-libs }} + --build-shared-libs ${{ matrix.build-shared-libs }} \ + "--cxxflags=//WX" - name: Run find_package tests with the built cmake distribution run: | @@ -67,7 +68,8 @@ jobs: --cxxstd ${{ matrix.cxxstd }} \ --toolset ${{ matrix.toolset }} \ --generator "${{ matrix.generator }}" \ - --build-shared-libs ${{ matrix.build-shared-libs }} + --build-shared-libs ${{ matrix.build-shared-libs }} \ + "--cxxflags=//WX" - name: Run find_package tests with the built b2 distribution run: | @@ -76,7 +78,8 @@ jobs: --cxxstd ${{ matrix.cxxstd }} \ --toolset ${{ matrix.toolset }} \ --generator "${{ matrix.generator }}" \ - --build-shared-libs ${{ matrix.build-shared-libs }} + --build-shared-libs ${{ matrix.build-shared-libs }} \ + "--cxxflags=//WX" windows-b2: name: "B2 ${{matrix.toolset}}" @@ -119,28 +122,91 @@ jobs: fail-fast: false matrix: include: + # gcc-11 is the default in Ubuntu 22.04, defaults to c++17 - toolset: gcc-11 install: g++-11 container: ubuntu:22.04 cxxstd: '17' build-type: 'Debug' - ldflags: '' - server: "redis:7.4.5-alpine" + server: "redis:8.2.1-alpine" + corosio-tests: '0' + + - toolset: gcc-11 + install: g++-11 + container: ubuntu:22.04 + cxxstd: '20' + build-type: 'Debug' + server: "valkey/valkey:8.1.3-alpine" + corosio-tests: '0' - toolset: gcc-11 install: g++-11 container: ubuntu:22.04 cxxstd: '20' build-type: 'Release' - ldflags: '' server: "redis:7.4.5-alpine" + corosio-tests: '0' - - toolset: clang-11 - install: clang-11 + - toolset: gcc-12 + install: g++-12 container: ubuntu:22.04 + cxxstd: '23' + build-type: 'Debug' + server: "redis:8.2.1-alpine" + + # gcc-13 is the default in Ubuntu 24.04, defaults to c++17 + - toolset: gcc-13 + install: g++-13 + container: ubuntu:24.04 + cxxstd: '17' + build-type: 'Debug' + server: "valkey/valkey:8.1.3-alpine" + corosio-tests: '0' + + - toolset: gcc-13 + install: g++-13 + container: ubuntu:24.04 + cxxstd: '23' + build-type: 'Debug' + server: "redis:7.4.5-alpine" + + - toolset: gcc-13 + install: g++-13 + container: ubuntu:24.04 + cxxstd: '23' + build-type: 'Release' + server: "redis:8.2.1-alpine" + + - toolset: gcc-14 + install: g++-14 + container: ubuntu:24.04 + cxxstd: '23' + build-type: 'Debug' + server: "valkey/valkey:8.1.3-alpine" + + # gcc-15 is the default in Ubuntu 26.04, defaults to c++17 + - toolset: gcc-15 + install: g++-15 + container: ubuntu:26.04 cxxstd: '17' build-type: 'Debug' - ldflags: '' + server: "redis:7.4.5-alpine" + corosio-tests: '0' + + - toolset: gcc-15 + install: g++-15 + container: ubuntu:26.04 + cxxstd: '26' + build-type: 'Debug' + cxxflags: '-fsanitize=address -fsanitize=undefined -fno-sanitize-recover=all' + ldflags: '-fsanitize=address -fsanitize=undefined' + server: "valkey/valkey:8.1.3-alpine" + + - toolset: gcc-15 + install: g++-15 + container: ubuntu:26.04 + cxxstd: '26' + build-type: 'Release' server: "redis:7.4.5-alpine" - toolset: clang-11 @@ -148,33 +214,43 @@ jobs: container: ubuntu:22.04 cxxstd: '20' build-type: 'Debug' - ldflags: '' server: "redis:7.4.5-alpine" + corosio-tests: '0' - - toolset: clang-13 - install: clang-13 + - toolset: clang-12 + install: clang-12 container: ubuntu:22.04 - cxxstd: '17' - build-type: 'Release' - ldflags: '' + cxxstd: '20' + build-type: 'Debug' server: "redis:8.2.1-alpine" + corosio-tests: '0' - toolset: clang-13 install: clang-13 container: ubuntu:22.04 cxxstd: '20' - build-type: 'Release' - ldflags: '' - server: "redis:8.2.1-alpine" + build-type: 'Debug' + server: "valkey/valkey:8.1.3-alpine" + corosio-tests: '0' + + # clang-14 is the default in Ubuntu 22.04 + - toolset: clang-14 + install: clang-14 + container: ubuntu:22.04 + cxxstd: '20' + build-type: 'Debug' + server: "redis:7.4.5-alpine" + corosio-tests: '0' - toolset: clang-14 install: 'clang-14 libc++-14-dev libc++abi-14-dev' container: ubuntu:22.04 - cxxstd: '17' + cxxstd: '20' build-type: 'Debug' cxxflags: '-stdlib=libc++' ldflags: '-lc++' server: "redis:8.2.1-alpine" + corosio-tests: '0' - toolset: clang-14 install: 'clang-14 libc++-14-dev libc++abi-14-dev' @@ -183,39 +259,108 @@ jobs: build-type: 'Release' cxxflags: '-stdlib=libc++' ldflags: '-lc++' + server: "valkey/valkey:8.1.3-alpine" + + - toolset: clang-15 + install: clang-15 + container: ubuntu:22.04 + cxxstd: '20' + build-type: 'Debug' + server: "redis:7.4.5-alpine" + corosio-tests: '0' + + - toolset: clang-16 + install: clang-16 + container: ubuntu:24.04 + cxxstd: '20' + build-type: 'Debug' + cxxflags: '-DBOOST_ASIO_DISABLE_LOCAL_SOCKETS=1' # If a system has no UNIX socket support, we build correctly server: "redis:8.2.1-alpine" - - - toolset: clang-19 - install: 'clang-19' + corosio-tests: '0' + + - toolset: clang-17 + install: clang-17 + container: ubuntu:24.04 + cxxstd: '20' + build-type: 'Debug' + server: "valkey/valkey:8.1.3-alpine" + + # clang-18 is the default in Ubuntu 24.04 + - toolset: clang-18 + install: clang-18 + container: ubuntu:24.04 + cxxstd: '20' + build-type: 'Debug' + server: "redis:7.4.5-alpine" + + - toolset: clang-18 + install: 'clang-18 libc++-18-dev libc++abi-18-dev' + container: ubuntu:24.04 + cxxstd: '20' + build-type: 'Release' + cxxflags: '-stdlib=libc++' + ldflags: '-lc++' + server: "valkey/valkey:8.1.3-alpine" + + - toolset: clang-18 + install: 'clang-18 libc++-18-dev libc++abi-18-dev' container: ubuntu:24.04 cxxstd: '23' build-type: 'Debug' - cxxflags: '-fsanitize=address -fsanitize=undefined -fno-sanitize-recover=all' - ldflags: '-fsanitize=address -fsanitize=undefined' + cxxflags: '-stdlib=libc++' + ldflags: '-lc++' server: "redis:8.2.1-alpine" - - toolset: gcc-14 - install: 'g++-14' + - toolset: clang-19 + install: clang-19 container: ubuntu:24.04 cxxstd: '23' build-type: 'Debug' - cxxflags: '-DBOOST_ASIO_DISABLE_LOCAL_SOCKETS=1' # If a system had no UNIX socket support, we build correctly - server: "valkey/valkey:8.1.3-alpine" - - - toolset: gcc-14 - install: 'g++-14' + server: "redis:7.4.5-alpine" + + - toolset: clang-20 + install: clang-20 container: ubuntu:24.04 cxxstd: '23' build-type: 'Debug' + server: "redis:8.2.1-alpine" + + # clang-21 is the default in Ubuntu 26.04 + - toolset: clang-21 + install: 'clang-21 libclang-rt-21-dev' + container: ubuntu:26.04 + cxxstd: '23' + build-type: 'Debug' cxxflags: '-fsanitize=address -fsanitize=undefined -fno-sanitize-recover=all' ldflags: '-fsanitize=address -fsanitize=undefined' server: "valkey/valkey:8.1.3-alpine" + - toolset: clang-21 + install: 'clang-21 libc++-21-dev libc++abi-21-dev' + container: ubuntu:26.04 + cxxstd: '23' + build-type: 'Debug' + cxxflags: '-stdlib=libc++' + ldflags: '-lc++' + server: "redis:7.4.5-alpine" + + - toolset: clang-21 + install: 'clang-21 libc++-21-dev libc++abi-21-dev' + container: ubuntu:26.04 + cxxstd: '23' + build-type: 'Release' + cxxflags: '-stdlib=libc++' + ldflags: '-lc++' + server: "redis:8.2.1-alpine" + + - toolset: clang-22 + install: clang-22 + container: ubuntu:26.04 + cxxstd: '23' + build-type: 'Debug' + server: "valkey/valkey:8.1.3-alpine" + runs-on: ubuntu-latest - env: - CXXFLAGS: ${{matrix.cxxflags}} -Wall -Wextra - LDFLAGS: ${{matrix.ldflags}} - CMAKE_BUILD_PARALLEL_LEVEL: 4 steps: - name: Checkout @@ -232,7 +377,7 @@ jobs: - name: Install dependencies run: | docker exec builder apt-get update - docker exec builder apt-get -y --no-install-recommends install \ + docker exec --env DEBIAN_FRONTEND=noninteractive builder apt-get -y --no-install-recommends install \ git \ g++ \ libssl-dev \ @@ -258,28 +403,36 @@ jobs: docker exec builder /boost-redis/tools/ci.py build-cmake-distro \ --build-type ${{ matrix.build-type }} \ --cxxstd ${{ matrix.cxxstd }} \ - --toolset ${{ matrix.toolset }} + --toolset ${{ matrix.toolset }} \ + "--cxxflags=${{ matrix.cxxflags }} -Werror" \ + "--ldflags=${{ matrix.ldflags }}" - name: Run add_subdirectory tests run: | docker exec builder /boost-redis/tools/ci.py run-cmake-add-subdirectory-tests \ --build-type ${{ matrix.build-type }} \ --cxxstd ${{ matrix.cxxstd }} \ - --toolset ${{ matrix.toolset }} + --toolset ${{ matrix.toolset }} \ + "--cxxflags=${{ matrix.cxxflags }} -Werror" \ + "--ldflags=${{ matrix.ldflags }}" - name: Run find_package tests with the built cmake distribution run: | docker exec builder /boost-redis/tools/ci.py run-cmake-find-package-tests \ --build-type ${{ matrix.build-type }} \ --cxxstd ${{ matrix.cxxstd }} \ - --toolset ${{ matrix.toolset }} + --toolset ${{ matrix.toolset }} \ + "--cxxflags=${{ matrix.cxxflags }} -Werror" \ + "--ldflags=${{ matrix.ldflags }}" - name: Run find_package tests with the built b2 distribution run: | docker exec builder /boost-redis/tools/ci.py run-cmake-b2-find-package-tests \ --build-type ${{ matrix.build-type }} \ --cxxstd ${{ matrix.cxxstd }} \ - --toolset ${{ matrix.toolset }} + --toolset ${{ matrix.toolset }} \ + "--cxxflags=${{ matrix.cxxflags }} -Werror" \ + "--ldflags=${{ matrix.ldflags }}" posix-b2: name: "B2 ${{ matrix.toolset }}" @@ -291,17 +444,29 @@ jobs: fail-fast: false matrix: include: + # We don't have integration tests here, so we only test the compilers + # that get installed by default in Ubuntu 22.04, 24.04 and 26.04 - toolset: gcc-11 install: g++-11 cxxstd: "11,17,20" # Having C++11 shouldn't break the build - os: ubuntu-latest container: ubuntu:22.04 - - toolset: clang-14 - install: clang-14 - os: ubuntu-latest - container: ubuntu:22.04 - cxxstd: "17,20" - runs-on: ${{ matrix.os }} + - toolset: gcc-13 + install: g++-13 + cxxstd: "17,20,23" + container: ubuntu:24.04 + - toolset: gcc-15 + install: g++-15 + cxxstd: "17,20,23" + container: ubuntu:26.04 + - toolset: clang-18 + install: clang-18 + cxxstd: "11,17,20" + container: ubuntu:24.04 + - toolset: clang-21 + install: clang-21 + cxxstd: "17,20,23" + container: ubuntu:26.04 + runs-on: ubuntu-latest container: ${{matrix.container}} steps: - name: Checkout @@ -310,13 +475,9 @@ jobs: - name: Setup container environment if: matrix.container run: | + export DEBIAN_FRONTEND=noninteractive apt-get update - apt-get -y install sudo python3 git g++ libssl-dev - - - name: Install dependencies - run: | - sudo apt-get update - sudo apt-get -y install python3 ${{ matrix.install }} + apt-get -y install python3 git g++ libssl-dev ${{ matrix.install }} - name: Setup Boost run: ./tools/ci.py setup-boost --source-dir=$(pwd) diff --git a/example/CMakeLists.txt b/example/CMakeLists.txt index eafbf7bc..54548bba 100644 --- a/example/CMakeLists.txt +++ b/example/CMakeLists.txt @@ -1,5 +1,5 @@ add_library(boost_redis_examples_main STATIC main.cpp) -target_link_libraries(boost_redis_examples_main PRIVATE boost_redis_project_options) +target_link_libraries(boost_redis_examples_main PRIVATE boost_redis_project_options boost_redis_src) function(boost_redis_make_example EXAMPLE_NAME) set(EXE_NAME "boost_redis_${EXAMPLE_NAME}") diff --git a/include/boost/redis/impl/sentinel_utils.hpp b/include/boost/redis/impl/sentinel_utils.hpp index 6b52ce7e..36586629 100644 --- a/include/boost/redis/impl/sentinel_utils.hpp +++ b/include/boost/redis/impl/sentinel_utils.hpp @@ -19,6 +19,7 @@ #include #include +#include #include #include #include diff --git a/test/CMakeLists.txt b/test/CMakeLists.txt index 338f3c88..385914b6 100644 --- a/test/CMakeLists.txt +++ b/test/CMakeLists.txt @@ -4,7 +4,7 @@ add_library(boost_redis_project_options INTERFACE) target_link_libraries(boost_redis_project_options INTERFACE boost_redis) if (MSVC) # C4459: name hides outer scope variable is issued by Asio - target_compile_options(boost_redis_project_options INTERFACE /bigobj /W4 /WX /wd4459) + target_compile_options(boost_redis_project_options INTERFACE /bigobj /W4 /wd4459) target_compile_definitions(boost_redis_project_options INTERFACE _WIN32_WINNT=0x0601 _CRT_SECURE_NO_WARNINGS=1) elseif(CMAKE_CXX_COMPILER_ID STREQUAL "Clang" OR CMAKE_CXX_COMPILER_ID STREQUAL "GNU") target_compile_options(boost_redis_project_options INTERFACE -Wall -Wextra) diff --git a/tools/ci.py b/tools/ci.py index edd6ace4..b60a55a6 100755 --- a/tools/ci.py +++ b/tools/ci.py @@ -10,6 +10,7 @@ import stat from shutil import rmtree, copytree, ignore_patterns import argparse +import multiprocessing # Variables @@ -19,6 +20,7 @@ _b2_distro = _home.joinpath('boost-b2-distro') _cmake_distro = _home.joinpath('boost-cmake-distro') _b2_command = str(_boost_root.joinpath('b2')) +_num_jobs = multiprocessing.cpu_count() * 2 # Utilities @@ -66,6 +68,20 @@ def _compiler_from_toolset(toolset: str) -> str: return toolset +# Sets the environment variables that affect CMake. +# CXXFLAGS should be used instead of CMAKE_CXX_FLAGS because otherwise +# the default flags for MSVC (like /EHsc) are overwritten +def _set_cmake_env(cxxflags: str = '', ldflags: str = '') -> None: + print('+ CMAKE_BUILD_PARALLEL_LEVEL={}'.format(_num_jobs), flush=True) + os.environ['CMAKE_BUILD_PARALLEL_LEVEL'] = str(_num_jobs) + + print('+ CXXFLAGS={}'.format(cxxflags), flush=True) + os.environ['CXXFLAGS'] = cxxflags + + print('+ LDFLAGS={}'.format(ldflags), flush=True) + os.environ['LDFLAGS'] = ldflags + + # If we're on the master branch, we should use the Boost superproject master branch. # Otherwise, use the superproject develop branch. def _deduce_boost_branch() -> str: @@ -151,8 +167,11 @@ def _build_cmake_distro( cxxstd: str, toolset: str, build_shared_libs: bool = False, - integration_tests: bool = False + integration_tests: bool = False, + cxxflags: str = '', + ldflags: str = '' ): + _set_cmake_env(cxxflags, ldflags) _mkdir_and_cd(_boost_root.joinpath('__build_cmake_test__')) _run([ 'cmake', @@ -181,8 +200,11 @@ def _run_cmake_add_subdirectory_tests( build_type: str, cxxstd: str, toolset: str, - build_shared_libs: bool = False + build_shared_libs: bool = False, + cxxflags: str = '', + ldflags: str = '' ): + _set_cmake_env(cxxflags, ldflags) test_folder = _boost_root.joinpath('libs', 'redis', 'test', 'cmake_subdir_test', '__build') _mkdir_and_cd(test_folder) _run([ @@ -206,8 +228,11 @@ def _run_cmake_find_package_tests( build_type: str, cxxstd: str, toolset: str, - build_shared_libs: bool = False + build_shared_libs: bool = False, + cxxflags: str = '', + ldflags: str = '' ): + _set_cmake_env(cxxflags, ldflags) _mkdir_and_cd(_boost_root.joinpath('libs', 'redis', 'test', 'cmake_install_test', '__build')) _run([ 'cmake', @@ -231,8 +256,11 @@ def _run_cmake_b2_find_package_tests( build_type: str, cxxstd: str, toolset: str, - build_shared_libs: bool = False + build_shared_libs: bool = False, + cxxflags: str = '', + ldflags: str = '' ): + _set_cmake_env(cxxflags, ldflags) _mkdir_and_cd(_boost_root.joinpath('libs', 'redis', 'test', 'cmake_b2_test', '__build')) _run([ 'cmake', @@ -291,6 +319,8 @@ def main(): subp.add_argument('--toolset', default='gcc') subp.add_argument('--build-shared-libs', type=_str2bool, default=False) subp.add_argument('--integration-tests', type=_str2bool, default=True) + subp.add_argument('--cxxflags', default='') + subp.add_argument('--ldflags', default='') subp.set_defaults(func=_build_cmake_distro) subp = subparsers.add_parser('run-cmake-add-subdirectory-tests') @@ -299,6 +329,8 @@ def main(): subp.add_argument('--cxxstd', default='20') subp.add_argument('--toolset', default='gcc') subp.add_argument('--build-shared-libs', type=_str2bool, default=False) + subp.add_argument('--cxxflags', default='') + subp.add_argument('--ldflags', default='') subp.set_defaults(func=_run_cmake_add_subdirectory_tests) subp = subparsers.add_parser('run-cmake-find-package-tests') @@ -307,6 +339,8 @@ def main(): subp.add_argument('--cxxstd', default='20') subp.add_argument('--toolset', default='gcc') subp.add_argument('--build-shared-libs', type=_str2bool, default=False) + subp.add_argument('--cxxflags', default='') + subp.add_argument('--ldflags', default='') subp.set_defaults(func=_run_cmake_find_package_tests) subp = subparsers.add_parser('run-cmake-b2-find-package-tests') @@ -315,6 +349,8 @@ def main(): subp.add_argument('--cxxstd', default='20') subp.add_argument('--toolset', default='gcc') subp.add_argument('--build-shared-libs', type=_str2bool, default=False) + subp.add_argument('--cxxflags', default='') + subp.add_argument('--ldflags', default='') subp.set_defaults(func=_run_cmake_b2_find_package_tests) subp = subparsers.add_parser('run-b2-tests') From 044324ecd92d3fc899615fa12e67b8a7b77a2ad4 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Anarthal=20=28Rub=C3=A9n=20P=C3=A9rez=29?= <34971811+anarthal@users.noreply.github.com> Date: Thu, 7 May 2026 11:40:55 +0200 Subject: [PATCH 07/14] Updates the checkout action to v6 in GHA (#420) --- .github/workflows/ci.yml | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 35add30d..adb46311 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -27,7 +27,7 @@ jobs: - { toolset: msvc-14.3, os: windows-2022, generator: "Visual Studio 17 2022", cxxstd: '20', build-type: 'Release', build-shared-libs: 1 } steps: - name: Checkout - uses: actions/checkout@v4 + uses: actions/checkout@v6 - name: Setup Boost run: python3 tools/ci.py setup-boost --source-dir=$(pwd) @@ -97,7 +97,7 @@ jobs: OPENSSL_ROOT: "C:\\Program Files\\OpenSSL" steps: - name: Checkout - uses: actions/checkout@v4 + uses: actions/checkout@v6 - name: Setup user-config.jam run: cp tools/user-config.jam "${HOMEDRIVE}${HOMEPATH}/" @@ -364,7 +364,7 @@ jobs: steps: - name: Checkout - uses: actions/checkout@v4 + uses: actions/checkout@v6 - name: Generate TLS certificates run: | @@ -470,7 +470,7 @@ jobs: container: ${{matrix.container}} steps: - name: Checkout - uses: actions/checkout@v4 + uses: actions/checkout@v6 - name: Setup container environment if: matrix.container @@ -499,7 +499,7 @@ jobs: runs-on: ubuntu-latest steps: - name: Checkout - uses: actions/checkout@v4 + uses: actions/checkout@v6 - name: Setup Boost run: ./tools/ci.py setup-boost --source-dir=$(pwd) From 9845e4fc9ed9076fcf6830e8288afcc048f2fcbf Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Anarthal=20=28Rub=C3=A9n=20P=C3=A9rez=29?= <34971811+anarthal@users.noreply.github.com> Date: Thu, 7 May 2026 14:07:19 +0200 Subject: [PATCH 08/14] Recovers the B2 build on Windows (#419) Correctly sets OpenSSL paths in the B2 Windows build in GHA Adds a check to prevent the build from succeeding in case OpenSSL is not found close #346 --- .github/workflows/ci.yml | 2 -- test/Jamfile | 18 +++++++++++++ test/test_serialization.cpp | 2 +- tools/ci.py | 3 ++- tools/user-config.jam | 51 +++++++++++++++++++++++++++++++++---- 5 files changed, 67 insertions(+), 9 deletions(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index adb46311..6f687a40 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -93,8 +93,6 @@ jobs: include: #- { toolset: msvc-14.2, os: windows-2019 } - { toolset: msvc-14.3, os: windows-2022 } - env: - OPENSSL_ROOT: "C:\\Program Files\\OpenSSL" steps: - name: Checkout uses: actions/checkout@v6 diff --git a/test/Jamfile b/test/Jamfile index 68d517da..3ddb1be7 100644 --- a/test/Jamfile +++ b/test/Jamfile @@ -2,10 +2,27 @@ import-search /boost/config/checks ; import config : requires ; import ac ; +import indirect ; # Configure openssl if it hasn't been done yet using openssl ; +# Provide a way to fail the build if OpenSSL is not found - used by CIs +rule do_fail_impl ( a * ) +{ + exit "OpenSSL could not be found. Don't build target fail_if_no_openssl to skip this check" ; +} + +local do_fail = [ indirect.make do_fail_impl ] ; + +alias fail_if_no_openssl + : requirements + [ ac.check-library /openssl//ssl : : @$(do_fail) ] + [ ac.check-library /openssl//crypto : : @$(do_fail) ] +; + +explicit fail_if_no_openssl ; + # Use these requirements as both regular and usage requirements across all tests local requirements = /boost/redis//boost_redis @@ -85,3 +102,4 @@ for local test in $(tests) : target-name $(test) ; } + diff --git a/test/test_serialization.cpp b/test/test_serialization.cpp index 18185ac4..4f5f1e1c 100644 --- a/test/test_serialization.cpp +++ b/test/test_serialization.cpp @@ -142,7 +142,7 @@ void test_pair_custom() void test_tuple() { std::vector> vec{ - {"k1", 42, 1} + {"k1", 42, static_cast(1)} }; request req; req.push_range("GET", vec); diff --git a/tools/ci.py b/tools/ci.py index b60a55a6..26ea8592 100755 --- a/tools/ci.py +++ b/tools/ci.py @@ -295,7 +295,8 @@ def _run_b2_tests( 'warnings=extra', 'warnings-as-errors=on', '-j4', - 'libs/redis/test' + 'libs/redis/test', + 'libs/redis/test//fail_if_no_openssl' ]) diff --git a/tools/user-config.jam b/tools/user-config.jam index 91fc619e..35e02c26 100644 --- a/tools/user-config.jam +++ b/tools/user-config.jam @@ -1,12 +1,53 @@ -# Used on CI. This is required on Windows to make b2 find openssl +# Used on CI. This is required on Windows to make b2 find OpenSSL. +# +# Layout matches the Shining Light Productions OpenSSL installer: +# /include/ headers +# /lib/VC/x64/MD/*.lib release libs (linked against /MD runtime) +# /lib/VC/x64/MDd/*.lib debug libs (linked against /MDd runtime) +# /lib/VC/x64/MT/*.lib release libs (linked against /MT runtime) +# /lib/VC/x64/MTd/*.lib debug libs (linked against /MTd runtime) -import os ; +local openssl_root = "C:/Program Files/OpenSSL" ; -local OPENSSL_ROOT = [ os.environ OPENSSL_ROOT ] ; +using openssl : : + $(openssl_root)/include + "$(openssl_root)/lib/VC/x64/MD" + libssl + libcrypto + : + shared + release +; using openssl : : - $(OPENSSL_ROOT)/include - $(OPENSSL_ROOT)/lib + $(openssl_root)/include + "$(openssl_root)/lib/VC/x64/MDd" libssl libcrypto + : + shared + debug ; + + +using openssl : : + $(openssl_root)/include + "$(openssl_root)/lib/VC/x64/MT" + libssl + libcrypto + : + static + release +; + + +using openssl : : + $(openssl_root)/include + "$(openssl_root)/lib/VC/x64/MTd" + libssl + libcrypto + : + static + debug +; + From 1d339e28babcb65940fa4c1b81e72bd60919e5bf Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Anarthal=20=28Rub=C3=A9n=20P=C3=A9rez=29?= <34971811+anarthal@users.noreply.github.com> Date: Thu, 7 May 2026 14:41:32 +0200 Subject: [PATCH 09/14] Adds CIs for OSX (#421) --- .github/workflows/ci.yml | 81 ++++++++++++++++++- tools/user-config-osx-gha.jam | 8 ++ ...ser-config.jam => user-config-win-gha.jam} | 0 3 files changed, 86 insertions(+), 3 deletions(-) create mode 100644 tools/user-config-osx-gha.jam rename tools/{user-config.jam => user-config-win-gha.jam} (100%) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 6f687a40..0ae26571 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -98,7 +98,7 @@ jobs: uses: actions/checkout@v6 - name: Setup user-config.jam - run: cp tools/user-config.jam "${HOMEDRIVE}${HOMEPATH}/" + run: cp tools/user-config-win-gha.jam "${HOMEDRIVE}${HOMEPATH}/user-config.jam" - name: Setup Boost run: python3 tools/ci.py setup-boost --source-dir=$(pwd) @@ -110,7 +110,7 @@ jobs: --cxxstd 17,20 \ --variant debug,release - posix-cmake: + linux-cmake: name: "CMake ${{ matrix.toolset }} ${{ matrix.cxxstd }} ${{ matrix.build-type }} ${{ matrix.cxxflags }}" defaults: run: @@ -432,7 +432,7 @@ jobs: "--cxxflags=${{ matrix.cxxflags }} -Werror" \ "--ldflags=${{ matrix.ldflags }}" - posix-b2: + linux-b2: name: "B2 ${{ matrix.toolset }}" defaults: run: @@ -487,6 +487,81 @@ jobs: --cxxstd ${{ matrix.cxxstd }} \ --variant debug,release + osx-cmake: + name: "CMake OSX" + + runs-on: macos-latest + + steps: + - name: Checkout + uses: actions/checkout@v6 + + - name: Setup Boost + run: ./tools/ci.py setup-boost --source-dir=$(pwd) + + - name: Build a Boost distribution using B2 + run: | + ./tools/ci.py build-b2-distro \ + --toolset clang + + - name: Build a Boost distribution and run the tests using CMake + run: | + ./tools/ci.py build-cmake-distro \ + --build-type Debug \ + --cxxstd 20 \ + --toolset clang \ + "--cxxflags=-Werror" \ + --integration-tests 0 + + - name: Run add_subdirectory tests + run: | + ./tools/ci.py run-cmake-add-subdirectory-tests \ + --build-type Debug \ + --cxxstd 20 \ + --toolset clang \ + "--cxxflags=-Werror" + + - name: Run find_package tests with the built cmake distribution + run: | + ./tools/ci.py run-cmake-find-package-tests \ + --build-type Debug \ + --cxxstd 20 \ + --toolset clang \ + "--cxxflags=-Werror" + + - name: Run find_package tests with the built b2 distribution + run: | + ./tools/ci.py run-cmake-b2-find-package-tests \ + --build-type Debug \ + --cxxstd 20 \ + --toolset clang \ + "--cxxflags=-Werror" + + osx-b2: + name: "B2 ${{ matrix.os }}" + defaults: + run: + shell: bash + + runs-on: macos-latest + steps: + - name: Checkout + uses: actions/checkout@v6 + + - name: Setup user-config.jam + run: cp tools/user-config-osx-gha.jam ~/user-config.jam + + - name: Setup Boost + run: ./tools/ci.py setup-boost --source-dir=$(pwd) + + - name: Build and run project tests using B2 + run: | + ./tools/ci.py run-b2-tests \ + --toolset clang \ + --cxxstd 20 \ + --variant debug,release + + # Checks that we don't have any errors in docs check-docs: name: Check docs diff --git a/tools/user-config-osx-gha.jam b/tools/user-config-osx-gha.jam new file mode 100644 index 00000000..1d4dff91 --- /dev/null +++ b/tools/user-config-osx-gha.jam @@ -0,0 +1,8 @@ +# +# Copyright (c) 2019-2025 Ruben Perez Hidalgo (rubenperez038 at gmail dot com) +# +# Distributed under the Boost Software License, Version 1.0. (See accompanying +# file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) +# + +using openssl : : /opt/homebrew/opt/openssl@3/include /opt/homebrew/opt/openssl@3/lib ; diff --git a/tools/user-config.jam b/tools/user-config-win-gha.jam similarity index 100% rename from tools/user-config.jam rename to tools/user-config-win-gha.jam From 45294d3909b88a4b9fad182eed892832829b8588 Mon Sep 17 00:00:00 2001 From: Marcelo Zimbres Date: Mon, 29 Dec 2025 02:07:36 +0100 Subject: [PATCH 10/14] Implements lazy buffer rotations The read buffer won't rotate data if it can be increased without allocating more memory. The table below compares the performance before and after the changes | time(s) | %usr | %sys | %CPU | rotation ------|---------| ------|------|-------|------------ Eager | 92.22 | 67.37 | 2.92 | 70.29 | 6.7 Gbps Lazy | 91.16 | 60.15 | 2.82 | 62.97 | 99.0 kbps The biggest gain in the benchmark above is in CPU usage because the overall time cannot be decreased much since the client is far from saturating the CPU. --- include/boost/redis/config.hpp | 4 +- include/boost/redis/connection.hpp | 2 +- include/boost/redis/detail/exec_one_fsm.hpp | 6 +- include/boost/redis/detail/multiplexer.hpp | 5 +- include/boost/redis/detail/read_buffer.hpp | 99 ++++++++- include/boost/redis/impl/exec_one_fsm.ipp | 12 +- include/boost/redis/impl/multiplexer.ipp | 20 +- include/boost/redis/impl/read_buffer.ipp | 92 +++++--- include/boost/redis/impl/sentinel_utils.hpp | 1 + test/test_exec_one_fsm.cpp | 95 ++++---- test/test_read_buffer.cpp | 226 ++++++++++++++++---- 11 files changed, 414 insertions(+), 148 deletions(-) diff --git a/include/boost/redis/config.hpp b/include/boost/redis/config.hpp index 98bb7624..a55c2d87 100644 --- a/include/boost/redis/config.hpp +++ b/include/boost/redis/config.hpp @@ -322,8 +322,8 @@ struct config { /** @brief Maximum size of the socket read-buffer in bytes. * - * Sets a limit on how much data is allowed to be read into the - * read buffer. It can be used to prevent DDOS. + * Sets a limit on how large the read-buffer is allowed to grow. It can be + * used to prevent DDOS. * * When using Sentinel, this setting applies to masters, replicas and Sentinels. */ diff --git a/include/boost/redis/connection.hpp b/include/boost/redis/connection.hpp index 46184c2c..5065b364 100644 --- a/include/boost/redis/connection.hpp +++ b/include/boost/redis/connection.hpp @@ -301,7 +301,7 @@ struct exec_one_op { void operator()(Self& self, system::error_code ec = {}, std::size_t bytes_written = 0u) { exec_one_action act = fsm_.resume( - conn_->st_.mpx.get_read_buffer(), + conn_->st_.mpx, ec, bytes_written, self.get_cancellation_state().cancelled()); diff --git a/include/boost/redis/detail/exec_one_fsm.hpp b/include/boost/redis/detail/exec_one_fsm.hpp index 4ad3bd3e..84a54a43 100644 --- a/include/boost/redis/detail/exec_one_fsm.hpp +++ b/include/boost/redis/detail/exec_one_fsm.hpp @@ -21,7 +21,7 @@ namespace boost::redis::detail { -class read_buffer; +class multiplexer; // What should we do next? enum class exec_one_action_type @@ -57,8 +57,10 @@ class exec_one_fsm { , remaining_responses_(expected_responses) { } + // Instead of using the read_buffer directly we use its facade in the + // multiplexer because it keeps track of usage information. exec_one_action resume( - read_buffer& buffer, + multiplexer& mpx, system::error_code ec, std::size_t bytes_transferred, asio::cancellation_type_t cancel_state); diff --git a/include/boost/redis/detail/multiplexer.hpp b/include/boost/redis/detail/multiplexer.hpp index 797c9493..c8c313c4 100644 --- a/include/boost/redis/detail/multiplexer.hpp +++ b/include/boost/redis/detail/multiplexer.hpp @@ -191,7 +191,7 @@ class multiplexer { auto get_prepared_read_buffer() noexcept -> read_buffer::span_type; [[nodiscard]] - auto prepare_read() noexcept -> system::error_code; + auto prepare_read()-> system::error_code; void commit_read(std::size_t read_size); @@ -209,7 +209,7 @@ class multiplexer { void set_config(config const& cfg); private: - void commit_usage(bool is_push, read_buffer::consume_result res); + void commit_usage(bool is_push, std::size_t consumed); [[nodiscard]] auto is_next_push(std::string_view data) const noexcept -> bool; @@ -229,6 +229,7 @@ class multiplexer { bool cancel_run_called_ = false; usage usage_; any_adapter receive_adapter_; + std::size_t append_size_ = 4096u; }; auto make_elem(request const& req, any_adapter adapter) -> std::shared_ptr; diff --git a/include/boost/redis/detail/read_buffer.hpp b/include/boost/redis/detail/read_buffer.hpp index 965845ec..b5573a4a 100644 --- a/include/boost/redis/detail/read_buffer.hpp +++ b/include/boost/redis/detail/read_buffer.hpp @@ -1,4 +1,4 @@ -/* Copyright (c) 2018-2025 Marcelo Zimbres Silva (mzimbres@gmail.com) +/* Copyright (c) 2018-2026 Marcelo Zimbres Silva (mzimbres@gmail.com) * * Distributed under the Boost Software License, Version 1.0. (See * accompanying file LICENSE.txt) @@ -17,24 +17,95 @@ namespace boost::redis::detail { +// Buffer class used in read operations that implements lazy rotations. It is +// split in three main parts +// +// 1. Consumed: Bytes that can be discarded but haven't to avoid rotating +// the buffer unnecessarily. +// +// 2. Commited: Area with data that is in use by the client code. +// +// 3. Prepared: Area waiting for data to be copied into it. +// +// The dynamics of the read-buffer is exemplified below +// +// 1. The buffer is empty and app start +// +// || +// +// 2. Client code calls "prepare" to reserve n bytes at the end of the +// buffer +// +// |+++++++++++++++++| +// +// 3. Bytes are read from the socket into that area and commited with the +// commit member function +// +// |-----------| +// +// 4. The steps above are repeated until the amount of data needed by the +// application is reached +// +// |-----------| +// |-----------+++++++++++++++++| Prepare +// |-----------------| Commit +// |-----------------++++++++++++++++| Prepare +// |---------------------------| Commit +// ... +// +// 5. Commited bytes are processed by the client and consumed. The consume +// op won't discard any data but increase an offset instead +// +// |============---------------| Consume +// +// 6. If preparing would cause the capacity to have to be increased we first +// discard already consumed data to perhaps avoid the reallocation +// +// |---------------| After the rotation +// |---------------++++++++++++++++| When prepare returns +// +// Buffer rotations can in principle be implemented both in the consume and in +// the prepare operation, the latter however produces better results because +// Redis commands are often very small e.g. "+OK\r\n" and a single read from +// the socket might bring in multiple responses, for example +// +// | Contains 100 responses | +// | | +// |=========-------------------------------| +// +// Consuming each response one by one would produce +// +// |=========-------------------------------| +// |===========-----------------------------| +// |=============---------------------------| +// |===============-------------------------| +// |=================-----------------------| +// |===================---------------------| +// +// When a prepare call comes we would like the consumed bytes to be zero so no +// reallocation must be performed, that can only be implemented in the consume +// op if it rotates eagerly i.e. on every consume call, something we don't want. + class read_buffer { public: using span_type = span; - struct consume_result { - std::size_t consumed; + struct prepare_result { std::size_t rotated; + system::error_code ec; }; // See config.hpp for the meaning of these parameters. struct config { - std::size_t read_buffer_append_size = 4096u; - std::size_t max_read_size = static_cast(-1); + std::size_t max_size = static_cast(-1); }; + read_buffer() noexcept = default; + read_buffer(config cfg) noexcept; + // Prepare the buffer to receive more data. [[nodiscard]] - auto prepare() -> system::error_code; + auto prepare(std::size_t append_size) -> prepare_result; [[nodiscard]] auto get_prepared() noexcept -> span_type; @@ -46,9 +117,7 @@ class read_buffer { void clear(); - // Consumes committed data by rotating the remaining data to the - // front of the buffer. - auto consume(std::size_t size) -> consume_result; + auto consume(std::size_t n) -> std::size_t; void reserve(std::size_t n); @@ -58,10 +127,20 @@ class read_buffer { void set_config(config const& cfg) noexcept { cfg_ = cfg; }; + // Returns the total size: consumed + commited + prepared. + std::size_t size() const noexcept + { return buffer_.size(); } + + std::size_t capacity() const noexcept + { return buffer_.capacity(); } + private: + bool needs_rotation(std::size_t append_size) const noexcept; + config cfg_ = config{}; std::vector buffer_; - std::size_t append_buf_begin_ = 0; + std::size_t consumed_ = 0; + std::size_t prepared_begin_ = 0; }; } // namespace boost::redis::detail diff --git a/include/boost/redis/impl/exec_one_fsm.ipp b/include/boost/redis/impl/exec_one_fsm.ipp index b4a7250b..50373986 100644 --- a/include/boost/redis/impl/exec_one_fsm.ipp +++ b/include/boost/redis/impl/exec_one_fsm.ipp @@ -27,7 +27,7 @@ namespace boost::redis::detail { exec_one_action exec_one_fsm::resume( - read_buffer& buffer, + multiplexer& mpx, system::error_code ec, std::size_t bytes_transferred, asio::cancellation_type_t cancel_state) @@ -49,10 +49,10 @@ exec_one_action exec_one_fsm::resume( return system::error_code{}; // Read responses until we're done - buffer.clear(); + mpx.get_read_buffer().clear(); while (true) { // Prepare the buffer to read some data - ec = buffer.prepare(); + ec = mpx.prepare_read(); if (ec) return ec; @@ -66,16 +66,16 @@ exec_one_action exec_one_fsm::resume( return ec; // Commit the data into the buffer - buffer.commit(bytes_transferred); + mpx.commit_read(bytes_transferred); // Consume the data until we run out or all the responses have been read - while (resp3::parse(parser_, buffer.get_commited(), adapter_, ec)) { + while (resp3::parse(parser_, mpx.get_read_buffer().get_commited(), adapter_, ec)) { // Check for errors if (ec) return ec; // We've finished parsing a response - buffer.consume(parser_.get_consumed()); + mpx.get_read_buffer().consume(parser_.get_consumed()); parser_.reset(); // When no more responses remain, we're done. diff --git a/include/boost/redis/impl/multiplexer.ipp b/include/boost/redis/impl/multiplexer.ipp index b6d118ce..f548b2ab 100644 --- a/include/boost/redis/impl/multiplexer.ipp +++ b/include/boost/redis/impl/multiplexer.ipp @@ -174,13 +174,18 @@ std::pair multiplexer::consume(system::error_code& parser_.reset(); auto const res = read_buffer_.consume(consumed); commit_usage(ret == consume_result::got_push, res); - return std::make_pair(ret, res.consumed); + return std::make_pair(ret, res); } return std::make_pair(consume_result::needs_more, consumed); } -auto multiplexer::prepare_read() noexcept -> system::error_code { return read_buffer_.prepare(); } +auto multiplexer::prepare_read() -> system::error_code +{ + auto const res = read_buffer_.prepare(append_size_); + usage_.bytes_rotated += res.rotated; + return res.ec; +} auto multiplexer::get_prepared_read_buffer() noexcept -> read_buffer::span_type { @@ -286,18 +291,16 @@ void multiplexer::cancel_on_conn_lost() }); } -void multiplexer::commit_usage(bool is_push, read_buffer::consume_result res) +void multiplexer::commit_usage(bool is_push, std::size_t consumed) { if (is_push) { usage_.pushes_received += 1; - usage_.push_bytes_received += res.consumed; + usage_.push_bytes_received += consumed; on_push_ = false; } else { usage_.responses_received += 1; - usage_.response_bytes_received += res.consumed; + usage_.response_bytes_received += consumed; } - - usage_.bytes_rotated += res.rotated; } bool multiplexer::is_next_push(std::string_view data) const noexcept @@ -359,7 +362,8 @@ void multiplexer::set_receive_adapter(any_adapter adapter) void multiplexer::set_config(config const& cfg) { - read_buffer_.set_config({cfg.read_buffer_append_size, cfg.max_read_size}); + append_size_ = cfg.read_buffer_append_size; + read_buffer_.set_config({cfg.max_read_size}); } auto make_elem(request const& req, any_adapter adapter) -> std::shared_ptr diff --git a/include/boost/redis/impl/read_buffer.ipp b/include/boost/redis/impl/read_buffer.ipp index b3705549..e817251b 100644 --- a/include/boost/redis/impl/read_buffer.ipp +++ b/include/boost/redis/impl/read_buffer.ipp @@ -13,66 +13,108 @@ namespace boost::redis::detail { -system::error_code read_buffer::prepare() +read_buffer::read_buffer(config cfg) noexcept +: cfg_{cfg} { - BOOST_ASSERT(append_buf_begin_ == buffer_.size()); +} + +bool read_buffer::needs_rotation(std::size_t append_size) const noexcept +{ + if (consumed_ == 0u) + return false; + + // If preparing would cause the capacity to have to be increased we first + // discard already consumed data to perhaps avoid the reallocation. + auto const capacity = buffer_.capacity(); + auto const size = buffer_.size(); + auto const remaining = capacity - size; + if (remaining < append_size) + return true; + + // Rotate if increasing the buffer size would require more than max. + return buffer_.size() > (cfg_.max_size - append_size); +} + +read_buffer::prepare_result read_buffer::prepare(std::size_t append_size) +{ + BOOST_ASSERT(prepared_begin_ == buffer_.size()); - auto const new_size = append_buf_begin_ + cfg_.read_buffer_append_size; + if (append_size > cfg_.max_size) { + return {0u, error::exceeds_maximum_read_buffer_size}; + } + + std::size_t rotated = 0u; + if (needs_rotation(append_size)) { + BOOST_ASSERT(consumed_ != 0u); + buffer_.erase(buffer_.begin(), buffer_.begin() + consumed_); + rotated = buffer_.size(); + + BOOST_ASSERT(consumed_ <= prepared_begin_); + prepared_begin_ -= consumed_; + consumed_ = 0u; + } + + auto const new_size = prepared_begin_ + append_size; - if (new_size > cfg_.max_read_size) { - return error::exceeds_maximum_read_buffer_size; + if (new_size > cfg_.max_size) { + return {rotated, error::exceeds_maximum_read_buffer_size}; } - buffer_.resize(new_size); - return {}; + { + auto const start_capacity = buffer_.capacity(); + auto const start_size = buffer_.size(); + buffer_.resize(new_size); + auto const end_capacity = buffer_.capacity(); + if (end_capacity > start_capacity) + rotated += start_size; + } + + return {rotated, {}}; } void read_buffer::commit(std::size_t read_size) { - BOOST_ASSERT(buffer_.size() >= (append_buf_begin_ + read_size)); - buffer_.resize(append_buf_begin_ + read_size); - append_buf_begin_ = buffer_.size(); + BOOST_ASSERT(buffer_.size() >= (prepared_begin_ + read_size)); + buffer_.resize(prepared_begin_ + read_size); + prepared_begin_ = buffer_.size(); } auto read_buffer::get_prepared() noexcept -> span_type { auto const size = buffer_.size(); - return make_span(buffer_.data() + append_buf_begin_, size - append_buf_begin_); + return make_span(buffer_.data() + prepared_begin_, size - prepared_begin_); } auto read_buffer::get_commited() const noexcept -> std::string_view { - return {buffer_.data(), append_buf_begin_}; + return {buffer_.data() + consumed_, prepared_begin_ - consumed_}; } void read_buffer::clear() { buffer_.clear(); - append_buf_begin_ = 0; + consumed_ = 0; + prepared_begin_ = 0; } -read_buffer::consume_result -read_buffer::consume(std::size_t size) +std::size_t read_buffer::consume(std::size_t n) { // For convenience, if the requested size is larger than the // committed buffer we cap it to the maximum. - if (size > append_buf_begin_) - size = append_buf_begin_; - - buffer_.erase(buffer_.begin(), buffer_.begin() + size); - auto const rotated = size == 0u ? 0u : buffer_.size(); - - BOOST_ASSERT(append_buf_begin_ >= size); - append_buf_begin_ -= size; + auto const consumable = prepared_begin_ - consumed_; + if (n > consumable) + n = consumable; - return {size, rotated}; + consumed_ += n; + BOOST_ASSERT(consumed_ <= prepared_begin_); + return n; } void read_buffer::reserve(std::size_t n) { buffer_.reserve(n); } bool operator==(read_buffer const& lhs, read_buffer const& rhs) { - return lhs.buffer_ == rhs.buffer_ && lhs.append_buf_begin_ == rhs.append_buf_begin_; + return lhs.buffer_ == rhs.buffer_ && lhs.prepared_begin_ == rhs.prepared_begin_; } bool operator!=(read_buffer const& lhs, read_buffer const& rhs) { return !(lhs == rhs); } diff --git a/include/boost/redis/impl/sentinel_utils.hpp b/include/boost/redis/impl/sentinel_utils.hpp index 36586629..dcb8efd1 100644 --- a/include/boost/redis/impl/sentinel_utils.hpp +++ b/include/boost/redis/impl/sentinel_utils.hpp @@ -25,6 +25,7 @@ #include #include #include +#include namespace boost::redis::detail { diff --git a/test/test_exec_one_fsm.cpp b/test/test_exec_one_fsm.cpp index 1e5d7c02..93c90bae 100644 --- a/test/test_exec_one_fsm.cpp +++ b/test/test_exec_one_fsm.cpp @@ -8,7 +8,7 @@ #include #include -#include +#include #include #include #include @@ -31,6 +31,7 @@ using detail::exec_one_fsm; using detail::exec_one_action; using detail::exec_one_action_type; using detail::read_buffer; +using detail::multiplexer; using boost::system::error_code; using boost::asio::cancellation_type_t; using parse_event = any_adapter::parse_event; @@ -109,20 +110,20 @@ void test_success() // Setup std::vector events; exec_one_fsm fsm{make_snoop_adapter(events), 2u}; - read_buffer buff; + multiplexer mpx; // Write the request - auto act = fsm.resume(buff, error_code(), 0u, cancellation_type_t::none); + auto act = fsm.resume(mpx, error_code(), 0u, cancellation_type_t::none); BOOST_TEST_EQ(act, exec_one_action_type::write); // FSM should now ask for data - act = fsm.resume(buff, error_code(), 25u, cancellation_type_t::none); + act = fsm.resume(mpx, error_code(), 25u, cancellation_type_t::none); BOOST_TEST_EQ(act, exec_one_action_type::read_some); // Read the entire response in one go constexpr std::string_view payload = "$5\r\nhello\r\n*1\r\n+goodbye\r\n"; - copy_to(buff, payload); - act = fsm.resume(buff, error_code(), payload.size(), cancellation_type_t::none); + copy_to(mpx.get_read_buffer(), payload); + act = fsm.resume(mpx, error_code(), payload.size(), cancellation_type_t::none); BOOST_TEST_EQ(act, exec_one_action_type::done); // Verify the adapter calls @@ -144,14 +145,14 @@ void test_no_expected_response() // Setup std::vector events; exec_one_fsm fsm{make_snoop_adapter(events), 0u}; - read_buffer buff; + multiplexer mpx; // Write the request - auto act = fsm.resume(buff, error_code(), 0u, cancellation_type_t::none); + auto act = fsm.resume(mpx, error_code(), 0u, cancellation_type_t::none); BOOST_TEST_EQ(act, exec_one_action_type::write); // FSM shouldn't ask for data - act = fsm.resume(buff, error_code(), 25u, cancellation_type_t::none); + act = fsm.resume(mpx, error_code(), 25u, cancellation_type_t::none); BOOST_TEST_EQ(act, error_code()); // No adapter calls should be done @@ -164,28 +165,28 @@ void test_short_reads() // Setup std::vector events; exec_one_fsm fsm{make_snoop_adapter(events), 2u}; - read_buffer buff; + multiplexer mpx; // Write the request - auto act = fsm.resume(buff, error_code(), 0u, cancellation_type_t::none); + auto act = fsm.resume(mpx, error_code(), 0u, cancellation_type_t::none); BOOST_TEST_EQ(act, exec_one_action_type::write); // FSM should now ask for data - act = fsm.resume(buff, error_code(), 25u, cancellation_type_t::none); + act = fsm.resume(mpx, error_code(), 25u, cancellation_type_t::none); BOOST_TEST_EQ(act, exec_one_action_type::read_some); // Read fragments constexpr std::string_view payload = "$5\r\nhello\r\n*1\r\n+goodbye\r\n"; - copy_to(buff, payload.substr(0, 6u)); - act = fsm.resume(buff, error_code(), 6u, cancellation_type_t::none); + copy_to(mpx.get_read_buffer(), payload.substr(0, 6u)); + act = fsm.resume(mpx, error_code(), 6u, cancellation_type_t::none); BOOST_TEST_EQ(act, exec_one_action_type::read_some); - copy_to(buff, payload.substr(6, 10u)); - act = fsm.resume(buff, error_code(), 10u, cancellation_type_t::none); + copy_to(mpx.get_read_buffer(), payload.substr(6, 10u)); + act = fsm.resume(mpx, error_code(), 10u, cancellation_type_t::none); BOOST_TEST_EQ(act, exec_one_action_type::read_some); - copy_to(buff, payload.substr(16)); - act = fsm.resume(buff, error_code(), payload.substr(16).size(), cancellation_type_t::none); + copy_to(mpx.get_read_buffer(), payload.substr(16)); + act = fsm.resume(mpx, error_code(), payload.substr(16).size(), cancellation_type_t::none); BOOST_TEST_EQ(act, exec_one_action_type::done); // Verify the adapter calls @@ -207,14 +208,14 @@ void test_write_error() // Setup std::vector events; exec_one_fsm fsm{make_snoop_adapter(events), 2u}; - read_buffer buff; + multiplexer mpx; // Write the request - auto act = fsm.resume(buff, error_code(), 0u, cancellation_type_t::none); + auto act = fsm.resume(mpx, error_code(), 0u, cancellation_type_t::none); BOOST_TEST_EQ(act, exec_one_action_type::write); // Write error - act = fsm.resume(buff, asio::error::connection_reset, 10u, cancellation_type_t::none); + act = fsm.resume(mpx, asio::error::connection_reset, 10u, cancellation_type_t::none); BOOST_TEST_EQ(act, error_code(asio::error::connection_reset)); } @@ -223,14 +224,14 @@ void test_write_cancel() // Setup std::vector events; exec_one_fsm fsm{make_snoop_adapter(events), 2u}; - read_buffer buff; + multiplexer mpx; // Write the request - auto act = fsm.resume(buff, error_code(), 0u, cancellation_type_t::none); + auto act = fsm.resume(mpx, error_code(), 0u, cancellation_type_t::none); BOOST_TEST_EQ(act, exec_one_action_type::write); // Edge case where the operation finished successfully but with the cancellation state set - act = fsm.resume(buff, error_code(), 10u, cancellation_type_t::terminal); + act = fsm.resume(mpx, error_code(), 10u, cancellation_type_t::terminal); BOOST_TEST_EQ(act, error_code(asio::error::operation_aborted)); } @@ -240,18 +241,18 @@ void test_read_error() // Setup std::vector events; exec_one_fsm fsm{make_snoop_adapter(events), 2u}; - read_buffer buff; + multiplexer mpx; // Write the request - auto act = fsm.resume(buff, error_code(), 0u, cancellation_type_t::none); + auto act = fsm.resume(mpx, error_code(), 0u, cancellation_type_t::none); BOOST_TEST_EQ(act, exec_one_action_type::write); // FSM should now ask for data - act = fsm.resume(buff, error_code(), 25u, cancellation_type_t::none); + act = fsm.resume(mpx, error_code(), 25u, cancellation_type_t::none); BOOST_TEST_EQ(act, exec_one_action_type::read_some); // Read error - act = fsm.resume(buff, asio::error::network_reset, 0u, cancellation_type_t::none); + act = fsm.resume(mpx, asio::error::network_reset, 0u, cancellation_type_t::none); BOOST_TEST_EQ(act, error_code(asio::error::network_reset)); } @@ -260,19 +261,19 @@ void test_read_cancelled() // Setup std::vector events; exec_one_fsm fsm{make_snoop_adapter(events), 2u}; - read_buffer buff; + multiplexer mpx; // Write the request - auto act = fsm.resume(buff, error_code(), 0u, cancellation_type_t::none); + auto act = fsm.resume(mpx, error_code(), 0u, cancellation_type_t::none); BOOST_TEST_EQ(act, exec_one_action_type::write); // FSM should now ask for data - act = fsm.resume(buff, error_code(), 25u, cancellation_type_t::none); + act = fsm.resume(mpx, error_code(), 25u, cancellation_type_t::none); BOOST_TEST_EQ(act, exec_one_action_type::read_some); // Edge case where the operation finished successfully but with the cancellation state set - copy_to(buff, "$5\r\n"); - act = fsm.resume(buff, error_code(), 4u, cancellation_type_t::terminal); + copy_to(mpx.get_read_buffer(), "$5\r\n"); + act = fsm.resume(mpx, error_code(), 4u, cancellation_type_t::terminal); BOOST_TEST_EQ(act, error_code(asio::error::operation_aborted)); } @@ -282,15 +283,15 @@ void test_buffer_prepare_error() // Setup std::vector events; exec_one_fsm fsm{make_snoop_adapter(events), 2u}; - read_buffer buff; - buff.set_config({4096u, 8u}); // max size is 8 bytes + multiplexer mpx; + mpx.get_read_buffer().set_config({8u}); // max size is 8 bytes // Write the request - auto act = fsm.resume(buff, error_code(), 0u, cancellation_type_t::none); + auto act = fsm.resume(mpx, error_code(), 0u, cancellation_type_t::none); BOOST_TEST_EQ(act, exec_one_action_type::write); // When preparing the buffer, we encounter an error - act = fsm.resume(buff, error_code(), 25u, cancellation_type_t::none); + act = fsm.resume(mpx, error_code(), 25u, cancellation_type_t::none); BOOST_TEST_EQ(act, error_code(error::exceeds_maximum_read_buffer_size)); } @@ -300,20 +301,20 @@ void test_parse_error() // Setup std::vector events; exec_one_fsm fsm{make_snoop_adapter(events), 2u}; - read_buffer buff; + multiplexer mpx; // Write the request - auto act = fsm.resume(buff, error_code(), 0u, cancellation_type_t::none); + auto act = fsm.resume(mpx, error_code(), 0u, cancellation_type_t::none); BOOST_TEST_EQ(act, exec_one_action_type::write); // FSM should now ask for data - act = fsm.resume(buff, error_code(), 25u, cancellation_type_t::none); + act = fsm.resume(mpx, error_code(), 25u, cancellation_type_t::none); BOOST_TEST_EQ(act, exec_one_action_type::read_some); // The response contains an invalid message constexpr std::string_view payload = "$bad\r\n"; - copy_to(buff, payload); - act = fsm.resume(buff, error_code(), payload.size(), cancellation_type_t::none); + copy_to(mpx.get_read_buffer(), payload); + act = fsm.resume(mpx, error_code(), payload.size(), cancellation_type_t::none); BOOST_TEST_EQ(act, error_code(error::not_a_number)); } @@ -326,20 +327,20 @@ void test_adapter_error() ec = error::empty_field; }}; exec_one_fsm fsm{std::move(adapter), 2u}; - read_buffer buff; + multiplexer mpx; // Write the request - auto act = fsm.resume(buff, error_code(), 0u, cancellation_type_t::none); + auto act = fsm.resume(mpx, error_code(), 0u, cancellation_type_t::none); BOOST_TEST_EQ(act, exec_one_action_type::write); // FSM should now ask for data - act = fsm.resume(buff, error_code(), 25u, cancellation_type_t::none); + act = fsm.resume(mpx, error_code(), 25u, cancellation_type_t::none); BOOST_TEST_EQ(act, exec_one_action_type::read_some); // Read the entire response in one go constexpr std::string_view payload = "$5\r\nhello\r\n*1\r\n+goodbye\r\n"; - copy_to(buff, payload); - act = fsm.resume(buff, error_code(), payload.size(), cancellation_type_t::none); + copy_to(mpx.get_read_buffer(), payload); + act = fsm.resume(mpx, error_code(), payload.size(), cancellation_type_t::none); BOOST_TEST_EQ(act, error_code(error::empty_field)); } diff --git a/test/test_read_buffer.cpp b/test/test_read_buffer.cpp index 85c0b07c..515e2dd6 100644 --- a/test/test_read_buffer.cpp +++ b/test/test_read_buffer.cpp @@ -14,89 +14,225 @@ using namespace boost::redis; using detail::read_buffer; using boost::system::error_code; +// NOTE1: The vector allocation behavior depends on the implementation. +// The test might not pass if we start testing on a new platform. + namespace { -void test_prepare_error() +void test_prepare_equals_max() { - read_buffer buf; + read_buffer buf{{10}}; - // Usual case, max size is bigger then requested size. - buf.set_config({10, 10}); - auto ec = buf.prepare(); - BOOST_TEST_EQ(ec, error_code()); - buf.commit(10); + // Corner case: prepare equals max. + auto const res = buf.prepare(10); + BOOST_TEST_EQ(res.ec, error_code()); +} - // Corner case, max size is equal to the requested size. - buf.set_config({10, 20}); - ec = buf.prepare(); - BOOST_TEST_EQ(ec, error_code()); - buf.commit(10); - buf.consume(20); +// TODO: bigger than max can happen in two situations. Check both +void test_prepare_bigger_than_max() +{ + read_buffer buf{{10}}; + auto const res = buf.prepare(11); + BOOST_TEST_EQ(res.ec, error_code{error::exceeds_maximum_read_buffer_size}); +} - auto const tmp = buf; +/* | max size | + * + * 1. |++++++++++++++++| prepare(16) + * 2. |----------------| commit(16) + * 3. |=========-------| consume(9) + * + * In this state the buffer has size 16 and a maximum configure size 20. + * Preparing for another 5 bytes exceeds the max size by one but should not + * fail since the 9 bytes in the front should be rotated by the implementation. + * + * 4. |=========-------| consume(9) + * 5. |-------+++++| prepare(5) + * + */ +void test_consume_avoids_prepare_max_error() +{ + read_buffer buf{{20}}; - // Error case, max size is smaller to the requested size. - buf.set_config({10, 9}); - ec = buf.prepare(); - BOOST_TEST_EQ(ec, error_code{error::exceeds_maximum_read_buffer_size}); + auto res = buf.prepare(16); + BOOST_TEST_EQ(res.ec, error_code()); + buf.commit(16); + auto consumed = buf.consume(9); + BOOST_TEST_EQ(consumed, 9u); - // Check that an error call has no side effects. - BOOST_TEST(buf == tmp); + res = buf.prepare(5); + BOOST_TEST_EQ(res.ec, error_code()); + BOOST_TEST_EQ(res.rotated, 7u); } void test_prepare_consume_only_committed_data() { - read_buffer buf; + read_buffer buf{{10}}; + + auto res = buf.prepare(10); + BOOST_TEST(!res.ec); + BOOST_TEST_EQ(res.rotated, 0u); + + // No data has been committed yet so nothing can be consumed. + auto consumed = buf.consume(5); + BOOST_TEST_EQ(consumed, 0u); + + buf.commit(10); + consumed = buf.consume(5); - buf.set_config({10, 10}); - auto ec = buf.prepare(); - BOOST_TEST(!ec); + // All five bytes should have been consumed. + BOOST_TEST_EQ(consumed, 5u); - auto res = buf.consume(5); + consumed = buf.consume(7); - // No data has been committed yet so nothing can be consummed. - BOOST_TEST_EQ(res.consumed, 0u); + // Only the remaining five bytes can be consumed + BOOST_TEST_EQ(consumed, 5u); +} - // If nothing was consumed, nothing got rotated. +void test_check_buffer_size() +{ + read_buffer buf{{10}}; + + auto res = buf.prepare(10); + BOOST_TEST_EQ(res.ec, error_code()); BOOST_TEST_EQ(res.rotated, 0u); - buf.commit(10); - res = buf.consume(5); + BOOST_TEST_EQ(buf.get_prepared().size(), 10u); +} - // All five bytes should have been consumed. - BOOST_TEST_EQ(res.consumed, 5u); +void test_prepared_erased_after_commit() +{ + read_buffer buf; - // We added a total of 10 bytes and consumed 5, that means, 5 were - // rotated. - BOOST_TEST_EQ(res.rotated, 5u); + auto res = buf.prepare(10); + BOOST_TEST_EQ(res.ec, error_code()); + BOOST_TEST_EQ(res.rotated, 0u); - res = buf.consume(7); + buf.commit(7); + auto prep = buf.get_prepared().size(); + BOOST_TEST_EQ(prep, 0u); - // Only the remaining five bytes can be consumed - BOOST_TEST_EQ(res.consumed, 5u); + res = buf.prepare(10); + prep = buf.get_prepared().size(); + BOOST_TEST_EQ(prep, 10u); +} - // No bytes to rotated. +/* 1. |++++++++++| - prepare(10) + * 2. |-------| - commit(7) + * 3. |-------++++++++++| - prepare(10) + * 4. |--------------| - commit(7) + * 5. |======--------| - consume(5) + */ +void test_prep_commit_consume_sizes() +{ + read_buffer buf; + + // 1. + auto res = buf.prepare(10); + BOOST_TEST_EQ(res.ec, error_code()); BOOST_TEST_EQ(res.rotated, 0u); + + // 2. + buf.commit(7); + BOOST_TEST_EQ(buf.size(), 7u); + auto prep = buf.get_prepared().size(); + BOOST_TEST_EQ(prep, 0u); + BOOST_TEST_EQ(buf.get_commited().size(), 7u); + + // 3. + res = buf.prepare(10); + BOOST_TEST_EQ(buf.size(), 17u); + prep = buf.get_prepared().size(); + BOOST_TEST_EQ(prep, 10u); + + // 4. + buf.commit(7); + BOOST_TEST_EQ(buf.size(), 14u); + prep = buf.get_prepared().size(); + BOOST_TEST_EQ(prep, 0u); + BOOST_TEST_EQ(buf.get_commited().size(), 14u); + + // 5. + buf.consume(5); + BOOST_TEST_EQ(buf.size(), 14u); + prep = buf.get_prepared().size(); + BOOST_TEST_EQ(prep, 0u); + BOOST_TEST_EQ(buf.get_commited().size(), 9u); } -void test_check_buffer_size() +/* | capacity | + * + * 1. |++++++++++++++++++++| prepare(20) + * 2. |--------------------| commit(20) + * 3. |===============-----| consume(15) + * 4. |-----++++++| prepare(6) + * + * Without rotation that last step would cause reallocation. The implementation + * should avoid that. + */ +void test_prepare_rotates_to_avoid_realloc() { read_buffer buf; + buf.reserve(25); + BOOST_TEST_EQ(buf.capacity(), 25); - buf.set_config({10, 10}); - auto ec = buf.prepare(); - BOOST_TEST_EQ(ec, error_code()); + // 1. + auto res = buf.prepare(20); + BOOST_TEST_EQ(res.ec, error_code()); - BOOST_TEST_EQ(buf.get_prepared().size(), 10u); + // 2. + buf.commit(20); + + // 3. + auto consumed = buf.consume(15); + BOOST_TEST_EQ(consumed, 15u); + + // 4. + res = buf.prepare(6); + BOOST_TEST_EQ(res.ec, error_code()); + BOOST_TEST_EQ(res.rotated, 5u); + BOOST_TEST_EQ(buf.capacity(), 25); +} + +/* 1. |++++++++++++++++| prepare(16) + * 2. |----------| commit(10) + * 3. |----------++++++++++| prepare(10) + * + * Step 3. should rotate no data since there is no consumed data. + */ +void test_no_rotation_when_consumed_zero() +{ + read_buffer buf; + buf.reserve(20); + BOOST_TEST_EQ(buf.capacity(), 20); + + // 1. + auto res = buf.prepare(16); + BOOST_TEST_EQ(res.ec, error_code()); + BOOST_TEST_EQ(res.rotated, 0u); + + // 2. + buf.commit(10); + + // 3. + res = buf.prepare(10); + BOOST_TEST_EQ(res.ec, error_code()); + BOOST_TEST_EQ(res.rotated, 0u); } } // namespace int main() { - test_prepare_error(); test_prepare_consume_only_committed_data(); test_check_buffer_size(); + test_prepared_erased_after_commit(); + test_prep_commit_consume_sizes(); + test_prepare_equals_max(); + test_prepare_bigger_than_max(); + test_consume_avoids_prepare_max_error(); + test_prepare_rotates_to_avoid_realloc(); + test_no_rotation_when_consumed_zero(); return boost::report_errors(); } From 7a622e9b13179977aa4c3b2d6d6fb3ac98bb0c95 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Anarthal=20=28Rub=C3=A9n=20P=C3=A9rez=29?= <34971811+anarthal@users.noreply.github.com> Date: Sun, 24 May 2026 13:48:04 +0200 Subject: [PATCH 11/14] Adds initializers to config to support designated initializers (#424) close #407 --- include/boost/redis/config.hpp | 4 +-- test/CMakeLists.txt | 1 + test/Jamfile | 1 + test/test_config.cpp | 55 ++++++++++++++++++++++++++++++++++ 4 files changed, 59 insertions(+), 2 deletions(-) create mode 100644 test/test_config.cpp diff --git a/include/boost/redis/config.hpp b/include/boost/redis/config.hpp index a55c2d87..326133c9 100644 --- a/include/boost/redis/config.hpp +++ b/include/boost/redis/config.hpp @@ -162,7 +162,7 @@ struct config { * * UNIX domain sockets can't be used with Sentinel. */ - std::string unix_socket; + std::string unix_socket{}; /** @brief (Deprecated) Username used for authentication during connection establishment. * @@ -212,7 +212,7 @@ struct config { * cfg.setup.hello("my_username", "my_password"); * @endcode */ - std::string password; + std::string password{}; /** @brief (Deprecated) Client name parameter to use during connection establishment. * diff --git a/test/CMakeLists.txt b/test/CMakeLists.txt index 385914b6..9631a701 100644 --- a/test/CMakeLists.txt +++ b/test/CMakeLists.txt @@ -52,6 +52,7 @@ boost_redis_make_test(test_generic_flat_response) boost_redis_make_test(test_read_buffer) boost_redis_make_test(test_subscription_tracker) boost_redis_make_test(test_push_parser) +boost_redis_make_test(test_config) # Tests that require a real Redis server if (BOOST_REDIS_INTEGRATION_TESTS) diff --git a/test/Jamfile b/test/Jamfile index 3ddb1be7..91e1529c 100644 --- a/test/Jamfile +++ b/test/Jamfile @@ -91,6 +91,7 @@ local tests = test_read_buffer test_subscription_tracker test_push_parser + test_config ; # Build and run the tests diff --git a/test/test_config.cpp b/test/test_config.cpp new file mode 100644 index 00000000..a43982c1 --- /dev/null +++ b/test/test_config.cpp @@ -0,0 +1,55 @@ +// +// Copyright (c) 2025 Marcelo Zimbres Silva (mzimbres@gmail.com), +// Ruben Perez Hidalgo (rubenperez038 at gmail dot com) +// +// Distributed under the Boost Software License, Version 1.0. (See accompanying +// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) +// + +#include + +#include + +#if !defined(__cpp_designated_initializers) || (__cpp_designated_initializers < 201707L) + +#include + +BOOST_PRAGMA_MESSAGE("test_config skipped because designated initializers are not supported"); + +int main() { } + +#else + +#include + +using namespace boost::redis; + +namespace { + +// A config object can be created using designated initializers. +// No initializer is missing +void test_designated_initializers_config() +{ + // Most members have initializers + config cfg1{ + .addr = {"127.0.0.1", "2000"}, + .use_setup = true, + }; + + // The ones included above have, too + config cfg2{.unix_socket = "/tmp/sock"}; + + // Sentinel config has them, too + sentinel_config sent_cfg{.addresses = {{"127.0.0.1", "1000"}}}; +} + +} // namespace + +int main() +{ + test_designated_initializers_config(); + + return boost::report_errors(); +} + +#endif From 62b0024fd81d518b3ac75f526e170f26a3561fc0 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Anarthal=20=28Rub=C3=A9n=20P=C3=A9rez=29?= <34971811+anarthal@users.noreply.github.com> Date: Fri, 19 Jun 2026 12:27:26 +0200 Subject: [PATCH 12/14] Package update (#429) --- doc/package-lock.json | 630 +++++++++++++++++++++--------------------- doc/package.json | 4 +- 2 files changed, 320 insertions(+), 314 deletions(-) diff --git a/doc/package-lock.json b/doc/package-lock.json index 4f8ee17a..f5d77300 100644 --- a/doc/package-lock.json +++ b/doc/package-lock.json @@ -5,18 +5,18 @@ "packages": { "": { "dependencies": { + "@cppalliance/antora-cpp-reference-extension": "^0.1.1", "@cppalliance/antora-downloads-extension": "^0.0.2", - "@cppalliance/antora-cpp-reference-extension": "^0.1.0", - "antora": "^3.1.10" + "antora": "^3.1.15" } }, "node_modules/@antora/asciidoc-loader": { - "version": "3.1.10", - "resolved": "https://registry.npmjs.org/@antora/asciidoc-loader/-/asciidoc-loader-3.1.10.tgz", - "integrity": "sha512-np0JkOV37CK7V4eDZUZXf4fQuCKYW3Alxl8FlyzBevXi2Ujv29O82JLbHbv1cyTsvGkGNNB+gzJIx9XBsQ7+Nw==", + "version": "3.1.15", + "resolved": "https://registry.npmjs.org/@antora/asciidoc-loader/-/asciidoc-loader-3.1.15.tgz", + "integrity": "sha512-MVspbcMPmBgxZms0EjmyC9nlCAWBJfHYSwQCXRZn6T7OujRrLvJFPgz+EROz9XOqh4v76BeqgEuLsUJIZjH3cw==", "license": "MPL-2.0", "dependencies": { - "@antora/logger": "3.1.10", + "@antora/logger": "3.1.15", "@antora/user-require-helper": "~3.0", "@asciidoctor/core": "~2.2" }, @@ -25,13 +25,13 @@ } }, "node_modules/@antora/cli": { - "version": "3.1.10", - "resolved": "https://registry.npmjs.org/@antora/cli/-/cli-3.1.10.tgz", - "integrity": "sha512-gp8u9aVM0w1DtWSsB5PwvEfFYKrooPENLhN58RAfdgTrcsTsWw+CDysFZPgEaHB0Y1ZbanR82ZH/f6JVKGcZfQ==", + "version": "3.1.15", + "resolved": "https://registry.npmjs.org/@antora/cli/-/cli-3.1.15.tgz", + "integrity": "sha512-76vLhkyzyFd49WJHsC04oPAZlK4qWbJaeZdS/pLUUVBqgaxeSeNqpTZ0pXo7f+5laGRO19fXyk1eDWGus9h8jA==", "license": "MPL-2.0", "dependencies": { - "@antora/logger": "3.1.10", - "@antora/playbook-builder": "3.1.10", + "@antora/logger": "3.1.15", + "@antora/playbook-builder": "3.1.15", "@antora/user-require-helper": "~3.0", "commander": "~11.1" }, @@ -43,13 +43,13 @@ } }, "node_modules/@antora/content-aggregator": { - "version": "3.1.10", - "resolved": "https://registry.npmjs.org/@antora/content-aggregator/-/content-aggregator-3.1.10.tgz", - "integrity": "sha512-OT6ZcCA7LrtNfrAZUr3hFh+Z/1isKpsfnqFjCDC66NEMqIyzJO99jq0CM66rYlYhyX7mb5BwEua8lHcwpOXNow==", + "version": "3.1.15", + "resolved": "https://registry.npmjs.org/@antora/content-aggregator/-/content-aggregator-3.1.15.tgz", + "integrity": "sha512-w84rJRKx+C4dsSbOHmjg78oM2T6xP9JRDsxpXjTmlh9T4zlNELCB6AD5s6Gztt3S6wlTiCNFLZw0v/HEVtuhzQ==", "license": "MPL-2.0", "dependencies": { "@antora/expand-path-helper": "~3.0", - "@antora/logger": "3.1.10", + "@antora/logger": "3.1.15", "@antora/user-require-helper": "~3.0", "braces": "~3.0", "cache-directory": "~2.0", @@ -93,14 +93,40 @@ "node": ">=12" } }, + "node_modules/@antora/content-aggregator/node_modules/js-yaml": { + "version": "4.1.1", + "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-4.1.1.tgz", + "integrity": "sha512-qQKT4zQxXl8lLwBtHMWwaTcGfFOZviOJet3Oy/xmGk2gZH677CJM9EvtfdSkgWcATZhj/55JZ0rmy3myCT5lsA==", + "license": "MIT", + "dependencies": { + "argparse": "^2.0.1" + }, + "bin": { + "js-yaml": "bin/js-yaml.js" + } + }, + "node_modules/@antora/content-aggregator/node_modules/readable-stream": { + "version": "3.6.2", + "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-3.6.2.tgz", + "integrity": "sha512-9u/sniCrY3D5WdsERHzHE4G2YCXqoG5FTHUiCC4SIbr6XcLZBY05ya9EKjYek9O5xOAwjGq+1JdGBAS7Q9ScoA==", + "license": "MIT", + "dependencies": { + "inherits": "^2.0.3", + "string_decoder": "^1.1.1", + "util-deprecate": "^1.0.1" + }, + "engines": { + "node": ">= 6" + } + }, "node_modules/@antora/content-classifier": { - "version": "3.1.10", - "resolved": "https://registry.npmjs.org/@antora/content-classifier/-/content-classifier-3.1.10.tgz", - "integrity": "sha512-3JJl4IIiTX00v/MirK603NoqIcHjGYAaRWt3Q4U03tI1Fv2Aho/ypO3FE45069jFf0Dx2uDJfp5kapb9gaIjdQ==", + "version": "3.1.15", + "resolved": "https://registry.npmjs.org/@antora/content-classifier/-/content-classifier-3.1.15.tgz", + "integrity": "sha512-m7INbqJcXBZU04HdBMqfL/NvezC3aaJGHHa0KfzeEKICg5FT22cVsEp6mYTgJVT4HqRy7JPCn9UeZvoa9x+MzQ==", "license": "MPL-2.0", "dependencies": { - "@antora/asciidoc-loader": "3.1.10", - "@antora/logger": "3.1.10", + "@antora/asciidoc-loader": "3.1.15", + "@antora/logger": "3.1.15", "mime-types": "~2.1", "vinyl": "~3.0" }, @@ -109,12 +135,12 @@ } }, "node_modules/@antora/document-converter": { - "version": "3.1.10", - "resolved": "https://registry.npmjs.org/@antora/document-converter/-/document-converter-3.1.10.tgz", - "integrity": "sha512-qi9ctgcKal8tZtWflVo66w+4zCJoBmUKRV+eA9aRRR09KDdU9r514vu1adWNgniPppISr90zD13V5l2JUy/2CQ==", + "version": "3.1.15", + "resolved": "https://registry.npmjs.org/@antora/document-converter/-/document-converter-3.1.15.tgz", + "integrity": "sha512-7YZsc/iIJVTxvHKy0/eqPTuRIJupBd7Pq49gWvCxiDBR9Zj4esqMZIU3HaIbkBgqJLlQv5TLBeTjiQ1Qpe1hNw==", "license": "MPL-2.0", "dependencies": { - "@antora/asciidoc-loader": "3.1.10" + "@antora/asciidoc-loader": "3.1.15" }, "engines": { "node": ">=16.0.0" @@ -130,24 +156,24 @@ } }, "node_modules/@antora/file-publisher": { - "version": "3.1.10", - "resolved": "https://registry.npmjs.org/@antora/file-publisher/-/file-publisher-3.1.10.tgz", - "integrity": "sha512-DPR/0d1P+kr3qV4T0Gh81POEO/aCmNWIp/oLUYAhr0HHOcFzgpTUUoLStgcYynZPFRIB7EYKSab+oYSCK17DGA==", + "version": "3.1.15", + "resolved": "https://registry.npmjs.org/@antora/file-publisher/-/file-publisher-3.1.15.tgz", + "integrity": "sha512-UfLYeyD6Na9YXespr3Xjy6OPIAGG6GTbdW3SNn8KxHl3hGeF/AtM3NaR+AJgyOmTb2r9lHzfODXeZevqX+vMww==", "license": "MPL-2.0", "dependencies": { "@antora/expand-path-helper": "~3.0", "@antora/user-require-helper": "~3.0", "vinyl": "~3.0", - "yazl": "~2.5" + "yazl": "~3.3" }, "engines": { "node": ">=16.0.0" } }, "node_modules/@antora/logger": { - "version": "3.1.10", - "resolved": "https://registry.npmjs.org/@antora/logger/-/logger-3.1.10.tgz", - "integrity": "sha512-WSuIxEP2tVrhWtTj/sIrwBDjpi4ldB/1Kpiu4PXmY4/qeWP8thW6u8nXdwdDcWss5zqkZWjourvWKwVq7y8Wjg==", + "version": "3.1.15", + "resolved": "https://registry.npmjs.org/@antora/logger/-/logger-3.1.15.tgz", + "integrity": "sha512-txA2Nv0QQ+hIt6arc3Rrh1BiUJNlucsyNF7ZA7LgtN+rzxyjcqQPpbQ2F3tU2lOV/LOQCEvMbmz9Dj0tY8oBuA==", "license": "MPL-2.0", "dependencies": { "@antora/expand-path-helper": "~3.0", @@ -160,24 +186,24 @@ } }, "node_modules/@antora/navigation-builder": { - "version": "3.1.10", - "resolved": "https://registry.npmjs.org/@antora/navigation-builder/-/navigation-builder-3.1.10.tgz", - "integrity": "sha512-aLMK49nYsSB3mEZbLkmUXDAUYmscv2AFWu+5c3eqVGkQ6Wgyd79WQ6Bz3/TN9YqkzGL+PqGs0G39F0VQzD23Hw==", + "version": "3.1.15", + "resolved": "https://registry.npmjs.org/@antora/navigation-builder/-/navigation-builder-3.1.15.tgz", + "integrity": "sha512-XRs4pfNd88GCG9lDAJ1J+2vwvre7OzNRSgRmZhEhtgv0A13NEZq37X4YuaH46F2kj2BqiZT8UOuxqAqarLaxmg==", "license": "MPL-2.0", "dependencies": { - "@antora/asciidoc-loader": "3.1.10" + "@antora/asciidoc-loader": "3.1.15" }, "engines": { "node": ">=16.0.0" } }, "node_modules/@antora/page-composer": { - "version": "3.1.10", - "resolved": "https://registry.npmjs.org/@antora/page-composer/-/page-composer-3.1.10.tgz", - "integrity": "sha512-JoEg8J8HVsnPmAgUrYSGzf0C8rQefXyCi/18ucy0utyfUvlJNsZvUbGUPx62Het9p0JP0FkAz2MTLyDlNdArVg==", + "version": "3.1.15", + "resolved": "https://registry.npmjs.org/@antora/page-composer/-/page-composer-3.1.15.tgz", + "integrity": "sha512-koKlhWilA0E0QdCCOeLLzCFLNViBVjNe3aIlmJnMCwAK0p85wyhVfyolNqbNejAvdCZ87YhUQJ52Q4ikCgkQQg==", "license": "MPL-2.0", "dependencies": { - "@antora/logger": "3.1.10", + "@antora/logger": "3.1.15", "handlebars": "~4.7", "require-from-string": "~2.0" }, @@ -186,9 +212,9 @@ } }, "node_modules/@antora/playbook-builder": { - "version": "3.1.10", - "resolved": "https://registry.npmjs.org/@antora/playbook-builder/-/playbook-builder-3.1.10.tgz", - "integrity": "sha512-UB8UmRYfkKgActTUlotdVS4FKGjaZgTnSXE7Fns1xb3/3HRanWvI+Yze1OmCkGC33cTpoQFnSYp7ySEH8LaiBw==", + "version": "3.1.15", + "resolved": "https://registry.npmjs.org/@antora/playbook-builder/-/playbook-builder-3.1.15.tgz", + "integrity": "sha512-L2bE9FS0Th/d37DeDjz/dg9YXrkHM1xI0WQB3eiW3K/6d0Mc7eJhbmDMT0K8S+hgdaO0AT4kqDWzvx2866ZobA==", "license": "MPL-2.0", "dependencies": { "@iarna/toml": "~2.2", @@ -200,10 +226,22 @@ "node": ">=16.0.0" } }, + "node_modules/@antora/playbook-builder/node_modules/js-yaml": { + "version": "4.1.1", + "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-4.1.1.tgz", + "integrity": "sha512-qQKT4zQxXl8lLwBtHMWwaTcGfFOZviOJet3Oy/xmGk2gZH677CJM9EvtfdSkgWcATZhj/55JZ0rmy3myCT5lsA==", + "license": "MIT", + "dependencies": { + "argparse": "^2.0.1" + }, + "bin": { + "js-yaml": "bin/js-yaml.js" + } + }, "node_modules/@antora/redirect-producer": { - "version": "3.1.10", - "resolved": "https://registry.npmjs.org/@antora/redirect-producer/-/redirect-producer-3.1.10.tgz", - "integrity": "sha512-IbWJGh6LmsxJQ821h0B9JfooofFZBgFLZxsbp/IoTLkBFGLFAY5tDRvB6rvubfNLRoSjM8VjEUXGqVLlwZOb+g==", + "version": "3.1.15", + "resolved": "https://registry.npmjs.org/@antora/redirect-producer/-/redirect-producer-3.1.15.tgz", + "integrity": "sha512-mV0KnRiTr9oi0hPm7okT/Bw8kkz+PWYxp9AVSGqzhkoQgr3crxhgyS0NFCoViHwjaj4NfQrf++yxbhr6Igd7Dw==", "license": "MPL-2.0", "dependencies": { "vinyl": "~3.0" @@ -213,24 +251,24 @@ } }, "node_modules/@antora/site-generator": { - "version": "3.1.10", - "resolved": "https://registry.npmjs.org/@antora/site-generator/-/site-generator-3.1.10.tgz", - "integrity": "sha512-NCULYtwUjIyr5FGCymhfG/zDVUmZ6pfmCPorka8mAzo4/GDx1T7bgaRL9rEIyf2AMqcm7apQiAz03mpU4kucsw==", + "version": "3.1.15", + "resolved": "https://registry.npmjs.org/@antora/site-generator/-/site-generator-3.1.15.tgz", + "integrity": "sha512-Z9YiRTqw3ssnLQxSHI3VZHEPLytQXt8cWC5C/o9vS+Fc560TSNs1UO4quPFIkg+bFUpxXtcAxqbp650aA4/N1g==", "license": "MPL-2.0", "dependencies": { - "@antora/asciidoc-loader": "3.1.10", - "@antora/content-aggregator": "3.1.10", - "@antora/content-classifier": "3.1.10", - "@antora/document-converter": "3.1.10", - "@antora/file-publisher": "3.1.10", - "@antora/logger": "3.1.10", - "@antora/navigation-builder": "3.1.10", - "@antora/page-composer": "3.1.10", - "@antora/playbook-builder": "3.1.10", - "@antora/redirect-producer": "3.1.10", - "@antora/site-mapper": "3.1.10", - "@antora/site-publisher": "3.1.10", - "@antora/ui-loader": "3.1.10", + "@antora/asciidoc-loader": "3.1.15", + "@antora/content-aggregator": "3.1.15", + "@antora/content-classifier": "3.1.15", + "@antora/document-converter": "3.1.15", + "@antora/file-publisher": "3.1.15", + "@antora/logger": "3.1.15", + "@antora/navigation-builder": "3.1.15", + "@antora/page-composer": "3.1.15", + "@antora/playbook-builder": "3.1.15", + "@antora/redirect-producer": "3.1.15", + "@antora/site-mapper": "3.1.15", + "@antora/site-publisher": "3.1.15", + "@antora/ui-loader": "3.1.15", "@antora/user-require-helper": "~3.0" }, "engines": { @@ -238,12 +276,12 @@ } }, "node_modules/@antora/site-mapper": { - "version": "3.1.10", - "resolved": "https://registry.npmjs.org/@antora/site-mapper/-/site-mapper-3.1.10.tgz", - "integrity": "sha512-KY1j/y0uxC2Y7RAo4r4yKv9cgFm8aZoRylZXEODJnwj3tffbZ2ZdRzSWHp6fN0QX/Algrr9JNd9CWrjcj2f3Zw==", + "version": "3.1.15", + "resolved": "https://registry.npmjs.org/@antora/site-mapper/-/site-mapper-3.1.15.tgz", + "integrity": "sha512-dV5zGeL1uMQ83sfkBWKg8vjaJQXz1Zh3ZSNQZYa64HnT4M7oSuQUzwDZdS0j6ZtTiYcNGulRi1ucz3uIoc9tqw==", "license": "MPL-2.0", "dependencies": { - "@antora/content-classifier": "3.1.10", + "@antora/content-classifier": "3.1.15", "vinyl": "~3.0" }, "engines": { @@ -251,21 +289,21 @@ } }, "node_modules/@antora/site-publisher": { - "version": "3.1.10", - "resolved": "https://registry.npmjs.org/@antora/site-publisher/-/site-publisher-3.1.10.tgz", - "integrity": "sha512-G4xcUWvgth8oeEQwiu9U1cE0miQtYHwKHOobUbDBt2Y6LlC5H31zQQmAyvMwTsGRlvYRgLVtG6j9d6JBwQ6w9Q==", + "version": "3.1.15", + "resolved": "https://registry.npmjs.org/@antora/site-publisher/-/site-publisher-3.1.15.tgz", + "integrity": "sha512-pBuNxgA+H+WB5F4gA/gim5wKx/884QwlqOl0CpOY+6Fqn7h2ooHA7Tv6O47Tra1nZzNbIe3CQRoA5pnrX6zyRw==", "license": "MPL-2.0", "dependencies": { - "@antora/file-publisher": "3.1.10" + "@antora/file-publisher": "3.1.15" }, "engines": { "node": ">=16.0.0" } }, "node_modules/@antora/ui-loader": { - "version": "3.1.10", - "resolved": "https://registry.npmjs.org/@antora/ui-loader/-/ui-loader-3.1.10.tgz", - "integrity": "sha512-H1f5wI5a5HjLuE/Wexvc8NZy8w83Bhqjka7t1DbwOOqP+LyxFGLx/QbBVKdTtgFNDHVMtNBlplQq0ixeoTSh0A==", + "version": "3.1.15", + "resolved": "https://registry.npmjs.org/@antora/ui-loader/-/ui-loader-3.1.15.tgz", + "integrity": "sha512-zYjF5ID7t6mUEJuMyeNW/AYu1U0026wZj58H0siGuaT5YhVoxZrfvZYWV5iCMntpKduMqkwZW/l+D4MIqjhFYQ==", "license": "MPL-2.0", "dependencies": { "@antora/expand-path-helper": "~3.0", @@ -278,12 +316,24 @@ "should-proxy": "~1.0", "simple-get": "~4.0", "vinyl": "~3.0", - "yauzl": "~3.1" + "yauzl": "~3.3" }, "engines": { "node": ">=16.0.0" } }, + "node_modules/@antora/ui-loader/node_modules/js-yaml": { + "version": "4.1.1", + "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-4.1.1.tgz", + "integrity": "sha512-qQKT4zQxXl8lLwBtHMWwaTcGfFOZviOJet3Oy/xmGk2gZH677CJM9EvtfdSkgWcATZhj/55JZ0rmy3myCT5lsA==", + "license": "MIT", + "dependencies": { + "argparse": "^2.0.1" + }, + "bin": { + "js-yaml": "bin/js-yaml.js" + } + }, "node_modules/@antora/user-require-helper": { "version": "3.0.0", "resolved": "https://registry.npmjs.org/@antora/user-require-helper/-/user-require-helper-3.0.0.tgz", @@ -297,13 +347,13 @@ } }, "node_modules/@asciidoctor/core": { - "version": "2.2.8", - "resolved": "https://registry.npmjs.org/@asciidoctor/core/-/core-2.2.8.tgz", - "integrity": "sha512-oozXk7ZO1RAd/KLFLkKOhqTcG4GO3CV44WwOFg2gMcCsqCUTarvMT7xERIoWW2WurKbB0/ce+98r01p8xPOlBw==", + "version": "2.2.9", + "resolved": "https://registry.npmjs.org/@asciidoctor/core/-/core-2.2.9.tgz", + "integrity": "sha512-tIPRHo1T2SFmAm+j77cDsj0RuaszP7xJxsaVTTAF5CwKyTbazw9TnIVlpIWM5yWfIWAWcAZy92RcnPgMJwny1w==", "license": "MIT", "dependencies": { - "asciidoctor-opal-runtime": "0.3.3", - "unxhr": "1.0.1" + "asciidoctor-opal-runtime": "0.3.4", + "unxhr": "~1.2" }, "engines": { "node": ">=8.11", @@ -312,18 +362,18 @@ } }, "node_modules/@cppalliance/antora-cpp-reference-extension": { - "version": "0.1.0", - "resolved": "https://registry.npmjs.org/@cppalliance/antora-cpp-reference-extension/-/antora-cpp-reference-extension-0.1.0.tgz", - "integrity": "sha512-3VD/gAFebR06GiBWAy2PgEHNqyRNrvAE0FfFvotLvA0RQmHn0q+ct+j0z53N64yxuvJVj8Hl0bRRPdhh2BGjXg==", + "version": "0.1.1", + "resolved": "https://registry.npmjs.org/@cppalliance/antora-cpp-reference-extension/-/antora-cpp-reference-extension-0.1.1.tgz", + "integrity": "sha512-otGW/pJNtYyji62ELGkyEc6PseHxz882HhQGB1YUxPehg+2CjzM/hqQ7aJFXtaNAedrXK0/upQ+KCCs2WPWFqg==", "license": "BSL-1.0", "dependencies": { "@antora/expand-path-helper": "^3.0.0", - "axios": "^1.13.2", + "axios": "^1.16.1", "cache-directory": "^2.0.0", "fast-glob": "^3.3.3", - "isomorphic-git": "^1.35.0", - "js-yaml": "^4.1.0", - "semver": "^7.7.3" + "isomorphic-git": "^1.38.1", + "js-yaml": "^4.1.1", + "semver": "^7.8.1" } }, "node_modules/@cppalliance/antora-downloads-extension": { @@ -385,14 +435,26 @@ "node": ">=6.5" } }, + "node_modules/agent-base": { + "version": "6.0.2", + "resolved": "https://registry.npmjs.org/agent-base/-/agent-base-6.0.2.tgz", + "integrity": "sha512-RZNwNclF7+MS/8bDg70amg32dyeZGZxiDuQmZxKLAlQjr3jGyLx+4Kkk58UO7D2QdgFIQCovuSuZESne6RG6XQ==", + "license": "MIT", + "dependencies": { + "debug": "4" + }, + "engines": { + "node": ">= 6.0.0" + } + }, "node_modules/antora": { - "version": "3.1.10", - "resolved": "https://registry.npmjs.org/antora/-/antora-3.1.10.tgz", - "integrity": "sha512-FcXPfqxi5xrGF2fTrFiiau45q8w0bzRcnfk97nxvpvztPDHX/lUOrBF/GpaGl1JT5K085VkI3/dbxTlvWK1jjw==", + "version": "3.1.15", + "resolved": "https://registry.npmjs.org/antora/-/antora-3.1.15.tgz", + "integrity": "sha512-nxz8n7sbKP58hhK13Mack+r3mELxFVYJm9fUBjefUhHWP3cjU/AX3LVVoFwssDOau3Gh+/id9xDNj8Vp5rbBNA==", "license": "MPL-2.0", "dependencies": { - "@antora/cli": "3.1.10", - "@antora/site-generator": "3.1.10" + "@antora/cli": "3.1.15", + "@antora/site-generator": "3.1.15" }, "bin": { "antora": "bin/antora" @@ -408,13 +470,13 @@ "license": "Python-2.0" }, "node_modules/asciidoctor-opal-runtime": { - "version": "0.3.3", - "resolved": "https://registry.npmjs.org/asciidoctor-opal-runtime/-/asciidoctor-opal-runtime-0.3.3.tgz", - "integrity": "sha512-/CEVNiOia8E5BMO9FLooo+Kv18K4+4JBFRJp8vUy/N5dMRAg+fRNV4HA+o6aoSC79jVU/aT5XvUpxSxSsTS8FQ==", + "version": "0.3.4", + "resolved": "https://registry.npmjs.org/asciidoctor-opal-runtime/-/asciidoctor-opal-runtime-0.3.4.tgz", + "integrity": "sha512-zqd6zn1LV+PZ69AP/kEbB00zuPHMIAJY3IX8+aZV+X1qOwatYvKGjsMmdMc5ApfhtkjZ4mYkqiTPJWnEnBiMJg==", "license": "MIT", "dependencies": { - "glob": "7.1.3", - "unxhr": "1.0.1" + "fast-glob": "~3.3", + "unxhr": "~1.2" }, "engines": { "node": ">=8.11" @@ -457,34 +519,44 @@ } }, "node_modules/axios": { - "version": "1.15.2", - "resolved": "https://registry.npmjs.org/axios/-/axios-1.15.2.tgz", - "integrity": "sha512-wLrXxPtcrPTsNlJmKjkPnNPK2Ihe0hn0wGSaTEiHRPxwjvJwT3hKmXF4dpqxmPO9SoNb2FsYXj/xEo0gHN+D5A==", + "version": "1.18.0", + "resolved": "https://registry.npmjs.org/axios/-/axios-1.18.0.tgz", + "integrity": "sha512-E32NzpYKp++W7XRe52rHiXV2ehxmh3wbdgO7MHeFM+vqxLBYHzt0ElkiImtOBxtOmyp0yoC8C6uESVV84Y2/hw==", "license": "MIT", "dependencies": { - "follow-redirects": "^1.15.11", + "follow-redirects": "^1.16.0", "form-data": "^4.0.5", + "https-proxy-agent": "^5.0.1", "proxy-from-env": "^2.1.0" } }, "node_modules/b4a": { - "version": "1.6.7", - "resolved": "https://registry.npmjs.org/b4a/-/b4a-1.6.7.tgz", - "integrity": "sha512-OnAYlL5b7LEkALw87fUVafQw5rVR9RjwGd4KUwNQ6DrrNmaVaUCgLipfVlzrPQ4tWOR9P0IXGNOx50jYCCdSJg==", - "license": "Apache-2.0" - }, - "node_modules/balanced-match": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.2.tgz", - "integrity": "sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw==", - "license": "MIT" + "version": "1.8.1", + "resolved": "https://registry.npmjs.org/b4a/-/b4a-1.8.1.tgz", + "integrity": "sha512-aiqre1Nr0B/6DgE2N5vwTc+2/oQZ4Wh1t4NznYY4E00y8LCt6NqdRv81so00oo27D8MVKTpUa/MwUUtBLXCoDw==", + "license": "Apache-2.0", + "peerDependencies": { + "react-native-b4a": "*" + }, + "peerDependenciesMeta": { + "react-native-b4a": { + "optional": true + } + } }, "node_modules/bare-events": { - "version": "2.5.4", - "resolved": "https://registry.npmjs.org/bare-events/-/bare-events-2.5.4.tgz", - "integrity": "sha512-+gFfDkR8pj4/TrWCGUGWmJIkBwuxPS5F+a5yWjOHQt2hHvNZd5YLzadjmDUtFmMM4y429bnKLa8bYBMHcYdnQA==", + "version": "2.9.1", + "resolved": "https://registry.npmjs.org/bare-events/-/bare-events-2.9.1.tgz", + "integrity": "sha512-Z0oHEHAFDZkffN8Qc39zNZjQlMDkPJRyyyZieU1VH7u8c5S+qHZ2S8ixdKIAxEjfHO7FJxXmJWgteOghVanIsg==", "license": "Apache-2.0", - "optional": true + "peerDependencies": { + "bare-abort-controller": "*" + }, + "peerDependenciesMeta": { + "bare-abort-controller": { + "optional": true + } + } }, "node_modules/base64-js": { "version": "1.5.1", @@ -506,16 +578,6 @@ ], "license": "MIT" }, - "node_modules/brace-expansion": { - "version": "1.1.12", - "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.12.tgz", - "integrity": "sha512-9T9UjW3r0UW5c1Q7GTwllptXwhvYmEzFhzMfZ9H7FQWt+uZePjZPjBP/W1ZEyZ1twGWom5/56TF4lPcqjnDHcg==", - "license": "MIT", - "dependencies": { - "balanced-match": "^1.0.0", - "concat-map": "0.0.1" - } - }, "node_modules/braces": { "version": "3.0.3", "resolved": "https://registry.npmjs.org/braces/-/braces-3.0.3.tgz", @@ -553,12 +615,12 @@ } }, "node_modules/buffer-crc32": { - "version": "0.2.13", - "resolved": "https://registry.npmjs.org/buffer-crc32/-/buffer-crc32-0.2.13.tgz", - "integrity": "sha512-VO9Ht/+p3SN7SKWqcrgEzjGbRSJYTx+Q1pTQC0wrWqHx0vpJraQ6GtHx8tvcg1rlK1byhU5gccxgOgj7B0TDkQ==", + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/buffer-crc32/-/buffer-crc32-1.0.0.tgz", + "integrity": "sha512-Db1SbgBS/fg/392AblrMJk97KggmvYhr4pB5ZIMTWtaivCPMWLkmb7m21cJvpvgK+J3nsU2CmmixNBZx4vFj/w==", "license": "MIT", "engines": { - "node": "*" + "node": ">=8.0.0" } }, "node_modules/cache-directory": { @@ -662,16 +724,10 @@ "node": ">=16" } }, - "node_modules/concat-map": { - "version": "0.0.1", - "resolved": "https://registry.npmjs.org/concat-map/-/concat-map-0.0.1.tgz", - "integrity": "sha512-/Srv4dswyQNBfohGpz9o6Yb3Gz3SrUDqBH5rTuhGR7ahtlbYKnVxw2bCFMRljaA7EXHaXZ8wsHdodFvbkhKmqg==", - "license": "MIT" - }, "node_modules/convict": { - "version": "6.2.4", - "resolved": "https://registry.npmjs.org/convict/-/convict-6.2.4.tgz", - "integrity": "sha512-qN60BAwdMVdofckX7AlohVJ2x9UvjTNoKVXCL2LxFk1l7757EJqf1nySdMkPQer0bt8kQ5lQiyZ9/2NvrFBuwQ==", + "version": "6.2.5", + "resolved": "https://registry.npmjs.org/convict/-/convict-6.2.5.tgz", + "integrity": "sha512-JtXpxqDqJ8P0UwEHwhxLzCIXQy97vlYBZR222Sbzb1q1Erex9ASrztJ29SyhWFQjod1AeFBaPzEEC8YvtZMIYg==", "license": "Apache-2.0", "dependencies": { "lodash.clonedeep": "^4.5.0", @@ -702,6 +758,23 @@ "node": "*" } }, + "node_modules/debug": { + "version": "4.4.3", + "resolved": "https://registry.npmjs.org/debug/-/debug-4.4.3.tgz", + "integrity": "sha512-RGwwWnwQvkVfavKVt22FGLw+xYSdzARwm0ru6DhTVA3umU5hZc28V3kO4stgYryrTlLpuvgI9GiijltAjNbcqA==", + "license": "MIT", + "dependencies": { + "ms": "^2.1.3" + }, + "engines": { + "node": ">=6.0" + }, + "peerDependenciesMeta": { + "supports-color": { + "optional": true + } + } + }, "node_modules/decompress-response": { "version": "6.0.0", "resolved": "https://registry.npmjs.org/decompress-response/-/decompress-response-6.0.0.tgz", @@ -791,9 +864,9 @@ } }, "node_modules/es-object-atoms": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/es-object-atoms/-/es-object-atoms-1.1.1.tgz", - "integrity": "sha512-FGgH2h8zKNim9ljj7dankFPcICIK9Cp5bm+c2gQSYePhpaG5+esrLODihIorn+Pe6FGJzWhXQotPv73jTaldXA==", + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/es-object-atoms/-/es-object-atoms-1.1.2.tgz", + "integrity": "sha512-HWcBoN6NileqtSydK2FqHbS/LoDd2pqrnQHLyJzBj4kOp/ky2MWMN694xOfkK8/SnUsW2DH7EfyVlydKCsm1Zw==", "license": "MIT", "dependencies": { "es-errors": "^1.3.0" @@ -835,6 +908,15 @@ "node": ">=0.8.x" } }, + "node_modules/events-universal": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/events-universal/-/events-universal-1.0.1.tgz", + "integrity": "sha512-LUd5euvbMLpwOF8m6ivPCbhQeSiYVNb8Vs0fQ8QjXo0JTkEHpz8pxdQf0gStltaPpw0Cca8b39KxvK9cfKRiAw==", + "license": "Apache-2.0", + "dependencies": { + "bare-events": "^2.7.0" + } + }, "node_modules/fast-copy": { "version": "3.0.2", "resolved": "https://registry.npmjs.org/fast-copy/-/fast-copy-3.0.2.tgz", @@ -879,9 +961,9 @@ "license": "MIT" }, "node_modules/fastq": { - "version": "1.19.1", - "resolved": "https://registry.npmjs.org/fastq/-/fastq-1.19.1.tgz", - "integrity": "sha512-GwLTyxkCXjXbxqIhTsMI2Nui8huMPtnxg7krajPJAjnEG/iiOS7i+zCtWGZR9G0NBKbXKh6X9m9UIsYX/N6vvQ==", + "version": "1.20.1", + "resolved": "https://registry.npmjs.org/fastq/-/fastq-1.20.1.tgz", + "integrity": "sha512-GGToxJ/w1x32s/D2EKND7kTil4n8OVk/9mycTc4VDza13lOvpUZTGX3mFSCtV9ksdGBVzvsyAVLM6mHFThxXxw==", "license": "ISC", "dependencies": { "reusify": "^1.0.4" @@ -935,27 +1017,21 @@ } }, "node_modules/form-data": { - "version": "4.0.5", - "resolved": "https://registry.npmjs.org/form-data/-/form-data-4.0.5.tgz", - "integrity": "sha512-8RipRLol37bNs2bhoV67fiTEvdTrbMUYcFTiy3+wuuOnUog2QBHCZWXDRijWQfAkhBj2Uf5UnVaiWwA5vdd82w==", + "version": "4.0.6", + "resolved": "https://registry.npmjs.org/form-data/-/form-data-4.0.6.tgz", + "integrity": "sha512-vKatAh4SlVfgbv+YtmhiRjhEMJsYpsG1Y2rMQtR+SVSbytsSD1YGzDIcrAJmdFec88u/+VoGmxnl+80gL1tRCQ==", "license": "MIT", "dependencies": { "asynckit": "^0.4.0", "combined-stream": "^1.0.8", "es-set-tostringtag": "^2.1.0", - "hasown": "^2.0.2", - "mime-types": "^2.1.12" + "hasown": "^2.0.4", + "mime-types": "^2.1.35" }, "engines": { "node": ">= 6" } }, - "node_modules/fs.realpath": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/fs.realpath/-/fs.realpath-1.0.0.tgz", - "integrity": "sha512-OO0pH2lK6a0hZnAdau5ItzHPI6pUlvI7jMVnxUQRtw4owF2wk8lOSabtGDCTP4Ggrg2MbGnWO9X8K1t4+fGMDw==", - "license": "ISC" - }, "node_modules/function-bind": { "version": "1.1.2", "resolved": "https://registry.npmjs.org/function-bind/-/function-bind-1.1.2.tgz", @@ -1002,24 +1078,6 @@ "node": ">= 0.4" } }, - "node_modules/glob": { - "version": "7.1.3", - "resolved": "https://registry.npmjs.org/glob/-/glob-7.1.3.tgz", - "integrity": "sha512-vcfuiIxogLV4DlGBHIUOwI0IbrJ8HWPc4MU7HzviGeNho/UJDfi6B5p3sHeWIQ0KGIU0Jpxi5ZHxemQfLkkAwQ==", - "deprecated": "Glob versions prior to v9 are no longer supported", - "license": "ISC", - "dependencies": { - "fs.realpath": "^1.0.0", - "inflight": "^1.0.4", - "inherits": "2", - "minimatch": "^3.0.4", - "once": "^1.3.0", - "path-is-absolute": "^1.0.0" - }, - "engines": { - "node": "*" - } - }, "node_modules/glob-parent": { "version": "5.1.2", "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-5.1.2.tgz", @@ -1045,9 +1103,9 @@ } }, "node_modules/handlebars": { - "version": "4.7.8", - "resolved": "https://registry.npmjs.org/handlebars/-/handlebars-4.7.8.tgz", - "integrity": "sha512-vafaFqs8MZkRrSX7sFVUdo3ap/eNiLnb4IakshzvP56X5Nr1iGKAIqdX6tMlm6HcNRIkr6AxO5jFEoJzzpT8aQ==", + "version": "4.7.9", + "resolved": "https://registry.npmjs.org/handlebars/-/handlebars-4.7.9.tgz", + "integrity": "sha512-4E71E0rpOaQuJR2A3xDZ+GM1HyWYv1clR58tC8emQNeQe3RH7MAzSbat+V0wG78LQBo6m6bzSG/L4pBuCsgnUQ==", "license": "MIT", "dependencies": { "minimist": "^1.2.5", @@ -1105,9 +1163,9 @@ } }, "node_modules/hasown": { - "version": "2.0.3", - "resolved": "https://registry.npmjs.org/hasown/-/hasown-2.0.3.tgz", - "integrity": "sha512-ej4AhfhfL2Q2zpMmLo7U1Uv9+PyhIZpgQLGT1F9miIGmiCJIoCgSmczFdrc97mWT4kVY72KA+WnnhJ5pghSvSg==", + "version": "2.0.4", + "resolved": "https://registry.npmjs.org/hasown/-/hasown-2.0.4.tgz", + "integrity": "sha512-T2UbfbBEF32wiepXIsMlTW9+dDYC6wMh/t/vYA4tuOMKqWz/n3vr1NFSxQiyP+zk2mXsoMA/i/7qV6LKut1t1A==", "license": "MIT", "dependencies": { "function-bind": "^1.1.2" @@ -1131,6 +1189,19 @@ "node": ">=14" } }, + "node_modules/https-proxy-agent": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/https-proxy-agent/-/https-proxy-agent-5.0.1.tgz", + "integrity": "sha512-dFcAjpTQFgoLMzC2VwU+C/CbS7uRL0lWmxDITmqm7C+7F0Odmj6s9l6alZc6AELXhrnggM2CeWSXHGOdX2YtwA==", + "license": "MIT", + "dependencies": { + "agent-base": "6", + "debug": "4" + }, + "engines": { + "node": ">= 6" + } + }, "node_modules/ieee754": { "version": "1.2.1", "resolved": "https://registry.npmjs.org/ieee754/-/ieee754-1.2.1.tgz", @@ -1160,17 +1231,6 @@ "node": ">= 4" } }, - "node_modules/inflight": { - "version": "1.0.6", - "resolved": "https://registry.npmjs.org/inflight/-/inflight-1.0.6.tgz", - "integrity": "sha512-k92I/b08q4wvFscXCLvqfsHCrjrF7yiXsQuIVvVE7N82W3+aqpzuUdBbfhWcy/FZR3/4IgflMgKLOsvPDrGCJA==", - "deprecated": "This module is not supported, and leaks memory. Do not use it. Check out lru-cache if you want a good and tested way to coalesce async requests by a key value, which is much more comprehensive and powerful.", - "license": "ISC", - "dependencies": { - "once": "^1.3.0", - "wrappy": "1" - } - }, "node_modules/inherits": { "version": "2.0.4", "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.4.tgz", @@ -1241,9 +1301,9 @@ "license": "MIT" }, "node_modules/isomorphic-git": { - "version": "1.37.5", - "resolved": "https://registry.npmjs.org/isomorphic-git/-/isomorphic-git-1.37.5.tgz", - "integrity": "sha512-wek54c5uFvd3WsxewLWt6h0GXKWQh0P8rRXns9bN1rHNjcgCb3+0lmyAsP594NeTtQFeCJQVS9b0kjbkD1l5qg==", + "version": "1.38.5", + "resolved": "https://registry.npmjs.org/isomorphic-git/-/isomorphic-git-1.38.5.tgz", + "integrity": "sha512-4HBmx1UB+SdRaQh7+zN7lvFQxc4zl7s0oIM/1+5xveu6+QrOeFfx5QDeRNpqCzFHICISEdbK7GilsTsrgE006Q==", "license": "MIT", "dependencies": { "async-lock": "^1.4.1", @@ -1265,22 +1325,6 @@ "node": ">=14.17" } }, - "node_modules/isomorphic-git/node_modules/readable-stream": { - "version": "4.7.0", - "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-4.7.0.tgz", - "integrity": "sha512-oIGGmcpTLwPga8Bn6/Z75SVaH1z5dUut2ibSyAMVhmUggWpmDn2dapB0n7f8nwaSiRtepAsfJyfXIO5DCVAODg==", - "license": "MIT", - "dependencies": { - "abort-controller": "^3.0.0", - "buffer": "^6.0.3", - "events": "^3.3.0", - "process": "^0.11.10", - "string_decoder": "^1.3.0" - }, - "engines": { - "node": "^12.22.0 || ^14.17.0 || >=16.0.0" - } - }, "node_modules/joycon": { "version": "3.1.1", "resolved": "https://registry.npmjs.org/joycon/-/joycon-3.1.1.tgz", @@ -1291,9 +1335,19 @@ } }, "node_modules/js-yaml": { - "version": "4.1.0", - "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-4.1.0.tgz", - "integrity": "sha512-wpxZs9NoxZaJESJGIZTyDEaYpl0FKSA+FB9aJiyemKhMwkxQg63h4T1KJgUGHpTqPDNRcmmYLugrRjJlBtWvRA==", + "version": "4.2.0", + "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-4.2.0.tgz", + "integrity": "sha512-ePWsvanv0DWuDRsW8dnt+R4jQ31SCRCQ7hhNcPXZPsoBZiemuZNYGf7adZdqX2D86j6rvKp3RpCxVTSb8WQlOw==", + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/puzrin" + }, + { + "type": "github", + "url": "https://github.com/sponsors/nodeca" + } + ], "license": "MIT", "dependencies": { "argparse": "^2.0.1" @@ -1352,9 +1406,9 @@ } }, "node_modules/micromatch/node_modules/picomatch": { - "version": "2.3.1", - "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-2.3.1.tgz", - "integrity": "sha512-JU3teHTNjmE2VCGFzuY8EXzCDVwEqB2a8fsIvwaStHhAWJEeVd1o1QD80CU6+ZdEXXSLbSsuLwJjkCBWqRQUVA==", + "version": "2.3.2", + "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-2.3.2.tgz", + "integrity": "sha512-V7+vQEJ06Z+c5tSye8S+nHUfI51xoXIXjHQ99cQtKUkQqqO1kO/KCJUfZXuB47h/YBlDhah2H3hdUGXn8ie0oA==", "license": "MIT", "engines": { "node": ">=8.6" @@ -1396,18 +1450,6 @@ "url": "https://github.com/sponsors/sindresorhus" } }, - "node_modules/minimatch": { - "version": "3.1.2", - "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.2.tgz", - "integrity": "sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw==", - "license": "ISC", - "dependencies": { - "brace-expansion": "^1.1.7" - }, - "engines": { - "node": "*" - } - }, "node_modules/minimist": { "version": "1.2.8", "resolved": "https://registry.npmjs.org/minimist/-/minimist-1.2.8.tgz", @@ -1426,6 +1468,12 @@ "minimist": "^1.2.5" } }, + "node_modules/ms": { + "version": "2.1.3", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz", + "integrity": "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==", + "license": "MIT" + }, "node_modules/multi-progress": { "version": "4.0.0", "resolved": "https://registry.npmjs.org/multi-progress/-/multi-progress-4.0.0.tgz", @@ -1465,15 +1513,6 @@ "integrity": "sha512-4hLB8Py4zZce5s4yd9XzopqwVv/yGNhV1Bl8NTmCq1763HeK2+EwVTv+leGeL13Dnh2wfbqowVPXCIO0z4taYw==", "license": "(MIT AND Zlib)" }, - "node_modules/path-is-absolute": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/path-is-absolute/-/path-is-absolute-1.0.1.tgz", - "integrity": "sha512-AVbw3UJ2e9bq64vSaS9Am0fje1Pa8pbGqTTsmXfaIiMpnr5DlDhfJOuLj9Sf95ZPVDAUerDfEk88MPmPe7UCQg==", - "license": "MIT", - "engines": { - "node": ">=0.10.0" - } - }, "node_modules/pend": { "version": "1.2.0", "resolved": "https://registry.npmjs.org/pend/-/pend-1.2.0.tgz", @@ -1481,9 +1520,9 @@ "license": "MIT" }, "node_modules/picomatch": { - "version": "4.0.2", - "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-4.0.2.tgz", - "integrity": "sha512-M7BAV6Rlcy5u+m6oPhAPFgJTzAioX/6B0DxyvDlo9l8+T3nLKbrczg2WLUyzd45L8RqfUMyGPzekbMvX2Ldkwg==", + "version": "4.0.4", + "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-4.0.4.tgz", + "integrity": "sha512-QP88BAKvMam/3NxH6vj2o21R6MjxZUAd6nlwAS/pnGvN9IVLocLHxGYIzFhg6fUQ+5th6P4dv4eW9jX3DSIj7A==", "license": "MIT", "engines": { "node": ">=12" @@ -1533,22 +1572,6 @@ "split2": "^4.0.0" } }, - "node_modules/pino-abstract-transport/node_modules/readable-stream": { - "version": "4.7.0", - "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-4.7.0.tgz", - "integrity": "sha512-oIGGmcpTLwPga8Bn6/Z75SVaH1z5dUut2ibSyAMVhmUggWpmDn2dapB0n7f8nwaSiRtepAsfJyfXIO5DCVAODg==", - "license": "MIT", - "dependencies": { - "abort-controller": "^3.0.0", - "buffer": "^6.0.3", - "events": "^3.3.0", - "process": "^0.11.10", - "string_decoder": "^1.3.0" - }, - "engines": { - "node": "^12.22.0 || ^14.17.0 || >=16.0.0" - } - }, "node_modules/pino-pretty": { "version": "11.2.2", "resolved": "https://registry.npmjs.org/pino-pretty/-/pino-pretty-11.2.2.tgz", @@ -1574,26 +1597,10 @@ "pino-pretty": "bin.js" } }, - "node_modules/pino-pretty/node_modules/readable-stream": { - "version": "4.7.0", - "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-4.7.0.tgz", - "integrity": "sha512-oIGGmcpTLwPga8Bn6/Z75SVaH1z5dUut2ibSyAMVhmUggWpmDn2dapB0n7f8nwaSiRtepAsfJyfXIO5DCVAODg==", - "license": "MIT", - "dependencies": { - "abort-controller": "^3.0.0", - "buffer": "^6.0.3", - "events": "^3.3.0", - "process": "^0.11.10", - "string_decoder": "^1.3.0" - }, - "engines": { - "node": "^12.22.0 || ^14.17.0 || >=16.0.0" - } - }, "node_modules/pino-std-serializers": { - "version": "7.0.0", - "resolved": "https://registry.npmjs.org/pino-std-serializers/-/pino-std-serializers-7.0.0.tgz", - "integrity": "sha512-e906FRY0+tV27iq4juKzSYPbUj2do2X2JX4EzSca1631EB2QJQUqGbDuERal7LCtOpxl6x3+nvo9NPZcmjkiFA==", + "version": "7.1.0", + "resolved": "https://registry.npmjs.org/pino-std-serializers/-/pino-std-serializers-7.1.0.tgz", + "integrity": "sha512-BndPH67/JxGExRgiX1dX0w1FvZck5Wa4aal9198SrRhZjH3GxKQUKIBnYJTdj2HDN3UQAS06HlfcSbQj2OHmaw==", "license": "MIT" }, "node_modules/possible-typed-array-names": { @@ -1639,9 +1646,9 @@ } }, "node_modules/pump": { - "version": "3.0.3", - "resolved": "https://registry.npmjs.org/pump/-/pump-3.0.3.tgz", - "integrity": "sha512-todwxLMY7/heScKmntwQG8CXVkWUOdYxIvY2s0VWAAMh/nd8SoYiRaKjlr7+iCs984f2P8zvrfWcDDYVb73NfA==", + "version": "3.0.4", + "resolved": "https://registry.npmjs.org/pump/-/pump-3.0.4.tgz", + "integrity": "sha512-VS7sjc6KR7e1ukRFhQSY5LM2uBWAUPiOPa/A3mkKmiMwSmRFUITt0xuj+/lesgnCv+dPIEYlkzrcyXgquIHMcA==", "license": "MIT", "dependencies": { "end-of-stream": "^1.1.0", @@ -1675,17 +1682,19 @@ "license": "MIT" }, "node_modules/readable-stream": { - "version": "3.6.2", - "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-3.6.2.tgz", - "integrity": "sha512-9u/sniCrY3D5WdsERHzHE4G2YCXqoG5FTHUiCC4SIbr6XcLZBY05ya9EKjYek9O5xOAwjGq+1JdGBAS7Q9ScoA==", + "version": "4.7.0", + "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-4.7.0.tgz", + "integrity": "sha512-oIGGmcpTLwPga8Bn6/Z75SVaH1z5dUut2ibSyAMVhmUggWpmDn2dapB0n7f8nwaSiRtepAsfJyfXIO5DCVAODg==", "license": "MIT", "dependencies": { - "inherits": "^2.0.3", - "string_decoder": "^1.1.1", - "util-deprecate": "^1.0.1" + "abort-controller": "^3.0.0", + "buffer": "^6.0.3", + "events": "^3.3.0", + "process": "^0.11.10", + "string_decoder": "^1.3.0" }, "engines": { - "node": ">= 6" + "node": "^12.22.0 || ^14.17.0 || >=16.0.0" } }, "node_modules/real-require": { @@ -1790,9 +1799,9 @@ "license": "BSD-3-Clause" }, "node_modules/semver": { - "version": "7.7.4", - "resolved": "https://registry.npmjs.org/semver/-/semver-7.7.4.tgz", - "integrity": "sha512-vFKC2IEtQnVhpT78h1Yp8wzwrf8CM+MzKMHGJZfBtzhZNycRFnXsHk6E5TxIkkMsgNS7mdX3AGB7x2QM2di4lA==", + "version": "7.8.4", + "resolved": "https://registry.npmjs.org/semver/-/semver-7.8.4.tgz", + "integrity": "sha512-rUCObTnP32Q08R2uuIrt7r9PlEonuTmtuXYcW6s5kjdlj3xbnwe+21yXptAUYcMAABLkYYTtnmzb3w3EDZfueA==", "license": "ISC", "bin": { "semver": "bin/semver.js" @@ -1917,16 +1926,14 @@ } }, "node_modules/streamx": { - "version": "2.22.1", - "resolved": "https://registry.npmjs.org/streamx/-/streamx-2.22.1.tgz", - "integrity": "sha512-znKXEBxfatz2GBNK02kRnCXjV+AA4kjZIUxeWSr3UGirZMJfTE9uiwKHobnbgxWyL/JWro8tTq+vOqAK1/qbSA==", + "version": "2.28.0", + "resolved": "https://registry.npmjs.org/streamx/-/streamx-2.28.0.tgz", + "integrity": "sha512-1Yowhzjf0ivGMrTIkY9hav5TxobO9qIVqUE41fiCGMGgc3CLlf4MY+9AHmZqBWgDTue0fY9zWjYFVyf6Diuobw==", "license": "MIT", "dependencies": { + "events-universal": "^1.0.0", "fast-fifo": "^1.3.2", "text-decoder": "^1.1.0" - }, - "optionalDependencies": { - "bare-events": "^2.2.0" } }, "node_modules/string_decoder": { @@ -1960,18 +1967,18 @@ } }, "node_modules/text-decoder": { - "version": "1.2.3", - "resolved": "https://registry.npmjs.org/text-decoder/-/text-decoder-1.2.3.tgz", - "integrity": "sha512-3/o9z3X0X0fTupwsYvR03pJ/DjWuqqrfwBgTQzdWDiQSm9KitAyz/9WqsT2JQW7KV2m+bC2ol/zqpW37NHxLaA==", + "version": "1.2.7", + "resolved": "https://registry.npmjs.org/text-decoder/-/text-decoder-1.2.7.tgz", + "integrity": "sha512-vlLytXkeP4xvEq2otHeJfSQIRyWxo/oZGEbXrtEEF9Hnmrdly59sUbzZ/QgyWuLYHctCHxFF4tRQZNQ9k60ExQ==", "license": "Apache-2.0", "dependencies": { "b4a": "^1.6.4" } }, "node_modules/thread-stream": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/thread-stream/-/thread-stream-3.1.0.tgz", - "integrity": "sha512-OqyPZ9u96VohAyMfJykzmivOrY2wfMSf3C5TtFJVgN+Hm6aj+voFhlK+kZEIv2FBh1X6Xp3DlnCOfEQ3B2J86A==", + "version": "3.2.0", + "resolved": "https://registry.npmjs.org/thread-stream/-/thread-stream-3.2.0.tgz", + "integrity": "sha512-zLBvqpwr4Esa0kRjcrzGU6zL25lePWaCLMx0RQFrmteozIfeNdaMLpG5U7PeHzvlFkAWaRKA9/KVW4F60iB+qw==", "license": "MIT", "dependencies": { "real-require": "^0.2.0" @@ -2031,9 +2038,9 @@ } }, "node_modules/unxhr": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/unxhr/-/unxhr-1.0.1.tgz", - "integrity": "sha512-MAhukhVHyaLGDjyDYhy8gVjWJyhTECCdNsLwlMoGFoNJ3o79fpQhtQuzmAE4IxCMDwraF4cW8ZjpAV0m9CRQbg==", + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/unxhr/-/unxhr-1.2.0.tgz", + "integrity": "sha512-6cGpm8NFXPD9QbSNx0cD2giy7teZ6xOkCUH3U89WKVkL9N9rBrWjlCwhR94Re18ZlAop4MOc3WU1M3Hv/bgpIw==", "license": "MIT", "engines": { "node": ">=8.11" @@ -2061,13 +2068,13 @@ } }, "node_modules/which-typed-array": { - "version": "1.1.20", - "resolved": "https://registry.npmjs.org/which-typed-array/-/which-typed-array-1.1.20.tgz", - "integrity": "sha512-LYfpUkmqwl0h9A2HL09Mms427Q1RZWuOHsukfVcKRq9q95iQxdw0ix1JQrqbcDR9PH1QDwf5Qo8OZb5lksZ8Xg==", + "version": "1.1.22", + "resolved": "https://registry.npmjs.org/which-typed-array/-/which-typed-array-1.1.22.tgz", + "integrity": "sha512-fvO4ExWMFsqyhG3AiPAObMuY1lxaqgYcxbc49CNdWDDECOJNgQyvsOWVwbZc+qf3rzRtxojBK+CMEv0Ld5CYpw==", "license": "MIT", "dependencies": { "available-typed-arrays": "^1.0.7", - "call-bind": "^1.0.8", + "call-bind": "^1.0.9", "call-bound": "^1.0.4", "for-each": "^0.3.5", "get-proto": "^1.0.1", @@ -2112,12 +2119,11 @@ } }, "node_modules/yauzl": { - "version": "3.1.3", - "resolved": "https://registry.npmjs.org/yauzl/-/yauzl-3.1.3.tgz", - "integrity": "sha512-JCCdmlJJWv7L0q/KylOekyRaUrdEoUxWkWVcgorosTROCFWiS9p2NNPE9Yb91ak7b1N5SxAZEliWpspbZccivw==", + "version": "3.3.2", + "resolved": "https://registry.npmjs.org/yauzl/-/yauzl-3.3.2.tgz", + "integrity": "sha512-Md9ankxxN23wncAN8s7+Tn3Co52zLUPMtnrLAbVCnfG5d2tKBFfmygYSgXlqFgXObtzIgqkx7aNgDBpso9+4qA==", "license": "MIT", "dependencies": { - "buffer-crc32": "~0.2.3", "pend": "~1.2.0" }, "engines": { @@ -2125,12 +2131,12 @@ } }, "node_modules/yazl": { - "version": "2.5.1", - "resolved": "https://registry.npmjs.org/yazl/-/yazl-2.5.1.tgz", - "integrity": "sha512-phENi2PLiHnHb6QBVot+dJnaAZ0xosj7p3fWl+znIjBDlnMI2PsZCJZ306BPTFOaHf5qdDEI8x5qFrSOBN5vrw==", + "version": "3.3.1", + "resolved": "https://registry.npmjs.org/yazl/-/yazl-3.3.1.tgz", + "integrity": "sha512-BbETDVWG+VcMUle37k5Fqp//7SDOK2/1+T7X8TD96M3D9G8jK5VLUdQVdVjGi8im7FGkazX7kk5hkU8X4L5Bng==", "license": "MIT", "dependencies": { - "buffer-crc32": "~0.2.3" + "buffer-crc32": "^1.0.0" } } } diff --git a/doc/package.json b/doc/package.json index bb5bd703..d4422374 100644 --- a/doc/package.json +++ b/doc/package.json @@ -1,7 +1,7 @@ { "dependencies": { "@cppalliance/antora-downloads-extension": "^0.0.2", - "@cppalliance/antora-cpp-reference-extension": "^0.1.0", - "antora": "^3.1.10" + "@cppalliance/antora-cpp-reference-extension": "^0.1.1", + "antora": "^3.1.15" } } \ No newline at end of file From 0d9f3f5c20fc6ce790707e4c5381c8f3e7a4eea5 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Anarthal=20=28Rub=C3=A9n=20P=C3=A9rez=29?= <34971811+anarthal@users.noreply.github.com> Date: Sat, 20 Jun 2026 13:46:25 +0200 Subject: [PATCH 13/14] Fixes a race condition in test_push_consumer (test_conn_push2.cpp) (#430) --- test/test_conn_push2.cpp | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/test/test_conn_push2.cpp b/test/test_conn_push2.cpp index c76191dd..d089b208 100644 --- a/test/test_conn_push2.cpp +++ b/test/test_conn_push2.cpp @@ -526,7 +526,12 @@ void test_push_consumer() if (ec) { BOOST_TEST_EQ(ec, net::error::operation_aborted); push_consumer_finished = true; - resp.clear(); + return; + } else if (!conn.will_reconnect()) { + // The connection might be cancelled after async_receive2 is scheduled + // for completion, but before the callback is called. An equivalent clause + // is also present in the examples. + push_consumer_finished = true; return; } launch_push_consumer(); From 741d3896601ade44beabae4fe59bf267bca1a421 Mon Sep 17 00:00:00 2001 From: Lorentz-Andrea Romeo <67752904+L0rentz@users.noreply.github.com> Date: Fri, 26 Jun 2026 16:59:08 +0200 Subject: [PATCH 14/14] Fix use two-iterator erase-remove in multiplexer::cancel to avoid UB (#428) --- include/boost/redis/impl/multiplexer.ipp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/include/boost/redis/impl/multiplexer.ipp b/include/boost/redis/impl/multiplexer.ipp index f548b2ab..f68bd2fa 100644 --- a/include/boost/redis/impl/multiplexer.ipp +++ b/include/boost/redis/impl/multiplexer.ipp @@ -58,7 +58,7 @@ void multiplexer::cancel(std::shared_ptr const& ptr) { if (ptr->is_waiting()) { // We can safely remove it from the queue, since it hasn't been sent yet - reqs_.erase(std::remove(std::begin(reqs_), std::end(reqs_), ptr)); + reqs_.erase(std::remove(std::begin(reqs_), std::end(reqs_), ptr), std::end(reqs_)); } else { // Removing the request would cause trouble when the response arrived. // Mark it as abandoned, so the response is discarded when it arrives