diff --git a/.drone.star b/.drone.star index 0a3271f5d..858f932cc 100644 --- a/.drone.star +++ b/.drone.star @@ -6,7 +6,7 @@ # _triggers = { "branch": [ "master", "develop" ] } -_container_tag = 'c3f5316cc19bf3c0f7a83e31dec58139581f5764' +_container_tag = 'b1f46a8305f62d0af54dda34231b199d76e945f1' _win_container_tag = 'e7bd656c3515263f9b3c69a2d73d045f6a0fed72' @@ -52,7 +52,6 @@ def _b2_command( def _cmake_command( source_dir, server_host='127.0.0.1', - db='mysql8', generator='Ninja', cmake_build_type='Debug', build_shared_libs=0, @@ -63,7 +62,6 @@ def _cmake_command( '--source-dir="{}" '.format(source_dir) + \ 'cmake ' + \ '--server-host={} '.format(server_host) + \ - '--db={} '.format(db) + \ '--generator="{}" '.format(generator) + \ '--cmake-build-type={} '.format(cmake_build_type) + \ '--build-shared-libs={} '.format(build_shared_libs) + \ @@ -100,7 +98,7 @@ def _pipeline( "name": "Build and run", "image": image, "pull": "if-not-exists", - "privileged": arch == "arm64", + "privileged": arch == "arm64", # TSAN tests fail otherwise (personality syscall) "volumes":[{ "name": "mysql-socket", "path": "/var/run/mysqld" @@ -124,7 +122,7 @@ def _pipeline( "steps": steps, "services": [{ "name": "mysql", - "image": _image(db), + "image": "ghcr.io/anarthal-containers/ci-db:{}-{}".format(db, _container_tag), "volumes": [{ "name": "mysql-socket", "path": "/var/run/mysqld" @@ -151,7 +149,8 @@ def linux_b2( undefined_sanitizer=0, valgrind=0, arch='amd64', - fail_if_no_openssl=1 + fail_if_no_openssl=1, + db='mysql-8.4.1', ): command = _b2_command( source_dir='$(pwd)', @@ -173,7 +172,7 @@ def linux_b2( image=image, os='linux', command=command, - db='mysql8', + db=db, arch=arch, disable_aslr=True ) @@ -203,10 +202,10 @@ def windows_b2( def linux_cmake( name, image, + db='mysql-8.4.1', build_shared_libs=0, cmake_build_type='Debug', cxxstd='20', - db='mysql8', install_test=1 ): command = _cmake_command( @@ -214,7 +213,6 @@ def linux_cmake( build_shared_libs=build_shared_libs, cmake_build_type=cmake_build_type, cxxstd=cxxstd, - db=db, server_host='mysql', install_test=install_test ) @@ -245,7 +243,6 @@ def windows_cmake( source_dir='$Env:DRONE_WORKSPACE', build_shared_libs=build_shared_libs, generator='Visual Studio 17 2022', - db='mysql8', server_host='127.0.0.1' ) return _pipeline( @@ -280,8 +277,8 @@ def docs(name): def main(ctx): return [ # CMake Linux - linux_cmake('Linux CMake MySQL 5.x', _image('build-gcc14'), db='mysql5', build_shared_libs=0), - linux_cmake('Linux CMake MariaDB', _image('build-gcc14'), db='mariadb', build_shared_libs=1), + linux_cmake('Linux CMake MySQL 5.x', _image('build-gcc14'), db='mysql-5.7.41', build_shared_libs=0), + linux_cmake('Linux CMake MariaDB', _image('build-gcc14'), db='mariadb-11.4.2', build_shared_libs=1), linux_cmake('Linux CMake cmake 3.8', _image('build-cmake3_8'), cxxstd='11', install_test=0), linux_cmake('Linux CMake gcc Release', _image('build-gcc14'), cmake_build_type='Release'), linux_cmake('Linux CMake gcc MinSizeRel', _image('build-gcc14'), cmake_build_type='MinSizeRel'), @@ -304,7 +301,7 @@ def main(ctx): linux_b2('Linux B2 clang-14-libc++', _image('build-clang14'), toolset='clang-14', cxxstd='20', stdlib='libc++'), linux_b2('Linux B2 clang-14-arm64', _image('build-clang14'), toolset='clang-14', cxxstd='20', arch='arm64'), linux_b2('Linux B2 clang-16-sanit', _image('build-clang16'), toolset='clang-16', cxxstd='20', address_sanitizer=1, undefined_sanitizer=1), - linux_b2('Linux B2 clang-16-i386-sanit', _image('build-clang16-i386'), toolset='clang-16', cxxstd='20', address_model=32, address_sanitizer=1, undefined_sanitizer=1), + linux_b2('Linux B2 clang-16-i386-sanit', _image('build-clang16-i386'), toolset='clang-16', cxxstd='20', address_model='32', address_sanitizer=1, undefined_sanitizer=1), linux_b2('Linux B2 clang-17', _image('build-clang17'), toolset='clang-17', cxxstd='20'), linux_b2('Linux B2 clang-18', _image('build-clang18'), toolset='clang-18', cxxstd='23'), linux_b2('Linux B2 gcc-5', _image('build-gcc5'), toolset='gcc-5', cxxstd='11'), # gcc-5 C++14 doesn't like my constexpr field_view diff --git a/.github/workflows/coverage.yml b/.github/workflows/coverage.yml index 2fbaadf98..8b6e26a4d 100644 --- a/.github/workflows/coverage.yml +++ b/.github/workflows/coverage.yml @@ -21,12 +21,12 @@ jobs: coverage: runs-on: ubuntu-latest container: - image: ghcr.io/anarthal-containers/build-gcc14:c3f5316cc19bf3c0f7a83e31dec58139581f5764 + image: ghcr.io/anarthal-containers/build-gcc14:b1f46a8305f62d0af54dda34231b199d76e945f1 volumes: - /var/run/mysqld:/var/run/mysqld services: mysql: - image: ghcr.io/anarthal-containers/mysql8:c3f5316cc19bf3c0f7a83e31dec58139581f5764 + image: ghcr.io/anarthal-containers/ci-db:mysql-8.4.1-b1f46a8305f62d0af54dda34231b199d76e945f1 ports: - 3306:3306 volumes: diff --git a/.github/workflows/docker-linux.yml b/.github/workflows/docker-linux.yml index 6e57f46a8..abfd424b7 100644 --- a/.github/workflows/docker-linux.yml +++ b/.github/workflows/docker-linux.yml @@ -36,14 +36,6 @@ jobs: - { image: build-cmake3_8, dockerfile: build-cmake3_8, platforms: "linux/amd64", } - { image: build-noopenssl, dockerfile: build-noopenssl, platforms: "linux/amd64", } - { image: build-docs, dockerfile: build-docs, platforms: "linux/amd64", } - - { image: mysql5, dockerfile: mysql5, platforms: "linux/amd64", } - - { image: mysql8, dockerfile: mysql8, platforms: "linux/amd64, linux/arm64/v8" } - - { image: mysql9, dockerfile: mysql9, platforms: "linux/amd64, linux/arm64/v8" } - - { image: mariadb, dockerfile: mariadb, platforms: "linux/amd64, linux/arm64/v8" } - - permissions: - contents: read - packages: write runs-on: ubuntu-latest @@ -73,3 +65,44 @@ jobs: build-args: ${{ matrix.build-args }} platforms: ${{ matrix.platforms }} tags: ghcr.io/anarthal-containers/${{ matrix.image }}:${{ github.sha }}, ghcr.io/anarthal-containers/${{ matrix.image }}:latest + + + dbs: + strategy: + matrix: + include: + - { flavor: mysql, version: 5.7.41, platforms: "linux/amd64" } # Unavailable for ARM + - { flavor: mysql, version: 8.4.1, platforms: "linux/amd64, linux/arm64/v8", entrypoint-args: "--mysql-native-password=ON" } + - { flavor: mysql, version: 9.0.0, platforms: "linux/amd64, linux/arm64/v8" } + - { flavor: mariadb, version: 11.4.2, platforms: "linux/amd64, linux/arm64/v8" } + + runs-on: ubuntu-latest + + steps: + - name: Checkout repository + uses: actions/checkout@v4 + + - name: Set up QEMU + uses: docker/setup-qemu-action@v3 + + - name: Set up Docker Buildx + uses: docker/setup-buildx-action@v3 + + - name: Log in to the Container registry + uses: docker/login-action@v3 + with: + registry: ghcr.io + username: anarthal-containers + password: ${{ secrets.ANARTHAL_CONTAINERS_TOKEN }} + + - name: Build and push + uses: docker/build-push-action@v6 + with: + context: . + push: true + file: tools/docker/${{ matrix.flavor }}.dockerfile + build-args: | + BASE_IMAGE_VERSION=${{ matrix.version }} + ENTRYPOINT_ARGS=${{ matrix.entrypoint-args }} + platforms: ${{ matrix.platforms }} + tags: ghcr.io/anarthal-containers/ci-db:${{ matrix.flavor }}-${{ matrix.version }}-${{ github.sha }} diff --git a/.github/workflows/fuzz.yml b/.github/workflows/fuzz.yml index f7d438e77..71d03b3bd 100644 --- a/.github/workflows/fuzz.yml +++ b/.github/workflows/fuzz.yml @@ -20,12 +20,12 @@ jobs: fuzz: runs-on: ubuntu-latest container: - image: ghcr.io/anarthal-containers/build-clang18:c3f5316cc19bf3c0f7a83e31dec58139581f5764 + image: ghcr.io/anarthal-containers/build-clang18:b1f46a8305f62d0af54dda34231b199d76e945f1 volumes: - /var/run/mysqld:/var/run/mysqld services: mysql: - image: ghcr.io/anarthal-containers/mysql8:c3f5316cc19bf3c0f7a83e31dec58139581f5764 + image: ghcr.io/anarthal-containers/ci-db:mysql-8.4.1-b1f46a8305f62d0af54dda34231b199d76e945f1 ports: - 3306:3306 volumes: diff --git a/CMakeLists.txt b/CMakeLists.txt index df9c9864b..81a8cca04 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -57,6 +57,13 @@ target_compile_features(boost_mysql INTERFACE cxx_std_11) option(BOOST_MYSQL_INTEGRATION_TESTS OFF "Whether to build and run integration tests or not") mark_as_advanced(BOOST_MYSQL_INTEGRATION_TESTS) +# List of server features that the CI DB server does not support. +# Disables running some integration tests and examples +set(BOOST_MYSQL_DISABLED_SERVER_FEATURES "" CACHE STRING + "A CMake list of server features not supported by the CI server, for integration tests" +) +mark_as_advanced(BOOST_MYSQL_DISABLED_SERVER_FEATURES) + # Examples and tests if(BUILD_TESTING) # Contains some functions to share code between examples and tests diff --git a/doc/qbk/00_main.qbk b/doc/qbk/00_main.qbk index 5069c3ea9..37c046e46 100644 --- a/doc/qbk/00_main.qbk +++ b/doc/qbk/00_main.qbk @@ -165,7 +165,6 @@ END [include 23_sql_formatting_advanced.qbk] [include 24_pipeline.qbk] [include 24_examples.qbk] -[include 25_tests.qbk] [section:ref Reference] diff --git a/doc/qbk/24_examples.qbk b/doc/qbk/24_examples.qbk index 55cae6368..a5e09cfe8 100644 --- a/doc/qbk/24_examples.qbk +++ b/doc/qbk/24_examples.qbk @@ -42,17 +42,12 @@ Examples make use of a database named `boost_mysql_examples`. The server hostname and credentials (username and password) are passed to the examples via the command line. -If you're using docker, you can use the `ghcr.io/anarthal-containers/mysql8` container -to simplify the process: +You can spin up a server quickly by using Docker: [!teletype] ``` - # If you're on a system supporting UNIX sockets. Note that /var/run/mysqld - # should be empty for this to work; you can specify a different directory, if it's not - > docker run -p 3306:3306 -v /var/run/mysqld:/var/run/mysqld -d ghcr.io/anarthal-containers/mysql8 - - # If you're on a system that does not support UNIX sockets - > docker run -p 3306:3306 -d ghcr.io/anarthal-containers/mysql8 + # Remove the "-v /var/run/mysqld:/var/run/mysqld" part if you don't need UNIX sockets + > docker run --name some-mysql -p 3306:3306 -v /var/run/mysqld:/var/run/mysqld -d -e MYSQL_ROOT_PASSWORD= -e MYSQL_ALLOW_EMPTY_PASSWORD=1 -d mysql # All the required data can be loaded by running example/db_setup.sql. # If you're using the above container, the root user has a blank password @@ -153,7 +148,7 @@ implement a minimal order management system for an online store. The example employs synchronous functions with exceptions as error handling. __see_error_handling__ -This examples requires you to run [link_to_file example/order_management/db_setup.sql]. +This example requires you to run [link_to_file example/order_management/db_setup.sql]. You can find table definitions there. [import ../../example/order_management/prepared_statements_cpp11.cpp] @@ -173,7 +168,7 @@ implement a minimal order management system for an online store. The example employs synchronous functions with exceptions as error handling. __see_error_handling__ -This examples requires you to run [link_to_file example/order_management/db_setup.sql]. +This example requires you to run [link_to_file example/order_management/db_setup.sql]. You can find table definitions there. [import ../../example/order_management/prepared_statements_cpp14.cpp] @@ -193,7 +188,7 @@ implement a minimal order management system for an online store. The example employs synchronous functions with exceptions as error handling. __see_error_handling__ -This examples requires you to run [link_to_file example/order_management/db_setup.sql]. +This example requires you to run [link_to_file example/order_management/db_setup.sql]. You can find table and procedure definitions there. [import ../../example/order_management/stored_procedures_cpp11.cpp] @@ -213,7 +208,7 @@ implement a minimal order management system for an online store. The example employs synchronous functions with exceptions as error handling. __see_error_handling__ -This examples requires you to run [link_to_file example/order_management/db_setup.sql]. +This example requires you to run [link_to_file example/order_management/db_setup.sql]. You can find table and procedure definitions there. [import ../../example/order_management/stored_procedures_cpp14.cpp] diff --git a/doc/qbk/25_tests.qbk b/doc/qbk/25_tests.qbk deleted file mode 100644 index 237d4a595..000000000 --- a/doc/qbk/25_tests.qbk +++ /dev/null @@ -1,67 +0,0 @@ -[/ - Copyright (c) 2019-2024 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) -] - - -[section:tests Building and running the tests] - -[teletype] - -This section explains how to build and run this library's tests. It is not intended -for the regular user. - -This library has both unit and integration tests. Considering the different flavors -the MySQL server has (v5.x, v8.x and MariaDB, with sutile differences) and the complex nature -of the client/server protocol, we have given certain weight to the latter. Additionally, all -examples are also built and run as integration tests, too (as they require access to a real database server). - -By default, the build will only compile and run unit tests (i.e. if you run `b2 libs/mysql/test` or `cmake` -with no special args). If you want to run the integration tests and the examples, you need a real database server. - -If you are using `docker`, you can use one of the following images: - -* `ghcr.io/anarthal-containers/mysql8` -* `ghcr.io/anarthal-containers/mysql5` -* `ghcr.io/anarthal-containers/mariadb` - -You can run the containers as follows: -``` - # If you're on a system supporting UNIX sockets. Note that /var/run/mysqld - # should be empty for this to work; you can specify a different directory, if it's not - > docker run -p 3306:3306 -v /var/run/mysqld:/var/run/mysqld -d # replace by the image you've chosen - - # If you're on a system that does not support UNIX sockets - > docker run -p 3306:3306 -d ghcr.io/anarthal-containers/mysql8 -``` - -You then need to run the following setup files, e.g. by running `mysql -u root < path/to/setup.sql`: - -* `example/db_setup.sql` -* `test/integration/db_setup.sql` -* If you are running a MySQL 8.x server, run also `test/integration/db_setup_sha256.sql`. - - -If you are using your own database server, you will need to install the SSL certificates in `tools/ssl` -in your MySQL server and change your config file so that your server uses them. -More information [mysqllink using-encrypted-connections.html here]. - -Next, define the following environment variables: - -* If your database server is NOT running in `localhost`, define `BOOST_MYSQL_SERVER_HOST` to the host where it is running. - If you are using the Docker image as provided in this document, you don't need this. -* If your system does not support UNIX sockets or your socket path is different than MySQL's default (`/var/run/mysqld/mysqld.sock`), - define `BOOST_MYSQL_NO_UNIX_SOCKET_TESTS=1`. -* Define `BOOST_MYSQL_TEST_DB` to either `mysql5`, `mysql8` or `mariadb`, depending on the server you're running. - -If you are using `b2`, you can build the targets `boost/mysql/example//boost_mysql_all_examples`, -`boost/mysql/test/integration//boost_mysql_integrationtests` and `boost/mysql/test` to build and run the tests. - -If you are using `cmake`, add `-DBOOST_MYSQL_INTEGRATION_TESTS=ON` to enable building and running integration tests -and examples, and then test regularly with `ctest`. - -[c++] - -[endsect] \ No newline at end of file diff --git a/example/CMakeLists.txt b/example/CMakeLists.txt index 737cd3245..fd10c1f10 100644 --- a/example/CMakeLists.txt +++ b/example/CMakeLists.txt @@ -107,8 +107,8 @@ endforeach() add_example_coroutines(async_coroutines) add_example_coroutines(any_connection) -# UNIX sockets -if("$ENV{BOOST_MYSQL_NO_UNIX_SOCKET_TESTS}" STREQUAL "") +# UNIX sockets. Don't run the example on Windows machines +if (NOT WIN32) add_example( boost_mysql_example_unix_socket unix_socket.cpp diff --git a/example/Jamfile b/example/Jamfile index 48b041ea9..ec1ead3a7 100644 --- a/example/Jamfile +++ b/example/Jamfile @@ -81,15 +81,15 @@ run_python_example dynamic_filters : run_dynamic_filters.py : dynamic_filters.cp run_python_example batch_inserts : run_batch_inserts.py : batch_inserts.cpp /boost/json//boost_json ; run_python_example batch_inserts_generic : run_batch_inserts.py : batch_inserts_generic.cpp /boost/json//boost_json ; -# UNIX. Honor BOOST_MYSQL_NO_UNIX_SOCKET_TESTS for homogeneity with cmake -if [ os.environ BOOST_MYSQL_NO_UNIX_SOCKET_TESTS ] = "" { - run - unix_socket.cpp - /boost/mysql/test//boost_mysql_compiled - : +# UNIX. Don't run under Windows systems +run + unix_socket.cpp + /boost/mysql/test//boost_mysql_compiled +: + requirements + windows:no "example_user example_password" - ; -} +; # Source script run diff --git a/test/common/include/test_common/ci_server.hpp b/test/common/include/test_common/ci_server.hpp index 98fa43198..874c5b500 100644 --- a/test/common/include/test_common/ci_server.hpp +++ b/test/common/include/test_common/ci_server.hpp @@ -39,7 +39,6 @@ constexpr const char* integ_user = "integ_user"; constexpr const char* integ_passwd = "integ_password"; constexpr const char* integ_db = "boost_mysql_integtests"; constexpr const char* default_unix_path = "/var/run/mysqld/mysqld.sock"; -inline bool is_mariadb() { return safe_getenv("BOOST_MYSQL_TEST_DB", "mysql8") == "mariadb"; } } // namespace test } // namespace mysql diff --git a/test/integration/CMakeLists.txt b/test/integration/CMakeLists.txt index 56c4df5c0..2e0bb9b27 100644 --- a/test/integration/CMakeLists.txt +++ b/test/integration/CMakeLists.txt @@ -9,6 +9,7 @@ add_executable( boost_mysql_integrationtests # Utilities + src/server_features.cpp src/get_endpoint.cpp src/metadata_validator.cpp src/er_network_variant.cpp @@ -70,18 +71,7 @@ if (${CMAKE_VERSION} VERSION_GREATER_EQUAL 3.16) ) endif() -# Compose the test filter -set (DB_SYSTEM $ENV{BOOST_MYSQL_TEST_DB}) -if ("${DB_SYSTEM}" STREQUAL "") - set(DB_SYSTEM "mysql8") -endif() -set(TEST_FILTER "!@skip_${DB_SYSTEM}") - -if (NOT "$ENV{BOOST_MYSQL_NO_UNIX_SOCKET_TESTS}" STREQUAL "") - set(TEST_FILTER "${TEST_FILTER}:!@unix") -endif() - add_test( NAME boost_mysql_integrationtests - COMMAND boost_mysql_integrationtests "-t" ${TEST_FILTER} + COMMAND boost_mysql_integrationtests ) diff --git a/test/integration/Jamfile b/test/integration/Jamfile index 4cb7a3de4..dec6a9500 100644 --- a/test/integration/Jamfile +++ b/test/integration/Jamfile @@ -5,24 +5,6 @@ # file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) # -import os ; -import sequence ; - -# Integration test filtering -local test_exclusions = "" ; -if [ os.environ BOOST_MYSQL_NO_UNIX_SOCKET_TESTS ] != "" { - test_exclusions += "!@unix" ; -} -local test_db = [ os.environ BOOST_MYSQL_TEST_DB ] ; -if $(test_db) = "" { - test_db = "mysql8" ; -} -test_exclusions += "!@skip_$(test_db)" ; - -local test_filter = [ sequence.join $(test_exclusions) : ":" ] ; - -local test_command = "-t $(test_filter)" ; - cpp-pch pch : pch.hpp @@ -39,6 +21,7 @@ run /boost/mysql/test//launch_with_valgrind # Utilities + src/server_features.cpp src/get_endpoint.cpp src/metadata_validator.cpp src/er_network_variant.cpp @@ -81,7 +64,6 @@ run test/snippets/pipeline.cpp : requirements - $(test_command) include : target-name boost_mysql_integrationtests ; diff --git a/test/integration/db_setup.sql b/test/integration/db_setup.sql index 719daca9d..8086d98ec 100644 --- a/test/integration/db_setup.sql +++ b/test/integration/db_setup.sql @@ -504,6 +504,10 @@ CREATE USER 'mysqlnp_empty_password_user'@'%' IDENTIFIED WITH 'mysql_native_pass ALTER USER 'mysqlnp_empty_password_user'@'%' IDENTIFIED BY ''; GRANT ALL PRIVILEGES ON boost_mysql_integtests.* TO 'mysqlnp_empty_password_user'@'%'; +-- Some containers don't allow remote root access. Enable it. +CREATE USER IF NOT EXISTS 'root'@'%' IDENTIFIED BY ''; +GRANT ALL PRIVILEGES ON *.* TO 'root'@'%' WITH GRANT OPTION; + -- Stored procedures DELIMITER // diff --git a/test/integration/include/test_integration/common.hpp b/test/integration/include/test_integration/common.hpp index a4e7ac027..ea38b9a22 100644 --- a/test/integration/include/test_integration/common.hpp +++ b/test/integration/include/test_integration/common.hpp @@ -62,22 +62,22 @@ struct network_fixture : network_fixture_base } } - void setup(er_network_variant* variant) + void setup(er_network_variant& variant) { - var = variant; + var = &variant; conn = var->create_connection(ctx.get_executor(), ssl_ctx); conn->set_metadata_mode(metadata_mode::full); } - void setup_and_physical_connect(er_network_variant* net) + void setup_and_physical_connect(er_network_variant& net) { setup(net); conn->physical_connect(); } - void setup_and_connect(er_network_variant* variant, ssl_mode m = ssl_mode::require) + void setup_and_connect(er_network_variant& net, ssl_mode m = ssl_mode::require) { - setup(variant); + setup(net); connect(m); } @@ -120,65 +120,7 @@ struct network_fixture : network_fixture_base } }; -// To be used as sample in data driven tests, when a test case should be run -// over all different network_function's. -struct network_sample -{ - er_network_variant* net; - - network_sample(er_network_variant* var) : net(var) {} - - void set_test_attributes(boost::unit_test::test_case& test) const - { - if (net->supports_ssl()) - { - test.add_label("ssl"); - } - if (net->is_unix_socket()) - { - test.add_label("unix"); - } - test.add_label(net->variant_name()); - } -}; - -inline std::ostream& operator<<(std::ostream& os, const network_sample& value) -{ - return os << value.net->stream_name() << "_" << value.net->variant_name(); -} - -inline std::vector create_network_samples(std::initializer_list names) -{ - std::vector res; - for (const char* name : names) - res.emplace_back(get_variant(name)); - return res; -} - -inline std::vector create_all_network_samples() -{ - std::vector res; - for (auto* var : all_variants()) - res.emplace_back(var); - return res; -} - -inline const std::vector& all_network_samples() -{ - static std::vector res = create_all_network_samples(); - return res; -} - -inline std::vector all_network_samples_with_handshake() -{ - std::vector res; - for (auto* var : all_variants_with_handshake()) - res.emplace_back(var); - return res; -} - inline void validate_2fields_meta(const metadata_collection_view& fields, const std::string& table) - { validate_meta( fields, diff --git a/test/integration/include/test_integration/er_network_variant.hpp b/test/integration/include/test_integration/er_network_variant.hpp index 19359cd7c..0bb1171e4 100644 --- a/test/integration/include/test_integration/er_network_variant.hpp +++ b/test/integration/include/test_integration/er_network_variant.hpp @@ -14,6 +14,9 @@ #include #include +#include +#include + #include "test_integration/er_connection.hpp" namespace boost { @@ -32,10 +35,14 @@ class er_network_variant std::string name() const { return std::string(stream_name()) + '_' + variant_name(); } virtual er_connection_ptr create_connection(boost::asio::any_io_executor, boost::asio::ssl::context&) = 0; }; - -boost::span all_variants(); -boost::span all_variants_with_handshake(); -er_network_variant* get_variant(string_view name); +std::ostream& operator<<(std::ostream& os, const er_network_variant& var); + +std::vector> all_variants(); +std::vector> all_variants_with_handshake(); +er_network_variant& get_network_variant(string_view name); +std::vector> get_network_variants( + boost::span names +); } // namespace test } // namespace mysql diff --git a/test/integration/include/test_integration/network_samples.hpp b/test/integration/include/test_integration/network_samples.hpp new file mode 100644 index 000000000..3967d7aef --- /dev/null +++ b/test/integration/include/test_integration/network_samples.hpp @@ -0,0 +1,84 @@ +// +// Copyright (c) 2019-2024 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) +// + +#ifndef BOOST_MYSQL_TEST_INTEGRATION_INCLUDE_TEST_INTEGRATION_NETWORK_SAMPLES_HPP +#define BOOST_MYSQL_TEST_INTEGRATION_INCLUDE_TEST_INTEGRATION_NETWORK_SAMPLES_HPP + +#include + +#include +#include + +#include +#include +#include + +#include "test_integration/er_network_variant.hpp" + +namespace boost { +namespace mysql { +namespace test { + +// A Boost.Test dataset of er_network_variant samples that are lazily computed. +// Laziness is required because creating the variants requires access to +// the enabled server features (and thus getenv), and that's not legal from global initializers. +class network_samples +{ + using col_t = std::vector>; + using factory_t = std::function; + + mutable bool created_{false}; + mutable col_t data_; + factory_t fn_; + + col_t& get() const + { + if (!created_) + { + data_ = fn_(); + created_ = true; + } + return data_; + } + +public: + static constexpr int arity = 1; + using iterator = col_t::const_iterator; + + network_samples(factory_t fn) : fn_(std::move(fn)) {} + network_samples(std::vector names) + : network_samples([names]() { return get_network_variants(names); }) + { + } + + static network_samples all() { return network_samples(&all_variants); } + static network_samples all_with_handshake() { return network_samples(&all_variants_with_handshake); } + + boost::unit_test::data::size_t size() const { return get().size(); } + iterator begin() const { return get().begin(); } +}; + +} // namespace test +} // namespace mysql +} // namespace boost + +namespace boost { +namespace unit_test { +namespace data { +namespace monomorphic { + +template <> +struct is_dataset : std::true_type +{ +}; + +} // namespace monomorphic +} // namespace data +} // namespace unit_test +} // namespace boost + +#endif diff --git a/test/integration/include/test_integration/network_test.hpp b/test/integration/include/test_integration/network_test.hpp deleted file mode 100644 index e405c1d83..000000000 --- a/test/integration/include/test_integration/network_test.hpp +++ /dev/null @@ -1,109 +0,0 @@ -// -// Copyright (c) 2019-2024 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) -// - -#ifndef BOOST_MYSQL_TEST_INTEGRATION_INCLUDE_TEST_INTEGRATION_NETWORK_TEST_HPP -#define BOOST_MYSQL_TEST_INTEGRATION_INCLUDE_TEST_INTEGRATION_NETWORK_TEST_HPP - -#include - -#include - -#include "test_common/stringize.hpp" - -/** - * Defines the required infrastructure for network tests. - * These are data-driven (parametrized) test cases. We don't - * use Boost.Test data-driven functionality for this because - * tests must have different labels depending on the parameters, - * which is not supported by Boost.Test. - * - * All network tests employ type-erased network objects, - * defined under the utils/ folder. Network tests are run under - * different network variants. Each variant is a combination of a Stream - * and a sync/async flavor. Examples: TCP + async with callbacks, - * UNIX SSL + sync with exceptions, TCP SSL + C++20 coroutines. - * - * Access this feature using the BOOST_MYSQL_NETWORK_TEST_* macros; - * they work similar to BOOST_AUTO_TEST_CASE. - */ - -namespace boost { -namespace mysql { -namespace test { - -// The type of a sample generated by DataGenerator -template -using sample_type = typename std::decay()))>::type; - -inline boost::unit_test::test_suite* create_test_suite( - boost::unit_test::const_string tc_name, - boost::unit_test::const_string tc_file, - std::size_t tc_line -) -{ - // Create a test suite with the name of the test - auto* suite = new boost::unit_test::test_suite(tc_name, tc_file, tc_line); - boost::unit_test::framework::current_auto_test_suite().add(suite); - - // Add decorators - auto& collector = boost::unit_test::decorator::collector_t::instance(); - collector.store_in(*suite); - collector.reset(); - - return suite; -} - -// Inspired in how Boost.Test auto-registers unit tests. -// BOOST_MYSQL_NETWORK_TEST defines a static variable of this -// type, which takes care of test registration. -template -struct network_test_registrar -{ - template - network_test_registrar( - boost::unit_test::const_string tc_name, - boost::unit_test::const_string tc_file, - std::size_t tc_line, - const SampleCollection& samples - ) - { - // Create suite - auto* suite = create_test_suite(tc_name, tc_file, tc_line); - - // Create a test for each sample - for (const auto& sample : samples) - { - std::string test_name = stringize(sample); - auto* test = boost::unit_test::make_test_case( - [sample] { - Testcase tc_struct; - tc_struct.test_method(sample); - }, - test_name, - tc_file, - tc_line - ); - sample.set_test_attributes(*test); - suite->add(test); - } - } -}; - -} // namespace test -} // namespace mysql -} // namespace boost - -#define BOOST_MYSQL_NETWORK_TEST(name, fixture, samples) \ - struct name : public fixture \ - { \ - void test_method(const sample_type&); \ - }; \ - static ::boost::mysql::test::network_test_registrar name##_registrar \ - BOOST_ATTRIBUTE_UNUSED(#name, __FILE__, __LINE__, samples); \ - void name::test_method(const sample_type& sample) - -#endif diff --git a/test/integration/include/test_integration/server_features.hpp b/test/integration/include/test_integration/server_features.hpp new file mode 100644 index 000000000..142c387d1 --- /dev/null +++ b/test/integration/include/test_integration/server_features.hpp @@ -0,0 +1,51 @@ +// +// Copyright (c) 2019-2024 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) +// + +#ifndef BOOST_MYSQL_TEST_INTEGRATION_INCLUDE_TEST_INTEGRATION_SERVER_FEATURES_HPP +#define BOOST_MYSQL_TEST_INTEGRATION_INCLUDE_TEST_INTEGRATION_SERVER_FEATURES_HPP + +#include + +namespace boost { +namespace mysql { +namespace test { + +// What does the CI server deployment support? +// We determine these from the command line arguments. +struct server_features +{ + // Is the server listening on a UNIX socket? + bool unix_sockets{true}; + + // Does the server support sha256 authentication methods? + // Includes caching_sha2_password and sha256_password + bool sha256{true}; + + // Does the server support the dedicated JSON type? + bool json_type{true}; + + // Does the server support MySQL8+ specific regex error codes? + bool regex_error_codes{true}; + + // Does the server support MariaDB specific dup_query error codes? + bool dup_query_error_codes{true}; +}; + +using server_feature_t = bool server_features::*; + +server_features get_server_features(); + +// Decorators to skip tests if the given features are not enabled. +// This is lazy because get_server_features requires access to getenv, not legal from global initializers. +unit_test::precondition run_if(server_feature_t feature); +unit_test::precondition run_if(server_feature_t feature1, server_feature_t feature2); + +} // namespace test +} // namespace mysql +} // namespace boost + +#endif diff --git a/test/integration/src/async_callback.cpp b/test/integration/src/async_callback.cpp index db48b4b2b..fd12c51cb 100644 --- a/test/integration/src/async_callback.cpp +++ b/test/integration/src/async_callback.cpp @@ -18,6 +18,7 @@ #include "test_common/netfun_helpers.hpp" #include "test_common/tracker_executor.hpp" #include "test_integration/er_connection.hpp" +#include "test_integration/server_features.hpp" #include "test_integration/streams.hpp" using namespace boost::mysql::test; @@ -76,7 +77,7 @@ class any_async_callback_connection : public async_callback_base -void add_async_callback_variant(std::vector& output) +void add_async_callback_variant(std::vector>& output) { add_variant>(output); } @@ -85,14 +86,17 @@ void add_async_callback_variant(std::vector& output) } // namespace mysql } // namespace boost -void boost::mysql::test::add_async_callback(std::vector& output) +void boost::mysql::test::add_async_callback(std::vector>& output) { // Spotcheck for both streams add_async_callback_variant(output); add_async_callback_variant(output); add_variant_any(output); -#if BOOST_ASIO_HAS_LOCAL_SOCKETS - add_async_callback_variant(output); - add_variant_any(output); +#ifdef BOOST_ASIO_HAS_LOCAL_SOCKETS + if (get_server_features().unix_sockets) + { + add_async_callback_variant(output); + add_variant_any(output); + } #endif } diff --git a/test/integration/src/async_coroutines.cpp b/test/integration/src/async_coroutines.cpp index 06e14e1b4..e9a38e023 100644 --- a/test/integration/src/async_coroutines.cpp +++ b/test/integration/src/async_coroutines.cpp @@ -73,7 +73,7 @@ class any_async_coroutine_connection : public async_coroutine_base& output) +void boost::mysql::test::add_async_coroutines(std::vector>& output) { add_variant>(output); add_variant_any(output); diff --git a/test/integration/src/async_coroutinescpp20.cpp b/test/integration/src/async_coroutinescpp20.cpp index 22ae2f160..3f5cc9d5a 100644 --- a/test/integration/src/async_coroutinescpp20.cpp +++ b/test/integration/src/async_coroutinescpp20.cpp @@ -107,7 +107,9 @@ class any_async_coroutinecpp20_connection : public async_coroutinecpp20_base& output) +void boost::mysql::test::add_async_coroutinescpp20( + std::vector>& output +) { add_variant>(output); add_variant_any(output); @@ -115,6 +117,8 @@ void boost::mysql::test::add_async_coroutinescpp20(std::vector&) {} +void boost::mysql::test::add_async_coroutinescpp20(std::vector>&) +{ +} #endif diff --git a/test/integration/src/er_impl_common.hpp b/test/integration/src/er_impl_common.hpp index b85230a4d..e3d0c2157 100644 --- a/test/integration/src/er_impl_common.hpp +++ b/test/integration/src/er_impl_common.hpp @@ -42,11 +42,11 @@ namespace mysql { namespace test { // Variants -void add_sync_errc(std::vector&); -void add_sync_exc(std::vector&); -void add_async_callback(std::vector&); -void add_async_coroutines(std::vector&); -void add_async_coroutinescpp20(std::vector&); +void add_sync_errc(std::vector>&); +void add_sync_exc(std::vector>&); +void add_async_callback(std::vector>&); +void add_async_coroutines(std::vector>&); +void add_async_coroutinescpp20(std::vector>&); // Helpers template @@ -428,17 +428,17 @@ inline void rethrow_on_failure(std::exception_ptr ptr) } template -void add_variant(std::vector& output) +void add_variant(std::vector>& output) { static er_network_variant_impl obj; - output.push_back(&obj); + output.push_back(obj); } template -void add_variant_any(std::vector& output) +void add_variant_any(std::vector>& output) { static er_network_variant_any_impl obj; - output.push_back(&obj); + output.push_back(obj); } } // namespace test diff --git a/test/integration/src/er_network_variant.cpp b/test/integration/src/er_network_variant.cpp index 5ab8dabf0..9eda1263f 100644 --- a/test/integration/src/er_network_variant.cpp +++ b/test/integration/src/er_network_variant.cpp @@ -5,19 +5,27 @@ // file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) // -#include "test_integration/er_network_variant.hpp" - -#include +#include #include +#include #include "er_impl_common.hpp" +#include "test_integration/er_network_variant.hpp" using namespace boost::mysql::test; using boost::mysql::error_code; -static std::vector make_all_variants() +static std::unordered_map make_variants_map() { - std::vector res; + std::unordered_map res; + for (er_network_variant& var : all_variants()) + res[var.name()] = &var; + return res; +} + +std::vector> boost::mysql::test::all_variants() +{ + std::vector> res; add_sync_errc(res); add_sync_exc(res); add_async_callback(res); @@ -26,43 +34,38 @@ static std::vector make_all_variants() return res; } -static std::unordered_map make_variants_map() -{ - std::unordered_map res; - for (auto* var : all_variants()) - res[var->name()] = var; - return res; -} - -static std::vector make_all_variants_with_handshake() +std::vector> boost::mysql::test::all_variants_with_handshake() { - std::vector res; - for (auto* var : all_variants()) + std::vector> res; + for (er_network_variant& var : all_variants()) { - if (var->supports_handshake()) + if (var.supports_handshake()) res.push_back(var); } return res; } -boost::span boost::mysql::test::all_variants() +er_network_variant& boost::mysql::test::get_network_variant(string_view name) { - static auto res = make_all_variants(); - return res; + static auto by_name = make_variants_map(); + std::string name_str(name); + auto it = by_name.find(name_str); + if (it == by_name.end()) + throw std::out_of_range("Unknown network variant: " + name_str); + return *it->second; } -boost::span boost::mysql::test::all_variants_with_handshake() +std::vector> boost::mysql::test::get_network_variants( + boost::span names +) { - static auto res = make_all_variants_with_handshake(); + std::vector> res; + for (auto name : names) + res.push_back(get_network_variant(name)); return res; } -er_network_variant* boost::mysql::test::get_variant(string_view name) +std::ostream& boost::mysql::test::operator<<(std::ostream& os, const er_network_variant& value) { - static auto by_name = make_variants_map(); - std::string name_str(name); - auto it = by_name.find(name_str); - if (it == by_name.end()) - throw std::out_of_range("Unknown network variant: " + name_str); - return it->second; + return os << value.stream_name() << "_" << value.variant_name(); } diff --git a/test/integration/src/server_features.cpp b/test/integration/src/server_features.cpp new file mode 100644 index 000000000..031b8f525 --- /dev/null +++ b/test/integration/src/server_features.cpp @@ -0,0 +1,112 @@ +// +// Copyright (c) 2019-2024 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 +#include +#include + +#include +#include +#include +#include + +#include "test_common/ci_server.hpp" +#include "test_integration/server_features.hpp" + +using namespace boost::mysql; + +static std::vector split_list(string_view s) +{ + std::vector res; + std::size_t pos = 0; + if (!s.empty()) + { + while ((pos = s.find(' ')) != string_view::npos) + { + res.push_back(s.substr(0, pos)); + s = s.substr(pos + 1); + } + res.push_back(s); + } + return res; +} + +static test::server_features do_get_server_features() +{ + // Get the disabled feature list from the environment variable + auto disabled_features_str = test::safe_getenv("BOOST_MYSQL_DISABLED_SERVER_FEATURES", ""); + + // Parse the disabled features list + auto disabled_features = split_list(disabled_features_str); + + // The list of possible features + const struct possible_feature_t + { + string_view name; + test::server_feature_t ptr; + } possible_features[]{ + {"unix-sockets", &test::server_features::unix_sockets }, + {"sha256", &test::server_features::sha256 }, + {"json-type", &test::server_features::json_type }, + {"regex-error-codes", &test::server_features::regex_error_codes }, + {"dup-query-error-codes", &test::server_features::dup_query_error_codes}, + }; + + // Match disabled features against the possible set + test::server_features res; + for (auto feature : disabled_features) + { + auto it = std::find_if( + std::begin(possible_features), + std::end(possible_features), + [feature](possible_feature_t v) { return v.name == feature; } + ); + if (it == std::end(possible_features)) + { + std::cerr << "Unknown disabled feature: " << feature << std::endl; + exit(1); + } + res.*it->ptr = false; + } + + // Report the configuration + std::cout << "Server features:\n"; + for (auto feature : possible_features) + { + std::cout << "+ " << feature.name << ": " << res.*feature.ptr << '\n'; + } + std::cout << '\n'; + + // Done + return res; +} + +boost::mysql::test::server_features boost::mysql::test::get_server_features() +{ + static server_features res = do_get_server_features(); + return res; +} + +boost::unit_test::precondition boost::mysql::test::run_if(server_feature_t feature) +{ + return unit_test::precondition([feature](unit_test::test_unit_id) { + return get_server_features().*feature; + }); +} + +boost::unit_test::precondition boost::mysql::test::run_if( + server_feature_t feature1, + server_feature_t feature2 +) +{ + return unit_test::precondition([=](unit_test::test_unit_id) { + const auto supported = get_server_features(); + return supported.*feature1 && supported.*feature2; + }); +} diff --git a/test/integration/src/sync_errc.cpp b/test/integration/src/sync_errc.cpp index 1d4f37390..2b8fa4759 100644 --- a/test/integration/src/sync_errc.cpp +++ b/test/integration/src/sync_errc.cpp @@ -9,6 +9,7 @@ #include "er_impl_common.hpp" #include "test_common/netfun_helpers.hpp" +#include "test_integration/server_features.hpp" #include "test_integration/streams.hpp" using namespace boost::mysql::test; @@ -69,7 +70,7 @@ class any_sync_errc_connection final : public sync_errc_base -void add_sync_errc_variant(std::vector& output) +void add_sync_errc_variant(std::vector>& output) { add_variant>(output); } @@ -78,16 +79,19 @@ void add_sync_errc_variant(std::vector& output) } // namespace mysql } // namespace boost -void boost::mysql::test::add_sync_errc(std::vector& output) +void boost::mysql::test::add_sync_errc(std::vector>& output) { // Verify that all streams work add_sync_errc_variant(output); add_sync_errc_variant(output); add_variant_any(output); -#if BOOST_ASIO_HAS_LOCAL_SOCKETS - add_sync_errc_variant(output); - add_sync_errc_variant(output); - add_variant_any(output); +#ifdef BOOST_ASIO_HAS_LOCAL_SOCKETS + if (get_server_features().unix_sockets) + { + add_sync_errc_variant(output); + add_sync_errc_variant(output); + add_variant_any(output); + } #endif } diff --git a/test/integration/src/sync_exc.cpp b/test/integration/src/sync_exc.cpp index 52763a795..3cccc412d 100644 --- a/test/integration/src/sync_exc.cpp +++ b/test/integration/src/sync_exc.cpp @@ -79,7 +79,7 @@ class any_sync_exc_connection final : public sync_exc_base } // namespace mysql } // namespace boost -void boost::mysql::test::add_sync_exc(std::vector& output) +void boost::mysql::test::add_sync_exc(std::vector>& output) { // Spotcheck add_variant>(output); diff --git a/test/integration/test/any_connection.cpp b/test/integration/test/any_connection.cpp index 793ea4d30..60d6cfcb8 100644 --- a/test/integration/test/any_connection.cpp +++ b/test/integration/test/any_connection.cpp @@ -26,6 +26,7 @@ #include #include #include +#include #include @@ -36,6 +37,7 @@ #include "test_common/printing.hpp" #include "test_integration/common.hpp" #include "test_integration/server_ca.hpp" +#include "test_integration/server_features.hpp" // Additional spotchecks for any_connection @@ -117,7 +119,7 @@ BOOST_AUTO_TEST_CASE(tcp_ssl_mode_enable) } // UNIX connections never use SSL -BOOST_TEST_DECORATOR(*boost::unit_test::label("unix")) +BOOST_TEST_DECORATOR(*run_if(&server_features::unix_sockets)) BOOST_AUTO_TEST_CASE(unix_ssl) { // Create the connection @@ -137,8 +139,7 @@ BOOST_AUTO_TEST_CASE(unix_ssl) } // Spotcheck: users can log-in using the caching_sha2_password auth plugin -BOOST_TEST_DECORATOR(*boost::unit_test::label("skip_mysql5")) -BOOST_TEST_DECORATOR(*boost::unit_test::label("skip_mariadb")) +BOOST_TEST_DECORATOR(*run_if(&server_features::sha256)) BOOST_AUTO_TEST_CASE(tcp_caching_sha2_password) { // Create the connection @@ -157,9 +158,7 @@ BOOST_AUTO_TEST_CASE(tcp_caching_sha2_password) // Users can log-in using the caching_sha2_password auth plugin // even if they're using UNIX sockets. -BOOST_TEST_DECORATOR(*boost::unit_test::label("unix")) -BOOST_TEST_DECORATOR(*boost::unit_test::label("skip_mysql5")) -BOOST_TEST_DECORATOR(*boost::unit_test::label("skip_mariadb")) +BOOST_TEST_DECORATOR(*run_if(&server_features::sha256, &server_features::unix_sockets)) BOOST_AUTO_TEST_CASE(unix_caching_sha2_password) { // Setup diff --git a/test/integration/test/connection_pool.cpp b/test/integration/test/connection_pool.cpp index a42725b7f..6679091a6 100644 --- a/test/integration/test/connection_pool.cpp +++ b/test/integration/test/connection_pool.cpp @@ -36,6 +36,7 @@ #include "test_common/create_diagnostics.hpp" #include "test_common/printing.hpp" #include "test_integration/run_stackful_coro.hpp" +#include "test_integration/server_features.hpp" using namespace boost::mysql; using namespace boost::mysql::test; @@ -516,7 +517,7 @@ BOOST_FIXTURE_TEST_CASE(get_connection_timeout, fixture) } // Spotcheck: pool works with unix sockets, too -BOOST_TEST_DECORATOR(*boost::unit_test::label("unix")) +BOOST_TEST_DECORATOR(*run_if(&server_features::unix_sockets)) BOOST_FIXTURE_TEST_CASE(unix_sockets, fixture) { run_stackful_coro([&](asio::yield_context yield) { diff --git a/test/integration/test/database_types.cpp b/test/integration/test/database_types.cpp index 343244908..8c8aa1c4c 100644 --- a/test/integration/test/database_types.cpp +++ b/test/integration/test/database_types.cpp @@ -46,10 +46,10 @@ #include #include -#include "test_common/ci_server.hpp" #include "test_common/create_basic.hpp" #include "test_common/printing.hpp" #include "test_integration/metadata_validator.hpp" +#include "test_integration/server_features.hpp" #include "test_integration/tcp_network_fixture.hpp" using namespace boost::mysql::test; @@ -336,7 +336,7 @@ struct int_table_row using tinyint_row = int_table_row; BOOST_DESCRIBE_STRUCT(tinyint_row, (), (id, field_signed, field_unsigned, field_width, field_zerofill)) -table_ptr types_tinyint() +static table_ptr types_tinyint() { auto res = make_table("types_tinyint"); int_table_columns(*res, column_type::tinyint); @@ -354,7 +354,7 @@ table_ptr types_tinyint() using smallint_row = int_table_row; BOOST_DESCRIBE_STRUCT(smallint_row, (), (id, field_signed, field_unsigned, field_width, field_zerofill)) -table_ptr types_smallint() +static table_ptr types_smallint() { auto res = make_table("types_smallint"); int_table_columns(*res, column_type::smallint); @@ -372,7 +372,7 @@ table_ptr types_smallint() using int_row = int_table_row; BOOST_DESCRIBE_STRUCT(int_row, (), (id, field_signed, field_unsigned, field_width, field_zerofill)) -table_ptr types_mediumint() +static table_ptr types_mediumint() { auto res = make_table("types_mediumint"); int_table_columns(*res, column_type::mediumint); @@ -387,7 +387,7 @@ table_ptr types_mediumint() } // INT -table_ptr types_int() +static table_ptr types_int() { auto res = make_table("types_int"); int_table_columns(*res, column_type::int_); @@ -405,7 +405,7 @@ table_ptr types_int() using bigint_row = int_table_row; BOOST_DESCRIBE_STRUCT(bigint_row, (), (id, field_signed, field_unsigned, field_width, field_zerofill)) -table_ptr types_bigint() +static table_ptr types_bigint() { auto res = make_table("types_bigint"); int_table_columns(*res, column_type::bigint); @@ -427,7 +427,7 @@ struct year_row }; BOOST_DESCRIBE_STRUCT(year_row, (), (id, field_default)) -table_ptr types_year() +static table_ptr types_year() { auto res = make_table("types_year"); res->add_meta("field_default", column_type::year, flags_zerofill); @@ -449,7 +449,7 @@ struct bool_row }; BOOST_DESCRIBE_STRUCT(bool_row, (), (id, field_default)) -table_ptr types_bool() +static table_ptr types_bool() { auto res = make_table("types_bool"); res->add_meta("field_default", column_type::tinyint); @@ -492,7 +492,7 @@ BOOST_DESCRIBE_STRUCT( field_64) ) -table_ptr types_bit() +static table_ptr types_bit() { auto res = make_table("types_bit"); const char* columns[] = { @@ -530,7 +530,7 @@ struct float_row }; BOOST_DESCRIBE_STRUCT(float_row, (), (id, field_signed, field_unsigned, field_width, field_zerofill)) -table_ptr types_float() +static table_ptr types_float() { auto res = make_table("types_float"); res->add_meta("field_signed", column_type::float_, no_flags, 31); @@ -564,7 +564,7 @@ struct double_row }; BOOST_DESCRIBE_STRUCT(double_row, (), (id, field_signed, field_unsigned, field_width, field_zerofill)) -table_ptr types_double() +static table_ptr types_double() { auto res = make_table("types_double"); res->add_meta("field_signed", column_type::double_, no_flags, 31); @@ -595,7 +595,7 @@ struct date_row }; BOOST_DESCRIBE_STRUCT(date_row, (), (id, field_date)) -table_ptr types_date() +static table_ptr types_date() { auto res = make_table("types_date"); res->add_meta("field_date", column_type::date); @@ -658,7 +658,7 @@ void datetime_timestamp_common_rows(table& res) // clang-format on } -table_ptr types_datetime() +static table_ptr types_datetime() { auto res = make_table("types_datetime"); res->add_meta("field_0", column_type::datetime, no_flags, 0, flags_unsigned); @@ -708,7 +708,7 @@ table_ptr types_datetime() return table_ptr(std::move(res)); } -table_ptr types_timestamp() +static table_ptr types_timestamp() { auto res = make_table("types_timestamp"); res->add_meta("field_0", column_type::timestamp, no_flags, 0, flags_unsigned); @@ -743,7 +743,7 @@ struct time_row }; BOOST_DESCRIBE_STRUCT(time_row, (), (id, field_0, field_1, field_2, field_3, field_4, field_5, field_6)) -table_ptr types_time() +static table_ptr types_time() { auto res = make_table("types_time"); res->add_meta("field_0", column_type::time, no_flags, 0, flags_unsigned); @@ -853,7 +853,7 @@ BOOST_DESCRIBE_STRUCT( field_set) ) -table_ptr types_string() +static table_ptr types_string() { auto res = make_table("types_string"); res->add_meta("field_char", column_type::char_); @@ -884,19 +884,19 @@ struct json_row }; BOOST_DESCRIBE_STRUCT(json_row, (), (id, field_json)) -table_ptr types_json() +static table_ptr types_json() { // MariaDB doesn't have a dedicated column type, so there is a difference in metadata. // Values should be the same, though. auto res = make_table("types_json"); - res->add_meta("field_json", is_mariadb() ? column_type::text : column_type::json); + res->add_meta("field_json", get_server_features().json_type ? column_type::json : column_type::text); using std::string; // clang-format off res->add_row("regular", string(R"([null, 42, false, "abc", {"key": "value"}])")); - res->add_row("unicode_escape", string(R"(["\\u0000value\\u0000"])")); - res->add_row("utf8", string("[\"adi\xc3\xb3os\"]")); + res->add_row("unicode_escape", string(R"(["\u0000value\u0000"])")); + res->add_row("utf8", string("[\"adi\xc3\xb3s\"]")); res->add_row("empty", string("{}")); // clang-format on return table_ptr(std::move(res)); @@ -919,7 +919,7 @@ BOOST_DESCRIBE_STRUCT( (id, field_binary, field_varbinary, field_tinyblob, field_blob, field_mediumblob, field_longblob) ) -table_ptr types_binary() +static table_ptr types_binary() { auto res = make_table("types_binary"); res->add_meta("field_binary", column_type::binary); @@ -947,7 +947,7 @@ struct not_implemented_row }; BOOST_DESCRIBE_STRUCT(not_implemented_row, (), (id, field_decimal, field_geometry)) -table_ptr types_not_implemented() +static table_ptr types_not_implemented() { auto res = make_table("types_not_implemented"); res->add_meta("field_decimal", column_type::decimal); @@ -976,7 +976,7 @@ BOOST_DESCRIBE_STRUCT( (id, field_timestamp, field_primary_key, field_not_null, field_unique, field_indexed) ) -table_ptr types_flags() +static table_ptr types_flags() { auto res = make_table("types_flags"); res->add_meta( @@ -1017,6 +1017,7 @@ std::vector make_all_tables() res.push_back(types_timestamp()); res.push_back(types_time()); res.push_back(types_string()); + res.push_back(types_json()); res.push_back(types_binary()); res.push_back(types_not_implemented()); res.push_back(types_flags()); diff --git a/test/integration/test/db_specific.cpp b/test/integration/test/db_specific.cpp index 272fa7964..0c83f7a7a 100644 --- a/test/integration/test/db_specific.cpp +++ b/test/integration/test/db_specific.cpp @@ -12,6 +12,7 @@ #include #include +#include "test_integration/server_features.hpp" #include "test_integration/tcp_network_fixture.hpp" using namespace boost::mysql::test; @@ -23,8 +24,7 @@ namespace { BOOST_AUTO_TEST_SUITE(test_db_specific) -BOOST_TEST_DECORATOR(*boost::unit_test::label("skip_mysql5")) -BOOST_TEST_DECORATOR(*boost::unit_test::label("skip_mariadb")) +BOOST_TEST_DECORATOR(*run_if(&server_features::regex_error_codes)) BOOST_FIXTURE_TEST_CASE(mysql_specific_error_code, tcp_network_fixture) { connect(); @@ -32,7 +32,7 @@ BOOST_FIXTURE_TEST_CASE(mysql_specific_error_code, tcp_network_fixture) diagnostics diag; results result; - // This is reported as a common, less desriptive error in MySQL5 and MariaDB + // This is reported as a common, less descriptive error in MySQL5 and MariaDB conn.execute("select * from one_row_table where field_varchar regexp '(('", result, ec, diag); error_code expected_ec( boost::mysql::mysql_server_errc::er_regexp_mismatched_paren, @@ -41,8 +41,7 @@ BOOST_FIXTURE_TEST_CASE(mysql_specific_error_code, tcp_network_fixture) BOOST_TEST(ec == expected_ec); } -BOOST_TEST_DECORATOR(*boost::unit_test::label("skip_mysql5")) -BOOST_TEST_DECORATOR(*boost::unit_test::label("skip_mysql8")) +BOOST_TEST_DECORATOR(*run_if(&server_features::dup_query_error_codes)) BOOST_FIXTURE_TEST_CASE(mariadb_specific_error_code, tcp_network_fixture) { connect(); diff --git a/test/integration/test/handshake.cpp b/test/integration/test/handshake.cpp index fda4290e3..e015aeb53 100644 --- a/test/integration/test/handshake.cpp +++ b/test/integration/test/handshake.cpp @@ -13,6 +13,7 @@ #include #include +#include #include #include @@ -21,8 +22,9 @@ #include "test_integration/common.hpp" #include "test_integration/er_network_variant.hpp" #include "test_integration/get_endpoint.hpp" -#include "test_integration/network_test.hpp" +#include "test_integration/network_samples.hpp" #include "test_integration/server_ca.hpp" +#include "test_integration/server_features.hpp" #include "test_integration/streams.hpp" #include "test_integration/tcp_network_fixture.hpp" @@ -38,17 +40,17 @@ using boost::mysql::tcp_ssl_connection; namespace { -auto net_samples_ssl = create_network_samples({ +auto net_samples_ssl = network_samples({ "tcp_ssl_sync_errc", "tcp_ssl_async_callback", }); -auto net_samples_nossl = create_network_samples({ +auto net_samples_nossl = network_samples({ "tcp_sync_errc", "tcp_async_callback", }); -auto net_samples_both = create_network_samples({ +auto net_samples_both = network_samples({ "tcp_ssl_sync_errc", "tcp_ssl_async_callback", "tcp_sync_exc", @@ -83,23 +85,23 @@ struct handshake_fixture : network_fixture // mysql_native_password BOOST_AUTO_TEST_SUITE(mysql_native_password) -BOOST_MYSQL_NETWORK_TEST(regular_user, handshake_fixture, net_samples_both) +BOOST_DATA_TEST_CASE_F(handshake_fixture, regular_user, net_samples_both) { - setup_and_physical_connect(sample.net); + setup_and_physical_connect(sample); set_credentials("mysqlnp_user", "mysqlnp_password"); do_handshake_ok(); } -BOOST_MYSQL_NETWORK_TEST(empty_password, handshake_fixture, net_samples_both) +BOOST_DATA_TEST_CASE_F(handshake_fixture, empty_password, net_samples_both) { - setup_and_physical_connect(sample.net); + setup_and_physical_connect(sample); set_credentials("mysqlnp_empty_password_user", ""); do_handshake_ok(); } -BOOST_MYSQL_NETWORK_TEST(bad_password, handshake_fixture, net_samples_both) +BOOST_DATA_TEST_CASE_F(handshake_fixture, bad_password, net_samples_both) { - setup_and_physical_connect(sample.net); + setup_and_physical_connect(sample); set_credentials("mysqlnp_user", "bad_password"); conn->handshake(params).validate_error( common_server_errc::er_access_denied_error, @@ -180,8 +182,7 @@ struct caching_sha2_user_creator : tcp_network_fixture } }; -BOOST_TEST_DECORATOR(*boost::unit_test::label("skip_mysql5")) -BOOST_TEST_DECORATOR(*boost::unit_test::label("skip_mariadb")) +BOOST_TEST_DECORATOR(*run_if(&server_features::sha256)) BOOST_AUTO_TEST_SUITE(caching_sha2_password, *boost::unit_test::fixture()) struct caching_sha2_fixture : handshake_fixture @@ -203,80 +204,80 @@ struct caching_sha2_fixture : handshake_fixture } }; -BOOST_MYSQL_NETWORK_TEST(ssl_on_cache_hit, caching_sha2_fixture, net_samples_ssl) +BOOST_DATA_TEST_CASE_F(caching_sha2_fixture, ssl_on_cache_hit, net_samples_ssl) { - setup_and_physical_connect(sample.net); + setup_and_physical_connect(sample); set_credentials(caching_sha2_user_creator::regular_username(), "csha2p_password"); load_sha256_cache(caching_sha2_user_creator::regular_username(), "csha2p_password"); do_handshake_ok_ssl(); } -BOOST_MYSQL_NETWORK_TEST(ssl_off_cache_hit, caching_sha2_fixture, net_samples_both) +BOOST_DATA_TEST_CASE_F(caching_sha2_fixture, ssl_off_cache_hit, net_samples_both) { // As we are sending password hashed, it is OK to not have SSL for this - setup_and_physical_connect(sample.net); + setup_and_physical_connect(sample); set_credentials(caching_sha2_user_creator::regular_username(), "csha2p_password"); load_sha256_cache(caching_sha2_user_creator::regular_username(), "csha2p_password"); do_handshake_ok_nossl(); } -BOOST_MYSQL_NETWORK_TEST(ssl_on_cache_miss, caching_sha2_fixture, net_samples_ssl) +BOOST_DATA_TEST_CASE_F(caching_sha2_fixture, ssl_on_cache_miss, net_samples_ssl) { - setup_and_physical_connect(sample.net); + setup_and_physical_connect(sample); set_credentials(caching_sha2_user_creator::regular_username(), "csha2p_password"); clear_sha256_cache(); do_handshake_ok_ssl(); } -BOOST_MYSQL_NETWORK_TEST(ssl_off_cache_miss, caching_sha2_fixture, net_samples_both) +BOOST_DATA_TEST_CASE_F(caching_sha2_fixture, ssl_off_cache_miss, net_samples_both) { // A cache miss would force us send a plaintext password over // a non-TLS connection, so we fail - setup_and_physical_connect(sample.net); + setup_and_physical_connect(sample); set_credentials(caching_sha2_user_creator::regular_username(), "csha2p_password"); clear_sha256_cache(); params.set_ssl(ssl_mode::disable); conn->handshake(params).validate_error(client_errc::auth_plugin_requires_ssl, {}); } -BOOST_MYSQL_NETWORK_TEST(empty_password_ssl_on_cache_hit, caching_sha2_fixture, net_samples_ssl) +BOOST_DATA_TEST_CASE_F(caching_sha2_fixture, empty_password_ssl_on_cache_hit, net_samples_ssl) { - setup_and_physical_connect(sample.net); + setup_and_physical_connect(sample); set_credentials(caching_sha2_user_creator::empty_password_username(), ""); load_sha256_cache(caching_sha2_user_creator::empty_password_username(), ""); do_handshake_ok_ssl(); } -BOOST_MYSQL_NETWORK_TEST(empty_password_ssl_off_cache_hit, caching_sha2_fixture, net_samples_both) +BOOST_DATA_TEST_CASE_F(caching_sha2_fixture, empty_password_ssl_off_cache_hit, net_samples_both) { // Empty passwords are allowed over non-TLS connections - setup_and_physical_connect(sample.net); + setup_and_physical_connect(sample); set_credentials(caching_sha2_user_creator::empty_password_username(), ""); load_sha256_cache(caching_sha2_user_creator::empty_password_username(), ""); do_handshake_ok_nossl(); } -BOOST_MYSQL_NETWORK_TEST(empty_password_ssl_on_cache_miss, caching_sha2_fixture, net_samples_ssl) +BOOST_DATA_TEST_CASE_F(caching_sha2_fixture, empty_password_ssl_on_cache_miss, net_samples_ssl) { - setup_and_physical_connect(sample.net); + setup_and_physical_connect(sample); set_credentials(caching_sha2_user_creator::empty_password_username(), ""); clear_sha256_cache(); do_handshake_ok_ssl(); } -BOOST_MYSQL_NETWORK_TEST(empty_password_ssl_off_cache_miss, caching_sha2_fixture, net_samples_both) +BOOST_DATA_TEST_CASE_F(caching_sha2_fixture, empty_password_ssl_off_cache_miss, net_samples_both) { // Empty passwords are allowed over non-TLS connections - setup_and_physical_connect(sample.net); + setup_and_physical_connect(sample); set_credentials(caching_sha2_user_creator::empty_password_username(), ""); clear_sha256_cache(); do_handshake_ok_nossl(); } -BOOST_MYSQL_NETWORK_TEST(bad_password_ssl_on_cache_hit, caching_sha2_fixture, net_samples_ssl) +BOOST_DATA_TEST_CASE_F(caching_sha2_fixture, bad_password_ssl_on_cache_hit, net_samples_ssl) { // Note: test over non-TLS would return "ssl required" - setup_and_physical_connect(sample.net); + setup_and_physical_connect(sample); set_credentials(caching_sha2_user_creator::regular_username(), "bad_password"); load_sha256_cache(caching_sha2_user_creator::regular_username(), "csha2p_password"); conn->handshake(params).validate_error( @@ -285,10 +286,10 @@ BOOST_MYSQL_NETWORK_TEST(bad_password_ssl_on_cache_hit, caching_sha2_fixture, ne ); } -BOOST_MYSQL_NETWORK_TEST(bad_password_ssl_on_cache_miss, caching_sha2_fixture, net_samples_ssl) +BOOST_DATA_TEST_CASE_F(caching_sha2_fixture, bad_password_ssl_on_cache_miss, net_samples_ssl) { // Note: test over non-TLS would return "ssl required" - setup_and_physical_connect(sample.net); + setup_and_physical_connect(sample); set_credentials(caching_sha2_user_creator::regular_username(), "bad_password"); clear_sha256_cache(); conn->handshake(params).validate_error( @@ -302,112 +303,111 @@ BOOST_AUTO_TEST_SUITE_END() // caching_sha2_password // SSL certificate validation BOOST_AUTO_TEST_SUITE(ssl_certificate_validation) -BOOST_MYSQL_NETWORK_TEST(certificate_valid, handshake_fixture, net_samples_ssl) +BOOST_DATA_TEST_CASE_F(handshake_fixture, certificate_valid, net_samples_ssl) { // Context changes need to be before setup ssl_ctx.set_verify_mode(boost::asio::ssl::verify_peer); ssl_ctx.add_certificate_authority(boost::asio::buffer(CA_PEM)); - setup_and_physical_connect(sample.net); + setup_and_physical_connect(sample); do_handshake_ok_ssl(); } -BOOST_MYSQL_NETWORK_TEST(certificate_invalid, handshake_fixture, net_samples_ssl) +BOOST_DATA_TEST_CASE_F(handshake_fixture, certificate_invalid, net_samples_ssl) { ssl_ctx.set_verify_mode(boost::asio::ssl::verify_peer); - setup_and_physical_connect(sample.net); + setup_and_physical_connect(sample); auto result = conn->handshake(params); BOOST_TEST(result.err.message().find("certificate verify failed") != std::string::npos); } -BOOST_MYSQL_NETWORK_TEST(custom_certificate_verification_failed, handshake_fixture, net_samples_ssl) +BOOST_DATA_TEST_CASE_F(handshake_fixture, custom_certificate_verification_failed, net_samples_ssl) { ssl_ctx.set_verify_mode(boost::asio::ssl::verify_peer); ssl_ctx.add_certificate_authority(boost::asio::buffer(CA_PEM)); ssl_ctx.set_verify_callback(boost::asio::ssl::host_name_verification("host.name")); - setup_and_physical_connect(sample.net); + setup_and_physical_connect(sample); auto result = conn->handshake(params); BOOST_TEST(result.err.message().find("certificate verify failed") != std::string::npos); } -BOOST_MYSQL_NETWORK_TEST(custom_certificate_verification_ok, handshake_fixture, net_samples_ssl) +BOOST_DATA_TEST_CASE_F(handshake_fixture, custom_certificate_verification_ok, net_samples_ssl) { ssl_ctx.set_verify_mode(boost::asio::ssl::verify_peer); ssl_ctx.add_certificate_authority(boost::asio::buffer(CA_PEM)); ssl_ctx.set_verify_callback(boost::asio::ssl::host_name_verification("mysql")); - setup_and_physical_connect(sample.net); + setup_and_physical_connect(sample); do_handshake_ok_ssl(); } BOOST_AUTO_TEST_SUITE_END() // ssl_certificate_validation // Other handshake tests -BOOST_MYSQL_NETWORK_TEST(no_database, handshake_fixture, net_samples_both) +BOOST_DATA_TEST_CASE_F(handshake_fixture, no_database, net_samples_both) { - setup_and_physical_connect(sample.net); + setup_and_physical_connect(sample); params.set_database(""); do_handshake_ok(); } -BOOST_TEST_DECORATOR(*boost::unit_test::label("skip_mysql5")) -BOOST_TEST_DECORATOR(*boost::unit_test::label("skip_mariadb")) -BOOST_MYSQL_NETWORK_TEST(unknown_auth_plugin, handshake_fixture, net_samples_ssl) +BOOST_TEST_DECORATOR(*run_if(&server_features::sha256)) +BOOST_DATA_TEST_CASE_F(handshake_fixture, unknown_auth_plugin, net_samples_ssl) { // Note: sha256_password is not supported, so it's an unknown plugin to us - setup_and_physical_connect(sample.net); + setup_and_physical_connect(sample); set_credentials("sha2p_user", "sha2p_password"); conn->handshake(params).validate_error(client_errc::unknown_auth_plugin, {}); } -BOOST_MYSQL_NETWORK_TEST(bad_user, handshake_fixture, net_samples_nossl) +BOOST_DATA_TEST_CASE_F(handshake_fixture, bad_user, net_samples_nossl) { // unreliable without SSL. If the default plugin requires SSL // (like SHA256), this would fail with 'ssl required' - setup_and_physical_connect(sample.net); + setup_and_physical_connect(sample); set_credentials("non_existing_user", "bad_password"); conn->handshake(params).validate_any_error(); // may be access denied or unknown auth plugin } -BOOST_MYSQL_NETWORK_TEST(ssl_disable, handshake_fixture, net_samples_both) +BOOST_DATA_TEST_CASE_F(handshake_fixture, ssl_disable, net_samples_both) { // Both SSL and non-SSL streams will act as non-SSL streams - setup_and_physical_connect(sample.net); + setup_and_physical_connect(sample); params.set_ssl(ssl_mode::disable); conn->handshake(params).validate_no_error(); BOOST_TEST(!conn->uses_ssl()); } -BOOST_MYSQL_NETWORK_TEST(ssl_enable_nonssl_streams, handshake_fixture, net_samples_nossl) +BOOST_DATA_TEST_CASE_F(handshake_fixture, ssl_enable_nonssl_streams, net_samples_nossl) { // Ignored by non-ssl streams - setup_and_physical_connect(sample.net); + setup_and_physical_connect(sample); params.set_ssl(ssl_mode::enable); conn->handshake(params).validate_no_error(); BOOST_TEST(!conn->uses_ssl()); } -BOOST_MYSQL_NETWORK_TEST(ssl_enable_ssl_streams, handshake_fixture, net_samples_ssl) +BOOST_DATA_TEST_CASE_F(handshake_fixture, ssl_enable_ssl_streams, net_samples_ssl) { // In all our CI systems, our servers support SSL, so // ssl_mode::enable will do the same as ssl_mode::require. // We test for this fact. - setup_and_physical_connect(sample.net); + setup_and_physical_connect(sample); params.set_ssl(ssl_mode::enable); conn->handshake(params).validate_no_error(); BOOST_TEST(conn->uses_ssl()); } -BOOST_MYSQL_NETWORK_TEST(ssl_require_nonssl_streams, handshake_fixture, net_samples_nossl) +BOOST_DATA_TEST_CASE_F(handshake_fixture, ssl_require_nonssl_streams, net_samples_nossl) { // Ignored by non-ssl streams - setup_and_physical_connect(sample.net); + setup_and_physical_connect(sample); params.set_ssl(ssl_mode::require); conn->handshake(params).validate_no_error(); BOOST_TEST(!conn->uses_ssl()); } -BOOST_MYSQL_NETWORK_TEST(ssl_require_ssl_streams, handshake_fixture, net_samples_ssl) +BOOST_DATA_TEST_CASE_F(handshake_fixture, ssl_require_ssl_streams, net_samples_ssl) { - setup_and_physical_connect(sample.net); + setup_and_physical_connect(sample); params.set_ssl(ssl_mode::require); conn->handshake(params).validate_no_error(); BOOST_TEST(conn->uses_ssl()); diff --git a/test/integration/test/reconnect.cpp b/test/integration/test/reconnect.cpp index 14d3dc9ce..cdf2bb87b 100644 --- a/test/integration/test/reconnect.cpp +++ b/test/integration/test/reconnect.cpp @@ -15,6 +15,7 @@ #include #include #include +#include #include #include @@ -25,6 +26,7 @@ #include #include #include +#include #include #include @@ -32,9 +34,11 @@ #include "test_common/netfun_maker.hpp" #include "test_integration/common.hpp" +#include "test_integration/er_network_variant.hpp" #include "test_integration/get_endpoint.hpp" -#include "test_integration/network_test.hpp" +#include "test_integration/network_samples.hpp" #include "test_integration/run_stackful_coro.hpp" +#include "test_integration/server_features.hpp" using namespace boost::mysql::test; using namespace boost::mysql; @@ -44,25 +48,39 @@ using boost::asio::experimental::wait_for_one; namespace { -// clang-format off -auto samples_with_reconnection = create_network_samples({ - "tcp_sync_errc", - "tcp_async_callback", - "any_tcp_sync_errc", - "any_tcp_async_callback", -#if BOOST_ASIO_HAS_LOCAL_SOCKETS - "any_unix_sync_errc", +auto samples_with_reconnection = network_samples([]() { + const string_view variant_names[] = { + "tcp_sync_errc", + "tcp_async_callback", + "any_tcp_sync_errc", + "any_tcp_async_callback", + }; + + auto res = get_network_variants(variant_names); +#ifdef BOOST_ASIO_HAS_LOCAL_SOCKETS + if (get_server_features().unix_sockets) + { + res.push_back(get_network_variant("any_unix_sync_errc")); + } #endif + return res; }); -auto samples_any = create_network_samples({ - "any_tcp_sync_errc", - "any_tcp_async_callback", -#if BOOST_ASIO_HAS_LOCAL_SOCKETS - "any_unix_sync_errc", +auto samples_any = network_samples([]() { + const string_view variant_names[] = { + "any_tcp_sync_errc", + "any_tcp_async_callback", + }; + + auto res = get_network_variants(variant_names); +#ifdef BOOST_ASIO_HAS_LOCAL_SOCKETS + if (get_server_features().unix_sockets) + { + res.push_back(get_network_variant("any_unix_sync_errc")); + } #endif + return res; }); -// clang-format on BOOST_AUTO_TEST_SUITE(test_reconnect) @@ -76,9 +94,9 @@ struct reconnect_fixture : network_fixture } }; -BOOST_MYSQL_NETWORK_TEST(reconnect_after_close, reconnect_fixture, samples_with_reconnection) +BOOST_DATA_TEST_CASE_F(reconnect_fixture, reconnect_after_close, samples_with_reconnection) { - setup(sample.net); + setup(sample); // Connect and use the connection connect(); @@ -92,9 +110,9 @@ BOOST_MYSQL_NETWORK_TEST(reconnect_after_close, reconnect_fixture, samples_with_ do_query_ok(); } -BOOST_MYSQL_NETWORK_TEST(reconnect_after_handshake_error, reconnect_fixture, samples_with_reconnection) +BOOST_DATA_TEST_CASE_F(reconnect_fixture, reconnect_after_handshake_error, samples_with_reconnection) { - setup(sample.net); + setup(sample); // Error during server handshake params.set_database("bad_database"); @@ -109,9 +127,9 @@ BOOST_MYSQL_NETWORK_TEST(reconnect_after_handshake_error, reconnect_fixture, sam do_query_ok(); } -BOOST_MYSQL_NETWORK_TEST(reconnect_while_connected, reconnect_fixture, samples_any) +BOOST_DATA_TEST_CASE_F(reconnect_fixture, reconnect_while_connected, samples_any) { - setup(sample.net); + setup(sample); // Connect and use the connection connect(); @@ -237,7 +255,7 @@ BOOST_FIXTURE_TEST_CASE(change_stream_type_tcp, change_stream_type_fixture) } // UNIX cases. Note that some sync cases are not included, to save testing time -BOOST_TEST_DECORATOR(*boost::unit_test::label("unix")) +BOOST_TEST_DECORATOR(*run_if(&server_features::unix_sockets)) BOOST_FIXTURE_TEST_CASE(change_stream_type_unix, change_stream_type_fixture) { // UNIX connect params diff --git a/test/integration/test/snippets/any_connection.cpp b/test/integration/test/snippets/any_connection.cpp index 29bbbe031..d5039c740 100644 --- a/test/integration/test/snippets/any_connection.cpp +++ b/test/integration/test/snippets/any_connection.cpp @@ -21,6 +21,7 @@ #include #include "test_common/ci_server.hpp" +#include "test_integration/server_features.hpp" #include "test_integration/snippets/credentials.hpp" using namespace boost::mysql; @@ -87,7 +88,7 @@ void create_and_connect_unix(string_view username, string_view password, string_ } //] -BOOST_TEST_DECORATOR(*boost::unit_test::label("unix")) +BOOST_TEST_DECORATOR(*run_if(&server_features::unix_sockets)) BOOST_AUTO_TEST_CASE(section_any_connection_unix) { create_and_connect_unix(mysql_username, mysql_password, "boost_mysql_examples"); diff --git a/test/integration/test/spotchecks.cpp b/test/integration/test/spotchecks.cpp index 8de2194f4..a90093bb1 100644 --- a/test/integration/test/spotchecks.cpp +++ b/test/integration/test/spotchecks.cpp @@ -21,6 +21,10 @@ #include +#include +#include +#include + #include #include "test_common/create_basic.hpp" @@ -28,32 +32,36 @@ #include "test_common/printing.hpp" #include "test_integration/common.hpp" #include "test_integration/er_connection.hpp" -#include "test_integration/network_test.hpp" +#include "test_integration/network_samples.hpp" #include "test_integration/static_rows.hpp" using namespace boost::mysql; using namespace boost::mysql::test; +using boost::test_tools::per_element; BOOST_AUTO_TEST_SUITE(test_spotchecks) -auto err_net_samples = create_network_samples({ +auto err_samples = network_samples({ "tcp_sync_errc", "tcp_sync_exc", "tcp_async_callback", "tcp_async_coroutines", }); +auto samples_with_handshake = network_samples::all_with_handshake(); +auto all_samples = network_samples::all(); + // Handshake -BOOST_MYSQL_NETWORK_TEST(handshake_success, network_fixture, all_network_samples_with_handshake()) +BOOST_DATA_TEST_CASE_F(network_fixture, handshake_success, samples_with_handshake) { - setup_and_physical_connect(sample.net); + setup_and_physical_connect(sample); conn->handshake(params).validate_no_error(); BOOST_TEST(conn->uses_ssl() == var->supports_ssl()); } -BOOST_MYSQL_NETWORK_TEST(handshake_error, network_fixture, err_net_samples) +BOOST_DATA_TEST_CASE_F(network_fixture, handshake_error, err_samples) { - setup_and_physical_connect(sample.net); + setup_and_physical_connect(sample); params.set_database("bad_database"); conn->handshake(params).validate_error( common_server_errc::er_dbaccess_denied_error, @@ -62,9 +70,9 @@ BOOST_MYSQL_NETWORK_TEST(handshake_error, network_fixture, err_net_samples) } // Connect: success is already widely tested throughout integ tests -BOOST_MYSQL_NETWORK_TEST(connect_error, network_fixture, err_net_samples) +BOOST_DATA_TEST_CASE_F(network_fixture, connect_error, err_samples) { - setup(sample.net); + setup(sample); set_credentials("integ_user", "bad_password"); conn->connect(params).validate_error( common_server_errc::er_access_denied_error, @@ -74,9 +82,9 @@ BOOST_MYSQL_NETWORK_TEST(connect_error, network_fixture, err_net_samples) } // Start execution (query) -BOOST_MYSQL_NETWORK_TEST(start_execution_query_success, network_fixture, all_network_samples()) +BOOST_DATA_TEST_CASE_F(network_fixture, start_execution_query_success, all_samples) { - setup_and_connect(sample.net); + setup_and_connect(sample); execution_state st; conn->start_execution("SELECT * FROM empty_table", st).get(); @@ -84,9 +92,9 @@ BOOST_MYSQL_NETWORK_TEST(start_execution_query_success, network_fixture, all_net validate_2fields_meta(st.meta(), "empty_table"); } -BOOST_MYSQL_NETWORK_TEST(start_execution_query_error, network_fixture, err_net_samples) +BOOST_DATA_TEST_CASE_F(network_fixture, start_execution_query_error, err_samples) { - setup_and_connect(sample.net); + setup_and_connect(sample); execution_state st; conn->start_execution("SELECT field_varchar, field_bad FROM one_row_table", st) @@ -94,20 +102,20 @@ BOOST_MYSQL_NETWORK_TEST(start_execution_query_error, network_fixture, err_net_s } // execute (query) -BOOST_MYSQL_NETWORK_TEST(execute_query_success, network_fixture, all_network_samples()) +BOOST_DATA_TEST_CASE_F(network_fixture, execute_query_success, all_samples) { - setup_and_connect(sample.net); + setup_and_connect(sample); results result; conn->execute("SELECT 'hello', 42", result).get(); BOOST_TEST(result.rows().size() == 1u); - BOOST_TEST(result.rows()[0] == makerow("hello", 42)); + BOOST_TEST(result.rows()[0] == makerow("hello", 42), per_element()); BOOST_TEST(result.meta().size() == 2u); } -BOOST_MYSQL_NETWORK_TEST(execute_query_error, network_fixture, err_net_samples) +BOOST_DATA_TEST_CASE_F(network_fixture, execute_query_error, err_samples) { - setup_and_connect(sample.net); + setup_and_connect(sample); results result; conn->execute("SELECT field_varchar, field_bad FROM one_row_table", result) @@ -115,26 +123,26 @@ BOOST_MYSQL_NETWORK_TEST(execute_query_error, network_fixture, err_net_samples) } // Prepare statement -BOOST_MYSQL_NETWORK_TEST(prepare_statement_success, network_fixture, all_network_samples()) +BOOST_DATA_TEST_CASE_F(network_fixture, prepare_statement_success, all_samples) { - setup_and_connect(sample.net); + setup_and_connect(sample); auto stmt = conn->prepare_statement("SELECT * FROM empty_table WHERE id IN (?, ?)").get(); BOOST_TEST_REQUIRE(stmt.valid()); BOOST_TEST(stmt.id() > 0u); BOOST_TEST(stmt.num_params() == 2u); } -BOOST_MYSQL_NETWORK_TEST(prepare_statement_error, network_fixture, err_net_samples) +BOOST_DATA_TEST_CASE_F(network_fixture, prepare_statement_error, err_samples) { - setup_and_connect(sample.net); + setup_and_connect(sample); conn->prepare_statement("SELECT * FROM bad_table WHERE id IN (?, ?)") .validate_error(common_server_errc::er_no_such_table, {"table", "doesn't exist", "bad_table"}); } // Start execution (statement, iterator) -BOOST_MYSQL_NETWORK_TEST(start_execution_stmt_it_success, network_fixture, all_network_samples()) +BOOST_DATA_TEST_CASE_F(network_fixture, start_execution_stmt_it_success, all_samples) { - setup_and_connect(sample.net); + setup_and_connect(sample); // Prepare auto stmt = conn->prepare_statement("SELECT * FROM empty_table WHERE id IN (?, ?)").get(); @@ -147,9 +155,9 @@ BOOST_MYSQL_NETWORK_TEST(start_execution_stmt_it_success, network_fixture, all_n BOOST_TEST(st.should_read_rows()); } -BOOST_MYSQL_NETWORK_TEST(start_execution_stmt_it_error, network_fixture, err_net_samples) +BOOST_DATA_TEST_CASE_F(network_fixture, start_execution_stmt_it_error, err_samples) { - setup_and_connect(sample.net); + setup_and_connect(sample); start_transaction(); // Prepare @@ -167,9 +175,9 @@ BOOST_MYSQL_NETWORK_TEST(start_execution_stmt_it_error, network_fixture, err_net } // start execution (statement, tuple) -BOOST_MYSQL_NETWORK_TEST(start_execution_statement_tuple_success, network_fixture, all_network_samples()) +BOOST_DATA_TEST_CASE_F(network_fixture, start_execution_statement_tuple_success, all_samples) { - setup_and_connect(sample.net); + setup_and_connect(sample); // Prepare auto stmt = conn->prepare_statement("SELECT * FROM empty_table WHERE id IN (?, ?)").get(); @@ -181,9 +189,9 @@ BOOST_MYSQL_NETWORK_TEST(start_execution_statement_tuple_success, network_fixtur BOOST_TEST(st.should_read_rows()); } -BOOST_MYSQL_NETWORK_TEST(start_execution_statement_tuple_error, network_fixture, err_net_samples) +BOOST_DATA_TEST_CASE_F(network_fixture, start_execution_statement_tuple_error, err_samples) { - setup_and_connect(sample.net); + setup_and_connect(sample); start_transaction(); // Prepare @@ -200,9 +208,9 @@ BOOST_MYSQL_NETWORK_TEST(start_execution_statement_tuple_error, network_fixture, } // Execute (statement, iterator) -BOOST_MYSQL_NETWORK_TEST(execute_statement_iterator_success, network_fixture, err_net_samples) +BOOST_DATA_TEST_CASE_F(network_fixture, execute_statement_iterator_success, err_samples) { - setup_and_connect(sample.net); + setup_and_connect(sample); // Prepare auto stmt = conn->prepare_statement("SELECT * FROM empty_table WHERE id IN (?, ?)").get(); @@ -214,9 +222,9 @@ BOOST_MYSQL_NETWORK_TEST(execute_statement_iterator_success, network_fixture, er BOOST_TEST(result.rows().size() == 0u); } -BOOST_MYSQL_NETWORK_TEST(execute_statement_error, network_fixture, err_net_samples) +BOOST_DATA_TEST_CASE_F(network_fixture, execute_statement_error, err_samples) { - setup_and_connect(sample.net); + setup_and_connect(sample); start_transaction(); // Prepare @@ -233,9 +241,9 @@ BOOST_MYSQL_NETWORK_TEST(execute_statement_error, network_fixture, err_net_sampl } // Execute (statement, tuple). No error spotcheck since it's the same underlying fn -BOOST_MYSQL_NETWORK_TEST(execute_statement_tuple_success, network_fixture, err_net_samples) +BOOST_DATA_TEST_CASE_F(network_fixture, execute_statement_tuple_success, err_samples) { - setup_and_connect(sample.net); + setup_and_connect(sample); // Prepare auto stmt = conn->prepare_statement("SELECT * FROM empty_table WHERE id IN (?, ?)").get(); @@ -247,9 +255,9 @@ BOOST_MYSQL_NETWORK_TEST(execute_statement_tuple_success, network_fixture, err_n } // Close statement: no server error spotcheck -BOOST_MYSQL_NETWORK_TEST(close_statement_success, network_fixture, all_network_samples()) +BOOST_DATA_TEST_CASE_F(network_fixture, close_statement_success, all_samples) { - setup_and_connect(sample.net); + setup_and_connect(sample); // Prepare a statement auto stmt = conn->prepare_statement("SELECT * FROM empty_table WHERE id IN (?, ?)").get(); @@ -263,9 +271,9 @@ BOOST_MYSQL_NETWORK_TEST(close_statement_success, network_fixture, all_network_s } // Read some rows: no server error spotcheck -BOOST_MYSQL_NETWORK_TEST(read_some_rows_success, network_fixture, all_network_samples()) +BOOST_DATA_TEST_CASE_F(network_fixture, read_some_rows_success, all_samples) { - setup_and_connect(sample.net); + setup_and_connect(sample); // Generate an execution state execution_state st; @@ -289,10 +297,10 @@ BOOST_MYSQL_NETWORK_TEST(read_some_rows_success, network_fixture, all_network_sa } // Read resultset head -BOOST_MYSQL_NETWORK_TEST(read_resultset_head_success, network_fixture, all_network_samples()) +BOOST_DATA_TEST_CASE_F(network_fixture, read_resultset_head_success, all_samples) { params.set_multi_queries(true); - setup_and_connect(sample.net); + setup_and_connect(sample); // Generate an execution state execution_state st; @@ -316,10 +324,10 @@ BOOST_MYSQL_NETWORK_TEST(read_resultset_head_success, network_fixture, all_netwo BOOST_TEST((rows == makerows(2, 1, "f0"))); } -BOOST_MYSQL_NETWORK_TEST(read_resultset_head_error, network_fixture, all_network_samples()) +BOOST_DATA_TEST_CASE_F(network_fixture, read_resultset_head_error, all_samples) { params.set_multi_queries(true); - setup_and_connect(sample.net); + setup_and_connect(sample); // Generate an execution state execution_state st; @@ -338,25 +346,25 @@ BOOST_MYSQL_NETWORK_TEST(read_resultset_head_error, network_fixture, all_network } // Ping -BOOST_MYSQL_NETWORK_TEST(ping_success, network_fixture, all_network_samples()) +BOOST_DATA_TEST_CASE_F(network_fixture, ping_success, all_samples) { - setup_and_connect(sample.net); + setup_and_connect(sample); conn->ping().validate_no_error(); } // TODO -BOOST_MYSQL_NETWORK_TEST(ping_error, network_fixture, all_network_samples_with_handshake()) +BOOST_DATA_TEST_CASE_F(network_fixture, ping_error, samples_with_handshake) { - setup(sample.net); + setup(sample); // Ping should return an error for an unconnected connection conn->ping().validate_any_error(); } // Reset connection: no server error spotcheck. -BOOST_MYSQL_NETWORK_TEST(reset_connection_success, network_fixture, all_network_samples()) +BOOST_DATA_TEST_CASE_F(network_fixture, reset_connection_success, all_samples) { - setup_and_connect(sample.net); + setup_and_connect(sample); // Set some variable results result; @@ -371,9 +379,9 @@ BOOST_MYSQL_NETWORK_TEST(reset_connection_success, network_fixture, all_network_ } // Quit connection: no server error spotcheck -BOOST_MYSQL_NETWORK_TEST(quit_success, network_fixture, all_network_samples_with_handshake()) +BOOST_DATA_TEST_CASE_F(network_fixture, quit_success, samples_with_handshake) { - setup_and_connect(sample.net); + setup_and_connect(sample); // Quit conn->quit().validate_no_error(); @@ -383,10 +391,10 @@ BOOST_MYSQL_NETWORK_TEST(quit_success, network_fixture, all_network_samples_with } // Close connection: no server error spotcheck -// TODO: all_network_samples_with_handshake -BOOST_MYSQL_NETWORK_TEST(close_connection_success, network_fixture, all_network_samples_with_handshake()) +// TODO: all_variants_with_handshake +BOOST_DATA_TEST_CASE_F(network_fixture, close_connection_success, samples_with_handshake) { - setup_and_connect(sample.net); + setup_and_connect(sample); // Close conn->close().validate_no_error(); @@ -406,29 +414,30 @@ BOOST_MYSQL_NETWORK_TEST(close_connection_success, network_fixture, all_network_ } // TODO: move this to a unit test -BOOST_MYSQL_NETWORK_TEST(not_open_connection, network_fixture, err_net_samples) +BOOST_DATA_TEST_CASE_F(network_fixture, not_open_connection, err_samples) { - setup(sample.net); + setup(sample); conn->close().validate_no_error(); BOOST_TEST(!conn->is_open()); } #ifdef BOOST_MYSQL_CXX14 // Execute (static) - errors are already covered by the other tests -BOOST_MYSQL_NETWORK_TEST(execute_static_success, network_fixture, all_network_samples()) +BOOST_DATA_TEST_CASE_F(network_fixture, execute_static_success, all_samples) { - setup_and_connect(sample.net); + setup_and_connect(sample); er_connection::static_results_t result; conn->execute("CALL sp_spotchecks()", result).get(); BOOST_TEST(result.rows<0>().size() == 1u); - BOOST_TEST((result.rows<0>()[0] == row_multifield{1.1f, 11, "aaa"})); + row_multifield expected{boost::optional(1.1f), 11, std::string("aaa")}; + BOOST_TEST(result.rows<0>()[0] == expected); } // start_execution, read_resultset_head, read_some_rows success -BOOST_MYSQL_NETWORK_TEST(start_execution_and_followups_static_success, network_fixture, all_network_samples()) +BOOST_DATA_TEST_CASE_F(network_fixture, start_execution_and_followups_static_success, all_samples) { - setup_and_connect(sample.net); + setup_and_connect(sample); er_connection::static_state_t st; @@ -439,8 +448,9 @@ BOOST_MYSQL_NETWORK_TEST(start_execution_and_followups_static_success, network_f // Read r1 rows std::array storage; std::size_t num_rows = conn->read_some_rows(st, storage).get(); + row_multifield expected_multifield{boost::optional(1.1f), 11, std::string("aaa")}; // MSVC 14.1 BOOST_TEST(num_rows == 1u); - BOOST_TEST((storage[0] == row_multifield{1.1f, 11, "aaa"})); + BOOST_TEST(storage[0] == expected_multifield); // Ensure we're in the next resultset num_rows = conn->read_some_rows(st, storage).get(); @@ -455,7 +465,8 @@ BOOST_MYSQL_NETWORK_TEST(start_execution_and_followups_static_success, network_f std::array storage2; num_rows = conn->read_some_rows(st, storage2).get(); BOOST_TEST(num_rows == 1u); - BOOST_TEST((storage2[0] == row_2fields{1, std::string("f0")})); + row_2fields expected_2fields{1, std::string("f0")}; + BOOST_TEST(storage2[0] == expected_2fields); // Ensure we're in the next resultset num_rows = conn->read_some_rows(st, storage2).get(); @@ -468,9 +479,9 @@ BOOST_MYSQL_NETWORK_TEST(start_execution_and_followups_static_success, network_f } // read_some_rows failure (the other error cases are already widely tested) -BOOST_MYSQL_NETWORK_TEST(read_some_rows_error, network_fixture, err_net_samples) +BOOST_DATA_TEST_CASE_F(network_fixture, read_some_rows_error, err_samples) { - setup_and_connect(sample.net); + setup_and_connect(sample); er_connection::static_state_t st; diff --git a/tools/ci/ci_util/b2.py b/tools/ci/ci_util/b2.py index b812f6d8c..077bab7d9 100644 --- a/tools/ci/ci_util/b2.py +++ b/tools/ci/ci_util/b2.py @@ -43,7 +43,6 @@ def b2_build( stdlib: str, address_model: str, boost_branch: str, - db: str, server_host: str, separate_compilation: bool, address_sanitizer: bool, @@ -65,7 +64,7 @@ def b2_build( ) # Setup DB - db_setup(source_dir, db, server_host) + db_setup(source_dir, server_host) # Invoke b2 _conditional_run([ diff --git a/tools/ci/ci_util/cmake.py b/tools/ci/ci_util/cmake.py index 1635a76f9..a3b5a1f64 100644 --- a/tools/ci/ci_util/cmake.py +++ b/tools/ci/ci_util/cmake.py @@ -68,7 +68,6 @@ def cmake_build( build_shared_libs: bool, build_type: str, cxxstd: str, - db: str, server_host: str, install_test: bool, ) -> None: @@ -84,7 +83,7 @@ def cmake_build( ) # Setup DB - db_setup(source_dir, db, server_host) + db_setup(source_dir, server_host) # Build the library, run the tests, and install, as the Boost superproject does bin_dir = BOOST_ROOT.joinpath('__build') diff --git a/tools/ci/ci_util/db_setup.py b/tools/ci/ci_util/db_setup.py index b50a4d6e9..849a66513 100644 --- a/tools/ci/ci_util/db_setup.py +++ b/tools/ci/ci_util/db_setup.py @@ -6,11 +6,19 @@ # file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) # -from typing import List +from typing import List, Dict from pathlib import Path import subprocess import os from .common import IS_WINDOWS +from enum import Enum +import re + + +class _DbSystemType(Enum): + mysql5 = 1 + mysql8 = 2 + mariadb = 3 def _run_piped_stdin(args: List[str], fname: Path) -> None: @@ -20,24 +28,75 @@ def _run_piped_stdin(args: List[str], fname: Path) -> None: subprocess.run(args, input=content.encode(), check=True) +def _run_piped_stdout(args: List[str]) -> str: + print('+ ', args, flush=True) + return subprocess.check_output(args).decode() + + def _run_sql_file(fname: Path) -> None: _run_piped_stdin(['mysql', '-u', 'root'], fname) +def _get_server_version() -> str: + return _run_piped_stdout([ + 'mysql', '-u', 'root', '--column-names=0', '--batch', '-e', 'SELECT VERSION()' + ]) + + +def _parse_db_version(db: str) -> _DbSystemType: + # Parse the version string + match = re.match(r'([0-9]*)\.[0-9]*\.[0-9]*(\-MariaDB)?.*', db, re.IGNORECASE) + if match is None: + raise ValueError('Bad DB version: {}'.format(db)) + vmaj = int(match.group(1)) + is_mariadb = match.group(2) is not None + + # Perform the matching + if is_mariadb: + return _DbSystemType.mariadb + else: + return _DbSystemType.mysql8 if vmaj >= 8 else _DbSystemType.mysql5 + + +def _compute_disabled_features(db: _DbSystemType) -> Dict[str, bool]: + return { + # UNIX sockets. Only Windows CI servers don't have them enabled + 'unix-sockets': IS_WINDOWS, + + # sha256. Disabled on mysql5 and mariadb + 'sha256': db in (_DbSystemType.mysql5, _DbSystemType.mariadb), + + # JSON type. Disabled in mariadb + 'json-type': db == _DbSystemType.mariadb, + + # regex-error-codes. Disabled in mysql5 and mariadb + 'regex-error-codes': db in (_DbSystemType.mysql5, _DbSystemType.mariadb), + + # dup-query-error-codes. Disabled in mysql systems + 'dup-query-error-codes': db in (_DbSystemType.mysql5, _DbSystemType.mysql8), + } + + def db_setup( source_dir: Path, - db: str, server_host: str, ) -> None: + # Get the server version + db = _get_server_version() + print('+ Server version: {}'.format(db), flush=True) + + # Get the disabled server features + disabled_features = _compute_disabled_features(_parse_db_version(db)) + disabled_features_str = ' '.join(feature for feature, disabled in disabled_features.items() if disabled) + print('+ Disabled server features: {}'.format(disabled_features_str)) + # Source files _run_sql_file(source_dir.joinpath('example', 'db_setup.sql')) _run_sql_file(source_dir.joinpath('example', 'order_management', 'db_setup.sql')) _run_sql_file(source_dir.joinpath('test', 'integration', 'db_setup.sql')) - if db == 'mysql8': + if not disabled_features['sha256']: _run_sql_file(source_dir.joinpath('test', 'integration', 'db_setup_sha256.sql')) # Setup environment variables os.environ['BOOST_MYSQL_SERVER_HOST'] = server_host - os.environ['BOOST_MYSQL_TEST_DB'] = db - if IS_WINDOWS: - os.environ['BOOST_MYSQL_NO_UNIX_SOCKET_TESTS'] = '1' + os.environ['BOOST_MYSQL_DISABLED_SERVER_FEATURES'] = disabled_features_str diff --git a/tools/ci/ci_util/fuzz.py b/tools/ci/ci_util/fuzz.py index fd69b5f62..0336ddce0 100644 --- a/tools/ci/ci_util/fuzz.py +++ b/tools/ci/ci_util/fuzz.py @@ -18,7 +18,6 @@ def fuzz_build( source_dir: Path, boost_branch: str, - db: str, server_host: str, ) -> None: # Config @@ -43,7 +42,7 @@ def fuzz_build( generate_seed_corpus() # Setup DB (required for injection testing) - db_setup(source_dir, db, server_host) + db_setup(source_dir, server_host) # Build and run the fuzzing targets run([ diff --git a/tools/ci/ci_util/main.py b/tools/ci/ci_util/main.py index 85bbca571..ac16d5371 100644 --- a/tools/ci/ci_util/main.py +++ b/tools/ci/ci_util/main.py @@ -55,7 +55,6 @@ def _deduce_boost_branch() -> str: # Adds any DB args for builds requiring these def _add_db_args(subp: argparse.ArgumentParser) -> None: subp.add_argument('--server-host', default='127.0.0.1') - subp.add_argument('--db', choices=['mysql5', 'mysql8', 'mariadb'], default='mysql8') # Fuzzing uses some Python features only available in newer CIs, diff --git a/tools/docker/mariadb.dockerfile b/tools/docker/mariadb.dockerfile index 3fb699cc3..d19cc4e1c 100644 --- a/tools/docker/mariadb.dockerfile +++ b/tools/docker/mariadb.dockerfile @@ -5,7 +5,9 @@ # file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) # -FROM mariadb:11.4.2 +ARG BASE_IMAGE_VERSION + +FROM mariadb:${BASE_IMAGE_VERSION} ENV MYSQL_ALLOW_EMPTY_PASSWORD=1 ENV MYSQL_ROOT_PASSWORD= diff --git a/tools/docker/mysql5.dockerfile b/tools/docker/mysql.dockerfile similarity index 74% rename from tools/docker/mysql5.dockerfile rename to tools/docker/mysql.dockerfile index a65adb5af..bb89e33d4 100644 --- a/tools/docker/mysql5.dockerfile +++ b/tools/docker/mysql.dockerfile @@ -5,7 +5,12 @@ # file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) # -FROM mysql:5.7.41 +ARG BASE_IMAGE_VERSION + +FROM mysql:${BASE_IMAGE_VERSION} + +# Must appear after FROM, otherwise its value gets cleared +ARG ENTRYPOINT_ARGS ENV MYSQL_ALLOW_EMPTY_PASSWORD=1 ENV MYSQL_ROOT_PASSWORD= @@ -16,7 +21,7 @@ COPY tools/ssl/*.pem /etc/ssl/certs/mysql/ # Custom entry point to correctly set UNIX socket permissions, even if using volumes RUN < /mysql_entrypoint.sh chown -R mysql:mysql /var/run/mysqld -/bin/bash /usr/local/bin/docker-entrypoint.sh mysqld +/bin/bash /usr/local/bin/docker-entrypoint.sh mysqld ${ENTRYPOINT_ARGS} EOF ENTRYPOINT ["/bin/bash", "/mysql_entrypoint.sh"] \ No newline at end of file diff --git a/tools/docker/mysql8.dockerfile b/tools/docker/mysql8.dockerfile deleted file mode 100644 index 03207e3f5..000000000 --- a/tools/docker/mysql8.dockerfile +++ /dev/null @@ -1,23 +0,0 @@ -# -# Copyright (c) 2019-2024 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) -# - -FROM mysql:8.4.1 - -ENV MYSQL_ALLOW_EMPTY_PASSWORD=1 -ENV MYSQL_ROOT_PASSWORD= - -COPY tools/docker/ssl.cnf /etc/mysql/conf.d/ -COPY tools/ssl/*.pem /etc/ssl/certs/mysql/ - -# Custom entry point to correctly set UNIX socket permissions, even if using volumes. -# Re-activate the native password plugin, which is now disabled by default -RUN < /mysql_entrypoint.sh -chown -R mysql:mysql /var/run/mysqld -/bin/bash /usr/local/bin/docker-entrypoint.sh mysqld --mysql-native-password=ON -EOF - -ENTRYPOINT ["/bin/bash", "/mysql_entrypoint.sh"] diff --git a/tools/docker/mysql9.dockerfile b/tools/docker/mysql9.dockerfile deleted file mode 100644 index 903a8eb27..000000000 --- a/tools/docker/mysql9.dockerfile +++ /dev/null @@ -1,22 +0,0 @@ -# -# Copyright (c) 2019-2024 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) -# - -FROM mysql:9.0.0 - -ENV MYSQL_ALLOW_EMPTY_PASSWORD=1 -ENV MYSQL_ROOT_PASSWORD= - -COPY tools/docker/ssl.cnf /etc/mysql/conf.d/ -COPY tools/ssl/*.pem /etc/ssl/certs/mysql/ - -# Custom entry point to correctly set UNIX socket permissions, even if using volumes -RUN < /mysql_entrypoint.sh -chown -R mysql:mysql /var/run/mysqld -/bin/bash /usr/local/bin/docker-entrypoint.sh mysqld -EOF - -ENTRYPOINT ["/bin/bash", "/mysql_entrypoint.sh"] diff --git a/tools/scripts/build_unix_local.sh b/tools/scripts/build_unix_local.sh index fcb9d0b4c..675187d86 100755 --- a/tools/scripts/build_unix_local.sh +++ b/tools/scripts/build_unix_local.sh @@ -11,17 +11,17 @@ set -e repo_base=$(realpath $(dirname $(realpath $0))/../..) BK=b2 -IMAGE=build-gcc14 -SHA=c3f5316cc19bf3c0f7a83e31dec58139581f5764 +IMAGE=build-gcc5 +SHA=b1f46a8305f62d0af54dda34231b199d76e945f1 CONTAINER=builder-$IMAGE FULL_IMAGE=ghcr.io/anarthal-containers/$IMAGE:$SHA -DB=mysql8 +DB=mysql-8.4.1 docker start $DB || docker run -d \ --name $DB \ -v /var/run/mysqld:/var/run/mysqld \ -p 3306:3306 \ - ghcr.io/anarthal-containers/$DB:$SHA + ghcr.io/anarthal-containers/ci-db:$DB-$SHA docker start $CONTAINER || docker run -dit \ --name $CONTAINER \ -v "$repo_base:/opt/boost-mysql" \ @@ -31,12 +31,12 @@ docker network connect my-net $DB || echo "DB already connected" docker network connect my-net $CONTAINER || echo "Network already connected" # Command line -db_args="--server-host=$DB --db=$DB" +db_args="--server-host=$DB" case $BK in b2) cmd="$db_args --toolset=gcc - --cxxstd=23 - --variant=release + --cxxstd=11 + --variant=debug --stdlib=native --address-model=64 --separate-compilation=1