From 7811611ec0954799ba1dc4286a64d86acc7378bc Mon Sep 17 00:00:00 2001 From: XiaoWei Zhang <1403767615@qq.com> Date: Thu, 26 Oct 2023 17:17:15 +0800 Subject: [PATCH] finish rocketmq cpp 4.x test(client cluster filter server pull) (#50) * finish rocketmq cpp 4.x test(client cluster filter server pull) * Add comments and modify the incorrect method name in the incorrect PullOrderTest * Improved the test content, adding the test content of batchproducer, transactions, offset, retry and other scenarios * Add the use of sockets to connect to rocketmq server during startup and detect whether the server starts normally. Added broadcast and offset tests. Formatted code --- cpp/bin/run.sh | 88 ++ cpp/rocketmq-client-cpp-tests/.gitignore | 6 + .../cpp4.x/CMakeLists.txt | 65 ++ .../cpp4.x/README.md | 47 + .../cpp4.x/cmake/FindRocketMQ.cmake | 47 + .../cpp4.x/config.ini | 21 + .../include/client/rmq/RMQNormalConsumer.h | 130 +++ .../include/client/rmq/RMQNormalProducer.h | 153 +++ .../include/common/AbstractMQConsumer.h | 26 + .../include/common/AbstractMQProducer.h | 29 + .../cpp4.x/include/common/MQCollector.h | 149 +++ .../include/common/MQMessageQueueSelector.h | 29 + .../cpp4.x/include/common/MQMsg.h | 62 + .../include/common/MQTransactionListener.h | 110 ++ .../cpp4.x/include/enums/ConsumeResult.h | 23 + .../cpp4.x/include/enums/MessageType.h | 31 + .../cpp4.x/include/factory/ConsumerFactory.h | 122 ++ .../cpp4.x/include/factory/MessageFactory.h | 58 + .../cpp4.x/include/factory/ProducerFactory.h | 66 ++ .../cpp4.x/include/frame/BaseOperate.h | 34 + .../cpp4.x/include/listener/MsgListener.h | 57 + .../include/listener/MsgQueueListener.h | 50 + .../include/listener/rmq/RMQNormalListener.h | 126 ++ .../include/listener/rmq/RMQOrderListener.h | 126 ++ .../cpp4.x/include/resource/Account.h | 97 ++ .../cpp4.x/include/resource/Resource.h | 94 ++ .../cpp4.x/include/utils/InitResourceUtils.h | 22 + .../cpp4.x/include/utils/MQAdminUtils.h | 42 + .../cpp4.x/include/utils/NameUtils.h | 46 + .../cpp4.x/include/utils/RandomUtils.h | 46 + .../utils/SimpleConcurrentHashMapUtils.h | 126 ++ .../utils/SimpleConcurrentVectorUtils.h | 69 ++ .../cpp4.x/include/utils/VerifyUtils.h | 58 + .../utils/data/collect/DataCollector.h | 37 + .../utils/data/collect/DataCollectorManager.h | 125 ++ .../data/collect/impl/ListDataCollector.h | 117 ++ .../data/collect/impl/MapDataCollector.h | 137 +++ cpp/rocketmq-client-cpp-tests/cpp4.x/main.cpp | 93 ++ .../cpp4.x/src/CMakeLists.txt | 20 + .../cpp4.x/src/enums/CMakeLists.txt | 20 + .../cpp4.x/src/enums/MessageType.cpp | 24 + .../cpp4.x/src/factory/CMakeLists.txt | 21 + .../cpp4.x/src/factory/MessageFactory.cpp | 108 ++ .../cpp4.x/src/frame/BaseOperate.cpp | 62 + .../cpp4.x/src/frame/CMakeLists.txt | 21 + .../cpp4.x/src/utils/CMakeLists.txt | 27 + .../cpp4.x/src/utils/InitResourceUtils.cpp | 56 + .../cpp4.x/src/utils/MQAdminUtils.cpp | 169 +++ .../cpp4.x/src/utils/NameUtils.cpp | 208 ++++ .../cpp4.x/src/utils/RandomUtils.cpp | 170 +++ .../cpp4.x/src/utils/VerifyUtils.cpp | 1013 +++++++++++++++++ .../cpp4.x/test/CMakeLists.txt | 21 + .../cpp4.x/test/client/CMakeLists.txt | 18 + .../test/client/consumer/CMakeLists.txt | 18 + .../client/consumer/ConsumerGroupTest.cpp | 65 ++ .../client/consumer/PullConsumerInitTest.cpp | 45 + .../client/consumer/PushConsumerInitTest.cpp | 230 ++++ .../cpp4.x/test/client/message/CMakeLists.txt | 19 + .../client/message/MessageAbnormalTest.cpp | 47 + .../client/message/MessageBodyContentTest.cpp | 126 ++ .../test/client/message/MessageKeyTest.cpp | 44 + .../test/client/message/MessageTagTest.cpp | 43 + .../message/MessageUserPropertyTest.cpp | 44 + .../client/message/NormalMessageSizeTest.cpp | 40 + .../test/client/producer/CMakeLists.txt | 17 + .../test/client/producer/ProducerInitTest.cpp | 40 + .../cpp4.x/test/cluster/CMakeLists.txt | 17 + .../cpp4.x/test/cluster/ClusterTest.cpp | 123 ++ .../cpp4.x/test/cluster/LoadBalancingTest.cpp | 87 ++ .../cpp4.x/test/filter/CMakeLists.txt | 17 + .../cpp4.x/test/filter/SqlFilterTest.cpp | 105 ++ .../cpp4.x/test/filter/TagFilterTest.cpp | 388 +++++++ .../cpp4.x/test/offset/CMakeLists.txt | 17 + .../cpp4.x/test/offset/OffsetTest.cpp | 237 ++++ .../cpp4.x/test/pull/CMakeLists.txt | 17 + .../cpp4.x/test/pull/PullAckTest.cpp | 70 ++ .../cpp4.x/test/pull/PullOrderParamTest.cpp | 103 ++ .../cpp4.x/test/pull/PullOrderTest.cpp | 71 ++ .../cpp4.x/test/pull/PullParamTest.cpp | 130 +++ .../cpp4.x/test/pull/PullTopicTypeTest.cpp | 68 ++ .../cpp4.x/test/server/CMakeLists.txt | 19 + .../cpp4.x/test/server/DelayMessageTest.cpp | 116 ++ .../cpp4.x/test/server/NormalMessageTest.cpp | 81 ++ .../cpp4.x/test/server/OrderMessageTest.cpp | 61 + .../test/server/TransactionMessageTest.cpp | 207 ++++ .../server/abnormal/PushConsumerRetryTest.cpp | 307 +++++ .../test/server/batch/BatchProducerTest.cpp | 121 ++ 87 files changed, 7862 insertions(+) create mode 100644 cpp/bin/run.sh create mode 100644 cpp/rocketmq-client-cpp-tests/.gitignore create mode 100644 cpp/rocketmq-client-cpp-tests/cpp4.x/CMakeLists.txt create mode 100644 cpp/rocketmq-client-cpp-tests/cpp4.x/README.md create mode 100644 cpp/rocketmq-client-cpp-tests/cpp4.x/cmake/FindRocketMQ.cmake create mode 100644 cpp/rocketmq-client-cpp-tests/cpp4.x/config.ini create mode 100644 cpp/rocketmq-client-cpp-tests/cpp4.x/include/client/rmq/RMQNormalConsumer.h create mode 100644 cpp/rocketmq-client-cpp-tests/cpp4.x/include/client/rmq/RMQNormalProducer.h create mode 100644 cpp/rocketmq-client-cpp-tests/cpp4.x/include/common/AbstractMQConsumer.h create mode 100644 cpp/rocketmq-client-cpp-tests/cpp4.x/include/common/AbstractMQProducer.h create mode 100644 cpp/rocketmq-client-cpp-tests/cpp4.x/include/common/MQCollector.h create mode 100644 cpp/rocketmq-client-cpp-tests/cpp4.x/include/common/MQMessageQueueSelector.h create mode 100644 cpp/rocketmq-client-cpp-tests/cpp4.x/include/common/MQMsg.h create mode 100644 cpp/rocketmq-client-cpp-tests/cpp4.x/include/common/MQTransactionListener.h create mode 100644 cpp/rocketmq-client-cpp-tests/cpp4.x/include/enums/ConsumeResult.h create mode 100644 cpp/rocketmq-client-cpp-tests/cpp4.x/include/enums/MessageType.h create mode 100644 cpp/rocketmq-client-cpp-tests/cpp4.x/include/factory/ConsumerFactory.h create mode 100644 cpp/rocketmq-client-cpp-tests/cpp4.x/include/factory/MessageFactory.h create mode 100644 cpp/rocketmq-client-cpp-tests/cpp4.x/include/factory/ProducerFactory.h create mode 100644 cpp/rocketmq-client-cpp-tests/cpp4.x/include/frame/BaseOperate.h create mode 100644 cpp/rocketmq-client-cpp-tests/cpp4.x/include/listener/MsgListener.h create mode 100644 cpp/rocketmq-client-cpp-tests/cpp4.x/include/listener/MsgQueueListener.h create mode 100644 cpp/rocketmq-client-cpp-tests/cpp4.x/include/listener/rmq/RMQNormalListener.h create mode 100644 cpp/rocketmq-client-cpp-tests/cpp4.x/include/listener/rmq/RMQOrderListener.h create mode 100644 cpp/rocketmq-client-cpp-tests/cpp4.x/include/resource/Account.h create mode 100644 cpp/rocketmq-client-cpp-tests/cpp4.x/include/resource/Resource.h create mode 100644 cpp/rocketmq-client-cpp-tests/cpp4.x/include/utils/InitResourceUtils.h create mode 100644 cpp/rocketmq-client-cpp-tests/cpp4.x/include/utils/MQAdminUtils.h create mode 100644 cpp/rocketmq-client-cpp-tests/cpp4.x/include/utils/NameUtils.h create mode 100644 cpp/rocketmq-client-cpp-tests/cpp4.x/include/utils/RandomUtils.h create mode 100644 cpp/rocketmq-client-cpp-tests/cpp4.x/include/utils/SimpleConcurrentHashMapUtils.h create mode 100644 cpp/rocketmq-client-cpp-tests/cpp4.x/include/utils/SimpleConcurrentVectorUtils.h create mode 100644 cpp/rocketmq-client-cpp-tests/cpp4.x/include/utils/VerifyUtils.h create mode 100644 cpp/rocketmq-client-cpp-tests/cpp4.x/include/utils/data/collect/DataCollector.h create mode 100644 cpp/rocketmq-client-cpp-tests/cpp4.x/include/utils/data/collect/DataCollectorManager.h create mode 100644 cpp/rocketmq-client-cpp-tests/cpp4.x/include/utils/data/collect/impl/ListDataCollector.h create mode 100644 cpp/rocketmq-client-cpp-tests/cpp4.x/include/utils/data/collect/impl/MapDataCollector.h create mode 100644 cpp/rocketmq-client-cpp-tests/cpp4.x/main.cpp create mode 100644 cpp/rocketmq-client-cpp-tests/cpp4.x/src/CMakeLists.txt create mode 100644 cpp/rocketmq-client-cpp-tests/cpp4.x/src/enums/CMakeLists.txt create mode 100644 cpp/rocketmq-client-cpp-tests/cpp4.x/src/enums/MessageType.cpp create mode 100644 cpp/rocketmq-client-cpp-tests/cpp4.x/src/factory/CMakeLists.txt create mode 100644 cpp/rocketmq-client-cpp-tests/cpp4.x/src/factory/MessageFactory.cpp create mode 100644 cpp/rocketmq-client-cpp-tests/cpp4.x/src/frame/BaseOperate.cpp create mode 100644 cpp/rocketmq-client-cpp-tests/cpp4.x/src/frame/CMakeLists.txt create mode 100644 cpp/rocketmq-client-cpp-tests/cpp4.x/src/utils/CMakeLists.txt create mode 100644 cpp/rocketmq-client-cpp-tests/cpp4.x/src/utils/InitResourceUtils.cpp create mode 100644 cpp/rocketmq-client-cpp-tests/cpp4.x/src/utils/MQAdminUtils.cpp create mode 100644 cpp/rocketmq-client-cpp-tests/cpp4.x/src/utils/NameUtils.cpp create mode 100644 cpp/rocketmq-client-cpp-tests/cpp4.x/src/utils/RandomUtils.cpp create mode 100644 cpp/rocketmq-client-cpp-tests/cpp4.x/src/utils/VerifyUtils.cpp create mode 100644 cpp/rocketmq-client-cpp-tests/cpp4.x/test/CMakeLists.txt create mode 100644 cpp/rocketmq-client-cpp-tests/cpp4.x/test/client/CMakeLists.txt create mode 100644 cpp/rocketmq-client-cpp-tests/cpp4.x/test/client/consumer/CMakeLists.txt create mode 100644 cpp/rocketmq-client-cpp-tests/cpp4.x/test/client/consumer/ConsumerGroupTest.cpp create mode 100644 cpp/rocketmq-client-cpp-tests/cpp4.x/test/client/consumer/PullConsumerInitTest.cpp create mode 100644 cpp/rocketmq-client-cpp-tests/cpp4.x/test/client/consumer/PushConsumerInitTest.cpp create mode 100644 cpp/rocketmq-client-cpp-tests/cpp4.x/test/client/message/CMakeLists.txt create mode 100644 cpp/rocketmq-client-cpp-tests/cpp4.x/test/client/message/MessageAbnormalTest.cpp create mode 100644 cpp/rocketmq-client-cpp-tests/cpp4.x/test/client/message/MessageBodyContentTest.cpp create mode 100644 cpp/rocketmq-client-cpp-tests/cpp4.x/test/client/message/MessageKeyTest.cpp create mode 100644 cpp/rocketmq-client-cpp-tests/cpp4.x/test/client/message/MessageTagTest.cpp create mode 100644 cpp/rocketmq-client-cpp-tests/cpp4.x/test/client/message/MessageUserPropertyTest.cpp create mode 100644 cpp/rocketmq-client-cpp-tests/cpp4.x/test/client/message/NormalMessageSizeTest.cpp create mode 100644 cpp/rocketmq-client-cpp-tests/cpp4.x/test/client/producer/CMakeLists.txt create mode 100644 cpp/rocketmq-client-cpp-tests/cpp4.x/test/client/producer/ProducerInitTest.cpp create mode 100644 cpp/rocketmq-client-cpp-tests/cpp4.x/test/cluster/CMakeLists.txt create mode 100644 cpp/rocketmq-client-cpp-tests/cpp4.x/test/cluster/ClusterTest.cpp create mode 100644 cpp/rocketmq-client-cpp-tests/cpp4.x/test/cluster/LoadBalancingTest.cpp create mode 100644 cpp/rocketmq-client-cpp-tests/cpp4.x/test/filter/CMakeLists.txt create mode 100644 cpp/rocketmq-client-cpp-tests/cpp4.x/test/filter/SqlFilterTest.cpp create mode 100644 cpp/rocketmq-client-cpp-tests/cpp4.x/test/filter/TagFilterTest.cpp create mode 100644 cpp/rocketmq-client-cpp-tests/cpp4.x/test/offset/CMakeLists.txt create mode 100644 cpp/rocketmq-client-cpp-tests/cpp4.x/test/offset/OffsetTest.cpp create mode 100644 cpp/rocketmq-client-cpp-tests/cpp4.x/test/pull/CMakeLists.txt create mode 100644 cpp/rocketmq-client-cpp-tests/cpp4.x/test/pull/PullAckTest.cpp create mode 100644 cpp/rocketmq-client-cpp-tests/cpp4.x/test/pull/PullOrderParamTest.cpp create mode 100644 cpp/rocketmq-client-cpp-tests/cpp4.x/test/pull/PullOrderTest.cpp create mode 100644 cpp/rocketmq-client-cpp-tests/cpp4.x/test/pull/PullParamTest.cpp create mode 100644 cpp/rocketmq-client-cpp-tests/cpp4.x/test/pull/PullTopicTypeTest.cpp create mode 100644 cpp/rocketmq-client-cpp-tests/cpp4.x/test/server/CMakeLists.txt create mode 100644 cpp/rocketmq-client-cpp-tests/cpp4.x/test/server/DelayMessageTest.cpp create mode 100644 cpp/rocketmq-client-cpp-tests/cpp4.x/test/server/NormalMessageTest.cpp create mode 100644 cpp/rocketmq-client-cpp-tests/cpp4.x/test/server/OrderMessageTest.cpp create mode 100644 cpp/rocketmq-client-cpp-tests/cpp4.x/test/server/TransactionMessageTest.cpp create mode 100644 cpp/rocketmq-client-cpp-tests/cpp4.x/test/server/abnormal/PushConsumerRetryTest.cpp create mode 100644 cpp/rocketmq-client-cpp-tests/cpp4.x/test/server/batch/BatchProducerTest.cpp diff --git a/cpp/bin/run.sh b/cpp/bin/run.sh new file mode 100644 index 0000000..706e820 --- /dev/null +++ b/cpp/bin/run.sh @@ -0,0 +1,88 @@ +#!/bin/sh + +# Licensed to the Apache Software Foundation (ASF) under one or more +# contributor license agreements. See the NOTICE file distributed with +# this work for additional information regarding copyright ownership. +# The ASF licenses this file to You under the Apache License, Version 2.0 +# (the "License"); you may not use this file except in compliance with +# the License. You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +# Detection distribution +if [ -f /etc/os-release ]; then + . /etc/os-release + DISTRO=$ID +elif [ -f /etc/lsb-release ]; then + . /etc/lsb-release + DISTRO=$DISTRIB_ID +elif [ -f /etc/debian_version ]; then + DISTRO="Debian" +else + DISTRO=$(uname -s) +fi + +DISTRO_LOWER=$(echo "$DISTRO" | tr '[:upper:]' '[:lower:]') + +# Select the package manager and install commands based on your distribution +if [ "$DISTRO_LOWER" == "ubuntu" ] || [ "$DISTRO_LOWER" == "debian" ]; then + PACKAGE_MANAGER="apt-get" + INSTALL_COMMAND="sudo $PACKAGE_MANAGER install -y" + $INSTALL_COMMAND libssl-dev libboost-all-dev libspdlog-dev libgtest-dev libfmt-dev libbz2-dev zlib1g-dev libc6-dev libpthread-stubs0-dev cmake automake g++ autoconf libtool +elif [ "$DISTRO_LOWER" == "fedora" ] || [ "$DISTRO_LOWER" == "centos" ] || [ "$DISTRO_LOWER" == "rhel" ]; then + PACKAGE_MANAGER="dnf" + INSTALL_COMMAND="sudo $PACKAGE_MANAGER install -y" + $INSTALL_COMMAND openssl-devel boost-devel spdlog-devel gtest-devel fmt-devel bzip2-devel zlib-devel glibc-devel libpthread-stubs cmake automake g++ autoconf libtool +elif [ "$DISTRO_LOWER" == "arch" ] || [ "$DISTRO_LOWER" == "manjaro" ]; then + PACKAGE_MANAGER="pacman" + INSTALL_COMMAND="sudo $PACKAGE_MANAGER -S --noconfirm" + $INSTALL_COMMAND openssl boost spdlog gtest fmt bzip2 zlib glibc libpthread-stubs cmake automake gcc autoconf libtool +else + echo "Unrecognized distribution: $DISTRO" + exit 1 +fi + +if [ ! -d "rocketmq-client-cpp-2.1.0" ]; then + echo "rocketmq-client-cpp-2.1.0 folder does not exist, start to download and decompress..." + curl -LO https://github.com/apache/rocketmq-client-cpp/archive/refs/tags/2.1.0.zip + unzip 2.1.0.zip + rm 2.1.0.zip + echo "rocketmq-client-cpp-2.1.0 Download and decompress complete." + cd rocketmq-client-cpp-2.1.0 + bash build.sh + cd .. +fi + +if [ ! -d "rocketmq-client-cpp-2.1.0/tmp_build_dir" ]; then + if [ ! -f "rocketmq-client-cpp-2.1.0/tmp_build_dir/librocketmq.a" ]; then + echo "librocketmq.a file does not exist, start to build..." + exit 1 + fi +fi + +if [ ! -d "rocketmq-client-cpp-2.1.0/tmp_include_dir" ]; then + mkdir -p rocketmq-client-cpp-2.1.0/tmp_include_dir/rocketmq + cp -r rocketmq-client-cpp-2.1.0/include/* rocketmq-client-cpp-2.1.0/tmp_include_dir/rocketmq +fi + +#设置环境变量ROCKETMQ_CPP_LIB为 pwd+/rocketmq-client-cpp-2.1.0/tmp_build_dir/librocketmq.a +export ROCKETMQ_CPP_LIB=$(pwd)/rocketmq-client-cpp-2.1.0/tmp_build_dir +#设置环境变量ROCKETMQ_CPP_INC为 pwd+/rocketmq-client-cpp-2.1.0/include +export ROCKETMQ_CPP_INC=$(pwd)/rocketmq-client-cpp-2.1.0/tmp_include_dir + +echo "Installation complete!" +# cd project base dir to compile mqadmin utils for other language e2e test using +cd ../common && mvn -Prelease -DskipTests clean package -U +# set env for mqadmin (use source to set linux env variables in current shell) +cd ../rocketmq-admintools && source bin/env.sh +# run cpp e2e test case +cd ../cpp/rocketmq-client-cpp-tests/cpp4.x +cmake . -B build && cd build +make -j && cd .. +./rocketmq_test diff --git a/cpp/rocketmq-client-cpp-tests/.gitignore b/cpp/rocketmq-client-cpp-tests/.gitignore new file mode 100644 index 0000000..08fce53 --- /dev/null +++ b/cpp/rocketmq-client-cpp-tests/.gitignore @@ -0,0 +1,6 @@ +cpp4.x/rocketmq_test +cpp4.x/build +cpp4.x/.cache +cpp4.x/logs +nohup.out +cpp5.x \ No newline at end of file diff --git a/cpp/rocketmq-client-cpp-tests/cpp4.x/CMakeLists.txt b/cpp/rocketmq-client-cpp-tests/cpp4.x/CMakeLists.txt new file mode 100644 index 0000000..d8c2275 --- /dev/null +++ b/cpp/rocketmq-client-cpp-tests/cpp4.x/CMakeLists.txt @@ -0,0 +1,65 @@ +# Licensed to the Apache Software Foundation (ASF) under one or more +# contributor license agreements. See the NOTICE file distributed with +# this work for additional information regarding copyright ownership. +# The ASF licenses this file to You under the Apache License, Version 2.0 +# (the "License"); you may not use this file except in compliance with +# the License. You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +cmake_minimum_required(VERSION 3.10) + +project(rocketmq_test) + +set(CMAKE_EXPORT_COMPILE_COMMANDS ON) +set(CMAKE_CXX_COMPILE "g++") +set(CMAKE_BUILD_TYPE "Release") +set(CMAKE_CXX_STANDARD_REQUIRED True) +set(CMAKE_CXX_STANDARD 17) +set(CMAKE_RUNTIME_OUTPUT_DIRECTORY ${PROJECT_SOURCE_DIR}) + +list(APPEND CMAKE_MODULE_PATH "${CMAKE_CURRENT_SOURCE_DIR}/cmake") + +find_package(GTest REQUIRED) +find_package(Threads REQUIRED) +find_package(ZLIB REQUIRED) + +find_package(spdlog REQUIRED) +find_package(fmt REQUIRED) +find_package(RocketMQ REQUIRED) +find_package(OpenSSL REQUIRED) + +set(SOURCE_FILES "") +add_subdirectory(test) +add_subdirectory(src) + +list(APPEND SOURCE_FILES ${CMAKE_CURRENT_SOURCE_DIR}/main.cpp) + +add_executable(${PROJECT_NAME} ${SOURCE_FILES}) + +target_link_libraries(${PROJECT_NAME} PRIVATE + ${GTEST_LIBRARIES} + ${RocketMQ_LIBRARIES} + ${ZLIB_LIBRARIES} + ${CMAKE_THREAD_LIBS_INIT} + spdlog::spdlog + fmt::fmt + dl + rt + frame + utils + enums + factory +) + +target_include_directories(${PROJECT_NAME} PRIVATE + ${CMAKE_SOURCE_DIR}/include + ${ROCKETMQ_INCLUDE_DIR} + /usr/include + /usr/local/include +) \ No newline at end of file diff --git a/cpp/rocketmq-client-cpp-tests/cpp4.x/README.md b/cpp/rocketmq-client-cpp-tests/cpp4.x/README.md new file mode 100644 index 0000000..89ff5f2 --- /dev/null +++ b/cpp/rocketmq-client-cpp-tests/cpp4.x/README.md @@ -0,0 +1,47 @@ +## Apache RocketMQ E2E +[![License](https://img.shields.io/badge/license-Apache%202-4EB1BA.svg)](https://www.apache.org/licenses/LICENSE-2.0.html) + + + +### RocketMQ E2E Cpp 4.x Test +The project is a rocketmq 4.x cpp client E2E test project built using CMake, and the following shows how to compile and configure it. + +#### Install dependent library +The project relies on the following libraries, make sure you have them installed: + +* GTest: Used for unit testing. +* Threads: Used for multithreading support. +* ZLIB: Used for compression and decompression support. +* spdlog: Used for logging. +* fmt: Used for format output. +* RocketMQ: Used for interacting with RocketMQ. + +If you already have these libraries installed, CMake will find them automatically. If they are not installed, you can install them by following the steps in the link below: + +* [GTest](https://github.com/google/googletest/blob/main/googletest/README.md) + +* [fmt](https://github.com/fmtlib/fmt) + +* [spdlog](https://github.com/gabime/spdlog) + +* [RocketMQ](https://github.com/apache/rocketmq-client-cpp) + +You can use the following command to help the project find the cmake configuration file to find the installation location of the library if they are installed in a custom directory. +```cmake +list(APPEND CMAKE_MODULE_PATH "custom_directory") +``` + +#### Compile project +To compile the project, follow these steps: + +1. Make sure you have CMake and the required compiler (e.g. g++) installed. + +2. Create a build directory under the project root, such as build. + +3. Go to the build directory and run the following command: + +```shell +cmake .. +make +``` +This will generate the executable file. \ No newline at end of file diff --git a/cpp/rocketmq-client-cpp-tests/cpp4.x/cmake/FindRocketMQ.cmake b/cpp/rocketmq-client-cpp-tests/cpp4.x/cmake/FindRocketMQ.cmake new file mode 100644 index 0000000..5a78ac0 --- /dev/null +++ b/cpp/rocketmq-client-cpp-tests/cpp4.x/cmake/FindRocketMQ.cmake @@ -0,0 +1,47 @@ +# Licensed to the Apache Software Foundation (ASF) under one or more +# contributor license agreements. See the NOTICE file distributed with +# this work for additional information regarding copyright ownership. +# The ASF licenses this file to You under the Apache License, Version 2.0 +# (the "License"); you may not use this file except in compliance with +# the License. You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +set(ROCKETMQ_CPP_LIB_PATH $ENV{ROCKETMQ_CPP_LIB}) +set(ROCKETMQ_CPP_INC_PATH $ENV{ROCKETMQ_CPP_INC}) + +find_path(ROCKETMQ_INCLUDE_DIR + NAMES + rocketmq/DefaultMQProducer.h + PATHS + /usr/local/include + /usr/include + ${ROCKETMQ_CPP_INC_PATH} +) + +find_library(ROCKETMQ_LIBRARY + NAMES + rocketmq + PATHS + /usr/local/lib + /usr/lib + ${ROCKETMQ_CPP_LIB_PATH} +) + +include(FindPackageHandleStandardArgs) +find_package_handle_standard_args(RocketMQ + REQUIRED_VARS + ROCKETMQ_LIBRARY + ROCKETMQ_INCLUDE_DIR +) + +if(RocketMQ_FOUND) + set(RocketMQ_LIBRARIES ${ROCKETMQ_LIBRARY}) + set(RocketMQ_INCLUDE_DIRS ${ROCKETMQ_INCLUDE_DIR}) + mark_as_advanced(RocketMQ_INCLUDE_DIRS RocketMQ_LIBRARIES) +endif() \ No newline at end of file diff --git a/cpp/rocketmq-client-cpp-tests/cpp4.x/config.ini b/cpp/rocketmq-client-cpp-tests/cpp4.x/config.ini new file mode 100644 index 0000000..8030a65 --- /dev/null +++ b/cpp/rocketmq-client-cpp-tests/cpp4.x/config.ini @@ -0,0 +1,21 @@ +# Licensed to the Apache Software Foundation (ASF) under one or more +# contributor license agreements. See the NOTICE file distributed with +# this work for additional information regarding copyright ownership. +# The ASF licenses this file to You under the Apache License, Version 2.0 +# (the "License"); you may not use this file except in compliance with +# the License. You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +[rocketmq] +namesrv = 127.0.0.1:9876 +brokerAddr = 127.0.0.1:10911 +cluster = +accessKey = +secretKey = +accessChannel = \ No newline at end of file diff --git a/cpp/rocketmq-client-cpp-tests/cpp4.x/include/client/rmq/RMQNormalConsumer.h b/cpp/rocketmq-client-cpp-tests/cpp4.x/include/client/rmq/RMQNormalConsumer.h new file mode 100644 index 0000000..13325d1 --- /dev/null +++ b/cpp/rocketmq-client-cpp-tests/cpp4.x/include/client/rmq/RMQNormalConsumer.h @@ -0,0 +1,130 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +#pragma once +#include "common/AbstractMQConsumer.h" +#include "listener/MsgListener.h" +#include "resource/Resource.h" +#include "common/MQCollector.h" +#include "listener/rmq/RMQNormalListener.h" +#include "listener/rmq/RMQOrderListener.h" +#include "rocketmq/DefaultMQPullConsumer.h" +#include "rocketmq/DefaultMQPushConsumer.h" +#include "spdlog/logger.h" +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +extern std::shared_ptr multi_logger; +extern std::shared_ptr resource; + +class RMQNormalConsumer +{ +private: + std::shared_ptr pushConsumer; + std::shared_ptr pullConsumer; + std::shared_ptr consumer; + std::vector> executorService; + static constexpr int WAIT_RESPONSE_MILLS = 15 * 1000; + std::shared_ptr normalListener = nullptr; + std::shared_ptr orderListener = nullptr; + bool needRun = true; + int consumerThreadNum = 20; + static std::atomic receivedIndex; + +public: + RMQNormalConsumer(std::shared_ptr consumer, std::shared_ptr listener) + : pushConsumer(consumer), normalListener(listener) {} + + RMQNormalConsumer(std::shared_ptr consumer, std::shared_ptr listener) + : pushConsumer(consumer), orderListener(listener) {} + + RMQNormalConsumer(std::shared_ptr consumer) + : pullConsumer(consumer) {} + + // void receiveThenNack(const std::string& topic,int maxMessageNum, std::optional> receiveInvisibleDuration, std::optional> changeInvisibleDuration) { + + // } + + void shutdown() + { + if (pushConsumer) + { + pushConsumer->shutdown(); + } + if (pullConsumer) + { + pullConsumer->shutdown(); + } + } + + std::shared_ptr getPushConsumer() + { + return pushConsumer; + } + + void setPushConsumer(std::shared_ptr pushConsumer) + { + this->pushConsumer = pushConsumer; + } + + std::shared_ptr getPullConsumer() + { + return pullConsumer; + } + + void setSimpleConsumer(std::shared_ptr pullConsumer) + { + this->pullConsumer = pullConsumer; + } + + std::shared_ptr getConsumer() + { + return consumer; + } + + void setConsumer(std::shared_ptr consumer) + { + this->consumer = consumer; + } + + std::shared_ptr getListener() + { + return normalListener; + } + + std::shared_ptr getOrderListener() + { + return orderListener; + } + + void setListener(std::shared_ptr listener) + { + this->normalListener = listener; + } + + void setOrderListener(std::shared_ptr listener) + { + this->orderListener = listener; + } +}; \ No newline at end of file diff --git a/cpp/rocketmq-client-cpp-tests/cpp4.x/include/client/rmq/RMQNormalProducer.h b/cpp/rocketmq-client-cpp-tests/cpp4.x/include/client/rmq/RMQNormalProducer.h new file mode 100644 index 0000000..a3d6971 --- /dev/null +++ b/cpp/rocketmq-client-cpp-tests/cpp4.x/include/client/rmq/RMQNormalProducer.h @@ -0,0 +1,153 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +#pragma once +#include "common/AbstractMQProducer.h" +#include "resource/Resource.h" +#include "common/MQMessageQueueSelector.h" +#include "rocketmq/DefaultMQProducer.h" +#include "rocketmq/TransactionMQProducer.h" +#include "rocketmq/MQMessage.h" +#include "spdlog/logger.h" +#include +#include +#include +#include + +extern std::shared_ptr multi_logger; +extern std::shared_ptr resource; + +class RMQNormalProducer : public AbstractMQProducer +{ +private: + std::shared_ptr producer; + std::shared_ptr transactionProducer; + +public: + RMQNormalProducer(std::shared_ptr producer) : producer(producer) {} + + RMQNormalProducer(std::shared_ptr producer) : transactionProducer(producer) {} + + std::shared_ptr getProducer() + { + return producer; + } + + std::shared_ptr getTransProducer() + { + return transactionProducer; + } + + void start() + { + producer->start(); + } + + void startTransaction() + { + transactionProducer->start(); + } + + rocketmq::SendResult sendTrans(rocketmq::MQMessage &msg, rocketmq::LocalTransactionState state) + { + rocketmq::SendResult sendResult; + try + { + // COMMIT_MESSAGE, ROLLBACK_MESSAGE, UNKNOWN + sendResult = transactionProducer->sendMessageInTransaction(msg, &state); + getEnqueueMessages()->addData(sendResult.getMsgId()); + } + catch (const std::exception &e) + { + multi_logger->error("TransProducer send message failed, {}", e.what()); + } + return sendResult; + } + + rocketmq::SendResult send(rocketmq::MQMessage &msg) + { + rocketmq::SendResult sendResult; + try + { + sendResult = producer->send(msg); + getEnqueueMessages()->addData(sendResult.getMsgId()); + } + catch (const std::exception &e) + { + multi_logger->error("Producer send message failed, {}", e.what()); + } + return sendResult; + } + + rocketmq::SendResult sendOrderMessage(rocketmq::MQMessage &msg, int orderId) + { + std::shared_ptr selector = std::make_shared(); + rocketmq::SendResult sendResult; + try + { + sendResult = producer->send(msg, selector.get(), &orderId); + getEnqueueMessages()->addData(sendResult.getMsgId()); + } + catch (const std::exception &e) + { + multi_logger->error("Producer send message failed, {}", e.what()); + } + return sendResult; + } + + void sendAsync(rocketmq::MQMessage &msg) + { + producer->send(msg); + } + + rocketmq::SendResult send(const std::string &topic, const std::string &tags, const std::string &body) + { + rocketmq::MQMessage msg(topic, // topic + tags, // tags + body); // body + rocketmq::SendResult sendResult = send(msg); + getEnqueueMessages()->addData(sendResult.getMsgId()); + return sendResult; + } + + void send(const std::string &topic, const std::string &tags, int messageNim) + { + for (int i = 0; i < messageNim; i++) + { + rocketmq::MQMessage msg(topic, tags, RandomUtils::getStringByUUID()); + + try + { + rocketmq::SendResult sendResult = producer->send(msg); + getEnqueueMessages()->addData(sendResult.getMsgId()); + } + catch (const std::exception &e) + { + multi_logger->error("Producer send message failed, {}", e.what()); + } + } + } + + void shutdown() + { + producer->shutdown(); + } + + void shutdownTransaction() + { + transactionProducer->shutdown(); + } +}; \ No newline at end of file diff --git a/cpp/rocketmq-client-cpp-tests/cpp4.x/include/common/AbstractMQConsumer.h b/cpp/rocketmq-client-cpp-tests/cpp4.x/include/common/AbstractMQConsumer.h new file mode 100644 index 0000000..bf26bca --- /dev/null +++ b/cpp/rocketmq-client-cpp-tests/cpp4.x/include/common/AbstractMQConsumer.h @@ -0,0 +1,26 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +#pragma once +#include "common/MQCollector.h" +#include "rocketmq/MQMessage.h" +#include + +class AbstractMQConsumer : public MQCollector +{ +public: + // AbstractMQConsumer() = default; +}; \ No newline at end of file diff --git a/cpp/rocketmq-client-cpp-tests/cpp4.x/include/common/AbstractMQProducer.h b/cpp/rocketmq-client-cpp-tests/cpp4.x/include/common/AbstractMQProducer.h new file mode 100644 index 0000000..a1a2397 --- /dev/null +++ b/cpp/rocketmq-client-cpp-tests/cpp4.x/include/common/AbstractMQProducer.h @@ -0,0 +1,29 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +#pragma once +#include +#include "common/MQCollector.h" +#include "rocketmq/MQMessage.h" + +class AbstractMQProducer : public MQCollector +{ +protected: + bool startSuccess = false; + std::string producerGroupName; + std::string producerInstanceName; + bool isDebug = false; +}; \ No newline at end of file diff --git a/cpp/rocketmq-client-cpp-tests/cpp4.x/include/common/MQCollector.h b/cpp/rocketmq-client-cpp-tests/cpp4.x/include/common/MQCollector.h new file mode 100644 index 0000000..ab39051 --- /dev/null +++ b/cpp/rocketmq-client-cpp-tests/cpp4.x/include/common/MQCollector.h @@ -0,0 +1,149 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +#pragma once +#include +#include "utils/RandomUtils.h" +#include "utils/data/collect/DataCollector.h" +#include "utils/data/collect/DataCollectorManager.h" + +template +class MQCollector +{ +protected: + DataCollector *enqueueMessages; + DataCollector *enqueueFailedMessages; + DataCollector *dequeueMessages; + DataCollector *dequeueAllMessages; + DataCollector *dequeueUndupMessageBody; + DataCollector *msgRTs; + +public: + MQCollector() + { + enqueueMessages = &DataCollectorManager::getInstance().fetchDataCollector(RandomUtils::getStringByUUID()); + enqueueFailedMessages = &DataCollectorManager::getInstance().fetchDataCollector(RandomUtils::getStringByUUID()); + dequeueMessages = &DataCollectorManager::getInstance().fetchDataCollector(RandomUtils::getStringByUUID()); + dequeueAllMessages = &DataCollectorManager::getInstance().fetchDataCollector(RandomUtils::getStringByUUID()); + dequeueUndupMessageBody = &DataCollectorManager::getInstance().fetchDataCollector(RandomUtils::getStringByUUID()); + msgRTs = &DataCollectorManager::getInstance().fetchDataCollector(RandomUtils::getStringByUUID()); + } + + void clearMsg() + { + enqueueMessages->resetData(); + enqueueFailedMessages->resetData(); + dequeueMessages->resetData(); + dequeueAllMessages->resetData(); + dequeueUndupMessageBody->resetData(); + msgRTs->resetData(); + } + + void lockCollectors() + { + enqueueMessages->lockIncrement(); + enqueueFailedMessages->lockIncrement(); + dequeueMessages->lockIncrement(); + dequeueAllMessages->lockIncrement(); + dequeueUndupMessageBody->lockIncrement(); + msgRTs->lockIncrement(); + } + + DataCollector *getEnqueueMessages() + { + return enqueueMessages; + } + + DataCollector *getEnqueueFailedMessages() + { + return enqueueFailedMessages; + } + + DataCollector *getDequeueMessages() + { + return dequeueMessages; + } + + DataCollector *getDequeueAllMessages() + { + return dequeueAllMessages; + } + + DataCollector *getDequeueUndupMessageBody() + { + return dequeueUndupMessageBody; + } +}; + +// class MQCollector { +// protected: +// DataCollector* enqueueMessages; +// DataCollector* enqueueFailedMessages; +// DataCollector* dequeueMessages; +// DataCollector* dequeueAllMessages; +// DataCollector* dequeueUndupMessageBody; +// DataCollector* msgRTs; + +// public: +// MQCollector() { +// enqueueMessages = &DataCollectorManager::getInstance().fetchDataCollector(RandomUtils::getStringByUUID()); +// enqueueFailedMessages = &DataCollectorManager::getInstance().fetchDataCollector(RandomUtils::getStringByUUID()); +// dequeueMessages = &DataCollectorManager::getInstance().fetchDataCollector(RandomUtils::getStringByUUID()); +// dequeueAllMessages = &DataCollectorManager::getInstance().fetchDataCollector(RandomUtils::getStringByUUID()); +// dequeueUndupMessageBody = &DataCollectorManager::getInstance().fetchDataCollector(RandomUtils::getStringByUUID()); +// msgRTs = &DataCollectorManager::getInstance().fetchDataCollector(RandomUtils::getStringByUUID()); + +// } + +// void clearMsg() { +// enqueueMessages->resetData(); +// enqueueFailedMessages->resetData(); +// dequeueMessages->resetData(); +// dequeueAllMessages->resetData(); +// dequeueUndupMessageBody->resetData(); +// msgRTs->resetData(); +// } + +// void lockCollectors() { +// enqueueMessages->lockIncrement(); +// enqueueFailedMessages->lockIncrement(); +// dequeueMessages->lockIncrement(); +// dequeueAllMessages->lockIncrement(); +// dequeueUndupMessageBody->lockIncrement(); +// msgRTs->lockIncrement(); +// } + +// DataCollector* getEnqueueMessages() { +// return enqueueMessages; +// } + +// DataCollector* getEnqueueFailedMessages() { +// return enqueueFailedMessages; +// } + +// DataCollector* getDequeueMessages() { +// return dequeueMessages; +// } + +// DataCollector* getDequeueAllMessages() { +// return dequeueAllMessages; +// } + +// DataCollector* getDequeueUndupMessageBody() { +// return dequeueUndupMessageBody; +// } + +// }; diff --git a/cpp/rocketmq-client-cpp-tests/cpp4.x/include/common/MQMessageQueueSelector.h b/cpp/rocketmq-client-cpp-tests/cpp4.x/include/common/MQMessageQueueSelector.h new file mode 100644 index 0000000..43c1cee --- /dev/null +++ b/cpp/rocketmq-client-cpp-tests/cpp4.x/include/common/MQMessageQueueSelector.h @@ -0,0 +1,29 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +#pragma once +#include "rocketmq/DefaultMQProducer.h" + +class MQMessageQueueSelector : public rocketmq::MessageQueueSelector +{ +public: + rocketmq::MQMessageQueue select(const std::vector &mqs, const rocketmq::MQMessage &msg, void *arg) + { + int orderId = *static_cast(arg); + int index = orderId % mqs.size(); + return mqs[index]; + } +}; \ No newline at end of file diff --git a/cpp/rocketmq-client-cpp-tests/cpp4.x/include/common/MQMsg.h b/cpp/rocketmq-client-cpp-tests/cpp4.x/include/common/MQMsg.h new file mode 100644 index 0000000..c167cf9 --- /dev/null +++ b/cpp/rocketmq-client-cpp-tests/cpp4.x/include/common/MQMsg.h @@ -0,0 +1,62 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +#pragma once +#include +#include "rocketmq/MQMessageExt.h" + +class MQMsg : public rocketmq::MQMessageExt +{ +public: + MQMsg() : MQMessageExt() {} + + MQMsg(int queueId, + int64 bornTimestamp, + sockaddr bornHost, + int64 storeTimestamp, + sockaddr storeHost, + const std::string &msgId, + const std::string &property) + : MQMessageExt(queueId, bornTimestamp, bornHost, storeTimestamp, storeHost, msgId), property_(property) {} + + MQMsg(const MQMessageExt &msg, const std::string &property) + : MQMessageExt(msg), property_(property) {} + + MQMsg(const MQMessageExt &msg) + : MQMessageExt(msg) {} + + MQMsg(const MQMsg &other) : MQMessageExt(other), property_(other.property_) {} + + virtual ~MQMsg() {} + + const std::string &getProperty() const + { + return property_; + } + + void setProperty(const std::string &property) + { + property_ = property; + } + + bool operator<(const MQMsg &other) const + { + return getMsgId() < other.getMsgId(); + } + +private: + std::string property_; +}; \ No newline at end of file diff --git a/cpp/rocketmq-client-cpp-tests/cpp4.x/include/common/MQTransactionListener.h b/cpp/rocketmq-client-cpp-tests/cpp4.x/include/common/MQTransactionListener.h new file mode 100644 index 0000000..fa72d06 --- /dev/null +++ b/cpp/rocketmq-client-cpp-tests/cpp4.x/include/common/MQTransactionListener.h @@ -0,0 +1,110 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +#pragma once +#include "rocketmq/TransactionListener.h" +#include "spdlog/logger.h" +#include "resource/Resource.h" + +extern std::shared_ptr multi_logger; +extern std::shared_ptr resource; + +class CommitMQTransactionListener : public rocketmq::TransactionListener +{ +public: + CommitMQTransactionListener(){}; + ~CommitMQTransactionListener(){}; + rocketmq::LocalTransactionState executeLocalTransaction(const rocketmq::MQMessage &msg, void *arg) + { + if (!arg) + { + multi_logger->info("CommitMQTransactionListener executeLocalTransaction transactionId:{}, return state: COMMIT_MESAGE", msg.getTransactionId()); + return rocketmq::LocalTransactionState::COMMIT_MESSAGE; + } + rocketmq::LocalTransactionState state = (rocketmq::LocalTransactionState)(*(int *)arg % 3); + multi_logger->info("CommitMQTransactionListener executeLocalTransaction transactionId:{}, return state: {}", msg.getTransactionId(), (*(int *)arg % 3)); + return state; + } + + rocketmq::LocalTransactionState checkLocalTransaction(const rocketmq::MQMessageExt &msg) + { + multi_logger->info("CommitMQTransactionListener checkLocalTransaction transactionId:{}, return state: COMMIT_MESAGE", msg.getTransactionId()); + return rocketmq::LocalTransactionState::COMMIT_MESSAGE; + } +}; + +class RollbackMQTransactionListener : public rocketmq::TransactionListener +{ +public: + RollbackMQTransactionListener(){}; + ~RollbackMQTransactionListener(){}; + rocketmq::LocalTransactionState executeLocalTransaction(const rocketmq::MQMessage &msg, void *arg) + { + if (!arg) + { + multi_logger->info("RollbackMQTransactionListener executeLocalTransaction transactionId:{}, return state: COMMIT_MESAGE", msg.getTransactionId()); + return rocketmq::LocalTransactionState::COMMIT_MESSAGE; + } + rocketmq::LocalTransactionState state = (rocketmq::LocalTransactionState)(*(int *)arg % 3); + multi_logger->info("RollbackMQTransactionListener executeLocalTransaction transactionId:{}, return state: {}", msg.getTransactionId(), (*(int *)arg % 3)); + return state; + } + + rocketmq::LocalTransactionState checkLocalTransaction(const rocketmq::MQMessageExt &msg) + { + multi_logger->info("RollbackMQTransactionListener checkLocalTransaction transactionId:{}, return state: ROLLBACK_MESSAGE", msg.getTransactionId()); + return rocketmq::LocalTransactionState::ROLLBACK_MESSAGE; + } +}; + +class UserdefinedMQTransactionListener : public rocketmq::TransactionListener +{ +private: + std::atomic &commitMsgNum; + std::atomic &rollbackMsgNum; + +public: + UserdefinedMQTransactionListener(std::atomic &commitMsgNum, std::atomic &rollbackMsgNum) : commitMsgNum(commitMsgNum), rollbackMsgNum(rollbackMsgNum){}; + ~UserdefinedMQTransactionListener(){}; + rocketmq::LocalTransactionState executeLocalTransaction(const rocketmq::MQMessage &msg, void *arg) + { + if (!arg) + { + multi_logger->info("UserdefinedMQTransactionListener executeLocalTransaction transactionId:{} , return state: COMMIT_MESAGE", msg.getTransactionId()); + return rocketmq::LocalTransactionState::COMMIT_MESSAGE; + } + rocketmq::LocalTransactionState state = (rocketmq::LocalTransactionState)(*(int *)arg % 3); + multi_logger->info("UserdefinedMQTransactionListener executeLocalTransaction transactionId:{}, return state: {}", msg.getTransactionId(), (*(int *)arg % 3)); + return state; + } + + rocketmq::LocalTransactionState checkLocalTransaction(const rocketmq::MQMessageExt &msg) + { + int content = std::stoi(msg.getBody()); + if (content % 2 == 0) + { + commitMsgNum++; + multi_logger->info("UserdefinedMQTransactionListener checkLocalTransaction transactionId:{}, message{}, return state: COMMIT_MESAGE", msg.getTransactionId(), msg.toString()); + return rocketmq::LocalTransactionState::COMMIT_MESSAGE; + } + else + { + rollbackMsgNum++; + multi_logger->info("UserdefinedMQTransactionListener checkLocalTransaction transactionId:{}, message{}, return state: ROLLBACK_MESSAGE", msg.getTransactionId(), msg.toString()); + return rocketmq::LocalTransactionState::ROLLBACK_MESSAGE; + } + } +}; \ No newline at end of file diff --git a/cpp/rocketmq-client-cpp-tests/cpp4.x/include/enums/ConsumeResult.h b/cpp/rocketmq-client-cpp-tests/cpp4.x/include/enums/ConsumeResult.h new file mode 100644 index 0000000..aa99913 --- /dev/null +++ b/cpp/rocketmq-client-cpp-tests/cpp4.x/include/enums/ConsumeResult.h @@ -0,0 +1,23 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +#pragma once + +enum class ConsumeResult +{ + SUCCESS, + FAILURE +}; \ No newline at end of file diff --git a/cpp/rocketmq-client-cpp-tests/cpp4.x/include/enums/MessageType.h b/cpp/rocketmq-client-cpp-tests/cpp4.x/include/enums/MessageType.h new file mode 100644 index 0000000..36196d0 --- /dev/null +++ b/cpp/rocketmq-client-cpp-tests/cpp4.x/include/enums/MessageType.h @@ -0,0 +1,31 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +#pragma once + +#include +#include + +enum class MessageType +{ + UNSPECIFIED, + NORMAL, + FIFO, + DELAY, + TRANSACTION +}; + +extern std::map MessageTypeToString; \ No newline at end of file diff --git a/cpp/rocketmq-client-cpp-tests/cpp4.x/include/factory/ConsumerFactory.h b/cpp/rocketmq-client-cpp-tests/cpp4.x/include/factory/ConsumerFactory.h new file mode 100644 index 0000000..443fb69 --- /dev/null +++ b/cpp/rocketmq-client-cpp-tests/cpp4.x/include/factory/ConsumerFactory.h @@ -0,0 +1,122 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +#include "client/rmq/RMQNormalConsumer.h" +#include "listener/MsgListener.h" +#include "listener/rmq/RMQNormalListener.h" +#include "listener/rmq/RMQOrderListener.h" +#include "resource/Resource.h" +#include "rocketmq/DefaultMQPullConsumer.h" +#include "rocketmq/DefaultMQPushConsumer.h" +#include "spdlog/logger.h" +#include + +extern std::shared_ptr multi_logger; +extern std::shared_ptr resource; + +class ConsumerFactory +{ +public: + ConsumerFactory() = delete; + + static std::shared_ptr getPushConsumer(const std::string &topic, const std::string &group, const std::string &subExpression, std::shared_ptr msglistener) + { + auto rmqPushConsumer = std::make_shared(group); + rmqPushConsumer->setNamesrvAddr(resource->getNamesrv()); + rmqPushConsumer->setSessionCredentials(resource->getAccessKey(), resource->getSecretKey(), resource->getAccessChannel()); + rmqPushConsumer->setConsumeFromWhere(rocketmq::CONSUME_FROM_LAST_OFFSET); + rmqPushConsumer->setConsumeThreadCount(4); + rmqPushConsumer->subscribe(topic, subExpression); + rmqPushConsumer->registerMessageListener(msglistener.get()); + rmqPushConsumer->start(); + return rmqPushConsumer; + } + + static std::shared_ptr getPushConsumer(const std::string &topic, const std::string &group, const std::string &subExpression, std::shared_ptr listener) + { + auto rmqPushConsumer = std::make_shared(group); + rmqPushConsumer->setNamesrvAddr(resource->getNamesrv()); + rmqPushConsumer->setSessionCredentials(resource->getAccessKey(), resource->getSecretKey(), resource->getAccessChannel()); + rmqPushConsumer->setConsumeFromWhere(rocketmq::CONSUME_FROM_LAST_OFFSET); + rmqPushConsumer->setConsumeThreadCount(4); + rmqPushConsumer->subscribe(topic, subExpression); + rmqPushConsumer->registerMessageListener(listener.get()); + rmqPushConsumer->start(); + return rmqPushConsumer; + } + + static std::shared_ptr getBroadcastPushConsumer(const std::string &topic, const std::string &group, const std::string &subExpression, std::shared_ptr listener) + { + auto rmqPushConsumer = std::make_shared(group); + rmqPushConsumer->setNamesrvAddr(resource->getNamesrv()); + rmqPushConsumer->setSessionCredentials(resource->getAccessKey(), resource->getSecretKey(), resource->getAccessChannel()); + rmqPushConsumer->setMessageModel(rocketmq::MessageModel::BROADCASTING); + rmqPushConsumer->setConsumeFromWhere(rocketmq::CONSUME_FROM_LAST_OFFSET); + rmqPushConsumer->setConsumeThreadCount(4); + rmqPushConsumer->subscribe(topic, subExpression); + rmqPushConsumer->registerMessageListener(listener.get()); + rmqPushConsumer->start(); + return rmqPushConsumer; + } + + static std::shared_ptr getRMQPushConsumer(const std::string &topic, const std::string &group, const std::string &subExpression, std::shared_ptr listener) + { + auto rmqPushConsumer = std::make_shared(group); + rmqPushConsumer->setNamesrvAddr(resource->getNamesrv()); + rmqPushConsumer->setSessionCredentials(resource->getAccessKey(), resource->getSecretKey(), resource->getAccessChannel()); + rmqPushConsumer->setConsumeFromWhere(rocketmq::CONSUME_FROM_LAST_OFFSET); + rmqPushConsumer->setConsumeThreadCount(4); + rmqPushConsumer->subscribe(topic, subExpression); + rmqPushConsumer->registerMessageListener(listener.get()); + rmqPushConsumer->start(); + return std::make_shared(rmqPushConsumer, listener); + } + + static std::shared_ptr getRMQPushConsumer(const std::string &topic, const std::string &group, const std::string &subExpression, std::shared_ptr listener) + { + auto rmqPushConsumer = std::make_shared(group); + rmqPushConsumer->setNamesrvAddr(resource->getNamesrv()); + rmqPushConsumer->setSessionCredentials(resource->getAccessKey(), resource->getSecretKey(), resource->getAccessChannel()); + rmqPushConsumer->setConsumeFromWhere(rocketmq::CONSUME_FROM_LAST_OFFSET); + rmqPushConsumer->setConsumeThreadCount(4); + rmqPushConsumer->subscribe(topic, subExpression); + rmqPushConsumer->registerMessageListener(listener.get()); + rmqPushConsumer->start(); + return std::make_shared(rmqPushConsumer, listener); + } + + static std::shared_ptr getPullConsumer(const std::string &topic, const std::string &group) + { + auto rmqPullConsumer = std::make_shared(group); + rmqPullConsumer->setNamesrvAddr(resource->getNamesrv()); + rmqPullConsumer->setInstanceName(group); + rmqPullConsumer->setSessionCredentials(resource->getAccessKey(), resource->getSecretKey(), resource->getAccessChannel()); + rmqPullConsumer->registerMessageQueueListener(topic, NULL); + rmqPullConsumer->start(); + return rmqPullConsumer; + } + + static std::shared_ptr getRMQPullConsumer(const std::string &topic, const std::string &group) + { + auto rmqPullConsumer = std::make_shared(group); + rmqPullConsumer->setNamesrvAddr(resource->getNamesrv()); + rmqPullConsumer->setInstanceName(group); + rmqPullConsumer->setSessionCredentials(resource->getAccessKey(), resource->getSecretKey(), resource->getAccessChannel()); + rmqPullConsumer->registerMessageQueueListener(topic, NULL); + rmqPullConsumer->start(); + return std::make_shared(rmqPullConsumer); + } +}; diff --git a/cpp/rocketmq-client-cpp-tests/cpp4.x/include/factory/MessageFactory.h b/cpp/rocketmq-client-cpp-tests/cpp4.x/include/factory/MessageFactory.h new file mode 100644 index 0000000..2cf312e --- /dev/null +++ b/cpp/rocketmq-client-cpp-tests/cpp4.x/include/factory/MessageFactory.h @@ -0,0 +1,58 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include "resource/Resource.h" +#include "utils/RandomUtils.h" +#include "utils/NameUtils.h" +#include "spdlog/logger.h" +#include "rocketmq/MQMessage.h" +#include + +extern std::shared_ptr multi_logger; +extern std::shared_ptr resource; + +class MessageFactory +{ +public: + static rocketmq::MQMessage getRandomMessgae(const std::string &topic); + + static rocketmq::MQMessage getStringMessage(const std::string &topic, std::string &body); + + static rocketmq::MQMessage getStringMessageByTag(const std::string &topic, const std::string &tags, const std::string &body); + + static rocketmq::MQMessage getRandomMessageByTag(const std::string &topic, std::string &tags); + + static rocketmq::MQMessage buildMessage(const std::string &topic); + + static rocketmq::MQMessage buildMessage(const std::string &topic, const std::string &tags); + + static rocketmq::MQMessage buildMessage(const std::string &topic, const std::string &tags, const std::string &body); + + static rocketmq::MQMessage buildMessageOnlyTag(const std::string &topic, const std::string &tags, const std::string &body); + + static rocketmq::MQMessage buildDelayMessage(const std::string &topic, const std::string &tags, const std::string &body, int delayTimeLevel); + + // static rocketmq::MQMessage buildOrderMessage(const std::string& topic, const std::string& tags, const std::string& body, const std::string& messageGroup); + + static rocketmq::MQMessage buildMessageWithProperty(const std::string &topic, std::map &properties); + + static rocketmq::MQMessage buildMessageWithProperty(const std::string &topic, const std::string &messageBody, std::map &properties); + + // static rocketmq::MQMessage buildOrderMessageWithProperty(const std::string& topic, const std::string& messageBody, const std::string& messageGroup, std::map& properties); + + static rocketmq::MQMessage buildMessageWithProperty(const std::string &topic, const std::string &tag, const std::string &body, std::map &properties, const std::vector &keys); +}; \ No newline at end of file diff --git a/cpp/rocketmq-client-cpp-tests/cpp4.x/include/factory/ProducerFactory.h b/cpp/rocketmq-client-cpp-tests/cpp4.x/include/factory/ProducerFactory.h new file mode 100644 index 0000000..7d42c86 --- /dev/null +++ b/cpp/rocketmq-client-cpp-tests/cpp4.x/include/factory/ProducerFactory.h @@ -0,0 +1,66 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +#include "client/rmq/RMQNormalProducer.h" +#include "resource/Resource.h" +#include "common/MQTransactionListener.h" +#include "rocketmq/DefaultMQProducer.h" +#include "rocketmq/TransactionMQProducer.h" +#include "spdlog/logger.h" +#include + +extern std::shared_ptr multi_logger; +extern std::shared_ptr resource; + +class ProducerFactory +{ +public: + ProducerFactory() = delete; + + static std::shared_ptr getProducer(const std::string &group) + { + auto producer = std::make_shared(group); + producer->setNamesrvAddr(resource->getNamesrv()); + producer->setTcpTransportTryLockTimeout(1000); + producer->setTcpTransportConnectTimeout(400); + producer->start(); + return producer; + } + + static std::shared_ptr getRMQProducer(const std::string &group) + { + auto producer = std::make_shared(group); + producer->setNamesrvAddr(resource->getNamesrv()); + producer->setTcpTransportTryLockTimeout(1000); + producer->setTcpTransportConnectTimeout(400); + producer->start(); + return std::make_shared(producer); + } + + static std::shared_ptr getRMQTransProducer(const std::string &group, rocketmq::TransactionListener *listener) + { + // rocketmq::LocalTransactionState& state + auto transProducer = std::make_shared(group); + transProducer->setNamesrvAddr(resource->getNamesrv()); + transProducer->setTransactionListener(listener); + transProducer->setGroupName(group); + transProducer->setSendMsgTimeout(500); + transProducer->setTcpTransportTryLockTimeout(1000); + transProducer->setTcpTransportConnectTimeout(400); + transProducer->start(); + return std::make_shared(transProducer); + } +}; \ No newline at end of file diff --git a/cpp/rocketmq-client-cpp-tests/cpp4.x/include/frame/BaseOperate.h b/cpp/rocketmq-client-cpp-tests/cpp4.x/include/frame/BaseOperate.h new file mode 100644 index 0000000..732aca0 --- /dev/null +++ b/cpp/rocketmq-client-cpp-tests/cpp4.x/include/frame/BaseOperate.h @@ -0,0 +1,34 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +#pragma once + +#include +#include +#include +#include +#include +#include +#include +#include +#include "spdlog/spdlog.h" +#include "utils/RandomUtils.h" +#include "utils/MQAdminUtils.h" +#include "enums/MessageType.h" + +std::string getTopic(MessageType messageType, const std::string &methodName, const std::string &brokerAddr, const std::string &namesrvAddr, const std::string &cluster); + +std::string getGroupId(const std::string &methodName); diff --git a/cpp/rocketmq-client-cpp-tests/cpp4.x/include/listener/MsgListener.h b/cpp/rocketmq-client-cpp-tests/cpp4.x/include/listener/MsgListener.h new file mode 100644 index 0000000..4096d06 --- /dev/null +++ b/cpp/rocketmq-client-cpp-tests/cpp4.x/include/listener/MsgListener.h @@ -0,0 +1,57 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +#pragma once + +#include "resource/Resource.h" +#include "spdlog/logger.h" +#include "rocketmq/MQMessageExt.h" +#include "rocketmq/MQMessageListener.h" +#include +#include +#include + +extern std::shared_ptr multi_logger; +extern std::shared_ptr resource; + +class MsgListener : public rocketmq::MessageListenerConcurrently +{ +private: + std::atomic count; + std::vector messages; + +public: + MsgListener() { count = 0; }; + + void resetMsgCount() { count = 0; } + + int getMsgCount() const { return count; } + + std::vector getMessages() const { return messages; } + + virtual ~MsgListener() {} + + virtual rocketmq::ConsumeStatus consumeMessage(const std::vector &msgs) override + { + for (const auto &msg : msgs) + { + multi_logger->info("Received message: {}", msg.toString()); + messages.push_back(msg); + count++; + } + return rocketmq::CONSUME_SUCCESS; + } +}; \ No newline at end of file diff --git a/cpp/rocketmq-client-cpp-tests/cpp4.x/include/listener/MsgQueueListener.h b/cpp/rocketmq-client-cpp-tests/cpp4.x/include/listener/MsgQueueListener.h new file mode 100644 index 0000000..1f276a4 --- /dev/null +++ b/cpp/rocketmq-client-cpp-tests/cpp4.x/include/listener/MsgQueueListener.h @@ -0,0 +1,50 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +#pragma once + +#include "resource/Resource.h" +#include "rocketmq/MQueueListener.h" +#include "rocketmq/DefaultMQPullConsumer.h" +#include "rocketmq/MQMessageQueue.h" +#include "spdlog/logger.h" +#include +#include +#include + + +extern std::shared_ptr multi_logger; +extern std::shared_ptr resource; + +class MsgQueueListener : public rocketmq::MessageQueueListener +{ +public: + virtual ~MsgQueueListener() {} + + virtual void messageQueueChanged(const std::string &topic, + const std::vector &mqAll, + const std::vector &mqDivided) override + { + multi_logger->info("Message queue changed for topic: {}", topic); + } + + virtual void messageQueueCreated(const std::string &topic, + const std::vector &mqAll, + const std::vector &mqDivided) override + { + multi_logger->info("Message queue created for topic: {}", topic); + } +} MyMessageQueueListener; diff --git a/cpp/rocketmq-client-cpp-tests/cpp4.x/include/listener/rmq/RMQNormalListener.h b/cpp/rocketmq-client-cpp-tests/cpp4.x/include/listener/rmq/RMQNormalListener.h new file mode 100644 index 0000000..16e639d --- /dev/null +++ b/cpp/rocketmq-client-cpp-tests/cpp4.x/include/listener/rmq/RMQNormalListener.h @@ -0,0 +1,126 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +#pragma once + +#include "common/MQCollector.h" +#include "common/MQMsg.h" +#include "common/MQMsg.h" +#include "resource/Resource.h" +#include "utils/RandomUtils.h" +#include "spdlog/logger.h" +#include "rocketmq/MQMessageListener.h" +#include +#include +#include +#include + +extern std::shared_ptr multi_logger; +extern std::shared_ptr resource; + +class RMQNormalListener : public MQCollector, public rocketmq::MessageListenerConcurrently +{ +private: + rocketmq::ConsumeStatus consumeStatus = rocketmq::CONSUME_SUCCESS; + std::atomic msgIndex{0}; + std::string listenerName; + int reconsumeTimes = 0; + int expectedMsgCount = 0; + bool isEvenNumber = false; + int workTime = 0; + +public: + ~RMQNormalListener() {} + + RMQNormalListener() + { + this->listenerName = RandomUtils::getStringByUUID(); + multi_logger->info("start listener: {}", listenerName); + } + + RMQNormalListener(int reconsumeTimes, rocketmq::ConsumeStatus consumeStatus) + { + this->reconsumeTimes = reconsumeTimes; + this->consumeStatus = consumeStatus; + this->listenerName = RandomUtils::getStringByUUID(); + multi_logger->info("start listener: {}", listenerName); + } + + RMQNormalListener(int reconsumeTimes, int workTime) + { + this->reconsumeTimes = reconsumeTimes; + this->workTime = workTime; + this->listenerName = RandomUtils::getStringByUUID(); + multi_logger->info("start listener: {}", listenerName); + } + + RMQNormalListener(std::string listenerName) + { + this->listenerName = listenerName; + multi_logger->info("start listener: {}", listenerName); + } + + RMQNormalListener(rocketmq::ConsumeStatus consumeStatus) + { + this->consumeStatus = consumeStatus; + this->listenerName = RandomUtils::getStringByUUID(); + multi_logger->info("start listener: {}", listenerName); + } + + // Overriding the consume method + virtual rocketmq::ConsumeStatus consumeMessage(const std::vector &msgs) override + { + rocketmq::ConsumeStatus result = consumeStatus; + + for (const auto &msg : msgs) + { + if (reconsumeTimes == 0 || reconsumeTimes == msg.getReconsumeTimes() - 1) + { + dequeueMessages->addData(MQMsg(msg)); + result = consumeStatus; + } + else + { + // if (isEvenNumber) { + // int body = atoi(message.getBody().c_str()); + // if (body % 2 != 0) { + // dequeueMessages->addData(message.getBody()); + // result = rocketmq::CONSUME_SUCCESS; + // } + // } + std::this_thread::sleep_for(std::chrono::milliseconds(workTime)); + } + std::string consumeStatStr; + if (result == rocketmq::RECONSUME_LATER) + { + consumeStatStr = "RECONSUME_LATER"; + } + else + { + consumeStatStr = "CONSUME_SUCCESS"; + } + std::string propertiesStr("{"); + for (const auto &pair : msg.getProperties()) + { + propertiesStr += pair.first + ":" + pair.second + ";"; + } + propertiesStr += "}"; + multi_logger->info("{} - MessageId:{}, body:{}, tag:{}, key:{}, recvIndex:{}, property:{}, action:{}", listenerName, msg.getMsgId(), msg.getBody(), msg.getTags(), msg.getKeys(), std::to_string(msgIndex.fetch_add(1, std::memory_order_relaxed)), propertiesStr, consumeStatStr); + } + + return result; + } +}; \ No newline at end of file diff --git a/cpp/rocketmq-client-cpp-tests/cpp4.x/include/listener/rmq/RMQOrderListener.h b/cpp/rocketmq-client-cpp-tests/cpp4.x/include/listener/rmq/RMQOrderListener.h new file mode 100644 index 0000000..da866c1 --- /dev/null +++ b/cpp/rocketmq-client-cpp-tests/cpp4.x/include/listener/rmq/RMQOrderListener.h @@ -0,0 +1,126 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +#pragma once + +#include "common/MQCollector.h" +#include "common/MQMsg.h" +#include "common/MQMsg.h" +#include "resource/Resource.h" +#include "utils/RandomUtils.h" +#include "rocketmq/MQMessageListener.h" +#include "spdlog/logger.h" +#include +#include +#include +#include + +extern std::shared_ptr multi_logger; +extern std::shared_ptr resource; + +class RMQOrderListener : public MQCollector, public rocketmq::MessageListenerOrderly +{ +private: + rocketmq::ConsumeStatus consumeStatus = rocketmq::CONSUME_SUCCESS; + std::atomic msgIndex{0}; + std::string listenerName; + int reconsumeTimes = 0; + int expectedMsgCount = 0; + bool isEvenNumber = false; + int workTime = 0; + +public: + ~RMQOrderListener() {} + + RMQOrderListener() + { + this->listenerName = RandomUtils::getStringByUUID(); + multi_logger->info("start listener: {}", listenerName); + } + + RMQOrderListener(int reconsumeTimes, rocketmq::ConsumeStatus consumeStatus) + { + this->reconsumeTimes = reconsumeTimes; + this->consumeStatus = consumeStatus; + this->listenerName = RandomUtils::getStringByUUID(); + multi_logger->info("start listener: {}", listenerName); + } + + RMQOrderListener(int reconsumeTimes, int workTime) + { + this->reconsumeTimes = reconsumeTimes; + this->workTime = workTime; + this->listenerName = RandomUtils::getStringByUUID(); + multi_logger->info("start listener: {}", listenerName); + } + + RMQOrderListener(std::string listenerName) + { + this->listenerName = listenerName; + multi_logger->info("start listener: {}", listenerName); + } + + RMQOrderListener(rocketmq::ConsumeStatus consumeStatus) + { + this->consumeStatus = consumeStatus; + this->listenerName = RandomUtils::getStringByUUID(); + multi_logger->info("start listener: {}", listenerName); + } + + // Overriding the consume method + virtual rocketmq::ConsumeStatus consumeMessage(const std::vector &msgs) override + { + rocketmq::ConsumeStatus result = consumeStatus; + + for (const auto &msg : msgs) + { + if (reconsumeTimes == 0 || reconsumeTimes == msg.getReconsumeTimes() - 1) + { + dequeueMessages->addData(MQMsg(msg)); + result = consumeStatus; + } + else + { + // if (isEvenNumber) { + // int body = atoi(message.getBody().c_str()); + // if (body % 2 != 0) { + // dequeueMessages->addData(message.getBody()); + // result = rocketmq::CONSUME_SUCCESS; + // } + // } + std::this_thread::sleep_for(std::chrono::milliseconds(workTime)); + } + std::string consumeStatStr; + if (result == rocketmq::RECONSUME_LATER) + { + consumeStatStr = "RECONSUME_LATER"; + } + else + { + consumeStatStr = "CONSUME_SUCCESS"; + } + std::string propertiesStr("{"); + for (const auto &pair : msg.getProperties()) + { + propertiesStr += pair.first + ":" + pair.second + ";"; + } + propertiesStr += "}"; + multi_logger->info("{} - MessageId:{}, body:{}, tag:{}, key:{}, recvIndex:{}, property:{}, action:{}", listenerName, msg.getMsgId(), msg.getBody(), msg.getTags(), msg.getKeys(), std::to_string(msgIndex.fetch_add(1, std::memory_order_relaxed)), propertiesStr, consumeStatStr); + } + + return result; + } +}; \ No newline at end of file diff --git a/cpp/rocketmq-client-cpp-tests/cpp4.x/include/resource/Account.h b/cpp/rocketmq-client-cpp-tests/cpp4.x/include/resource/Account.h new file mode 100644 index 0000000..8888f7a --- /dev/null +++ b/cpp/rocketmq-client-cpp-tests/cpp4.x/include/resource/Account.h @@ -0,0 +1,97 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +#pragma once +#include + +class Account +{ +private: + // aliyun AccessKey + std::string accessKey; + // aliyun SecretKey + std::string secretKey; + // instanceUserName + std::string instanceUserName; + // instancePassword + std::string instancePassword; + // endpoint + std::string endpoint; + // instanceId + std::string instanceId; + // consoleEndpoint + std::string consoleEndpoint; + // region + std::string regionId; + // Account User ID + std::string userId; + // Account User Name + std::string accountName; + +public: + Account() {} + Account(std::string endpoint) : endpoint(endpoint) {} + + Account(std::string instanceUserName, std::string instancePassword) + : instanceUserName(instanceUserName), + instancePassword(instancePassword) {} + + Account(std::string accessKey, std::string secretKey, std::string endpoint) + : accessKey(accessKey), + secretKey(secretKey), + endpoint(endpoint) {} + + Account(std::string accessKey, std::string secretKey, std::string endpoint, + std::string instanceId, std::string consoleEndpoint, std::string regionId, + std::string userId) + : accessKey(accessKey), + secretKey(secretKey), + endpoint(endpoint), + instanceId(instanceId), + consoleEndpoint(consoleEndpoint), + regionId(regionId), + userId(userId) {} + + std::string getAccessKey() const { return accessKey; } + void setAccessKey(const std::string &accessKey) { accessKey = accessKey; } + + std::string getSecretKey() const { return secretKey; } + void setSecretKey(const std::string &secretKey) { secretKey = secretKey; } + + std::string getInstanceUserName() const { return instanceUserName; } + void setInstanceUserName(const std::string &instanceUserName) { instanceUserName = instanceUserName; } + + std::string getInstancePassword() const { return instancePassword; } + void setInstancePassword(const std::string &instancePassword) { instancePassword = instancePassword; } + + std::string getEndpoint() const { return endpoint; } + void setEndpoint(const std::string &endpoint) { endpoint = endpoint; } + + std::string getInstanceId() const { return instanceId; } + void setInstanceId(const std::string &instanceId) { instanceId = instanceId; } + + std::string getConsoleEndpoint() const { return consoleEndpoint; } + void setConsoleEndpoint(const std::string &consoleEndpoint) { consoleEndpoint = consoleEndpoint; } + + std::string getRegionId() const { return regionId; } + void setRegionId(const std::string ®ionId) { regionId = regionId; } + + std::string getUserId() const { return userId; } + void setUserId(const std::string &userId) { userId = userId; } + + std::string getAccountName() const { return accountName; } + void setAccountName(const std::string &accountName) { accountName = accountName; } +}; \ No newline at end of file diff --git a/cpp/rocketmq-client-cpp-tests/cpp4.x/include/resource/Resource.h b/cpp/rocketmq-client-cpp-tests/cpp4.x/include/resource/Resource.h new file mode 100644 index 0000000..0f7a26b --- /dev/null +++ b/cpp/rocketmq-client-cpp-tests/cpp4.x/include/resource/Resource.h @@ -0,0 +1,94 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +#pragma once +#include + +class Resource +{ +private: + std::string namesrv; + std::string brokerAddr; + std::string cluster; + std::string accessKey; + std::string secretKey; + std::string accessChannel; + +public: + Resource() {} + + Resource(std::string namesrv, std::string brokerAddr, std::string cluster, std::string accessKey, std::string secretKey, std::string accessChannel) + : namesrv(namesrv), + brokerAddr(brokerAddr), + cluster(cluster), + accessKey(accessKey), + secretKey(secretKey), + accessChannel(accessChannel) + { + } + + // set + void setNamesrv(std::string namesrv) + { + this->namesrv = namesrv; + } + void setBrokerAddr(std::string brokerAddr) + { + this->brokerAddr = brokerAddr; + } + void setCluster(std::string cluster) + { + this->cluster = cluster; + } + void setAccessKey(std::string accessKey) + { + this->accessKey = accessKey; + } + void setSecretKey(std::string secretKey) + { + this->secretKey = secretKey; + } + void setAccessChannel(std::string accessChannel) + { + this->accessChannel = accessChannel; + } + + // get + std::string getNamesrv() + { + return namesrv; + } + std::string getBrokerAddr() + { + return brokerAddr; + } + std::string getCluster() + { + return cluster; + } + std::string getAccessKey() + { + return accessKey; + } + std::string getSecretKey() + { + return secretKey; + } + std::string getAccessChannel() + { + return accessChannel; + } +}; \ No newline at end of file diff --git a/cpp/rocketmq-client-cpp-tests/cpp4.x/include/utils/InitResourceUtils.h b/cpp/rocketmq-client-cpp-tests/cpp4.x/include/utils/InitResourceUtils.h new file mode 100644 index 0000000..5124544 --- /dev/null +++ b/cpp/rocketmq-client-cpp-tests/cpp4.x/include/utils/InitResourceUtils.h @@ -0,0 +1,22 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +#pragma once + +#include +#include "resource/Resource.h" + +void initResource(std::shared_ptr resource); \ No newline at end of file diff --git a/cpp/rocketmq-client-cpp-tests/cpp4.x/include/utils/MQAdminUtils.h b/cpp/rocketmq-client-cpp-tests/cpp4.x/include/utils/MQAdminUtils.h new file mode 100644 index 0000000..71e852c --- /dev/null +++ b/cpp/rocketmq-client-cpp-tests/cpp4.x/include/utils/MQAdminUtils.h @@ -0,0 +1,42 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +#pragma once + +#include +#include +#include +#include + +class MQAdminUtils +{ +public: + static std::string getRootPath(); + + static std::string executeShellCommand(const std::string &command); + + static std::string createTopic(const std::string &topicName, const std::string &brokerAddr, const std::string &clusterName, const std::string &nameserver); + + static std::string createDelayTopic(const std::string &topicName, const std::string &brokerAddr, const std::string &clusterName, const std::string &nameserver); + + static std::string createFIFOTopic(const std::string &topicName, const std::string &brokerAddr, const std::string &clusterName, const std::string &nameserver); + + static std::string createTransactionTopic(const std::string &topicName, const std::string &brokerAddr, const std::string &clusterName, const std::string &nameserver); + + static std::string createOrderlyConsumerGroup(const std::string &consumerGroup, const std::string &brokerAddr, const std::string &clusterName, const std::string &nameserver); + + static std::string clusterList(const std::string &nameserver); +}; \ No newline at end of file diff --git a/cpp/rocketmq-client-cpp-tests/cpp4.x/include/utils/NameUtils.h b/cpp/rocketmq-client-cpp-tests/cpp4.x/include/utils/NameUtils.h new file mode 100644 index 0000000..783d90f --- /dev/null +++ b/cpp/rocketmq-client-cpp-tests/cpp4.x/include/utils/NameUtils.h @@ -0,0 +1,46 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +#pragma once + +#include +#include +#include +#include +#include "openssl/md5.h" + +class NameUtils +{ +private: + static std::unordered_map alreadyUsed; + static std::mutex mtx; + + static std::string generateRandomAlphanumeric(int length); + static std::string generateMD5Sum(const std::string &input); + +public: + NameUtils() = delete; + static std::string getTopicName(); + static std::string getGroupName(); + static std::string getTagName(); + static std::string getRandomTopicName(); + static std::string getTopicName(const std::string &messageType, const std::string &className, const std::string &methodName); + static std::string getRandomTopicName(const std::string &suffix); + static std::string getRandomGroupName(); + static std::string getGroupName(const std::string &className, const std::string &methodName); + static std::string getRandomGroupName(const std::string &suffix); + static std::string getRandomTagName(); +}; \ No newline at end of file diff --git a/cpp/rocketmq-client-cpp-tests/cpp4.x/include/utils/RandomUtils.h b/cpp/rocketmq-client-cpp-tests/cpp4.x/include/utils/RandomUtils.h new file mode 100644 index 0000000..be55fae --- /dev/null +++ b/cpp/rocketmq-client-cpp-tests/cpp4.x/include/utils/RandomUtils.h @@ -0,0 +1,46 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +#pragma once + +#include +#include +#include + +class RandomUtils +{ + +public: + RandomUtils() = delete; + + static std::string getStringByUUID(); + static std::string getChineseWord(int len); + static std::string getStringWithNumber(int n); + static std::string getStringWithCharacter(int n); + static int getIntegerBetween(int n, int m); + static int getIntegerMoreThanZero(); + static std::string randomAlphabetic(int length); + +private: + static constexpr int UNICODE_START = 0x4E00; + static constexpr int UNICODE_END = 0x9FA0; + static std::random_device rd; + static std::mt19937 rng; + + static std::string getString(int n, int arg[], int size); + static char getChar(int arg[], int size); + static char getChineseChar(int codePoint); +}; \ No newline at end of file diff --git a/cpp/rocketmq-client-cpp-tests/cpp4.x/include/utils/SimpleConcurrentHashMapUtils.h b/cpp/rocketmq-client-cpp-tests/cpp4.x/include/utils/SimpleConcurrentHashMapUtils.h new file mode 100644 index 0000000..6aefec2 --- /dev/null +++ b/cpp/rocketmq-client-cpp-tests/cpp4.x/include/utils/SimpleConcurrentHashMapUtils.h @@ -0,0 +1,126 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +#pragma once + +#include +#include +#include +#include +#include +#include + +template +class SimpleConcurrentHashMap +{ +private: + std::unordered_map map_; + mutable std::shared_mutex mutex_; + +public: + size_t size() const + { + std::lock_guard lock(mutex_); + return map_.size(); + } + + void insert(const Key &key, const Value &value) + { + std::lock_guard lock(mutex_); + map_[key] = value; + } + + bool contains(const Key &key) const + { + std::shared_lock lock(mutex_); + return (map_.find(key) != map_.end()); + } + + void update(const Key &key, const Value &value) + { + std::lock_guard lock(mutex_); + map_[key] = value; + } + + std::vector getAllValues() const + { + std::shared_lock lock(mutex_); + std::vector values; + values.reserve(map_.size()); + for (const auto &entry : map_) + { + values.push_back(entry.second); + } + return values; + } + + Value &operator[](const Key &key) + { + std::lock_guard lock(mutex_); + return map_[key]; + } +}; + +template +class SimpleConcurrentHashMap> +{ +private: + std::unordered_map map_; + mutable std::shared_mutex mutex_; + +public: + size_t size() const + { + std::lock_guard lock(mutex_); + return map_.size(); + } + + void insert(const Key &key, const Value &value) + { + std::lock_guard lock(mutex_); + map_[key] = value; + } + + bool contains(const Key &key) const + { + std::shared_lock lock(mutex_); + return (map_.find(key) != map_.end()); + } + + void update(const Key &key, const Value &value) + { + std::lock_guard lock(mutex_); + map_[key] = value; + } + + std::vector getAllValues() const + { + std::shared_lock lock(mutex_); + std::vector values; + values.reserve(map_.size()); + for (const auto &entry : map_) + { + values.push_back(entry.second); + } + return values; + } + + Value &operator[](const Key &key) + { + std::lock_guard lock(mutex_); + return map_[key]; + } +}; diff --git a/cpp/rocketmq-client-cpp-tests/cpp4.x/include/utils/SimpleConcurrentVectorUtils.h b/cpp/rocketmq-client-cpp-tests/cpp4.x/include/utils/SimpleConcurrentVectorUtils.h new file mode 100644 index 0000000..1dfd730 --- /dev/null +++ b/cpp/rocketmq-client-cpp-tests/cpp4.x/include/utils/SimpleConcurrentVectorUtils.h @@ -0,0 +1,69 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +#pragma once + +#include +#include +#include +#include +#include +#include + +template +class SimpleConcurrentVector +{ +private: + std::vector v_; + mutable std::shared_mutex mutex_; + +public: + size_t size() const + { + std::lock_guard lock(mutex_); + return v_.size(); + } + + void push_back(const Value &value) + { + std::lock_guard lock(mutex_); + v_.push_back(value); + } + + void insert(const size_t &index, const Value &value) + { + std::lock_guard lock(mutex_); + v_.insert(v_.begin() + index, value); + } + + void erase(const size_t &index) + { + std::lock_guard lock(mutex_); + v_.erase(v_.begin() + index); + } + + std::vector getCopy() const + { + std::shared_lock lock(mutex_); + return v_; + } + + Value &operator[](const size_t &index) + { + std::shared_lock lock(mutex_); + return v_[index]; + } +}; diff --git a/cpp/rocketmq-client-cpp-tests/cpp4.x/include/utils/VerifyUtils.h b/cpp/rocketmq-client-cpp-tests/cpp4.x/include/utils/VerifyUtils.h new file mode 100644 index 0000000..8731b55 --- /dev/null +++ b/cpp/rocketmq-client-cpp-tests/cpp4.x/include/utils/VerifyUtils.h @@ -0,0 +1,58 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +#pragma once +#include "utils/data/collect/DataCollector.h" +#include "client/rmq/RMQNormalProducer.h" +#include "common/MQMsg.h" +#include "rocketmq/DefaultMQPullConsumer.h" +#include +#include +#include +#include + +class VerifyUtils +{ +private: + const static int TIMEOUT = 90; + const static int defaultSimpleThreadNums = 4; + static std::atomic receivedIndex; + static std::unordered_map checkDelay(DataCollector &dequeueMessages, int delayLevel); + static bool checkOrder(DataCollector &dequeueMessages); + static std::vector msgs; + static std::vector waitForMessageConsume(DataCollector &enqueueMessages, DataCollector &dequeueMessages, long long timeoutMills, int consumedTimes); + static std::vector waitForMessageConsume(DataCollector &enqueueMessages, DataCollector &dequeueMessages, long long timeoutMills, int consumedTimes); + +public: + VerifyUtils() = delete; + static long long getDelayTime(int delayLevel); + static bool checkOrderMessage(std::unordered_map> &receivedMessage); + static bool tryReceiveOnce(const std::string &topic, const std::string &subExpression, std::shared_ptr pullConsumer); + static std::vector fetchMessages(std::shared_ptr pullConsumer, const std::string &topic); + static bool verifyNormalMessage(DataCollector &enqueueMessages, DataCollector &dequeueMessages); + static bool verifyNormalMessage(DataCollector &enqueueMessages, DataCollector &dequeueMessages); + static bool verifyNormalMessage(DataCollector &enqueueMessages, DataCollector &dequeueMessages, std::unordered_set &unconsumedMsgIds); + static bool verifyNormalMessageWithUserProperties(DataCollector &enqueueMessages, DataCollector &dequeueMessages, std::map &props, int expectedUnrecvMsgNum); + static bool verifyDelayMessage(DataCollector &enqueueMessages, DataCollector &dequeueMessages, int delayLevel); + static bool verifyOrderMessage(DataCollector &enqueueMessages, DataCollector &dequeueMessages); + static bool waitReceiveThenAck(std::shared_ptr producer, std::shared_ptr pullConsumer, std::string &topic, std::string &tag, int maxMessageNum); + static bool waitFIFOParamReceiveThenNAck(std::shared_ptr producer, std::shared_ptr pullConsumer, std::string &topic, std::string &tag, int maxMessageNum); + static bool waitFIFOParamReceiveThenAckExceptedLast(std::shared_ptr producer, std::shared_ptr pullConsumer, std::string &topic, std::string &tag, int maxMessageNum = 3); + static bool waitFIFOReceiveThenAck(std::shared_ptr producer, std::shared_ptr pullConsumer, std::string &topic, std::string &tag, int maxMessageNum); + static bool waitAckExceptionReReceiveAck(std::shared_ptr producer, std::shared_ptr pullConsumer, std::string &topic, std::string &tag, int maxMessageNum); + static bool waitReceiveMaxsizeSync(std::shared_ptr producer, std::shared_ptr pullConsumer, std::string &topic, std::string &tag, int maxMessageNum); + static bool waitReceiveMultiNack(std::shared_ptr producer, std::shared_ptr pullConsumer, std::string &topic, std::string &tag, int maxMessageNum); +}; \ No newline at end of file diff --git a/cpp/rocketmq-client-cpp-tests/cpp4.x/include/utils/data/collect/DataCollector.h b/cpp/rocketmq-client-cpp-tests/cpp4.x/include/utils/data/collect/DataCollector.h new file mode 100644 index 0000000..6b0f8c6 --- /dev/null +++ b/cpp/rocketmq-client-cpp-tests/cpp4.x/include/utils/data/collect/DataCollector.h @@ -0,0 +1,37 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +#pragma once +#include +#include +#include + +template +class DataCollector +{ +public: + virtual void resetData() = 0; + virtual std::vector getAllData() = 0; + virtual std::set getAllDataWithoutDuplicate() = 0; + virtual void addData(const T &data) = 0; + virtual std::size_t getDataSizeWithoutDuplicate() = 0; + virtual std::size_t getDataSize() = 0; + virtual bool isRepeatedData(const T &data) = 0; + virtual int getRepeatedTimeForData(const T &data) = 0; + virtual void removeData(const T &data) = 0; + virtual void lockIncrement() = 0; + virtual void unlockIncrement() = 0; +}; \ No newline at end of file diff --git a/cpp/rocketmq-client-cpp-tests/cpp4.x/include/utils/data/collect/DataCollectorManager.h b/cpp/rocketmq-client-cpp-tests/cpp4.x/include/utils/data/collect/DataCollectorManager.h new file mode 100644 index 0000000..7ca96b5 --- /dev/null +++ b/cpp/rocketmq-client-cpp-tests/cpp4.x/include/utils/data/collect/DataCollectorManager.h @@ -0,0 +1,125 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +#pragma once +#include +#include +#include +#include +#include "utils/data/collect/DataCollector.h" +#include "utils/data/collect/impl/ListDataCollector.h" +#include "utils/data/collect/impl/MapDataCollector.h" + +template +class DataCollectorManager +{ +private: + static std::unique_ptr> instance; + std::unordered_map>> collectMap; + static std::mutex mtx; + + DataCollectorManager() {} + +public: + static DataCollectorManager &getInstance() + { + if (instance == nullptr) + { + std::lock_guard lock(mtx); + if (instance == nullptr) + instance = std::unique_ptr>(new DataCollectorManager()); + } + return *instance; + } + + DataCollector &fetchDataCollector(const std::string &key) + { + std::lock_guard lock(mtx); + + if (collectMap.find(key) == collectMap.end()) + { + collectMap[key] = std::make_unique>(); + } + + return *collectMap[key]; + } + + DataCollector &fetchMapDataCollector(const std::string &key) + { + std::lock_guard lock(mtx); + + if (collectMap.find(key) == collectMap.end() || + dynamic_cast *>(collectMap[key].get()) == nullptr) + { + collectMap[key] = std::make_unique>(); + } + + return *collectMap[key]; + } + + DataCollector &fetchListDataCollector(const std::string &key) + { + std::lock_guard lock(mtx); + + if (collectMap.find(key) == collectMap.end() || + dynamic_cast *>(collectMap[key].get()) == nullptr) + { + collectMap[key] = std::make_unique>(); + } + + return *collectMap[key]; + } + + void resetDataCollect(const std::string &key) + { + std::lock_guard lock(mtx); + + if (collectMap.find(key) != collectMap.end()) + { + collectMap[key]->resetData(); + } + } + + void resetAll() + { + std::lock_guard lock(mtx); + + for (auto &pair : collectMap) + { + pair.second->resetData(); + } + } + + void removeDataCollect(const std::string &key) + { + std::lock_guard lock(mtx); + + collectMap.erase(key); + } + + void removeAll() + { + std::lock_guard lock(mtx); + + collectMap.clear(); + } +}; + +template +std::mutex DataCollectorManager::mtx; + +template +std::unique_ptr> DataCollectorManager::instance = nullptr; \ No newline at end of file diff --git a/cpp/rocketmq-client-cpp-tests/cpp4.x/include/utils/data/collect/impl/ListDataCollector.h b/cpp/rocketmq-client-cpp-tests/cpp4.x/include/utils/data/collect/impl/ListDataCollector.h new file mode 100644 index 0000000..8a094f4 --- /dev/null +++ b/cpp/rocketmq-client-cpp-tests/cpp4.x/include/utils/data/collect/impl/ListDataCollector.h @@ -0,0 +1,117 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +#pragma once +#include +#include +#include +#include +#include +#include "utils/data/collect/DataCollector.h" + +template +class ListDataCollectorImpl : public DataCollector +{ +private: + std::vector data; + bool lock = false; + std::mutex mtx; + +public: + ListDataCollectorImpl() {} + + ListDataCollectorImpl(const std::vector &initial_data) + { + for (const std::string &item : initial_data) + { + addData(item); + } + } + + void resetData() override + { + std::lock_guard lock_this(mtx); + data.clear(); + unlockIncrement(); + } + + std::vector getAllData() override + { + std::lock_guard lock_this(mtx); + return std::vector(data.begin(), data.end()); + } + + std::set getAllDataWithoutDuplicate() override + { + std::lock_guard lock_this(mtx); + return std::set(data.begin(), data.end()); + } + + void addData(const T &new_data) override + { + std::lock_guard lock_this(mtx); + if (!lock) + { + data.push_back(new_data); + } + } + + size_t getDataSizeWithoutDuplicate() override + { + std::lock_guard lock_thislock(mtx); + return getAllDataWithoutDuplicate().size(); + } + + size_t getDataSize() override + { + std::lock_guard lock_this(mtx); + return data.size(); + } + + bool isRepeatedData(const T &data) override + { + std::lock_guard lock_this(mtx); + return std::count(this->data.begin(), this->data.end(), data) > 1; + } + + int getRepeatedTimeForData(const T &data) override + { + std::lock_guard lock_this(mtx); + return std::count(this->data.begin(), this->data.end(), data); + } + + void removeData(const T &data) override + { + std::lock_guard lock_this(mtx); + this->data.erase(std::remove(this->data.begin(), this->data.end(), data), this->data.end()); + } + + void lockIncrement() override + { + lock = true; + } + + void unlockIncrement() override + { + lock = false; + } + + std::string getFirstElement() + { + std::lock_guard lock_this(mtx); + return data.front(); + } +}; \ No newline at end of file diff --git a/cpp/rocketmq-client-cpp-tests/cpp4.x/include/utils/data/collect/impl/MapDataCollector.h b/cpp/rocketmq-client-cpp-tests/cpp4.x/include/utils/data/collect/impl/MapDataCollector.h new file mode 100644 index 0000000..4bf655b --- /dev/null +++ b/cpp/rocketmq-client-cpp-tests/cpp4.x/include/utils/data/collect/impl/MapDataCollector.h @@ -0,0 +1,137 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +#pragma once +#include +#include +#include +#include +#include +#include +#include +#include "utils/data/collect/DataCollector.h" + +template +class MapDataCollectorImpl : public DataCollector +{ + std::map> data; + bool lock = false; + mutable std::mutex mtx; + +public: + MapDataCollectorImpl() {} + + MapDataCollectorImpl(const std::vector &initial_data) + { + for (const std::string &item : initial_data) + { + addData(item); + } + } + + void resetData() override + { + std::lock_guard lock_this(mtx); + data.clear(); + unlockIncrement(); + } + + std::vector getAllData() override + { + std::lock_guard lock_this(mtx); + std::vector result; + for (const auto &pair : data) + { + for (int i = 0; i < pair.second; i++) + { + result.push_back(pair.first); + } + } + return result; + } + + size_t getDataSizeWithoutDuplicate() override + { + std::lock_guard lock_this(mtx); + return data.size(); + } + + void addData(const T &new_data) override + { + std::lock_guard lock_this(mtx); + if (!lock) + { + if (data.find(new_data) == data.end()) + { + data[new_data] = 1; + } + else + { + data[new_data]++; + } + } + } + + size_t getDataSize() override + { + std::lock_guard lock_this(mtx); + size_t count = 0; + for (const auto &pair : data) + { + count += pair.second; + } + return count; + } + + bool isRepeatedData(const T &data) override + { + std::lock_guard lock_this(mtx); + return this->data[data] > 1; + } + + std::set getAllDataWithoutDuplicate() override + { + std::lock_guard lock_this(mtx); + std::set result; + for (const auto &pair : data) + { + result.insert(pair.first); + } + return result; + } + + int getRepeatedTimeForData(const T &data) override + { + std::lock_guard lock_this(mtx); + return this->data[data]; + } + + void removeData(const T &data) override + { + std::lock_guard lock_this(mtx); + this->data.erase(data); + } + + void lockIncrement() override + { + lock = true; + } + + void unlockIncrement() override + { + lock = false; + } +}; \ No newline at end of file diff --git a/cpp/rocketmq-client-cpp-tests/cpp4.x/main.cpp b/cpp/rocketmq-client-cpp-tests/cpp4.x/main.cpp new file mode 100644 index 0000000..04d2533 --- /dev/null +++ b/cpp/rocketmq-client-cpp-tests/cpp4.x/main.cpp @@ -0,0 +1,93 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include "resource/Resource.h" +#include "utils/InitResourceUtils.h" + +std::shared_ptr multi_logger; +std::shared_ptr resource; + +int main(int argc, char *argv[]) +{ + // Register console and file log output + auto console_sink = std::make_shared(); + auto file_sink = std::make_shared("logs/output.log", true); + spdlog::sinks_init_list sink_list = {console_sink, file_sink}; + multi_logger = std::make_shared("multi", sink_list.begin(), sink_list.end()); + multi_logger->set_level(spdlog::level::trace); + spdlog::register_logger(multi_logger); + + // Read configuration file + resource = std::make_shared(); + initResource(resource); + + int sockfd = socket(AF_INET, SOCK_STREAM, 0); + if (sockfd == -1) + { + multi_logger->error("Unable to create socket"); + return 1; + } + + // Set target IP address and port number + std::string target_ip = resource->getNamesrv(); + std::string ip; + int port = 9876; + size_t colonPos = target_ip.find(":"); + if (colonPos != std::string::npos) + { + ip = target_ip.substr(0, colonPos); + port = std::stoi(target_ip.substr(colonPos + 1, target_ip.size())); + } + else + { + multi_logger->error("Unable to find port number in namesrv address"); + close(sockfd); + return 1; + } + + // Set the target address structure + sockaddr_in serverAddress{}; + serverAddress.sin_family = AF_INET; + serverAddress.sin_addr.s_addr = inet_addr(ip.c_str()); + serverAddress.sin_port = htons(port); + + // try to connect + int connectResult = connect(sockfd, (struct sockaddr *)&serverAddress, sizeof(serverAddress)); + if (connectResult == -1) + { + multi_logger->error("Unable to connect to namesrv"); + close(sockfd); + return 1; + } + + multi_logger->info("Connect to namesrv successfully"); + close(sockfd); + + // Start-up test + testing::InitGoogleTest(&argc, argv); + return RUN_ALL_TESTS(); +} \ No newline at end of file diff --git a/cpp/rocketmq-client-cpp-tests/cpp4.x/src/CMakeLists.txt b/cpp/rocketmq-client-cpp-tests/cpp4.x/src/CMakeLists.txt new file mode 100644 index 0000000..f943566 --- /dev/null +++ b/cpp/rocketmq-client-cpp-tests/cpp4.x/src/CMakeLists.txt @@ -0,0 +1,20 @@ +# Licensed to the Apache Software Foundation (ASF) under one or more +# contributor license agreements. See the NOTICE file distributed with +# this work for additional information regarding copyright ownership. +# The ASF licenses this file to You under the Apache License, Version 2.0 +# (the "License"); you may not use this file except in compliance with +# the License. You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +add_subdirectory(frame) +add_subdirectory(utils) +add_subdirectory(enums) +add_subdirectory(factory) + +set(SOURCE_FILES ${SOURCE_FILES} PARENT_SCOPE) \ No newline at end of file diff --git a/cpp/rocketmq-client-cpp-tests/cpp4.x/src/enums/CMakeLists.txt b/cpp/rocketmq-client-cpp-tests/cpp4.x/src/enums/CMakeLists.txt new file mode 100644 index 0000000..7874dab --- /dev/null +++ b/cpp/rocketmq-client-cpp-tests/cpp4.x/src/enums/CMakeLists.txt @@ -0,0 +1,20 @@ +# Licensed to the Apache Software Foundation (ASF) under one or more +# contributor license agreements. See the NOTICE file distributed with +# this work for additional information regarding copyright ownership. +# The ASF licenses this file to You under the Apache License, Version 2.0 +# (the "License"); you may not use this file except in compliance with +# the License. You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +set(SRC_FILES + MessageType.cpp +) + +add_library(enums STATIC ${SRC_FILES}) +target_include_directories(enums PUBLIC ${CMAKE_SOURCE_DIR}/include) \ No newline at end of file diff --git a/cpp/rocketmq-client-cpp-tests/cpp4.x/src/enums/MessageType.cpp b/cpp/rocketmq-client-cpp-tests/cpp4.x/src/enums/MessageType.cpp new file mode 100644 index 0000000..57b468b --- /dev/null +++ b/cpp/rocketmq-client-cpp-tests/cpp4.x/src/enums/MessageType.cpp @@ -0,0 +1,24 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +#include "enums/MessageType.h" + +std::map MessageTypeToString = { + {MessageType::UNSPECIFIED, "UNSPECIFIED"}, + {MessageType::NORMAL, "NORMAL"}, + {MessageType::FIFO, "FIFO"}, + {MessageType::DELAY, "DELAY"}, + {MessageType::TRANSACTION, "TRANSACTION"}}; \ No newline at end of file diff --git a/cpp/rocketmq-client-cpp-tests/cpp4.x/src/factory/CMakeLists.txt b/cpp/rocketmq-client-cpp-tests/cpp4.x/src/factory/CMakeLists.txt new file mode 100644 index 0000000..51a2d80 --- /dev/null +++ b/cpp/rocketmq-client-cpp-tests/cpp4.x/src/factory/CMakeLists.txt @@ -0,0 +1,21 @@ +# Licensed to the Apache Software Foundation (ASF) under one or more +# contributor license agreements. See the NOTICE file distributed with +# this work for additional information regarding copyright ownership. +# The ASF licenses this file to You under the Apache License, Version 2.0 +# (the "License"); you may not use this file except in compliance with +# the License. You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +set(SRC_FILES + MessageFactory.cpp +) + +add_library(factory STATIC ${SRC_FILES}) + +target_include_directories(factory PUBLIC ${CMAKE_SOURCE_DIR}/include ${RocketMQ_INCLUDE_DIRS}) \ No newline at end of file diff --git a/cpp/rocketmq-client-cpp-tests/cpp4.x/src/factory/MessageFactory.cpp b/cpp/rocketmq-client-cpp-tests/cpp4.x/src/factory/MessageFactory.cpp new file mode 100644 index 0000000..557342f --- /dev/null +++ b/cpp/rocketmq-client-cpp-tests/cpp4.x/src/factory/MessageFactory.cpp @@ -0,0 +1,108 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +#include "factory/MessageFactory.h" +#include + +rocketmq::MQMessage MessageFactory::getRandomMessgae(const std::string &topic) +{ + std::string body = RandomUtils::getStringByUUID(); + return getStringMessage(topic, body); +} + +rocketmq::MQMessage MessageFactory::getStringMessage(const std::string &topic, std::string &body) +{ + rocketmq::MQMessage msg(topic, body); + return msg; +} + +rocketmq::MQMessage MessageFactory::getStringMessageByTag(const std::string &topic, const std::string &tags, const std::string &body) +{ + rocketmq::MQMessage msg(topic, tags, body); + return msg; +} + +rocketmq::MQMessage MessageFactory::getRandomMessageByTag(const std::string &topic, std::string &tags) +{ + std::string body = RandomUtils::getStringByUUID(); + return getStringMessageByTag(topic, tags, body); +} + +rocketmq::MQMessage MessageFactory::buildMessage(const std::string &topic) +{ + std::string body = RandomUtils::getStringByUUID(); + std::string tag = NameUtils::getRandomTagName(); + rocketmq::MQMessage msg(topic, tag, body); + return msg; +} + +rocketmq::MQMessage MessageFactory::buildMessage(const std::string &topic, const std::string &tags) +{ + std::string body = RandomUtils::getStringByUUID(); + std::string keys = RandomUtils::getStringByUUID(); + rocketmq::MQMessage msg(topic, tags, keys, body); + return msg; +} + +rocketmq::MQMessage MessageFactory::buildMessage(const std::string &topic, const std::string &tags, const std::string &body) +{ + std::string keys = RandomUtils::getStringByUUID(); + rocketmq::MQMessage msg(topic, tags, keys, body); + return msg; +} + +rocketmq::MQMessage MessageFactory::buildMessageOnlyTag(const std::string &topic, const std::string &tags, const std::string &body) +{ + rocketmq::MQMessage msg(topic, tags, body); + return msg; +} + +rocketmq::MQMessage MessageFactory::buildDelayMessage(const std::string &topic, const std::string &tags, const std::string &body, int delayTimeLevel) +{ + std::string keys = RandomUtils::getStringByUUID(); + rocketmq::MQMessage msg(topic, tags, keys, body); + msg.setDelayTimeLevel(delayTimeLevel); + return msg; +} + +// rocketmq::MQMessage MessageFactory::buildOrderMessage(const std::string& topic, const std::string& tags, const std::string& body, const std::string& messageGroup){ + +// } + +rocketmq::MQMessage MessageFactory::buildMessageWithProperty(const std::string &topic, std::map &properties) +{ + std::string body = RandomUtils::getStringByUUID(); + rocketmq::MQMessage msg(topic, body); + msg.setProperties(properties); + return msg; +} + +rocketmq::MQMessage MessageFactory::buildMessageWithProperty(const std::string &topic, const std::string &messageBody, std::map &properties) +{ + rocketmq::MQMessage msg(topic, messageBody); + msg.setProperties(properties); + return msg; +} + +// rocketmq::MQMessage MessageFactory::buildOrderMessageWithProperty(const std::string& topic, const std::string& messageBody, const std::string& messageGroup, std::map& properties); + +rocketmq::MQMessage MessageFactory::buildMessageWithProperty(const std::string &topic, const std::string &tag, const std::string &body, std::map &properties, const std::vector &keys) +{ + rocketmq::MQMessage msg(topic, tag, body); + msg.setProperties(properties); + msg.setKeys(keys); + return msg; +} \ No newline at end of file diff --git a/cpp/rocketmq-client-cpp-tests/cpp4.x/src/frame/BaseOperate.cpp b/cpp/rocketmq-client-cpp-tests/cpp4.x/src/frame/BaseOperate.cpp new file mode 100644 index 0000000..56f5be4 --- /dev/null +++ b/cpp/rocketmq-client-cpp-tests/cpp4.x/src/frame/BaseOperate.cpp @@ -0,0 +1,62 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include "frame/BaseOperate.h" + +extern std::shared_ptr multi_logger; + +std::string getTopic(MessageType messageType, const std::string &methodName, const std::string &brokerAddr, const std::string &namesrvAddr, const std::string &cluster) +{ + std::string messageTypeStr = MessageTypeToString[messageType]; + std::string topic = "topic_" + messageTypeStr + "_" + methodName + "_" + RandomUtils::getStringWithCharacter(6); + multi_logger->info("[Topic] topic:{}, messageType:{}, methodName:{}", topic, messageTypeStr, methodName); + if (messageType == MessageType::NORMAL) + { + MQAdminUtils::createTopic(topic, brokerAddr, "", namesrvAddr); + } + else if (messageType == MessageType::DELAY) + { + MQAdminUtils::createDelayTopic(topic, brokerAddr, "", namesrvAddr); + } + else if (messageType == MessageType::FIFO) + { + MQAdminUtils::createFIFOTopic(topic, brokerAddr, "", namesrvAddr); + } + else if (messageType == MessageType::TRANSACTION) + { + MQAdminUtils::createTransactionTopic(topic, brokerAddr, "", namesrvAddr); + } + + return topic; +} + +std::string getGroupId(const std::string &methodName) +{ + std::string randomStr = ""; + static const char CHARACTERS[] = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ"; + static const int STRING_LENGTH = 6; + std::random_device rd; + std::mt19937 gen(rd()); + std::uniform_int_distribution<> dis(0, sizeof(CHARACTERS) - 2); + for (int i = 0; i < STRING_LENGTH; i++) + { + randomStr += CHARACTERS[dis(gen)]; + } + std::string groupId = "GID_" + methodName + "_" + randomStr; + std::cout << "[ConsumerGroupId] groupId:" << groupId << ", methodName:" << methodName << std::endl; + return groupId; +} diff --git a/cpp/rocketmq-client-cpp-tests/cpp4.x/src/frame/CMakeLists.txt b/cpp/rocketmq-client-cpp-tests/cpp4.x/src/frame/CMakeLists.txt new file mode 100644 index 0000000..906e585 --- /dev/null +++ b/cpp/rocketmq-client-cpp-tests/cpp4.x/src/frame/CMakeLists.txt @@ -0,0 +1,21 @@ +# Licensed to the Apache Software Foundation (ASF) under one or more +# contributor license agreements. See the NOTICE file distributed with +# this work for additional information regarding copyright ownership. +# The ASF licenses this file to You under the Apache License, Version 2.0 +# (the "License"); you may not use this file except in compliance with +# the License. You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +set(SRC_FILES + BaseOperate.cpp +) + +add_library(frame STATIC ${SRC_FILES}) +target_link_libraries(frame fmt::fmt) +target_include_directories(frame PUBLIC ${CMAKE_SOURCE_DIR}/include) \ No newline at end of file diff --git a/cpp/rocketmq-client-cpp-tests/cpp4.x/src/utils/CMakeLists.txt b/cpp/rocketmq-client-cpp-tests/cpp4.x/src/utils/CMakeLists.txt new file mode 100644 index 0000000..8b59493 --- /dev/null +++ b/cpp/rocketmq-client-cpp-tests/cpp4.x/src/utils/CMakeLists.txt @@ -0,0 +1,27 @@ +# Licensed to the Apache Software Foundation (ASF) under one or more +# contributor license agreements. See the NOTICE file distributed with +# this work for additional information regarding copyright ownership. +# The ASF licenses this file to You under the Apache License, Version 2.0 +# (the "License"); you may not use this file except in compliance with +# the License. You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +set(SRC_FILES + MQAdminUtils.cpp + NameUtils.cpp + RandomUtils.cpp + InitResourceUtils.cpp + VerifyUtils.cpp +) + +add_library(utils STATIC ${SRC_FILES}) + +target_include_directories(utils PUBLIC ${CMAKE_SOURCE_DIR}/include ${RocketMQ_INCLUDE_DIRS}) + +target_link_libraries(utils PUBLIC ${OPENSSL_CRYPTO_LIBRARY}) \ No newline at end of file diff --git a/cpp/rocketmq-client-cpp-tests/cpp4.x/src/utils/InitResourceUtils.cpp b/cpp/rocketmq-client-cpp-tests/cpp4.x/src/utils/InitResourceUtils.cpp new file mode 100644 index 0000000..e9c7622 --- /dev/null +++ b/cpp/rocketmq-client-cpp-tests/cpp4.x/src/utils/InitResourceUtils.cpp @@ -0,0 +1,56 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +#include "utils/InitResourceUtils.h" +#include "utils/VerifyUtils.h" +#include "resource/Resource.h" +#include "utils/data/collect/DataCollectorManager.h" +#include "client/rmq/RMQNormalConsumer.h" +#include "utils/NameUtils.h" +#include "boost/property_tree/ptree.hpp" +#include "boost/property_tree/ini_parser.hpp" +#include "spdlog/spdlog.h" + +extern std::shared_ptr multi_logger; +extern std::shared_ptr resource; + +std::mutex NameUtils::mtx; + +std::unordered_map NameUtils::alreadyUsed; + +std::atomic RMQNormalConsumer::receivedIndex(0); + +std::vector VerifyUtils::msgs; + +void initResource(std::shared_ptr resource) +{ + boost::property_tree::ptree pt; + try + { + boost::property_tree::ini_parser::read_ini("config.ini", pt); + } + catch (boost::property_tree::ini_parser::ini_parser_error &e) + { + multi_logger->info("ini_parser_error: {}", e.what()); + } + boost::property_tree::ini_parser::read_ini("config.ini", pt); + resource->setNamesrv(pt.get("rocketmq.namesrv")); + resource->setBrokerAddr(pt.get("rocketmq.brokerAddr")); + resource->setCluster(pt.get("rocketmq.cluster")); + resource->setAccessKey(pt.get("rocketmq.accessKey")); + resource->setSecretKey(pt.get("rocketmq.secretKey")); + resource->setAccessChannel(pt.get("rocketmq.accessChannel")); +} \ No newline at end of file diff --git a/cpp/rocketmq-client-cpp-tests/cpp4.x/src/utils/MQAdminUtils.cpp b/cpp/rocketmq-client-cpp-tests/cpp4.x/src/utils/MQAdminUtils.cpp new file mode 100644 index 0000000..6c41f7c --- /dev/null +++ b/cpp/rocketmq-client-cpp-tests/cpp4.x/src/utils/MQAdminUtils.cpp @@ -0,0 +1,169 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +#include "spdlog/spdlog.h" +#include "utils/MQAdminUtils.h" + +extern std::shared_ptr multi_logger; + +std::string MQAdminUtils::getRootPath() +{ + std::string projectBasePath = std::getenv("PWD"); + std::string path = projectBasePath; + path = path.substr(0, path.find_last_of("/")); + path = path.substr(0, path.find_last_of("/")); + path = path.substr(0, path.find_last_of("/")); + return path; +} + +std::string MQAdminUtils::executeShellCommand(const std::string &command) +{ + std::string output; + char buffer[128]; + FILE *pipe = popen(command.c_str(), "r"); + if (!pipe) + { + return "ERROR"; + } + while (fgets(buffer, sizeof(buffer), pipe) != nullptr) + { + output += buffer; + } + pclose(pipe); + multi_logger->info("{}", output); + return output; +} + +std::string MQAdminUtils::createTopic(const std::string &topicName, const std::string &brokerAddr, const std::string &clusterName, const std::string &nameserver) +{ + // use absolute path + std::string path = getRootPath(); + std::string command = "sh " + path + "/common/bin/mqadmin updateTopic -t " + topicName; + if (!nameserver.empty()) + { + command = command + " -n " + nameserver; + } + if (!brokerAddr.empty()) + { + command = command + " -b " + brokerAddr; + } + if (!clusterName.empty()) + { + command = command + " -c " + clusterName; + } + multi_logger->info("{}", command); + return executeShellCommand(command); +} + +std::string MQAdminUtils::createDelayTopic(const std::string &topicName, const std::string &brokerAddr, const std::string &clusterName, const std::string &nameserver) +{ + // use absolute path + std::string path = getRootPath(); + std::string command = "sh " + path + "/common/bin/mqadmin updateTopic -t " + topicName; + if (!nameserver.empty()) + { + command = command + " -n " + nameserver; + } + if (!brokerAddr.empty()) + { + command = command + " -b " + brokerAddr; + } + if (!clusterName.empty()) + { + command = command + " -c " + clusterName; + } + command = command + " -a " + "+message.type=DELAY"; + multi_logger->info("{}", command); + return executeShellCommand(command); +} + +std::string MQAdminUtils::createFIFOTopic(const std::string &topicName, const std::string &brokerAddr, const std::string &clusterName, const std::string &nameserver) +{ + // use absolute path + std::string path = getRootPath(); + std::string command = "sh " + path + "/common/bin/mqadmin updateTopic -t " + topicName; + if (!nameserver.empty()) + { + command = command + " -n " + nameserver; + } + if (!brokerAddr.empty()) + { + command = command + " -b " + brokerAddr; + } + if (!clusterName.empty()) + { + command = command + " -c " + clusterName; + } + command = command + " -a " + "+message.type=FIFO"; + multi_logger->info("{}", command); + return executeShellCommand(command); +} + +std::string MQAdminUtils::createTransactionTopic(const std::string &topicName, const std::string &brokerAddr, const std::string &clusterName, const std::string &nameserver) +{ + // use absolute path + std::string path = getRootPath(); + std::string command = "sh " + path + "/common/bin/mqadmin updateTopic -t " + topicName; + if (!nameserver.empty()) + { + command = command + " -n " + nameserver; + } + if (!brokerAddr.empty()) + { + command = command + " -b " + brokerAddr; + } + if (!clusterName.empty()) + { + command = command + " -c " + clusterName; + } + command = command + " -a " + "+message.type=TRANSACTION"; + multi_logger->info("{}", command); + return executeShellCommand(command); +} + +std::string MQAdminUtils::createOrderlyConsumerGroup(const std::string &consumerGroup, const std::string &brokerAddr, const std::string &clusterName, const std::string &nameserver) +{ + // use absolute path + std::string path = getRootPath(); + std::string command = "sh " + path + "/common/bin/mqadmin updateSubGroup -g " + consumerGroup; + if (!nameserver.empty()) + { + command = command + " -n " + nameserver; + } + if (!brokerAddr.empty()) + { + command = command + " -b " + brokerAddr; + } + if (!clusterName.empty()) + { + command = command + " -c " + clusterName; + } + command = command + " -s true -o true -m false -d false "; + multi_logger->info("{}", command); + return executeShellCommand(command); +} + +std::string MQAdminUtils::clusterList(const std::string &nameserver) +{ + std::string path = getRootPath(); + std::string command = "sh " + path + "/common/bin/mqadmin clusterlist"; + if (!nameserver.empty()) + { + command = command + " -n " + nameserver; + } + multi_logger->info("{}", command); + return executeShellCommand(command); +} \ No newline at end of file diff --git a/cpp/rocketmq-client-cpp-tests/cpp4.x/src/utils/NameUtils.cpp b/cpp/rocketmq-client-cpp-tests/cpp4.x/src/utils/NameUtils.cpp new file mode 100644 index 0000000..819ac71 --- /dev/null +++ b/cpp/rocketmq-client-cpp-tests/cpp4.x/src/utils/NameUtils.cpp @@ -0,0 +1,208 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +#include +#include +#include +#include +#include +#include "openssl/evp.h" +#include "utils/NameUtils.h" + +std::string NameUtils::generateRandomAlphanumeric(int length) +{ + std::random_device rd; + std::mt19937 gen(rd()); + std::uniform_int_distribution<> dis(0, 35); + std::string chars = "abcdefghijklmnopqrstuvwxyz0123456789"; + std::string result; + for (int i = 0; i < length; i++) + { + result += chars[dis(gen)]; + } + return result; +} + +std::string NameUtils::generateMD5Sum(const std::string &input) +{ + unsigned char hash[MD5_DIGEST_LENGTH]; + unsigned int hash_length; + + const EVP_MD *sha256_md = EVP_sha256(); + EVP_MD_CTX *mdctx = EVP_MD_CTX_new(); + EVP_DigestInit_ex(mdctx, sha256_md, nullptr); + EVP_DigestUpdate(mdctx, reinterpret_cast(input.c_str()), input.length()); + EVP_DigestFinal_ex(mdctx, hash, &hash_length); + EVP_MD_CTX_free(mdctx); + + std::stringstream ss; + for (unsigned int i = 0; i < hash_length; i++) + { + ss << std::hex << std::setw(2) << std::setfill('0') << static_cast(hash[i]); + } + + return ss.str(); +} + +std::string NameUtils::getTopicName() +{ + while (true) + { + std::string topic = "topic-server-" + generateRandomAlphanumeric(20); + std::lock_guard lock(mtx); + auto it = alreadyUsed.find(topic); + if (it == alreadyUsed.end()) + { + alreadyUsed[topic] = topic; + return topic; + } + } +} + +std::string NameUtils::getGroupName() +{ + while (true) + { + std::string gid = "GID-server-" + generateRandomAlphanumeric(20); + std::lock_guard lock(mtx); + auto it = alreadyUsed.find(gid); + if (it == alreadyUsed.end()) + { + alreadyUsed[gid] = gid; + return gid; + } + } +} + +std::string NameUtils::getTagName() +{ + while (true) + { + std::string tag = "tag-server-" + generateRandomAlphanumeric(20); + std::lock_guard lock(mtx); + auto it = alreadyUsed.find(tag); + if (it == alreadyUsed.end()) + { + alreadyUsed[tag] = tag; + return tag; + } + } +} + +std::string NameUtils::getRandomTopicName() +{ + while (true) + { + std::string topic = "topic-server-" + generateRandomAlphanumeric(20); + std::lock_guard lock(mtx); + auto it = alreadyUsed.find(topic); + if (it == alreadyUsed.end()) + { + alreadyUsed[topic] = topic; + return topic; + } + } +} + +std::string NameUtils::getTopicName(const std::string &messageType, const std::string &className, const std::string &methodName) +{ + while (true) + { + std::string topic = "topic-" + messageType + "-" + generateMD5Sum(className + methodName); + std::lock_guard lock(mtx); + auto it = alreadyUsed.find(topic); + if (it == alreadyUsed.end()) + { + alreadyUsed[topic] = topic; + return topic; + } + } +} + +std::string NameUtils::getRandomTopicName(const std::string &suffix) +{ + while (true) + { + std::string topic = "topic-" + generateRandomAlphanumeric(6) + "-" + suffix; + std::lock_guard lock(mtx); + auto it = alreadyUsed.find(topic); + if (it == alreadyUsed.end()) + { + alreadyUsed[topic] = topic; + return topic; + } + } +} + +std::string NameUtils::getRandomGroupName() +{ + while (true) + { + std::string gid = "GID-server-" + generateRandomAlphanumeric(20); + std::lock_guard lock(mtx); + auto it = alreadyUsed.find(gid); + if (it == alreadyUsed.end()) + { + alreadyUsed[gid] = gid; + return gid; + } + } +} + +std::string NameUtils::getGroupName(const std::string &className, const std::string &methodName) +{ + while (true) + { + std::string gid = "GID-" + generateMD5Sum(className + methodName); + std::lock_guard lock(mtx); + auto it = alreadyUsed.find(gid); + if (it == alreadyUsed.end()) + { + alreadyUsed[gid] = gid; + return gid; + } + } +} + +std::string NameUtils::getRandomGroupName(const std::string &suffix) +{ + while (true) + { + std::string gid = "GID-" + generateRandomAlphanumeric(6) + "-" + suffix; + std::lock_guard lock(mtx); + auto it = alreadyUsed.find(gid); + if (it == alreadyUsed.end()) + { + alreadyUsed[gid] = gid; + return gid; + } + } +} + +std::string NameUtils::getRandomTagName() +{ + while (true) + { + std::string tag = "tag-server-" + generateRandomAlphanumeric(20); + std::lock_guard lock(mtx); + auto it = alreadyUsed.find(tag); + if (it == alreadyUsed.end()) + { + alreadyUsed[tag] = tag; + return tag; + } + } +} \ No newline at end of file diff --git a/cpp/rocketmq-client-cpp-tests/cpp4.x/src/utils/RandomUtils.cpp b/cpp/rocketmq-client-cpp-tests/cpp4.x/src/utils/RandomUtils.cpp new file mode 100644 index 0000000..313617a --- /dev/null +++ b/cpp/rocketmq-client-cpp-tests/cpp4.x/src/utils/RandomUtils.cpp @@ -0,0 +1,170 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +#include +#include +#include +#include +#include + +#include "utils/RandomUtils.h" + +std::random_device RandomUtils::rd; +std::mt19937 RandomUtils::rng(RandomUtils::rd()); + +std::string RandomUtils::getStringByUUID() +{ + std::random_device dev; + std::mt19937 rng(dev()); + std::uniform_int_distribution dist(0, 15); + + std::string res(36, 0); + const char *hex = "0123456789abcdef"; + for (int i = 0; i < 36; ++i) + { + if (i == 8 || i == 13 || i == 18 || i == 23) + { + res[i] = '-'; + } + else + { + res[i] = hex[dist(rng)]; + } + } + return res; +} + +std::string RandomUtils::getChineseWord(int len) +{ + std::uniform_int_distribution dist(UNICODE_START, UNICODE_END); + std::stringstream ss; + for (int i = 0; i < len; ++i) + { + ss << getChineseChar(dist(rng)); + } + return ss.str(); +} + +std::string RandomUtils::getStringWithNumber(int n) +{ + int arg[] = {'0', '9' + 1}; + return getString(n, arg, sizeof(arg) / sizeof(arg[0])); +} + +std::string RandomUtils::getStringWithCharacter(int n) +{ + int arg[] = {'a', 'z' + 1, 'A', 'Z' + 1}; + return getString(n, arg, sizeof(arg) / sizeof(arg[0])); +} + +int RandomUtils::getIntegerBetween(int n, int m) +{ + if (m == n) + { + return n; + } + int res = RandomUtils::getIntegerMoreThanZero(); + return n + res % (m - n); +} + +int RandomUtils::getIntegerMoreThanZero() +{ + int res = rd(); + while (res <= 0) + { + res = rd(); + } + return res; +} + +std::string RandomUtils::getString(int n, int arg[], int size) +{ + std::stringstream ss; + for (int i = 0; i < n; i++) + { + ss << RandomUtils::getChar(arg, size); + } + return ss.str(); +} + +char RandomUtils::getChar(int arg[], int size) +{ + int c = rd() % (size / 2); + c = c * 2; + return static_cast(getIntegerBetween(arg[c], arg[c + 1])); +} + +char RandomUtils::getChineseChar(int codePoint) +{ + std::stringstream ss; + ss << std::hex << codePoint; + std::string hexStr = ss.str(); + + if (hexStr.size() > 4) + { + return 0; + } + + std::array bytes; + + for (size_t i = 0; i < hexStr.size(); ++i) + { + bytes[i] = static_cast(hexStr[i]); + } + + int value = std::stoi(hexStr, nullptr, 16); + + if (value < UNICODE_START || value >= UNICODE_END) + { + return 0; + } + + char utf8Bytes[4]; + if (value <= 0x7F) + { + utf8Bytes[0] = static_cast(value); + utf8Bytes[1] = '\0'; + utf8Bytes[2] = '\0'; + utf8Bytes[3] = '\0'; + } + else if (value <= 0x7FF) + { + utf8Bytes[0] = static_cast((value >> 6) | 0xC0); + utf8Bytes[1] = static_cast((value & 0x3F) | 0x80); + utf8Bytes[2] = '\0'; + utf8Bytes[3] = '\0'; + } + else + { + utf8Bytes[0] = static_cast((value >> 12) | 0xE0); + utf8Bytes[1] = static_cast(((value >> 6) & 0x3F) | 0x80); + utf8Bytes[2] = static_cast((value & 0x3F) | 0x80); + utf8Bytes[3] = '\0'; + } + + return utf8Bytes[0]; +} + +std::string RandomUtils::randomAlphabetic(int length) +{ + std::string str("abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ"); + std::random_device rd; + std::mt19937 generator(rd()); + + std::shuffle(str.begin(), str.end(), generator); + + return str.substr(0, length); +} \ No newline at end of file diff --git a/cpp/rocketmq-client-cpp-tests/cpp4.x/src/utils/VerifyUtils.cpp b/cpp/rocketmq-client-cpp-tests/cpp4.x/src/utils/VerifyUtils.cpp new file mode 100644 index 0000000..1b0334a --- /dev/null +++ b/cpp/rocketmq-client-cpp-tests/cpp4.x/src/utils/VerifyUtils.cpp @@ -0,0 +1,1013 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +#include "utils/VerifyUtils.h" +#include "utils/data/collect/DataCollector.h" +#include "utils/data/collect/DataCollectorManager.h" +#include "utils/SimpleConcurrentHashMapUtils.h" +#include "gtest/gtest.h" +#include "resource/Resource.h" +#include "spdlog/logger.h" +#include +#include +#include +#include +#include +#include +#include +#include + +extern std::shared_ptr multi_logger; +extern std::shared_ptr resource; + +std::atomic VerifyUtils::receivedIndex(0); + +long long VerifyUtils::getDelayTime(int delayLevel) +{ + long long delayTime = 0; + switch (delayLevel) + { + case 1: + delayTime = 1 * 1000; + break; + case 2: + delayTime = 5 * 1000; + break; + case 3: + delayTime = 10 * 1000; + break; + case 4: + delayTime = 30 * 1000; + break; + case 5: + delayTime = 1 * 60 * 1000; + break; + case 6: + delayTime = 2 * 60 * 1000; + break; + case 7: + delayTime = 3 * 60 * 1000; + break; + case 8: + delayTime = 4 * 60 * 1000; + break; + case 9: + delayTime = 5 * 60 * 1000; + break; + case 10: + delayTime = 6 * 60 * 1000; + break; + case 11: + delayTime = 7 * 60 * 1000; + break; + case 12: + delayTime = 8 * 60 * 1000; + break; + case 13: + delayTime = 9 * 60 * 1000; + break; + case 14: + delayTime = 10 * 60 * 1000; + break; + case 15: + delayTime = 20 * 60 * 1000; + break; + case 16: + delayTime = 30 * 60 * 1000; + break; + case 17: + delayTime = 1 * 60 * 60 * 1000; + break; + case 18: + delayTime = 2 * 60 * 60 * 1000; + break; + } + return delayTime; +} + +std::unordered_map VerifyUtils::checkDelay(DataCollector &dequeueMessages, int delayLevel) +{ + std::unordered_map map; + std::vector receivedMessages = dequeueMessages.getAllData(); + std::chrono::system_clock::time_point now = std::chrono::system_clock::now(); + // 将时间点转换为时间戳(以毫秒为单位) + std::chrono::seconds duration = std::chrono::duration_cast(now.time_since_epoch()); + long consumeTime = duration.count() * 1000L; + // std::cout< 5000) + { + map.insert(std::make_pair(msg.getMsgId(), consumeTime - bornTimestamp)); + } + } + return map; +} + +bool VerifyUtils::checkOrder(DataCollector &dequeueMessages) +{ + std::vector receivedMessages = dequeueMessages.getAllData(); + std::unordered_map> map; + for (const auto &receivedMessage : receivedMessages) + { + const std::string &shardingKey = std::to_string(std::stoi(receivedMessage.getBody()) % 2); + std::vector messages; + if (map.find(shardingKey) != map.end()) + { + map[shardingKey].push_back(receivedMessage); + } + else + { + messages.push_back(receivedMessage); + map[shardingKey] = messages; + } + } + return checkOrderMessage(map); +} + +bool async_function(const std::string &topic, const std::string &subExpression, std::shared_ptr pullConsumer) +{ + std::vector mqs; + try + { + pullConsumer->fetchSubscribeMessageQueues(topic, mqs); + for (auto &mq : mqs) + { + long long offset = pullConsumer->fetchConsumeOffset(mq, true); + if (offset < 0) + continue; + rocketmq::PullResult pullResult = pullConsumer->pull(mq, subExpression, offset, 32); + switch (pullResult.pullStatus) + { + case rocketmq::FOUND: + for (auto &msg : pullResult.msgFoundList) + { + multi_logger->info("Message: {}", msg.toString()); + } + offset = pullResult.nextBeginOffset; + pullConsumer->updateConsumeOffset(mq, offset); + break; + case rocketmq::NO_MATCHED_MSG: + break; + case rocketmq::NO_NEW_MSG: + break; + case rocketmq::OFFSET_ILLEGAL: + break; + default: + break; + } + } + } + catch (const rocketmq::MQException &e) + { + multi_logger->error("fetchSubscribeMessageQueues exception: {}", e.what()); + return false; + } + return true; +} + +bool VerifyUtils::tryReceiveOnce(const std::string &topic, const std::string &subExpression, std::shared_ptr pullConsumer) +{ + // async_function(topic, pullConsumer); + std::future future1 = std::async(std::launch::async, [topic, subExpression, pullConsumer]() + { return async_function(topic, subExpression, pullConsumer); }); + // std::future future2 = std::async(std::launch::async, [topic, pullConsumer](){ return async_function(topic, pullConsumer); }); + // std::future future3 = std::async(std::launch::async, [topic, pullConsumer](){ return async_function(topic, pullConsumer); }); + // std::future future4 = std::async(std::launch::async, [topic, pullConsumer](){ return async_function(topic, pullConsumer); }); + // std::future future5 = std::async(std::launch::async, [topic, pullConsumer](){ return async_function(topic, pullConsumer); }); + + auto status1 = future1.wait_for(std::chrono::seconds(30)); + // auto status2 = future2.wait_for(std::chrono::seconds(30)); + // auto status3 = future3.wait_for(std::chrono::seconds(30)); + // auto status4 = future4.wait_for(std::chrono::seconds(30)); + // auto status5 = future5.wait_for(std::chrono::seconds(30)); + + if (status1 == std::future_status::ready && future1.get() == true) + { + return true; + } + else + { + return false; + } +} + +std::vector VerifyUtils::fetchMessages(std::shared_ptr pullConsumer, const std::string &topic) +{ + std::vector mqs; + pullConsumer->fetchSubscribeMessageQueues(topic, mqs); + // rocekmq获取队列中所有未消费的消息,首先判断消息数量是不是为1,然后判断消息体是否为空 + for (auto &mq : mqs) + { + long long offset = pullConsumer->fetchConsumeOffset(mq, true); + if (offset < 0) + continue; + rocketmq::PullResult pullResult = pullConsumer->pull(mq, "", offset, 32); + switch (pullResult.pullStatus) + { + case rocketmq::FOUND: + for (auto &msg : pullResult.msgFoundList) + { + msgs.push_back(msg); + // std::cout << "msg body: " << msg.getBody() << std::endl; + } + offset = pullResult.nextBeginOffset; + pullConsumer->updateConsumeOffset(mq, offset); + break; + case rocketmq::NO_MATCHED_MSG: + break; + case rocketmq::NO_NEW_MSG: + break; + case rocketmq::OFFSET_ILLEGAL: + break; + default: + break; + } + } + return msgs; +} + +std::vector VerifyUtils::waitForMessageConsume(DataCollector &enqueueMessages, DataCollector &dequeueMessages, long long timeoutMills, int consumedTimes) +{ + multi_logger->info("Set timeout: {}ms", timeoutMills); + + std::vector sendMessages = enqueueMessages.getAllData(); + + auto currentTime = std::chrono::steady_clock::now(); + + while (!sendMessages.empty()) + { + std::vector receivedMessagesCopy = dequeueMessages.getAllData(); + sendMessages.erase(std::remove_if(sendMessages.begin(), sendMessages.end(), + [&](const std::string &enqueueMessageId) + { + auto count = std::count_if(receivedMessagesCopy.begin(), receivedMessagesCopy.end(), + [&](const std::string &msg) + { + return msg == enqueueMessageId; + }); + + if (count >= consumedTimes) + { + if (count > consumedTimes) + { + multi_logger->error("More retry messages were consumed than expected (including one original message)" + "Except: {}, Actual: {}, MsgId: {}", + consumedTimes, count, enqueueMessageId); + assert(false); + } + return true; + } + return false; + }), + sendMessages.end()); + + if (sendMessages.empty()) + { + break; + } + + if (std::chrono::duration_cast(std::chrono::steady_clock::now() - currentTime).count() >= timeoutMills) + { + multi_logger->error("Timeout but not received all send messages"); + break; + } + std::this_thread::sleep_for(std::chrono::milliseconds(500)); + } + return sendMessages; +} + +std::vector VerifyUtils::waitForMessageConsume(DataCollector &enqueueMessages, DataCollector &dequeueMessages, long long timeoutMills, int consumedTimes) +{ + multi_logger->info("Set timeout: {}ms", timeoutMills); + + std::vector sendMessages = enqueueMessages.getAllData(); + + auto currentTime = std::chrono::steady_clock::now(); + + while (!sendMessages.empty()) + { + std::vector receivedMessagesCopy = dequeueMessages.getAllData(); + // std::cout << "receivedMessagesCopy size: " << receivedMessagesCopy.size() << std::endl; + sendMessages.erase(std::remove_if(sendMessages.begin(), sendMessages.end(), + [&](const std::string &enqueueMessageId) + { + auto count = std::count_if(receivedMessagesCopy.begin(), receivedMessagesCopy.end(), + [&](const MQMsg &msg) + { + return msg.getMsgId() == enqueueMessageId; + }); + + if (count >= consumedTimes) + { + if (count > consumedTimes) + { + multi_logger->error("More retry messages were consumed than expected (including one original message)" + "Except: {}, Actual: {}, MsgId: {}", + consumedTimes, count, enqueueMessageId); + assert(false); + } + return true; + } + return false; + }), + sendMessages.end()); + + if (sendMessages.empty()) + { + break; + } + + if (std::chrono::duration_cast(std::chrono::steady_clock::now() - currentTime).count() >= timeoutMills) + { + multi_logger->error("Timeout but not received all send messages"); + break; + } + std::this_thread::sleep_for(std::chrono::milliseconds(500)); + } + return sendMessages; +} + +bool VerifyUtils::verifyNormalMessage(DataCollector &enqueueMessages, DataCollector &dequeueMessages) +{ + std::vector unConsumedMessages = waitForMessageConsume(enqueueMessages, dequeueMessages, TIMEOUT * 1000L, 1); + if (unConsumedMessages.size() > 0) + { + multi_logger->error("Not all messages were consumed, unConsumedMessages size: {}", unConsumedMessages.size()); + return false; + } + return true; +} + +bool VerifyUtils::verifyNormalMessage(DataCollector &enqueueMessages, DataCollector &dequeueMessages, std::unordered_set &unconsumedMsgIds) +{ + std::vector unConsumedMessages = waitForMessageConsume(enqueueMessages, dequeueMessages, 30 * 1000L, 1); + for (auto &unConsumedMessage : unConsumedMessages) + { + auto it = unconsumedMsgIds.find(unConsumedMessage); + if (it == unconsumedMsgIds.end()) + { + multi_logger->error("Message {} should be consumed", unConsumedMessage); + return false; + } + else + { + unconsumedMsgIds.erase(it); + } + } + if (unconsumedMsgIds.size() > 0) + { + multi_logger->error("UnConsumedMessages size: {}", unConsumedMessages.size()); + return false; + } + return true; +} + +bool VerifyUtils::verifyNormalMessage(DataCollector &enqueueMessages, DataCollector &dequeueMessages) +{ + std::vector unConsumedMessages = waitForMessageConsume(enqueueMessages, dequeueMessages, TIMEOUT * 1000L, 1); + if (unConsumedMessages.size() > 0) + { + multi_logger->error("Not all messages were consumed, unConsumedMessages size: {}", unConsumedMessages.size()); + return false; + } + return true; +} + +bool VerifyUtils::verifyNormalMessageWithUserProperties(DataCollector &enqueueMessages, DataCollector &dequeueMessages, std::map &props, int expectedUnrecvMsgNum) +{ + std::vector unConsumedMessages = waitForMessageConsume(enqueueMessages, dequeueMessages, TIMEOUT * 1000L, 1); + std::vector recvMessages = dequeueMessages.getAllData(); + for (auto &recvMessage : recvMessages) + { + auto recvProps = recvMessage.getProperties(); + for (auto &prop : props) + { + auto it = recvProps.find(prop.first); + if (it != recvProps.end() && it->second == prop.second) + { + multi_logger->error("sql attribute filtering is not in effect, consuming messages to other attributes"); + return false; + } + } + } + if (unConsumedMessages.size() != expectedUnrecvMsgNum) + { + multi_logger->error("Failed to consume all the sent data by sql filter"); + return false; + } + return true; +} + +bool VerifyUtils::verifyDelayMessage(DataCollector &enqueueMessages, DataCollector &dequeueMessages, int delayLevel) +{ + std::vector unConsumedMessages = waitForMessageConsume(enqueueMessages, dequeueMessages, TIMEOUT * 1000L + getDelayTime(delayLevel), 1); + if (unConsumedMessages.size() > 0) + { + multi_logger->error("Not all messages were consumed, unConsumedMessages size: {}", unConsumedMessages.size()); + return false; + } + std::unordered_map delayUnExcept = checkDelay(dequeueMessages, delayLevel); + std::ostringstream oss; + oss << "The following messages do not meet the delay requirements \n"; + for (const auto &pair : delayUnExcept) + { + std::string key = pair.first; + oss << key << " , interval:" << delayUnExcept[key] << "\n"; + } + if (delayUnExcept.size() > 0) + { + multi_logger->error(oss.str()); + return false; + } + return true; +} + +bool VerifyUtils::verifyOrderMessage(DataCollector &enqueueMessages, DataCollector &dequeueMessages) +{ + std::vector unConsumedMessages = waitForMessageConsume(enqueueMessages, dequeueMessages, TIMEOUT * 1000L, 1); + if (unConsumedMessages.size() > 0) + { + multi_logger->error("Not all messages were consumed, unConsumedMessages size: {}", unConsumedMessages.size()); + return false; + } + + bool result = checkOrder(dequeueMessages); + + if (!result) + { + multi_logger->error("Message out of order"); + } + return result; +} + +bool VerifyUtils::checkOrderMessage(std::unordered_map> &receivedMessage) +{ + for (auto &pair : receivedMessage) + { + std::ostringstream oss; + int preNode = -1; + std::string key = pair.first; + std::vector msgs = pair.second; + std::string tag = msgs[0].getTags(); + for (auto &msg : msgs) + { + if (msg.getTags() != tag) + { + preNode = -1; + } + int curNode = std::stoi(msg.getBody()); + oss << curNode << ","; + if (preNode > curNode) + { + multi_logger->error(oss.str()); + return false; + } + preNode = curNode; + } + } + return true; +} + +void modifyString2Empty(const std::string &msgId, std::vector &msgs, std::mutex &mtx, std::atomic &recvCount) +{ + std::lock_guard lock(mtx); + for (auto &msg : msgs) + { + if (msgId == msg) + { + std::cout << "msg id: " << msg << std::endl; + msg = ""; + std::cout << "msg id change: " << msg << std::endl; + recvCount--; + break; + } + } +} + +bool VerifyUtils::waitReceiveThenAck(std::shared_ptr producer, std::shared_ptr pullConsumer, std::string &topic, std::string &tag, int maxMessageNum) +{ + long endTime = std::chrono::duration_cast(std::chrono::system_clock::now().time_since_epoch()).count() + TIMEOUT * 1000L; + + std::vector mqs; + pullConsumer->fetchSubscribeMessageQueues(topic, mqs); + + std::vector sendMsgs = producer->getEnqueueMessages()->getAllData(); + std::atomic recvCount(sendMsgs.size()); + + while (endTime > std::chrono::duration_cast(std::chrono::system_clock::now().time_since_epoch()).count()) + { + std::vector> runnables; + for (auto &mq : mqs) + { + runnables.push_back([&]() + { + long long offset = pullConsumer->fetchConsumeOffset(mq, false); + if(offset<0) return; + rocketmq::PullResult pullResult = pullConsumer->pull(mq, tag, offset, maxMessageNum); + switch (pullResult.pullStatus) { + case rocketmq::FOUND: + for (auto& msg : pullResult.msgFoundList) { + for (auto& sendMsg : sendMsgs) { + if(msg.getMsgId() == sendMsg){ + sendMsg = ""; + recvCount--; + offset += 1; + pullConsumer->updateConsumeOffset(mq, offset); + } + } + } + break; + case rocketmq::NO_MATCHED_MSG: + break; + case rocketmq::NO_NEW_MSG: + break; + case rocketmq::OFFSET_ILLEGAL: + break; + default: + break; + } }); + } + + if (recvCount == 0) + { + break; + } + + std::vector> futures; + for (const auto &runnable : runnables) + { + futures.push_back(std::async(std::launch::async, runnable)); + } + + // 等待所有函数对象完成 + for (auto &future : futures) + { + future.get(); + } + } + + if (recvCount != 0) + { + multi_logger->error("Not all messages were consumed, unConsumedMessages size: {}", recvCount); + return false; + } + else + { + return true; + } +} + +bool VerifyUtils::waitFIFOParamReceiveThenNAck(std::shared_ptr producer, std::shared_ptr pullConsumer, std::string &topic, std::string &tag, int maxMessageNum) +{ + long endTime = std::chrono::duration_cast(std::chrono::system_clock::now().time_since_epoch()).count() + 30 * 1000L; + + std::vector mqs; + pullConsumer->fetchSubscribeMessageQueues(topic, mqs); + + SimpleConcurrentHashMap receivedMap; + + while (endTime > std::chrono::duration_cast(std::chrono::system_clock::now().time_since_epoch()).count()) + { + std::vector> runnables; + for (auto &mq : mqs) + { + runnables.push_back([&]() + { + long long offset = pullConsumer->fetchConsumeOffset(mq, false); + if(offset<0) return; + rocketmq::PullResult pullResult = pullConsumer->pull(mq, tag, offset, maxMessageNum); + switch (pullResult.pullStatus) { + case rocketmq::FOUND: + for (auto& msg : pullResult.msgFoundList) { + if(receivedMap.contains(msg.getMsgId())){ + receivedMap.insert(msg.getMsgId(),msg); + } + } + break; + case rocketmq::NO_MATCHED_MSG: + break; + case rocketmq::NO_NEW_MSG: + break; + case rocketmq::OFFSET_ILLEGAL: + break; + default: + break; + } }); + } + std::vector> futures; + for (const auto &runnable : runnables) + { + futures.push_back(std::async(std::launch::async, runnable)); + } + + // 等待所有函数对象完成并获取结果 + for (auto &future : futures) + { + future.get(); + } + } + + for (auto &msg : receivedMap.getAllValues()) + { + int id = std::stoi(msg.getBody()); + if (id >= 8) + { + multi_logger->error("Consumption out of order, expected :Body=0 Actual :Body={}", id); + return false; + } + } + return true; +} + +bool VerifyUtils::waitFIFOParamReceiveThenAckExceptedLast(std::shared_ptr producer, std::shared_ptr pullConsumer, std::string &topic, std::string &tag, int maxMessageNum) +{ + long endTime = std::chrono::duration_cast(std::chrono::system_clock::now().time_since_epoch()).count() + 30 * 1000L; + + std::vector mqs; + pullConsumer->fetchSubscribeMessageQueues(topic, mqs); + + std::vector receivedMessage; + SimpleConcurrentHashMap> map; + + while (endTime > std::chrono::duration_cast(std::chrono::system_clock::now().time_since_epoch()).count()) + { + std::vector> runnables; + for (auto &mq : mqs) + { + runnables.push_back([&]() + { + int count = 0; //same message queue,so dont't need atomic + long long offset = pullConsumer->fetchConsumeOffset(mq, false); + if(offset<0) return true; + rocketmq::PullResult pullResult = pullConsumer->pull(mq, tag, offset, maxMessageNum); + switch (pullResult.pullStatus) { + case rocketmq::FOUND: + for(int j=0;jerror("Consumption out of order, expected :order={} Actual :order={}",count,id); + return false; + } + count++; + if(id != 19){ + offset+=1; + pullConsumer->updateConsumeOffset(mq, offset); + if(map.contains(msgId)){ + map[msgId]++; + }else{ + std::atomic val(1); + map.insert(msgId,val.load()); + } + } + } + break; + case rocketmq::NO_MATCHED_MSG: + break; + case rocketmq::NO_NEW_MSG: + break; + case rocketmq::OFFSET_ILLEGAL: + break; + default: + break; + } + return true; }); + } + if (map.size() >= 20) + { + return false; + } + + for (auto &value : map.getAllValues()) + { + if (value > 1) + { + return false; + } + } + + std::vector> futures; + for (const auto &runnable : runnables) + { + futures.push_back(std::async(std::launch::async, runnable)); + } + + // 等待所有函数对象完成并获取结果 + for (auto &future : futures) + { + bool result = future.get(); + if (!result) + return false; + } + } + + return true; +} + +bool VerifyUtils::waitFIFOReceiveThenAck(std::shared_ptr producer, std::shared_ptr pullConsumer, std::string &topic, std::string &tag, int maxMessageNum) +{ + long endTime = std::chrono::duration_cast(std::chrono::system_clock::now().time_since_epoch()).count() + TIMEOUT * 1000L; + + std::vector sendCollection = producer->getEnqueueMessages()->getAllData(); + + try + { + while (endTime > std::chrono::duration_cast(std::chrono::system_clock::now().time_since_epoch()).count()) + { + std::vector mqs; + pullConsumer->fetchSubscribeMessageQueues(topic, mqs); + for (auto &mq : mqs) + { + std::unordered_map> receivedMessage; + long long offset = pullConsumer->fetchConsumeOffset(mq, false); + if (offset < 0) + continue; + rocketmq::PullResult pullResult = pullConsumer->pull(mq, tag, offset, maxMessageNum); + switch (pullResult.pullStatus) + { + case rocketmq::FOUND: + for (int j = 0; j < pullResult.msgFoundList.size(); j++) + { + int id = std::stoi(pullResult.msgFoundList[j].getBody()) / 20; + offset += 1; + pullConsumer->updateConsumeOffset(mq, offset); + sendCollection.erase(std::remove_if(sendCollection.begin(), sendCollection.end(), + [&](const std::string &enqueueMessageId) + { + if (pullResult.msgFoundList[j].getMsgId() == enqueueMessageId) + { + return true; + } + return false; + }), + sendCollection.end()); + + std::string msgId(std::to_string(id)); + if (receivedMessage.find(msgId) != receivedMessage.end()) + { + receivedMessage[msgId].push_back(MQMsg(pullResult.msgFoundList[j])); + } + else + { + std::vector msgs; + msgs.push_back(MQMsg(pullResult.msgFoundList[j])); + receivedMessage[msgId] = msgs; + } + } + break; + case rocketmq::NO_MATCHED_MSG: + break; + case rocketmq::NO_NEW_MSG: + break; + case rocketmq::OFFSET_ILLEGAL: + break; + default: + break; + } + if (!checkOrderMessage(receivedMessage)) + { + return false; + } + } + if (sendCollection.size() == 0) + break; + } + if (sendCollection.size() != 0) + { + multi_logger->error("Not all messages were consumed, unConsumedMessages size: {}", sendCollection.size()); + return false; + } + } + catch (const std::exception &e) + { + multi_logger->error("{}", e.what()); + } + return true; +} + +bool VerifyUtils::waitAckExceptionReReceiveAck(std::shared_ptr producer, std::shared_ptr pullConsumer, std::string &topic, std::string &tag, int maxMessageNum) +{ + long endTime = std::chrono::duration_cast(std::chrono::system_clock::now().time_since_epoch()).count() + 60 * 1000L; + + try + { + while (endTime > std::chrono::duration_cast(std::chrono::system_clock::now().time_since_epoch()).count()) + { + std::vector mqs; + pullConsumer->fetchSubscribeMessageQueues(topic, mqs); + for (auto &mq : mqs) + { + long long offset = pullConsumer->fetchConsumeOffset(mq, false); + if (offset < 0) + continue; + rocketmq::PullResult pullResult = pullConsumer->pull(mq, tag, offset, maxMessageNum); + switch (pullResult.pullStatus) + { + case rocketmq::FOUND: + for (int j = 0; j < pullResult.msgFoundList.size(); j++) + { + multi_logger->info("Message: {}", pullResult.msgFoundList[j].toString()); + std::this_thread::sleep_for(std::chrono::seconds(11)); + offset += 1; + pullConsumer->updateConsumeOffset(mq, offset); + } + break; + case rocketmq::NO_MATCHED_MSG: + break; + case rocketmq::NO_NEW_MSG: + break; + case rocketmq::OFFSET_ILLEGAL: + break; + default: + break; + } + } + } + } + catch (const std::exception &e) + { + multi_logger->error("{}", e.what()); + return true; + } + return true; +} + +bool VerifyUtils::waitReceiveMaxsizeSync(std::shared_ptr producer, std::shared_ptr pullConsumer, std::string &topic, std::string &tag, int maxMessageNum) +{ + long endTime = std::chrono::duration_cast(std::chrono::system_clock::now().time_since_epoch()).count() + TIMEOUT * 1000L; + + SimpleConcurrentHashMap map; + + try + { + while (endTime > std::chrono::duration_cast(std::chrono::system_clock::now().time_since_epoch()).count()) + { + std::vector mqs; + pullConsumer->fetchSubscribeMessageQueues(topic, mqs); + for (auto &mq : mqs) + { + long long offset = pullConsumer->fetchConsumeOffset(mq, false); + if (offset < 0) + continue; + rocketmq::PullResult pullResult = pullConsumer->pull(mq, tag, offset, maxMessageNum); + switch (pullResult.pullStatus) + { + case rocketmq::FOUND: + for (int j = 0; j < pullResult.msgFoundList.size(); j++) + { + multi_logger->info("Message: {}", pullResult.msgFoundList[j].toString()); + std::string msgId = pullResult.msgFoundList[j].getMsgId(); + if (map.contains(msgId)) + { + multi_logger->error("Duplicate message"); + return false; + } + else + { + offset += 1; + pullConsumer->updateConsumeOffset(mq, offset); + map.insert(msgId, pullResult.msgFoundList[j]); + } + } + break; + case rocketmq::NO_MATCHED_MSG: + break; + case rocketmq::NO_NEW_MSG: + break; + case rocketmq::OFFSET_ILLEGAL: + break; + default: + break; + } + } + multi_logger->info("receive {} messages", map.size()); + if (map.size() == 300) + break; + } + DataCollector &dequeueMessages = DataCollectorManager::getInstance().fetchListDataCollector(RandomUtils::getStringByUUID()); + + for (auto &value : map.getAllValues()) + { + dequeueMessages.addData(value.getMsgId()); + } + if (!VerifyUtils::verifyNormalMessage(*(producer->getEnqueueMessages()), dequeueMessages)) + { + return false; + } + } + catch (const std::exception &e) + { + multi_logger->error("{}", e.what()); + return false; + } + return true; +} + +bool VerifyUtils::waitReceiveMultiNack(std::shared_ptr producer, std::shared_ptr pullConsumer, std::string &topic, std::string &tag, int maxMessageNum) +{ + long endTime = std::chrono::duration_cast(std::chrono::system_clock::now().time_since_epoch()).count() + 30 * 1000L; + + std::vector> runnables; + SimpleConcurrentHashMap recvMsgs; + bool flag{true}; + std::unordered_set unconsumedMsgIds; + + for (int i = 0; i < 4; i++) + { + while (endTime > std::chrono::duration_cast(std::chrono::system_clock::now().time_since_epoch()).count()) + { + try + { + std::vector mqs; + pullConsumer->fetchSubscribeMessageQueues(topic, mqs); + for (auto &mq : mqs) + { + long long offset = pullConsumer->fetchConsumeOffset(mq, false); + if (offset < 0) + continue; + rocketmq::PullResult pullResult = pullConsumer->pull(mq, tag, offset, maxMessageNum); + switch (pullResult.pullStatus) + { + case rocketmq::FOUND: + for (int j = 0; j < pullResult.msgFoundList.size(); j++) + { + int id = std::stoi(pullResult.msgFoundList[j].getBody()); + if (id == 19 && flag) + { + flag = false; + unconsumedMsgIds.insert(pullResult.msgFoundList[j].getMsgId()); + } + else + { + if (id == 19) + { + unconsumedMsgIds.insert(pullResult.msgFoundList[j].getMsgId()); + } + else + { + multi_logger->info("Message: {}", pullResult.msgFoundList[j].toString()); + offset += 1; + pullConsumer->updateConsumeOffset(mq, offset); + if (recvMsgs.contains(pullResult.msgFoundList[j].getMsgId())) + { + multi_logger->error("Duplicate message"); + return false; + } + else + { + recvMsgs.insert(pullResult.msgFoundList[j].getMsgId(), pullResult.msgFoundList[j]); + } + } + } + } + break; + case rocketmq::NO_MATCHED_MSG: + break; + case rocketmq::NO_NEW_MSG: + break; + case rocketmq::OFFSET_ILLEGAL: + break; + default: + break; + } + } + } + catch (const std::exception &e) + { + multi_logger->error("{}", e.what()); + return false; + } + if (recvMsgs.size() == 20) + return false; + } + return true; + } + + DataCollector &dequeueMessages = DataCollectorManager::getInstance().fetchListDataCollector(RandomUtils::getStringByUUID()); + for (auto &value : recvMsgs.getAllValues()) + { + dequeueMessages.addData(value.getMsgId()); + } + if (!verifyNormalMessage(*(producer->getEnqueueMessages()), dequeueMessages, unconsumedMsgIds)) + { + return false; + } + + return true; +} \ No newline at end of file diff --git a/cpp/rocketmq-client-cpp-tests/cpp4.x/test/CMakeLists.txt b/cpp/rocketmq-client-cpp-tests/cpp4.x/test/CMakeLists.txt new file mode 100644 index 0000000..b6a9846 --- /dev/null +++ b/cpp/rocketmq-client-cpp-tests/cpp4.x/test/CMakeLists.txt @@ -0,0 +1,21 @@ +# Licensed to the Apache Software Foundation (ASF) under one or more +# contributor license agreements. See the NOTICE file distributed with +# this work for additional information regarding copyright ownership. +# The ASF licenses this file to You under the Apache License, Version 2.0 +# (the "License"); you may not use this file except in compliance with +# the License. You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +add_subdirectory(client) +add_subdirectory(cluster) +add_subdirectory(offset) +add_subdirectory(filter) +add_subdirectory(server) +add_subdirectory(pull) +set(SOURCE_FILES ${SOURCE_FILES} PARENT_SCOPE) \ No newline at end of file diff --git a/cpp/rocketmq-client-cpp-tests/cpp4.x/test/client/CMakeLists.txt b/cpp/rocketmq-client-cpp-tests/cpp4.x/test/client/CMakeLists.txt new file mode 100644 index 0000000..7b0c52a --- /dev/null +++ b/cpp/rocketmq-client-cpp-tests/cpp4.x/test/client/CMakeLists.txt @@ -0,0 +1,18 @@ +# Licensed to the Apache Software Foundation (ASF) under one or more +# contributor license agreements. See the NOTICE file distributed with +# this work for additional information regarding copyright ownership. +# The ASF licenses this file to You under the Apache License, Version 2.0 +# (the "License"); you may not use this file except in compliance with +# the License. You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +add_subdirectory(consumer) +add_subdirectory(message) +add_subdirectory(producer) +set(SOURCE_FILES ${SOURCE_FILES} PARENT_SCOPE) \ No newline at end of file diff --git a/cpp/rocketmq-client-cpp-tests/cpp4.x/test/client/consumer/CMakeLists.txt b/cpp/rocketmq-client-cpp-tests/cpp4.x/test/client/consumer/CMakeLists.txt new file mode 100644 index 0000000..4ed6c92 --- /dev/null +++ b/cpp/rocketmq-client-cpp-tests/cpp4.x/test/client/consumer/CMakeLists.txt @@ -0,0 +1,18 @@ +# Licensed to the Apache Software Foundation (ASF) under one or more +# contributor license agreements. See the NOTICE file distributed with +# this work for additional information regarding copyright ownership. +# The ASF licenses this file to You under the Apache License, Version 2.0 +# (the "License"); you may not use this file except in compliance with +# the License. You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# list(APPEND SOURCE_FILES ${CMAKE_CURRENT_SOURCE_DIR}/ConsumerGroupTest.cpp) +file(GLOB CPP_FILES ${CMAKE_CURRENT_SOURCE_DIR}/*.cpp) +list(APPEND SOURCE_FILES ${CPP_FILES}) +set(SOURCE_FILES ${SOURCE_FILES} PARENT_SCOPE) \ No newline at end of file diff --git a/cpp/rocketmq-client-cpp-tests/cpp4.x/test/client/consumer/ConsumerGroupTest.cpp b/cpp/rocketmq-client-cpp-tests/cpp4.x/test/client/consumer/ConsumerGroupTest.cpp new file mode 100644 index 0000000..79772c9 --- /dev/null +++ b/cpp/rocketmq-client-cpp-tests/cpp4.x/test/client/consumer/ConsumerGroupTest.cpp @@ -0,0 +1,65 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +#include +#include +#include +#include +#include +#include +#include +#include "gtest/gtest.h" +#include "spdlog/spdlog.h" +#include "rocketmq/MQClientException.h" +#include "rocketmq/DefaultMQPullConsumer.h" +#include "rocketmq/MQMessageQueue.h" +#include "resource/Resource.h" +#include "enums/MessageType.h" +#include "frame/BaseOperate.h" + +extern std::shared_ptr multi_logger; +extern std::shared_ptr resource; + +// Use the built-in ConsumerGroup[DEFAULT_CONSUMER] to consume messages and expect consume failed +TEST(ConsumerGroupTest, testSystemInnerConsumerGroup) +{ + std::string groupId = "DEFAULT_CONSUMER"; + std::string topic = getTopic(MessageType::NORMAL, "testSystemInnerConsumerGroup", resource->getBrokerAddr(), resource->getNamesrv(), resource->getCluster()); + ASSERT_THROW({ + rocketmq::DefaultMQPullConsumer consumer(groupId); + consumer.setNamesrvAddr(resource->getNamesrv()); + consumer.setSessionCredentials(resource->getAccessKey(), resource->getSecretKey(), resource->getAccessChannel()); + consumer.registerMessageQueueListener(topic, NULL); + consumer.start(); + std::vector mqs; + + try + { + consumer.fetchSubscribeMessageQueues(topic, mqs); + auto iter = mqs.begin(); + for (; iter != mqs.end(); ++iter) + { + spdlog::info("mq: {}", (*iter).toString()); + } + } + catch (const rocketmq::MQException &e) + { + multi_logger->info("fetchSubscribeMessageQueues exception: {}", e.what()); + } + consumer.shutdown(); + }, + rocketmq::MQException); +} \ No newline at end of file diff --git a/cpp/rocketmq-client-cpp-tests/cpp4.x/test/client/consumer/PullConsumerInitTest.cpp b/cpp/rocketmq-client-cpp-tests/cpp4.x/test/client/consumer/PullConsumerInitTest.cpp new file mode 100644 index 0000000..34b6746 --- /dev/null +++ b/cpp/rocketmq-client-cpp-tests/cpp4.x/test/client/consumer/PullConsumerInitTest.cpp @@ -0,0 +1,45 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +#include +#include +#include +#include +#include "gtest/gtest.h" +#include "rocketmq/DefaultMQPullConsumer.h" +#include "rocketmq/MQMessage.h" +#include "resource/Resource.h" +#include "frame/BaseOperate.h" + +extern std::shared_ptr multi_logger; +extern std::shared_ptr resource; + +// Test the normal launch of PullConsumer and expect success +TEST(PullConsumerInitTest, testNormalPullConsumer) +{ + SCOPED_TRACE("Start [PullConsumer] failed, expected success."); + std::string groupId = getGroupId("testNoClientConfiguration"); + std::string topic = getTopic(MessageType::NORMAL, "testNoClientConfiguration", resource->getBrokerAddr(), resource->getNamesrv(), resource->getCluster()); + ASSERT_NO_THROW({ + rocketmq::DefaultMQPullConsumer consumer(groupId); + consumer.setNamesrvAddr(resource->getNamesrv()); + consumer.setSessionCredentials(resource->getAccessKey(), resource->getSecretKey(), resource->getAccessChannel()); + consumer.registerMessageQueueListener(topic, NULL); + consumer.start(); + std::this_thread::sleep_for(std::chrono::seconds(5)); + consumer.shutdown(); + }); +} \ No newline at end of file diff --git a/cpp/rocketmq-client-cpp-tests/cpp4.x/test/client/consumer/PushConsumerInitTest.cpp b/cpp/rocketmq-client-cpp-tests/cpp4.x/test/client/consumer/PushConsumerInitTest.cpp new file mode 100644 index 0000000..7cce9ec --- /dev/null +++ b/cpp/rocketmq-client-cpp-tests/cpp4.x/test/client/consumer/PushConsumerInitTest.cpp @@ -0,0 +1,230 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +#include +#include +#include +#include +#include "gtest/gtest.h" +#include "rocketmq/DefaultMQPushConsumer.h" +#include "rocketmq/DefaultMQProducer.h" +#include "rocketmq/MQMessageListener.h" +#include "rocketmq/MQMessage.h" +#include "resource/Resource.h" +#include "frame/BaseOperate.h" +#include "listener/MsgListener.h" + +extern std::shared_ptr multi_logger; +extern std::shared_ptr resource; + +// PushConsumer all parameters are set properly, expect start success +TEST(PushConsumerInitTest, testNormalSetting) +{ + SCOPED_TRACE("Start [PushConsumer] failed, expected success."); + std::string groupId = getGroupId("testNormalSetting"); + std::string topic = getTopic(MessageType::NORMAL, "testNormalSetting", resource->getBrokerAddr(), resource->getNamesrv(), resource->getCluster()); + ASSERT_NO_THROW({ + rocketmq::DefaultMQPushConsumer consumer(groupId); + consumer.setNamesrvAddr(resource->getNamesrv()); + consumer.setSessionCredentials(resource->getAccessKey(), resource->getSecretKey(), resource->getAccessChannel()); + consumer.setConsumeThreadCount(20); + consumer.setConsumeMessageBatchMaxSize(4 * 1024 * 1024); + consumer.subscribe(topic, "*"); + MsgListener msglistener; + consumer.registerMessageListener(&msglistener); + consumer.start(); + std::this_thread::sleep_for(std::chrono::seconds(5)); + consumer.shutdown(); + }); +} + +//// TEST(PushConsumerInitTest, testErrorAK){ +//// SCOPED_TRACE("Expected Start [PushConsumer] ClientException to throw, but it didn't."); +//// std::string groupId = getGroupId("testErrorAK"); +//// std::string topic = getTopic(MessageType::NORMAL, "testErrorAK", resource->getBrokerAddr(),resource->getNamesrv(),resource->getCluster()); +//// ASSERT_THROW({ +//// rocketmq::DefaultMQPushConsumer consumer(groupId); +//// consumer.setNamesrvAddr(resource->getNamesrv()); +//// consumer.setSessionCredentials("errorAk", resource->getSecretKey(), "YUN"); +//// consumer.setConsumeThreadCount(20); +//// consumer.setConsumeMessageBatchMaxSize(4 * 1024 * 1024); +//// consumer.subscribe(topic, "*"); +//// MsgListener msglistener; +//// consumer.registerMessageListener(&msglistener); +//// consumer.start(); +//// std::this_thread::sleep_for(std::chrono::seconds(5)); +//// consumer.shutdown(); +//// },rocketmq::MQException); +//// } + +//// TEST(PushConsumerInitTest, testErrorSK){ +//// SCOPED_TRACE("Expected Start [PushConsumer] ClientException to throw, but it didn't."); +//// std::string groupId = getGroupId("testErrorSK"); +//// std::string topic = getTopic(MessageType::NORMAL, "testErrorSK", resource->getBrokerAddr(),resource->getNamesrv(),resource->getCluster()); +//// ASSERT_THROW({ +//// rocketmq::DefaultMQPushConsumer consumer(groupId); +//// consumer.setNamesrvAddr(resource->getNamesrv()); +//// consumer.setSessionCredentials(resource->getAccessKey(), "errorAk", "YUN"); +//// consumer.setConsumeThreadCount(20); +//// consumer.setConsumeMessageBatchMaxSize(4 * 1024 * 1024); +//// consumer.subscribe(topic, "*"); +//// MsgListener msglistener; +//// consumer.registerMessageListener(&msglistener); +//// consumer.start(); +//// std::this_thread::sleep_for(std::chrono::seconds(5)); +//// consumer.shutdown(); +//// },rocketmq::MQException); +//// } + +// Correct setting the 'EndPoint' of the consumer client,expect start failed +TEST(PushConsumerInitTest, testNormalNameserver) +{ + SCOPED_TRACE("Start [PushConsumer] [Producer], expected success."); + std::string groupId = getGroupId("testNormalNameserver"); + std::string topic = getTopic(MessageType::NORMAL, "testNormalNameserver", resource->getBrokerAddr(), resource->getNamesrv(), resource->getCluster()); + + rocketmq::DefaultMQPushConsumer consumer(groupId); + consumer.setNamesrvAddr(resource->getNamesrv()); + consumer.setSessionCredentials(resource->getAccessKey(), resource->getSecretKey(), resource->getAccessChannel()); + consumer.subscribe(topic, "*"); + consumer.setConsumeFromWhere(rocketmq::CONSUME_FROM_LAST_OFFSET); + consumer.setConsumeThreadCount(4); + MsgListener *msglistener = new MsgListener(); + consumer.registerMessageListener(msglistener); + consumer.start(); + + std::this_thread::sleep_for(std::chrono::seconds(2)); + + rocketmq::DefaultMQProducer producer(groupId); + producer.setTcpTransportTryLockTimeout(1000); + producer.setTcpTransportConnectTimeout(400); + producer.setNamesrvAddr(resource->getNamesrv()); + producer.start(); + + int msgcount = 10; + for (int i = 0; i < msgcount; ++i) + { + rocketmq::MQMessage msg(topic, "*", RandomUtils::getStringByUUID()); + producer.send(msg); + } + std::this_thread::sleep_for(std::chrono::seconds(10)); + ASSERT_EQ(msgcount, msglistener->getMsgCount()); + producer.shutdown(); + consumer.shutdown(); + delete msglistener; +} + +////TEST(PushConsumerInitTest, testErrorNameserver){ +//// SCOPED_TRACE("Error setting the 'EndPoint' of the consumer client,expect start failed."); +//// std::string groupId = getGroupId("testErrorNameserver"); +//// std::string topic = "testErrorNameserver"; +//// ASSERT_THROW({ +//// rocketmq::DefaultMQPushConsumer consumer(groupId); +//// consumer.setNamesrvAddr("https://www.aliyun.com"); +//// consumer.setConsumeThreadCount(20); +//// consumer.setConsumeMessageBatchMaxSize(4 * 1024 * 1024); +//// consumer.subscribe(topic, "*"); +//// MsgListener msglistener; +//// consumer.registerMessageListener(&msglistener); +//// consumer.setAsyncPull(true); +//// consumer.start(); +//// std::this_thread::sleep_for(std::chrono::seconds(5)); +//// consumer.shutdown(); +//// },rocketmq::MQException); +////} + +//// TEST(PushConsumerInitTest, testErrorTopic){ +//// SCOPED_TRACE("Expected Start [PushConsumer] ClientException to throw, but it didn't."); +//// std::string groupId = getGroupId("testErrorTopic"); +//// std::string topic = "testErrorTopic"; +//// ASSERT_THROW({ +//// rocketmq::DefaultMQPushConsumer consumer(groupId); +//// consumer.setNamesrvAddr(resource->getNamesrv()); +//// consumer.setConsumeThreadCount(20); +//// consumer.setConsumeMessageBatchMaxSize(4 * 1024 * 1024); +//// consumer.subscribe("testErrorTopic", "*"); +//// MsgListener msglistener; +//// consumer.registerMessageListener(&msglistener); +//// consumer.start(); +//// std::this_thread::sleep_for(std::chrono::seconds(5)); +//// consumer.shutdown(); +//// },rocketmq::MQException); +//// } + +//// TEST(PushConsumerInitTest, testNoGroupId){ +//// SCOPED_TRACE("Expected Start [PushConsumer] ClientException to throw, but it didn't."); +//// std::string groupId = "123"; +//// std::string topic = getTopic(MessageType::NORMAL, "testNoGroupId", resource->getBrokerAddr(),resource->getNamesrv(),resource->getCluster()); +//// ASSERT_THROW({ +//// rocketmq::DefaultMQPushConsumer consumer(groupId); +//// consumer.setNamesrvAddr(resource->getNamesrv()); +//// consumer.setConsumeThreadCount(20); +//// consumer.setConsumeMessageBatchMaxSize(4 * 1024 * 1024); +//// consumer.subscribe("testErrorTopic", "*"); +//// MsgListener msglistener; +//// consumer.registerMessageListener(&msglistener); +//// consumer.start(); +//// std::this_thread::sleep_for(std::chrono::seconds(5)); +//// consumer.shutdown(); +//// },rocketmq::MQException); +//// } + +////TEST(PushConsumerInitTest, testNoSubscription){ +//// SCOPED_TRACE("Expected Start [PushConsumer] ClientException to throw, but it didn't."); +//// std::string groupId = "testNoSubscription"; +//// std::string topic = getTopic(MessageType::NORMAL, "testNoSubscription", resource->getBrokerAddr(),resource->getNamesrv(),resource->getCluster()); +//// ASSERT_THROW({ +//// rocketmq::DefaultMQPushConsumer consumer(groupId); +//// consumer.setNamesrvAddr(resource->getNamesrv()); +//// consumer.setConsumeThreadCount(20); +//// consumer.setConsumeMessageBatchMaxSize(4 * 1024 * 1024); +//// MsgListener msglistener; +//// consumer.registerMessageListener(&msglistener); +//// consumer.start(); +//// std::this_thread::sleep_for(std::chrono::seconds(5)); +//// consumer.shutdown(); +//// },rocketmq::MQException); +//// } + +////TEST(PushConsumerInitTest, testNoClientConfiguration){ +//// SCOPED_TRACE("Expected Start [PushConsumer] ClientException to throw, but it didn't."); +//// std::string groupId = "testNoClientConfiguration"; +//// std::string topic = getTopic(MessageType::NORMAL, "testNoClientConfiguration", resource->getBrokerAddr(),resource->getNamesrv(),resource->getCluster()); +//// ASSERT_THROW({ +//// rocketmq::DefaultMQPushConsumer consumer(groupId); +//// MsgListener msglistener; +//// consumer.registerMessageListener(&msglistener); +//// consumer.subscribe(topic, "*"); +//// MsgListener msglistener; +//// consumer.registerMessageListener(&msglistener); +//// consumer.start(); +//// std::this_thread::sleep_for(std::chrono::seconds(5)); +//// consumer.shutdown(); +//// },rocketmq::MQException); +//// } + +////TEST(PushConsumerInitTest, testNoClientConfiguration){ +//// SCOPED_TRACE("Expected Start [PushConsumer] ClientException to throw, but it didn't."); +//// std::string groupId = "testNoListener"; +//// std::string topic = getTopic(MessageType::NORMAL, "testNoListener", resource->getBrokerAddr(),resource->getNamesrv(),resource->getCluster()); +//// ASSERT_THROW({ +//// rocketmq::DefaultMQPushConsumer consumer(groupId); +//// consumer.subscribe(topic, "*"); +//// consumer.start(); +//// std::this_thread::sleep_for(std::chrono::seconds(5)); +//// consumer.shutdown(); +//// },rocketmq::MQException); +//// } \ No newline at end of file diff --git a/cpp/rocketmq-client-cpp-tests/cpp4.x/test/client/message/CMakeLists.txt b/cpp/rocketmq-client-cpp-tests/cpp4.x/test/client/message/CMakeLists.txt new file mode 100644 index 0000000..b431eab --- /dev/null +++ b/cpp/rocketmq-client-cpp-tests/cpp4.x/test/client/message/CMakeLists.txt @@ -0,0 +1,19 @@ +# Licensed to the Apache Software Foundation (ASF) under one or more +# contributor license agreements. See the NOTICE file distributed with +# this work for additional information regarding copyright ownership. +# The ASF licenses this file to You under the Apache License, Version 2.0 +# (the "License"); you may not use this file except in compliance with +# the License. You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# list(APPEND SOURCE_FILES ${CMAKE_CURRENT_SOURCE_DIR}/MessageAbnormalTest.cpp) +# list(APPEND SOURCE_FILES ${CMAKE_CURRENT_SOURCE_DIR}/MessageBodyContentTest.cpp) +file(GLOB CPP_FILES ${CMAKE_CURRENT_SOURCE_DIR}/*.cpp) +list(APPEND SOURCE_FILES ${CPP_FILES}) +set(SOURCE_FILES ${SOURCE_FILES} PARENT_SCOPE) \ No newline at end of file diff --git a/cpp/rocketmq-client-cpp-tests/cpp4.x/test/client/message/MessageAbnormalTest.cpp b/cpp/rocketmq-client-cpp-tests/cpp4.x/test/client/message/MessageAbnormalTest.cpp new file mode 100644 index 0000000..357a33a --- /dev/null +++ b/cpp/rocketmq-client-cpp-tests/cpp4.x/test/client/message/MessageAbnormalTest.cpp @@ -0,0 +1,47 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +#include +#include +#include +#include "gtest/gtest.h" +#include "spdlog/spdlog.h" +#include "rocketmq/MQMessage.h" +#include "frame/BaseOperate.h" +#include "resource/Resource.h" + +extern std::shared_ptr multi_logger; +extern std::shared_ptr resource; + +//// TEST(MessageAbnormalTest, sendMsgBodyIsEmpty){ +//// std::string topic = getTopic(MessageType::NORMAL, "sendMsgBodyIsEmpty", resource->getBrokerAddr(), resource->getNamesrv(),resource->getCluster()); +//// ASSERT_THROW({ +//// rocketmq::MQMessage msg(topic,"*",""); +//// }, std::exception); +//// } + +////TEST(MessageAbnormalTest, sendMsgTopicIsEmpty){ +//// ASSERT_THROW({ +//// rocketmq::MQMessage msg("","*","body"); +//// }, std::exception); +////} + +////TEST(MessageAbnormalTest, sendMsgTopicIsEmpty){ +//// std::string topic = getTopic(MessageType::NORMAL, "sendMsgTopicIsEmpty", resource->getBrokerAddr(), resource->getNamesrv(),resource->getCluster()); +//// ASSERT_THROW({ +//// rocketmq::MQMessage msg(topic,"","body"); +//// }, std::exception); +////} \ No newline at end of file diff --git a/cpp/rocketmq-client-cpp-tests/cpp4.x/test/client/message/MessageBodyContentTest.cpp b/cpp/rocketmq-client-cpp-tests/cpp4.x/test/client/message/MessageBodyContentTest.cpp new file mode 100644 index 0000000..b4fb034 --- /dev/null +++ b/cpp/rocketmq-client-cpp-tests/cpp4.x/test/client/message/MessageBodyContentTest.cpp @@ -0,0 +1,126 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +#include +#include +#include +#include +#include +#include "gtest/gtest.h" +#include "spdlog/spdlog.h" +#include "rocketmq/MQMessage.h" +#include "rocketmq/DefaultMQProducer.h" +#include "rocketmq/DefaultMQPushConsumer.h" +#include "rocketmq/DefaultMQPullConsumer.h" +#include "frame/BaseOperate.h" +#include "listener/MsgListener.h" +#include "resource/Resource.h" +#include "factory/ConsumerFactory.h" +#include "factory/ProducerFactory.h" +#include "utils/VerifyUtils.h" + +extern std::shared_ptr multi_logger; +extern std::shared_ptr resource; + +// Send normal message, setting message body with space character, expect consume success +TEST(MessageBodyContentTest, testMessageBodyContentIsSpace) +{ + std::string topic = getTopic(MessageType::NORMAL, "testMessageBodyContentIsSpace", resource->getBrokerAddr(), resource->getNamesrv(), resource->getCluster()); + std::string group = getGroupId("testMessageBodyContentIsSpace"); + ASSERT_NO_THROW({ + std::shared_ptr msglistener = std::make_shared(); + auto pushConsumer = ConsumerFactory::getPushConsumer(topic, group, "*", msglistener); + std::this_thread::sleep_for(std::chrono::seconds(5)); + + auto producer = ProducerFactory::getProducer(group); + + std::string body = " "; + rocketmq::MQMessage msg(topic, "*", body); + rocketmq::SendResult sendResult = producer->send(msg); + + ASSERT_EQ(sendResult.getSendStatus(), rocketmq::SendStatus::SEND_OK); + + std::this_thread::sleep_for(std::chrono::seconds(5)); + + // std::vector msgs = VerifyUtils::fetchMessages(pullConsumer, topic); + auto msgs = msglistener->getMessages(); + + ASSERT_EQ(msgs.size(), 1); + ASSERT_EQ(msgs[0].getBody(), body); + + pushConsumer->shutdown(); + producer->shutdown(); + }); +} + +// Send normal message, setting message body with chinese character, expect consume success +TEST(MessageBodyContentTest, testMessageBodyContentIsChinese) +{ + std::string topic = getTopic(MessageType::NORMAL, "testMessageBodyContentIsChinese", resource->getBrokerAddr(), resource->getNamesrv(), resource->getCluster()); + std::string group = getGroupId("testMessageBodyContentIsChinese"); + ASSERT_NO_THROW({ + std::shared_ptr msglistener = std::make_shared(); + auto pushConsumer = ConsumerFactory::getPushConsumer(topic, group, "*", msglistener); + + auto producer = ProducerFactory::getProducer(group); + + std::string body = "中文字符"; + rocketmq::MQMessage msg(topic, "*", body); + rocketmq::SendResult sendResult = producer->send(msg); + + ASSERT_EQ(sendResult.getSendStatus(), rocketmq::SendStatus::SEND_OK); + + std::this_thread::sleep_for(std::chrono::seconds(5)); + + auto msgs = msglistener->getMessages(); + + ASSERT_EQ(msgs.size(), 1); + ASSERT_EQ(msgs[0].getBody(), body); + + pushConsumer->shutdown(); + producer->shutdown(); + }); +} + +// Send normal message, setting message body with emoji(😱) character, expect consume success +TEST(MessageBodyContentTest, testMessageBodyContentIsEmoji) +{ + std::string topic = getTopic(MessageType::NORMAL, "testMessageBodyContentIsEmoji", resource->getBrokerAddr(), resource->getNamesrv(), resource->getCluster()); + std::string group = getGroupId("testMessageBodyContentIsEmoji"); + ASSERT_NO_THROW({ + std::shared_ptr msglistener = std::make_shared(); + auto pushConsumer = ConsumerFactory::getPushConsumer(topic, group, "*", msglistener); + std::this_thread::sleep_for(std::chrono::seconds(5)); + + auto producer = ProducerFactory::getProducer(group); + + std::string body = "😱"; + rocketmq::MQMessage msg(topic, "*", body); + rocketmq::SendResult sendResult = producer->send(msg); + + ASSERT_EQ(sendResult.getSendStatus(), rocketmq::SendStatus::SEND_OK); + + std::this_thread::sleep_for(std::chrono::seconds(5)); + + auto msgs = msglistener->getMessages(); + + ASSERT_EQ(msgs.size(), 1); + ASSERT_EQ(msgs[0].getBody(), body); + + pushConsumer->shutdown(); + producer->shutdown(); + }); +} \ No newline at end of file diff --git a/cpp/rocketmq-client-cpp-tests/cpp4.x/test/client/message/MessageKeyTest.cpp b/cpp/rocketmq-client-cpp-tests/cpp4.x/test/client/message/MessageKeyTest.cpp new file mode 100644 index 0000000..219d606 --- /dev/null +++ b/cpp/rocketmq-client-cpp-tests/cpp4.x/test/client/message/MessageKeyTest.cpp @@ -0,0 +1,44 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +#include +#include "gtest/gtest.h" +#include "spdlog/spdlog.h" +#include "rocketmq/MQMessage.h" +#include "frame/BaseOperate.h" +#include "resource/Resource.h" +#include "utils/RandomUtils.h" +#include "factory/ProducerFactory.h" + +extern std::shared_ptr multi_logger; +extern std::shared_ptr resource; + +//// TEST(MessageKeyTest, testMessageKeyBeyond16KB){ +//// SCOPED_TRACE("message key beyond 16KB , expect throw exception but it didn't"); +//// std::string topic = getTopic(MessageType::NORMAL, "testMessageKeyBeyond16KB", resource->getBrokerAddr(), resource->getNamesrv(),resource->getCluster()); +//// std::string group = getGroupId("testMessageKeyBeyond16KB"); +//// auto producer = ProducerFactory::getProducer(group); +//// producer->start(); +//// ASSERT_THROW({ +//// std::string body = RandomUtils::randomAlphabetic(64); +//// std::string key = RandomUtils::randomAlphabetic(16 * 1024 + 1); +//// rocketmq::MQMessage msg(topic,"*",body); +//// rocketmq::SendResult sendResult = producer->send(msg); + +//// // ASSERT_EQ(sendResult.getSendStatus(), rocketmq::SendStatus::SEND_OK); +//// producer->shutdown(); +//// }, std::exception); +//// } \ No newline at end of file diff --git a/cpp/rocketmq-client-cpp-tests/cpp4.x/test/client/message/MessageTagTest.cpp b/cpp/rocketmq-client-cpp-tests/cpp4.x/test/client/message/MessageTagTest.cpp new file mode 100644 index 0000000..f039828 --- /dev/null +++ b/cpp/rocketmq-client-cpp-tests/cpp4.x/test/client/message/MessageTagTest.cpp @@ -0,0 +1,43 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +#include +#include "gtest/gtest.h" +#include "spdlog/spdlog.h" +#include "rocketmq/MQMessage.h" +#include "frame/BaseOperate.h" +#include "resource/Resource.h" +#include "utils/RandomUtils.h" +#include "factory/ProducerFactory.h" + +extern std::shared_ptr multi_logger; +extern std::shared_ptr resource; + +////TEST(MessageTagTest, testMessageTagBeyond16KB){ +//// SCOPED_TRACE("message tag beyond 16KB ,expect throw exception but it didn't"); +//// std::string topic = getTopic(MessageType::NORMAL, "testMessageTagBeyond16KB", resource->getBrokerAddr(), resource->getNamesrv(),resource->getCluster()); +//// std::string group = getGroupId("testMessageTagBeyond16KB"); +//// auto producer = ProducerFactory::getProducer(group); +//// producer->start(); +//// ASSERT_THROW({ +//// std::string body = RandomUtils::randomAlphabetic(16 * 1024 + 1); +//// std::string key = RandomUtils::randomAlphabetic(64); +//// rocketmq::MQMessage msg(topic,"*",body); +//// rocketmq::SendResult sendResult = producer->send(msg); +//// producer->shutdown(); +//// }, std::exception); +//// +////} \ No newline at end of file diff --git a/cpp/rocketmq-client-cpp-tests/cpp4.x/test/client/message/MessageUserPropertyTest.cpp b/cpp/rocketmq-client-cpp-tests/cpp4.x/test/client/message/MessageUserPropertyTest.cpp new file mode 100644 index 0000000..a2ac883 --- /dev/null +++ b/cpp/rocketmq-client-cpp-tests/cpp4.x/test/client/message/MessageUserPropertyTest.cpp @@ -0,0 +1,44 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +#include +#include "gtest/gtest.h" +#include "spdlog/spdlog.h" +#include "rocketmq/MQMessage.h" +#include "frame/BaseOperate.h" +#include "resource/Resource.h" +#include "utils/RandomUtils.h" +#include "factory/ProducerFactory.h" + +extern std::shared_ptr multi_logger; +extern std::shared_ptr resource; + +////TEST(MessageUserPropertyTest, testMessageUserPropertyBeyondSize128){ +//// SCOPED_TRACE("message user property beyond limit 128 ,expect throw exception but it didn't"); +//// std::string topic = getTopic(MessageType::NORMAL, "testMessageUserPropertyBeyondSize128", resource->getBrokerAddr(), resource->getNamesrv(),resource->getCluster()); +//// std::string group = getGroupId("testMessageUserPropertyBeyondSize128"); +//// auto producer = ProducerFactory::getProducer(group); +//// producer->start(); +//// ASSERT_THROW({ +//// std::string body = RandomUtils::randomAlphabetic(64); +//// rocketmq::MQMessage msg(topic,body); +//// for (int i = 0; i<=128; i++) { +//// msg.setProperty(std::to_string(i),RandomUtils::randomAlphabetic(2)); +//// } +//// producer->send(msg); +//// producer->shutdown(); +//// }, std::exception); +////} \ No newline at end of file diff --git a/cpp/rocketmq-client-cpp-tests/cpp4.x/test/client/message/NormalMessageSizeTest.cpp b/cpp/rocketmq-client-cpp-tests/cpp4.x/test/client/message/NormalMessageSizeTest.cpp new file mode 100644 index 0000000..b8efb93 --- /dev/null +++ b/cpp/rocketmq-client-cpp-tests/cpp4.x/test/client/message/NormalMessageSizeTest.cpp @@ -0,0 +1,40 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +#include +#include "gtest/gtest.h" +#include "spdlog/spdlog.h" +#include "rocketmq/MQMessage.h" +#include "frame/BaseOperate.h" +#include "resource/Resource.h" +#include "utils/RandomUtils.h" +#include "factory/ProducerFactory.h" + +extern std::shared_ptr multi_logger; +extern std::shared_ptr resource; + +////TEST(MessageUserPropertyTest, testDelayMsgSize4MAdd1){ +//// std::string topic = getTopic(MessageType::NORMAL, "testDelayMsgSize4MAdd1", resource->getBrokerAddr(), resource->getNamesrv(),resource->getCluster()); +//// std::string group = getGroupId("testDelayMsgSize4MAdd1"); +//// auto producer = ProducerFactory::getProducer(group); +//// producer->start(); +//// ASSERT_THROW({ +//// std::string body = RandomUtils::randomAlphabetic(4 * 1024 * 1024 + 1); +//// rocketmq::MQMessage msg(topic,"*",body); +//// producer->send(msg); +//// producer->shutdown(); +//// }, std::exception); +////} \ No newline at end of file diff --git a/cpp/rocketmq-client-cpp-tests/cpp4.x/test/client/producer/CMakeLists.txt b/cpp/rocketmq-client-cpp-tests/cpp4.x/test/client/producer/CMakeLists.txt new file mode 100644 index 0000000..c02d64b --- /dev/null +++ b/cpp/rocketmq-client-cpp-tests/cpp4.x/test/client/producer/CMakeLists.txt @@ -0,0 +1,17 @@ +# Licensed to the Apache Software Foundation (ASF) under one or more +# contributor license agreements. See the NOTICE file distributed with +# this work for additional information regarding copyright ownership. +# The ASF licenses this file to You under the Apache License, Version 2.0 +# (the "License"); you may not use this file except in compliance with +# the License. You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +file(GLOB CPP_FILES ${CMAKE_CURRENT_SOURCE_DIR}/*.cpp) +list(APPEND SOURCE_FILES ${CPP_FILES}) +set(SOURCE_FILES ${SOURCE_FILES} PARENT_SCOPE) \ No newline at end of file diff --git a/cpp/rocketmq-client-cpp-tests/cpp4.x/test/client/producer/ProducerInitTest.cpp b/cpp/rocketmq-client-cpp-tests/cpp4.x/test/client/producer/ProducerInitTest.cpp new file mode 100644 index 0000000..15e09ea --- /dev/null +++ b/cpp/rocketmq-client-cpp-tests/cpp4.x/test/client/producer/ProducerInitTest.cpp @@ -0,0 +1,40 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +#include +#include +#include "gtest/gtest.h" +#include "spdlog/spdlog.h" +#include "rocketmq/DefaultMQProducer.h" +#include "frame/BaseOperate.h" +#include "resource/Resource.h" +#include "utils/RandomUtils.h" + +extern std::shared_ptr multi_logger; +extern std::shared_ptr resource; + +////TEST(ProducerInitTest, testErrorNameSrvAddr){ +//// std::string groupId = getGroupId("testErrorNameSrvAddr"); +//// ASSERT_ANY_THROW({ +//// rocketmq::DefaultMQProducer producer(groupId); +//// producer.setTcpTransportTryLockTimeout(1000); +//// producer.setTcpTransportConnectTimeout(400); +//// producer.setNamesrvDomain("https://www.aliyun.com"); +//// producer.start(); +//// std::this_thread::sleep_for(std::chrono::seconds(5)); +//// producer.shutdown(); +//// }); +////} \ No newline at end of file diff --git a/cpp/rocketmq-client-cpp-tests/cpp4.x/test/cluster/CMakeLists.txt b/cpp/rocketmq-client-cpp-tests/cpp4.x/test/cluster/CMakeLists.txt new file mode 100644 index 0000000..c02d64b --- /dev/null +++ b/cpp/rocketmq-client-cpp-tests/cpp4.x/test/cluster/CMakeLists.txt @@ -0,0 +1,17 @@ +# Licensed to the Apache Software Foundation (ASF) under one or more +# contributor license agreements. See the NOTICE file distributed with +# this work for additional information regarding copyright ownership. +# The ASF licenses this file to You under the Apache License, Version 2.0 +# (the "License"); you may not use this file except in compliance with +# the License. You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +file(GLOB CPP_FILES ${CMAKE_CURRENT_SOURCE_DIR}/*.cpp) +list(APPEND SOURCE_FILES ${CPP_FILES}) +set(SOURCE_FILES ${SOURCE_FILES} PARENT_SCOPE) \ No newline at end of file diff --git a/cpp/rocketmq-client-cpp-tests/cpp4.x/test/cluster/ClusterTest.cpp b/cpp/rocketmq-client-cpp-tests/cpp4.x/test/cluster/ClusterTest.cpp new file mode 100644 index 0000000..c3dcc9c --- /dev/null +++ b/cpp/rocketmq-client-cpp-tests/cpp4.x/test/cluster/ClusterTest.cpp @@ -0,0 +1,123 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +#include +#include +#include +#include +#include +#include +#include "gtest/gtest.h" +#include "spdlog/spdlog.h" +#include "rocketmq/MQMessage.h" +#include "rocketmq/DefaultMQProducer.h" +#include "rocketmq/DefaultMQPushConsumer.h" +#include "rocketmq/DefaultMQPullConsumer.h" +#include "frame/BaseOperate.h" +#include "listener/MsgListener.h" +#include "listener/rmq/RMQNormalListener.h" +#include "resource/Resource.h" +#include "factory/ConsumerFactory.h" +#include "factory/ProducerFactory.h" +#include "utils/RandomUtils.h" +#include "utils/VerifyUtils.h" +#include "client/rmq/RMQNormalProducer.h" + +extern std::shared_ptr multi_logger; +extern std::shared_ptr resource; + +// Send 100 normal messages synchronously, start two consumers on different GroupId, and expect each client to consume up to 100 messages +TEST(ClusterTest, testBroadcastConsume) +{ + std::string topic = getTopic(MessageType::NORMAL, "testBroadcastConsume", resource->getBrokerAddr(), resource->getNamesrv(), resource->getCluster()); + std::string group1 = getGroupId("testBroadcastConsume1"); + std::string group2 = getGroupId("testBroadcastConsume2"); + std::string group = getGroupId("testBroadcastConsume"); + + std::shared_ptr listener1 = std::make_shared("Listener1"); + std::shared_ptr listener2 = std::make_shared("Listener2"); + auto pushConsumer1 = ConsumerFactory::getBroadcastPushConsumer(topic, group1, "*", listener1); + auto pushConsumer2 = ConsumerFactory::getBroadcastPushConsumer(topic, group2, "*", listener2); + + auto producer = ProducerFactory::getRMQProducer(group); + + for (int i = 0; i < 100; i++) + { + rocketmq::MQMessage msg(topic, "*", RandomUtils::getStringByUUID()); + rocketmq::SendResult sendResult = producer->send(msg); + ASSERT_EQ(sendResult.getSendStatus(), rocketmq::SendStatus::SEND_OK); + } + + ASSERT_EQ(100, producer->getEnqueueMessages()->getDataSize()); + + long endTime = std::chrono::duration_cast(std::chrono::system_clock::now().time_since_epoch()).count() + 240 * 1000L; + while (endTime > std::chrono::duration_cast(std::chrono::system_clock::now().time_since_epoch()).count()) + { + if (listener1->getDequeueMessages()->getDataSize() == 100 && listener2->getDequeueMessages()->getDataSize() == 100) + { + break; + } + std::this_thread::sleep_for(std::chrono::seconds(5)); + } + + ASSERT_TRUE(VerifyUtils::verifyNormalMessage(*(producer->getEnqueueMessages()), *(listener1->getDequeueMessages()))); + ASSERT_TRUE(VerifyUtils::verifyNormalMessage(*(producer->getEnqueueMessages()), *(listener2->getDequeueMessages()))); + + pushConsumer1->shutdown(); + pushConsumer2->shutdown(); + producer->shutdown(); +} + +// Send 100 normal messages synchronously, start three consumers on different GroupId, and expect each client to consume up to 100 messages +TEST(ClusterTest, testClusterConsume) +{ + std::string topic = getTopic(MessageType::NORMAL, "testClusterConsume", resource->getBrokerAddr(), resource->getNamesrv(), resource->getCluster()); + std::string group1 = getGroupId("testClusterConsume1"); + std::string group2 = getGroupId("testClusterConsume2"); + std::string group3 = getGroupId("testClusterConsume3"); + ASSERT_NO_THROW({ + std::shared_ptr listener1 = std::make_shared("Listener1"); + std::shared_ptr listener2 = std::make_shared("Listener2"); + std::shared_ptr listener3 = std::make_shared("Listener3"); + auto pushConsumer1 = ConsumerFactory::getPushConsumer(topic, group1, "*", listener1); + auto pushConsumer2 = ConsumerFactory::getPushConsumer(topic, group2, "*", listener2); + auto pushConsumer3 = ConsumerFactory::getPushConsumer(topic, group3, "*", listener3); + + auto producer = ProducerFactory::getRMQProducer(group1); + + int count = 0; + for (int i = 0; i < 100; i++) + { + rocketmq::MQMessage msg(topic, "*", RandomUtils::getStringByUUID()); + rocketmq::SendResult sendResult = producer->send(msg); + if (sendResult.getSendStatus() == rocketmq::SendStatus::SEND_OK) + { + count++; + } + } + + ASSERT_EQ(count, 100); + + ASSERT_TRUE(VerifyUtils::verifyNormalMessage(*(producer->getEnqueueMessages()), *(listener1->getDequeueMessages()))); + ASSERT_TRUE(VerifyUtils::verifyNormalMessage(*(producer->getEnqueueMessages()), *(listener2->getDequeueMessages()))); + ASSERT_TRUE(VerifyUtils::verifyNormalMessage(*(producer->getEnqueueMessages()), *(listener3->getDequeueMessages()))); + + pushConsumer1->shutdown(); + pushConsumer2->shutdown(); + pushConsumer3->shutdown(); + producer->shutdown(); + }); +} \ No newline at end of file diff --git a/cpp/rocketmq-client-cpp-tests/cpp4.x/test/cluster/LoadBalancingTest.cpp b/cpp/rocketmq-client-cpp-tests/cpp4.x/test/cluster/LoadBalancingTest.cpp new file mode 100644 index 0000000..aac3254 --- /dev/null +++ b/cpp/rocketmq-client-cpp-tests/cpp4.x/test/cluster/LoadBalancingTest.cpp @@ -0,0 +1,87 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +#include +#include +#include +#include +#include +#include +#include "gtest/gtest.h" +#include "spdlog/spdlog.h" +#include "rocketmq/MQMessage.h" +#include "rocketmq/DefaultMQProducer.h" +#include "rocketmq/DefaultMQPushConsumer.h" +#include "rocketmq/DefaultMQPullConsumer.h" +#include "frame/BaseOperate.h" +#include "listener/MsgListener.h" +#include "listener/rmq/RMQNormalListener.h" +#include "resource/Resource.h" +#include "factory/MessageFactory.h" +#include "factory/ConsumerFactory.h" +#include "factory/ProducerFactory.h" +#include "utils/RandomUtils.h" +#include "utils/NameUtils.h" +#include "utils/VerifyUtils.h" +#include "client/rmq/RMQNormalProducer.h" + +extern std::shared_ptr multi_logger; +extern std::shared_ptr resource; + +// Normal message load balancing, start 4 consumers, send 240 messages, expect 4 consumers to consume load balancing, each consume 1/4, then shutdown 2 of them, send 240 messages again, still load balancing, each consume half, and start 2 new consumers. Another 240 messages are sent, still load balanced, each consuming 1/4(bug: A segment error occurred when two pushConsumers were started for the same consumer group) +// TEST(LoadBalancingTest, testLoadBalancing_normal_message){ +// int SEND_NUM = 240; +// std::string topic = getTopic(MessageType::NORMAL, "testLoadBalancing_normal_message", resource->getBrokerAddr(), resource->getNamesrv(),resource->getCluster()); +// std::string group = getGroupId("testLoadBalancing_normal_message"); +// std::string tag = NameUtils::getRandomTagName(); + +// std::shared_ptr listener1 = std::make_shared("Listener1"); +// std::shared_ptr listener2 = std::make_shared("Listener2"); +// std::shared_ptr listener3 = std::make_shared("Listener3"); +// std::shared_ptr listener4 = std::make_shared("Listener4"); +// auto pushConsumer1 = ConsumerFactory::getPushConsumer(topic,group,tag,listener1); +// auto pushConsumer2 = ConsumerFactory::getPushConsumer(topic,group,tag,listener2); +// auto pushConsumer3 = ConsumerFactory::getPushConsumer(topic,group,tag,listener3); +// auto pushConsumer4 = ConsumerFactory::getPushConsumer(topic,group,tag,listener4); + +// auto producer = ProducerFactory::getRMQProducer(group); + +// ASSERT_NE(producer, nullptr); + +// for(int i=0;isend(message); +// } + +// long endTime = std::chrono::duration_cast(std::chrono::system_clock::now().time_since_epoch()).count()+240*1000L; +// while(endTime > std::chrono::duration_cast(std::chrono::system_clock::now().time_since_epoch()).count()){ +// std::cout<getDequeueMessages()->getDataSize() + +// listener2->getDequeueMessages()->getDataSize() + +// listener3->getDequeueMessages()->getDataSize() + +// listener4->getDequeueMessages()->getDataSize() <getDequeueMessages()->getDataSize() + +// listener2->getDequeueMessages()->getDataSize() + +// listener3->getDequeueMessages()->getDataSize() + +// listener4->getDequeueMessages()->getDataSize() == SEND_NUM){ +// break; +// } +// std::this_thread::sleep_for(std::chrono::seconds(5)); +// } +// ASSERT_EQ(SEND_NUM/4,listener1->getDequeueMessages()->getDataSize()); +// ASSERT_EQ(SEND_NUM/4,listener2->getDequeueMessages()->getDataSize()); +// ASSERT_EQ(SEND_NUM/4,listener3->getDequeueMessages()->getDataSize()); +// ASSERT_EQ(SEND_NUM/4,listener4->getDequeueMessages()->getDataSize()); +// } diff --git a/cpp/rocketmq-client-cpp-tests/cpp4.x/test/filter/CMakeLists.txt b/cpp/rocketmq-client-cpp-tests/cpp4.x/test/filter/CMakeLists.txt new file mode 100644 index 0000000..c02d64b --- /dev/null +++ b/cpp/rocketmq-client-cpp-tests/cpp4.x/test/filter/CMakeLists.txt @@ -0,0 +1,17 @@ +# Licensed to the Apache Software Foundation (ASF) under one or more +# contributor license agreements. See the NOTICE file distributed with +# this work for additional information regarding copyright ownership. +# The ASF licenses this file to You under the Apache License, Version 2.0 +# (the "License"); you may not use this file except in compliance with +# the License. You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +file(GLOB CPP_FILES ${CMAKE_CURRENT_SOURCE_DIR}/*.cpp) +list(APPEND SOURCE_FILES ${CPP_FILES}) +set(SOURCE_FILES ${SOURCE_FILES} PARENT_SCOPE) \ No newline at end of file diff --git a/cpp/rocketmq-client-cpp-tests/cpp4.x/test/filter/SqlFilterTest.cpp b/cpp/rocketmq-client-cpp-tests/cpp4.x/test/filter/SqlFilterTest.cpp new file mode 100644 index 0000000..834ab90 --- /dev/null +++ b/cpp/rocketmq-client-cpp-tests/cpp4.x/test/filter/SqlFilterTest.cpp @@ -0,0 +1,105 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +#include +#include +#include +#include +#include +#include +#include "gtest/gtest.h" +#include "spdlog/spdlog.h" +#include "rocketmq/MQMessage.h" +#include "rocketmq/DefaultMQProducer.h" +#include "rocketmq/DefaultMQPushConsumer.h" +#include "rocketmq/DefaultMQPullConsumer.h" +#include "frame/BaseOperate.h" +#include "listener/MsgListener.h" +#include "listener/rmq/RMQNormalListener.h" +#include "resource/Resource.h" +#include "factory/ConsumerFactory.h" +#include "factory/ProducerFactory.h" +#include "utils/RandomUtils.h" +#include "utils/NameUtils.h" +#include "utils/VerifyUtils.h" +#include "client/rmq/RMQNormalProducer.h" + +extern std::shared_ptr multi_logger; +extern std::shared_ptr resource; + +//// TEST(SqlFilterTest, testSendWithTagAndPropsRecvWithOutFilter){ +//// int SEND_NUM = 10; +//// std::string topic = getTopic(MessageType::NORMAL, "testSendWithTagAndPropsRecvWithOutFilter", resource->getBrokerAddr(), resource->getNamesrv(),resource->getCluster()); +//// std::string group = getGroupId("testSendWithTagAndPropsRecvWithOutFilter"); +//// std::map properties; +//// properties["regionId"] = "cn-hangzhou"; +//// properties["price"] = "30"; +//// std::string subExpression = "TRUE"; +//// ASSERT_NO_THROW({ +//// auto pushConsumer = ConsumerFactory::getRMQPushConsumer(topic,group,subExpression,std::make_shared()); +//// auto pullConsumer = ConsumerFactory::getRMQPullConsumer(topic,group); +//// std::this_thread::sleep_for(std::chrono::seconds(5)); +//// ASSERT_TRUE(VerifyUtils::tryReceiveOnce(topic,subExpression,pullConsumer->getPullConsumer())); +//// auto producer = ProducerFactory::getRMQProducer(group); +//// ASSERT_NE(producer, nullptr); +//// for (int i=0; isend(msg); +//// } +//// std::this_thread::sleep_for(std::chrono::seconds(5)); +//// ASSERT_EQ(SEND_NUM,producer->getEnqueueMessages()->getDataSize()); +//// ASSERT_TRUE(VerifyUtils::verifyNormalMessage(*(producer->getEnqueueMessages()),*(pushConsumer->getListener()->getDequeueMessages()))); +//// pushConsumer->shutdown(); +//// pullConsumer->shutdown(); +//// producer->shutdown(); +//// }); +//// } + +//// TEST(SqlFilterTest, testSqlSendTwoProps_SubFilterOne){ +//// int SEND_NUM = 10; +//// std::string topic = getTopic(MessageType::NORMAL, "testSqlSendTwoProps_SubFilterOne", resource->getBrokerAddr(), resource->getNamesrv(),resource->getCluster()); +//// std::string group = getGroupId("testSqlSendTwoProps_SubFilterOne"); +//// std::map properties1; +//// properties1["price"] = "10"; +//// std::map properties2; +//// properties2["price"] = "30"; +//// std::string subExpression = "*"; +//// ASSERT_NO_THROW({ +//// auto pushConsumer = ConsumerFactory::getRMQPushConsumer(topic,group,subExpression,std::make_shared()); +//// auto pullConsumer = ConsumerFactory::getRMQPullConsumer(topic,group); +//// std::this_thread::sleep_for(std::chrono::seconds(5)); +//// ASSERT_TRUE(VerifyUtils::tryReceiveOnce(topic,subExpression,pullConsumer->getPullConsumer())); +//// auto producer = ProducerFactory::getRMQProducer(group); +//// ASSERT_NE(producer, nullptr); +//// for (int i=0; isend(msg); +//// } +//// for (int i=0; isend(msg); +//// } +//// std::this_thread::sleep_for(std::chrono::seconds(5)); +//// ASSERT_EQ(SEND_NUM*2,producer->getEnqueueMessages()->getDataSize()); +//// ASSERT_TRUE(VerifyUtils::verifyNormalMessageWithUserProperties(*(producer->getEnqueueMessages()),*(pushConsumer->getListener()->getDequeueMessages()),properties1,10)); +//// pushConsumer->shutdown(); +//// pullConsumer->shutdown(); +//// producer->shutdown(); +//// }); +//// } \ No newline at end of file diff --git a/cpp/rocketmq-client-cpp-tests/cpp4.x/test/filter/TagFilterTest.cpp b/cpp/rocketmq-client-cpp-tests/cpp4.x/test/filter/TagFilterTest.cpp new file mode 100644 index 0000000..c97c064 --- /dev/null +++ b/cpp/rocketmq-client-cpp-tests/cpp4.x/test/filter/TagFilterTest.cpp @@ -0,0 +1,388 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +#include +#include +#include +#include +#include +#include +#include "gtest/gtest.h" +#include "spdlog/spdlog.h" +#include "rocketmq/MQMessage.h" +#include "rocketmq/DefaultMQProducer.h" +#include "rocketmq/DefaultMQPushConsumer.h" +#include "rocketmq/DefaultMQPullConsumer.h" +#include "frame/BaseOperate.h" +#include "listener/MsgListener.h" +#include "listener/rmq/RMQNormalListener.h" +#include "resource/Resource.h" +#include "factory/ConsumerFactory.h" +#include "factory/ProducerFactory.h" +#include "utils/RandomUtils.h" +#include "utils/NameUtils.h" +#include "utils/VerifyUtils.h" +#include "client/rmq/RMQNormalProducer.h" + +extern std::shared_ptr multi_logger; +extern std::shared_ptr resource; + +// Using tagA sent 10 messages, the use of tagA | | tagB filter messages, expect consumption to send 10 messages +TEST(TagFilterTest, testSendTagA_SubTagAorTagB) +{ + int SEND_NUM = 10; + std::string topic = getTopic(MessageType::NORMAL, "testSendTagA_SubTagAorTagB", resource->getBrokerAddr(), resource->getNamesrv(), resource->getCluster()); + std::string group = getGroupId("testSendTagA_SubTagAorTagB"); + std::string sendTag = NameUtils::getRandomTagName(); + std::string receiveTag = sendTag + "||TagB"; + ASSERT_NO_THROW({ + auto pushConsumer = ConsumerFactory::getRMQPushConsumer(topic, group, receiveTag, std::make_shared()); + + multi_logger->info("Wait for the PullConsumer"); + + auto producer = ProducerFactory::getRMQProducer(group); + + ASSERT_NE(producer, nullptr); + + producer->send(topic, sendTag, SEND_NUM); + + ASSERT_EQ(SEND_NUM, producer->getEnqueueMessages()->getDataSize()); + + ASSERT_TRUE(VerifyUtils::verifyNormalMessage(*(producer->getEnqueueMessages()), *(pushConsumer->getListener()->getDequeueMessages()))); + + pushConsumer->shutdown(); + producer->shutdown(); + }); +} + +// Use tagA sent 10 messages first, after using tagB sent 10 messages, use tagA | | tagB filter messages, expect consumption to send 20 messages +TEST(TagFilterTest, testSndTagATagB_SubTagATagB) +{ + int SEND_NUM = 10; + std::string topic = getTopic(MessageType::NORMAL, "testSndTagATagB_SubTagATagB", resource->getBrokerAddr(), resource->getNamesrv(), resource->getCluster()); + std::string group = getGroupId("testSndTagATagB_SubTagATagB"); + std::string sendTagA = NameUtils::getRandomTagName(); + std::string sendTagB = NameUtils::getRandomTagName(); + std::string receiveTag = sendTagA + "||" + sendTagB; + ASSERT_NO_THROW({ + auto pushConsumer = ConsumerFactory::getRMQPushConsumer(topic, group, receiveTag, std::make_shared()); + + multi_logger->info("Wait for the PullConsumer"); + + auto producer = ProducerFactory::getRMQProducer(group); + + ASSERT_NE(producer, nullptr); + + producer->send(topic, sendTagA, SEND_NUM); + producer->send(topic, sendTagB, SEND_NUM); + + ASSERT_EQ(SEND_NUM * 2, producer->getEnqueueMessages()->getDataSize()); + + ASSERT_TRUE(VerifyUtils::verifyNormalMessage(*(producer->getEnqueueMessages()), *(pushConsumer->getListener()->getDequeueMessages()))); + + pushConsumer->shutdown(); + producer->shutdown(); + }); +} + +// The tagA is used to send 10 messages, then the tagB is used to send 10 messages, and the * is used to filter the messages, expecting to consume 20 messages sent +TEST(TagFilterTest, testSendTagAAndTagB_SubAll) +{ + int SEND_NUM = 10; + std::string topic = getTopic(MessageType::NORMAL, "testSendTagAAndTagB_SubAll", resource->getBrokerAddr(), resource->getNamesrv(), resource->getCluster()); + std::string group = getGroupId("testSendTagAAndTagB_SubAll"); + std::string sendTagA = NameUtils::getRandomTagName(); + std::string sendTagB = NameUtils::getRandomTagName(); + std::string receiveTag = "*"; + ASSERT_NO_THROW({ + auto pushConsumer = ConsumerFactory::getRMQPushConsumer(topic, group, receiveTag, std::make_shared()); + + auto producer = ProducerFactory::getRMQProducer(group); + + ASSERT_NE(producer, nullptr); + + producer->send(topic, sendTagA, SEND_NUM); + producer->send(topic, sendTagB, SEND_NUM); + + ASSERT_EQ(SEND_NUM * 2, producer->getEnqueueMessages()->getDataSize()); + + ASSERT_TRUE(VerifyUtils::verifyNormalMessage(*(producer->getEnqueueMessages()), *(pushConsumer->getListener()->getDequeueMessages()))); + + pushConsumer->shutdown(); + producer->shutdown(); + }); +} + +// Send 10 tagA messages, subscribe to tagB messages, expect to consume up to 0 messages +TEST(TagFilterTest, testSendTagA_SubTagB) +{ + int SEND_NUM = 10; + std::string topic = getTopic(MessageType::NORMAL, "testSendTagA_SubTagB", resource->getBrokerAddr(), resource->getNamesrv(), resource->getCluster()); + std::string group = getGroupId("testSendTagA_SubTagB"); + std::string sendTag = NameUtils::getRandomTagName(); + std::string receiveTag = NameUtils::getRandomTagName(); + ASSERT_NO_THROW({ + auto pushConsumer = ConsumerFactory::getRMQPushConsumer(topic, group, receiveTag, std::make_shared()); + + auto producer = ProducerFactory::getRMQProducer(group); + + ASSERT_NE(producer, nullptr); + + producer->send(topic, sendTag, SEND_NUM); + + ASSERT_EQ(SEND_NUM, producer->getEnqueueMessages()->getDataSize()); + + std::this_thread::sleep_for(std::chrono::seconds(20)); + + ASSERT_EQ(0, pushConsumer->getListener()->getDequeueMessages()->getDataSize()); + + pushConsumer->shutdown(); + producer->shutdown(); + }); +} + +// Send 10 tagA messages, subscribe to tagA messages, expect to consume up to 10 messages +TEST(TagFilterTest, testSendTagA_SubTagA) +{ + int SEND_NUM = 10; + std::string topic = getTopic(MessageType::NORMAL, "testSendTagA_SubTagA", resource->getBrokerAddr(), resource->getNamesrv(), resource->getCluster()); + std::string group = getGroupId("testSendTagA_SubTagA"); + std::string sendTagA = NameUtils::getRandomTagName(); + ASSERT_NO_THROW({ + auto pushConsumer = ConsumerFactory::getRMQPushConsumer(topic, group, sendTagA, std::make_shared()); + + auto producer = ProducerFactory::getRMQProducer(group); + + ASSERT_NE(producer, nullptr); + + producer->send(topic, sendTagA, SEND_NUM); + + ASSERT_EQ(SEND_NUM, producer->getEnqueueMessages()->getDataSize()); + + ASSERT_TRUE(VerifyUtils::verifyNormalMessage(*(producer->getEnqueueMessages()), *(pushConsumer->getListener()->getDequeueMessages()))); + + pushConsumer->shutdown(); + producer->shutdown(); + }); +} + +// Consumption uses a very long tagA, sending 10 messages, expecting to consume 10 tagA messages +TEST(TagFilterTest, testLongTagSize) +{ + int SEND_NUM = 10; + std::string topic = getTopic(MessageType::NORMAL, "testLongTagSize", resource->getBrokerAddr(), resource->getNamesrv(), resource->getCluster()); + std::string group = getGroupId("testLongTagSize"); + std::string sendTag = RandomUtils::getStringWithNumber(1024 * 10); + ASSERT_NO_THROW({ + auto pushConsumer = ConsumerFactory::getRMQPushConsumer(topic, group, sendTag, std::make_shared()); + + auto producer = ProducerFactory::getRMQProducer(group); + + ASSERT_NE(producer, nullptr); + + producer->send(topic, sendTag, SEND_NUM); + + ASSERT_EQ(SEND_NUM, producer->getEnqueueMessages()->getDataSize()); + + ASSERT_TRUE(VerifyUtils::verifyNormalMessage(*(producer->getEnqueueMessages()), *(pushConsumer->getListener()->getDequeueMessages()))); + + pushConsumer->shutdown(); + producer->shutdown(); + }); +} + +// The consumption uses a space-spaced tag, and two tags are used to send 10 messages each, with the expectation of consuming up to 20 messages +TEST(TagFilterTest, testSubTagWithSpace) +{ + int SEND_NUM = 10; + std::string topic = getTopic(MessageType::NORMAL, "testSubTagWithSpace", resource->getBrokerAddr(), resource->getNamesrv(), resource->getCluster()); + std::string group = getGroupId("testSubTagWithSpace"); + std::string sendTagA = NameUtils::getRandomTagName(); + std::string sendTagB = NameUtils::getRandomTagName(); + std::string receiveTag = " " + sendTagA + " || " + sendTagB + " "; + ASSERT_NO_THROW({ + auto pushConsumer = ConsumerFactory::getRMQPushConsumer(topic, group, receiveTag, std::make_shared()); + + auto producer = ProducerFactory::getRMQProducer(group); + + ASSERT_NE(producer, nullptr); + + producer->send(topic, sendTagA, SEND_NUM); + producer->send(topic, sendTagB, SEND_NUM); + + ASSERT_EQ(SEND_NUM * 2, producer->getEnqueueMessages()->getDataSize()); + + ASSERT_TRUE(VerifyUtils::verifyNormalMessage(*(producer->getEnqueueMessages()), *(pushConsumer->getListener()->getDequeueMessages()))); + + pushConsumer->shutdown(); + producer->shutdown(); + }); +} + +////TEST(TagFilterTest, testTagWithSpecialSymbol01){ +//// int SEND_NUM = 10; +//// SCOPED_TRACE("Send messages with tag \"|@\", Expected send() to throw exception, but it didn't"); +//// std::string topic = getTopic(MessageType::NORMAL, "testTagWithSpecialSymbol01", resource->getBrokerAddr(), resource->getNamesrv(),resource->getCluster()); +//// std::string group = getGroupId("testTagWithSpecialSymbol01"); +//// +//// auto producer = ProducerFactory::getRMQProducer(group); +//// +//// ASSERT_NE(producer, nullptr); +//// +//// ASSERT_THROW({ +//// producer->send(topic,"|@",SEND_NUM); +//// }, std::exception); +//// +//// producer->shutdown(); +////} + +// Send 10 messages with tag='*', subscribe to messages with tag='*', expect to consume the message +TEST(TagFilterTest, testTagWithSpecialSymbol02) +{ + int SEND_NUM = 10; + std::string topic = getTopic(MessageType::NORMAL, "testTagWithSpecialSymbol02", resource->getBrokerAddr(), resource->getNamesrv(), resource->getCluster()); + std::string group = getGroupId("testTagWithSpecialSymbol02"); + ASSERT_NO_THROW({ + auto pushConsumer = ConsumerFactory::getRMQPushConsumer(topic, group, "*", std::make_shared()); + + auto producer = ProducerFactory::getRMQProducer(group); + + ASSERT_NE(producer, nullptr); + + producer->send(topic, "*", SEND_NUM); + + ASSERT_EQ(SEND_NUM, producer->getEnqueueMessages()->getDataSize()); + + VerifyUtils::verifyNormalMessage(*(producer->getEnqueueMessages()), *(pushConsumer->getListener()->getDequeueMessages())); + pushConsumer->shutdown(); + producer->shutdown(); + }); +} + +// Consumer use | | separators between the tag, respectively using two tag each 10 messages sent, and expect consumption to 20 messages +TEST(TagFilterTest, testTagWithSpecialSymbol03) +{ + int SEND_NUM = 10; + std::string topic = getTopic(MessageType::NORMAL, "testTagWithSpecialSymbol03", resource->getBrokerAddr(), resource->getNamesrv(), resource->getCluster()); + std::string group = getGroupId("testTagWithSpecialSymbol03"); + std::string sendTagA = NameUtils::getRandomTagName(); + std::string sendTagB = NameUtils::getRandomTagName(); + std::string receiveTag = sendTagA + "||||" + sendTagB; + ASSERT_NO_THROW({ + auto pushConsumer = ConsumerFactory::getRMQPushConsumer(topic, group, receiveTag, std::make_shared()); + + auto producer = ProducerFactory::getRMQProducer(group); + + ASSERT_NE(producer, nullptr); + + producer->send(topic, sendTagA, SEND_NUM); + producer->send(topic, sendTagB, SEND_NUM); + + ASSERT_EQ(SEND_NUM * 2, producer->getEnqueueMessages()->getDataSize()); + + VerifyUtils::verifyNormalMessage(*(producer->getEnqueueMessages()), *(pushConsumer->getListener()->getDequeueMessages())); + pushConsumer->shutdown(); + producer->shutdown(); + }); +} + +////TEST(TagFilterTest, testTagWithBlankSymbol){ +//// int SEND_NUM = 10; +//// std::string topic = getTopic(MessageType::NORMAL, "testTagWithBlankSymbol", resource->getBrokerAddr(), resource->getNamesrv(),resource->getCluster()); +//// std::string group = getGroupId("testTagWithBlankSymbol"); +//// std::string sendTagA = ""; +//// std::string sendTagB = " "; +//// ASSERT_NO_THROW({ +//// auto producer = ProducerFactory::getRMQProducer(group); +//// +//// ASSERT_NE(producer, nullptr); +//// +//// ASSERT_THROW({ +//// producer->send(topic,sendTagA,SEND_NUM); +//// }, std::exception); +//// +//// ASSERT_THROW({ +//// producer->send(topic,sendTagB,SEND_NUM); +//// }, std::exception); +//// producer->shutdown(); +//// }); +////} + +// The sent tag uses two strings with the same hash value, and the consumed tag uses BB, expecting to consume messages with tag=BB +TEST(TagFilterTest, testSendTagWithSameHashCode_SubWithOne) +{ + int SEND_NUM = 10; + std::string topic = getTopic(MessageType::NORMAL, "testSendTagWithSameHashCode_SubWithOne", resource->getBrokerAddr(), resource->getNamesrv(), resource->getCluster()); + std::string group = getGroupId("testSendTagWithSameHashCode_SubWithOne"); + std::string sendTagA = "BB"; + std::string sendTagB = "Aa"; + std::string receiveTag = "BB"; + ASSERT_NO_THROW({ + auto pushConsumer = ConsumerFactory::getRMQPushConsumer(topic, group, receiveTag, std::make_shared()); + + auto producer = ProducerFactory::getRMQProducer(group); + + ASSERT_NE(producer, nullptr); + + producer->send(topic, sendTagA, SEND_NUM); + + VerifyUtils::verifyNormalMessage(*(producer->getEnqueueMessages()), *(pushConsumer->getListener()->getDequeueMessages())); + + pushConsumer->getListener()->clearMsg(); + + producer->send(topic, sendTagB, SEND_NUM); + + std::this_thread::sleep_for(std::chrono::seconds(10)); + + ASSERT_EQ(0, pushConsumer->getListener()->getDequeueMessages()->getDataSize()); + + pushConsumer->shutdown(); + producer->shutdown(); + }); +} + +// Send 10 messages with tag=BB, 10 messages with tag=bb, subscribe with tag=BB, expect case-sensitive messages to be consumed to tag=BB +TEST(TagFilterTest, testTagCaseSensitive) +{ + int SEND_NUM = 10; + std::string topic = getTopic(MessageType::NORMAL, "testTagCaseSensitive", resource->getBrokerAddr(), resource->getNamesrv(), resource->getCluster()); + std::string group = getGroupId("testTagCaseSensitive"); + std::string sendTagA = "BB"; + std::string sendTagB = "bb"; + std::string receiveTag = "BB"; + ASSERT_NO_THROW({ + auto pushConsumer = ConsumerFactory::getRMQPushConsumer(topic, group, receiveTag, std::make_shared()); + + auto producer = ProducerFactory::getRMQProducer(group); + + ASSERT_NE(producer, nullptr); + + producer->send(topic, sendTagA, SEND_NUM); + + VerifyUtils::verifyNormalMessage(*(producer->getEnqueueMessages()), *(pushConsumer->getListener()->getDequeueMessages())); + + pushConsumer->getListener()->clearMsg(); + + producer->send(topic, sendTagB, SEND_NUM); + + std::this_thread::sleep_for(std::chrono::seconds(10)); + + ASSERT_EQ(0, pushConsumer->getListener()->getDequeueMessages()->getDataSize()); + + pushConsumer->shutdown(); + producer->shutdown(); + }); +} \ No newline at end of file diff --git a/cpp/rocketmq-client-cpp-tests/cpp4.x/test/offset/CMakeLists.txt b/cpp/rocketmq-client-cpp-tests/cpp4.x/test/offset/CMakeLists.txt new file mode 100644 index 0000000..c02d64b --- /dev/null +++ b/cpp/rocketmq-client-cpp-tests/cpp4.x/test/offset/CMakeLists.txt @@ -0,0 +1,17 @@ +# Licensed to the Apache Software Foundation (ASF) under one or more +# contributor license agreements. See the NOTICE file distributed with +# this work for additional information regarding copyright ownership. +# The ASF licenses this file to You under the Apache License, Version 2.0 +# (the "License"); you may not use this file except in compliance with +# the License. You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +file(GLOB CPP_FILES ${CMAKE_CURRENT_SOURCE_DIR}/*.cpp) +list(APPEND SOURCE_FILES ${CPP_FILES}) +set(SOURCE_FILES ${SOURCE_FILES} PARENT_SCOPE) \ No newline at end of file diff --git a/cpp/rocketmq-client-cpp-tests/cpp4.x/test/offset/OffsetTest.cpp b/cpp/rocketmq-client-cpp-tests/cpp4.x/test/offset/OffsetTest.cpp new file mode 100644 index 0000000..3a72bdc --- /dev/null +++ b/cpp/rocketmq-client-cpp-tests/cpp4.x/test/offset/OffsetTest.cpp @@ -0,0 +1,237 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +#include +#include +#include +#include +#include "gtest/gtest.h" +#include "spdlog/logger.h" +#include "rocketmq/MQMessageExt.h" +#include "rocketmq/DefaultMQPushConsumer.h" +#include "rocketmq/MQMessageListener.h" +#include "enums/MessageType.h" +#include "frame/BaseOperate.h" +#include "resource/Resource.h" +#include "utils/NameUtils.h" +#include "utils/RandomUtils.h" +#include "utils/VerifyUtils.h" +#include "utils/SimpleConcurrentHashMapUtils.h" +#include "utils/SimpleConcurrentVectorUtils.h" +#include "factory/ConsumerFactory.h" +#include "factory/ProducerFactory.h" +#include "factory/MessageFactory.h" + +extern std::shared_ptr multi_logger; +extern std::shared_ptr resource; + +// Send 10 messages, set other groupid's pushconsumer consumption from first, expect to accept all messages again +TEST(OffsetTest, testConsumeFromFisrtOffset) +{ + int SEND_NUM = 10; + std::string topic = getTopic(MessageType::NORMAL, "testConsumeFromFisrtOffset", resource->getBrokerAddr(), resource->getNamesrv(), resource->getCluster()); + std::string group1 = getGroupId("testConsumeFromFisrtOffset1"); + std::string group2 = getGroupId("testConsumeFromFisrtOffset2"); + std::string tag = NameUtils::getRandomTagName(); + + std::shared_ptr listener1 = std::make_shared("Listener1"); + std::shared_ptr listener2 = std::make_shared("Listener2"); + + auto rmqPushConsumer1 = std::make_shared(group1); + rmqPushConsumer1->setNamesrvAddr(resource->getNamesrv()); + rmqPushConsumer1->setSessionCredentials(resource->getAccessKey(), resource->getSecretKey(), resource->getAccessChannel()); + rmqPushConsumer1->setConsumeFromWhere(rocketmq::CONSUME_FROM_LAST_OFFSET); + rmqPushConsumer1->setConsumeThreadCount(4); + rmqPushConsumer1->subscribe(topic, tag); + rmqPushConsumer1->registerMessageListener(listener1.get()); + rmqPushConsumer1->start(); + + auto producer = ProducerFactory::getRMQProducer(group1); + + ASSERT_NE(producer, nullptr); + + for (int i = 0; i < SEND_NUM; i++) + { + auto message = MessageFactory::buildMessage(topic, tag, std::to_string(i)); + producer->send(message); + } + + long endTime = std::chrono::duration_cast(std::chrono::system_clock::now().time_since_epoch()).count() + 240 * 1000L; + while (endTime > std::chrono::duration_cast(std::chrono::system_clock::now().time_since_epoch()).count()) + { + if (listener1->getDequeueMessages()->getDataSize() == SEND_NUM) + { + break; + } + std::this_thread::sleep_for(std::chrono::seconds(5)); + } + + ASSERT_EQ(listener1->getDequeueMessages()->getDataSize(), SEND_NUM); + + rmqPushConsumer1->shutdown(); + + multi_logger->info("first pushconsumer end"); + + auto rmqPushConsumer2 = std::make_shared(group2); + rmqPushConsumer2->setNamesrvAddr(resource->getNamesrv()); + rmqPushConsumer2->setSessionCredentials(resource->getAccessKey(), resource->getSecretKey(), resource->getAccessChannel()); + rmqPushConsumer2->setConsumeFromWhere(rocketmq::CONSUME_FROM_FIRST_OFFSET); + rmqPushConsumer2->setConsumeThreadCount(4); + rmqPushConsumer2->subscribe(topic, tag); + rmqPushConsumer2->registerMessageListener(listener2.get()); + rmqPushConsumer2->start(); + + long endTime2 = std::chrono::duration_cast(std::chrono::system_clock::now().time_since_epoch()).count() + 240 * 1000L; + while (endTime2 > std::chrono::duration_cast(std::chrono::system_clock::now().time_since_epoch()).count()) + { + if (listener2->getDequeueMessages()->getDataSize() == SEND_NUM) + { + break; + } + std::this_thread::sleep_for(std::chrono::seconds(5)); + } + + ASSERT_EQ(listener2->getDequeueMessages()->getDataSize(), SEND_NUM); + + rmqPushConsumer2->shutdown(); + producer->shutdown(); +} + +// Backlog 100 messages, start the consumer, and set the pull message from the LAST, expect to consume 100 messages +TEST(OffsetTest, testConsumeFromLastOffset){ + int SEND_NUM = 100; + std::string topic = getTopic(MessageType::NORMAL, "testConsumeFromLastOffset", resource->getBrokerAddr(), resource->getNamesrv(), resource->getCluster()); + std::string group = getGroupId("testConsumeFromLastOffset"); + std::string tag = NameUtils::getRandomTagName(); + + auto producer = ProducerFactory::getRMQProducer(group); + ASSERT_NE(producer, nullptr); + + for (int i = 0; i < SEND_NUM; i++) + { + auto message = MessageFactory::buildMessage(topic, tag, std::to_string(i)); + producer->send(message); + } + + std::shared_ptr listener = std::make_shared("Listener"); + + auto rmqPushConsumer = std::make_shared(group); + rmqPushConsumer->setNamesrvAddr(resource->getNamesrv()); + rmqPushConsumer->setSessionCredentials(resource->getAccessKey(), resource->getSecretKey(), resource->getAccessChannel()); + rmqPushConsumer->setConsumeFromWhere(rocketmq::CONSUME_FROM_LAST_OFFSET); + rmqPushConsumer->setConsumeThreadCount(4); + rmqPushConsumer->subscribe(topic, tag); + rmqPushConsumer->registerMessageListener(listener.get()); + rmqPushConsumer->start(); + + long endTime = std::chrono::duration_cast(std::chrono::system_clock::now().time_since_epoch()).count() + 240 * 1000L; + while (endTime > std::chrono::duration_cast(std::chrono::system_clock::now().time_since_epoch()).count()) + { + if (listener->getDequeueMessages()->getDataSize() == SEND_NUM) + { + break; + } + std::this_thread::sleep_for(std::chrono::seconds(5)); + } + + ASSERT_EQ(listener->getDequeueMessages()->getDataSize(), SEND_NUM); + + rmqPushConsumer->shutdown(); + producer->shutdown(); +} + +// send 10 messages, PullConsumer normally receives messages, but does not update messages offset, expect the messages are receive again +TEST(OffsetTest, test_pull_receive_nack) +{ + int SEND_NUM = 10; + std::string topic = getTopic(MessageType::NORMAL, "test_pull_receive_nack", resource->getBrokerAddr(), resource->getNamesrv(), resource->getCluster()); + std::string group = getGroupId("test_pull_receive_nack"); + std::string tag = NameUtils::getRandomTagName(); + + auto pullConsumer = ConsumerFactory::getPullConsumer(topic, group); + + auto producer = ProducerFactory::getRMQProducer(group); + ASSERT_NE(producer, nullptr); + + for (int i = 0; i < SEND_NUM; i++) + { + auto message = MessageFactory::buildMessage(topic, tag, tag + "-" + std::to_string(i)); + producer->send(message); + } + std::this_thread::sleep_for(std::chrono::seconds(2)); + + ASSERT_EQ(SEND_NUM, producer->getEnqueueMessages()->getDataSize()); + + std::vector mqs; + pullConsumer->fetchSubscribeMessageQueues(topic, mqs); + + std::vector receivedMessage; + SimpleConcurrentHashMap> map; + + long endTime = std::chrono::duration_cast(std::chrono::system_clock::now().time_since_epoch()).count() + 30 * 1000L; + while (endTime > std::chrono::duration_cast(std::chrono::system_clock::now().time_since_epoch()).count()) + { + std::vector> runnables; + for (auto &mq : mqs) + { + runnables.push_back([&]() + { + long long offset = pullConsumer->fetchConsumeOffset(mq, false); + if(offset<0) return; + rocketmq::PullResult pullResult = pullConsumer->pull(mq, tag, offset, SEND_NUM); + switch (pullResult.pullStatus) { + case rocketmq::FOUND: + for(int j=0;j val(1); + map.insert(msgId,val.load()); + } + } + break; + case rocketmq::NO_MATCHED_MSG: + break; + case rocketmq::NO_NEW_MSG: + break; + case rocketmq::OFFSET_ILLEGAL: + break; + default: + break; + } }); + } + + std::vector> futures; + for (const auto &runnable : runnables) + { + futures.push_back(std::async(std::launch::async, runnable)); + } + + for (auto &future : futures) + { + future.get(); + } + } + + for (auto &value : map.getAllValues()) + { + ASSERT_TRUE(value > 1); + } + + pullConsumer->shutdown(); + producer->shutdown(); +} \ No newline at end of file diff --git a/cpp/rocketmq-client-cpp-tests/cpp4.x/test/pull/CMakeLists.txt b/cpp/rocketmq-client-cpp-tests/cpp4.x/test/pull/CMakeLists.txt new file mode 100644 index 0000000..c02d64b --- /dev/null +++ b/cpp/rocketmq-client-cpp-tests/cpp4.x/test/pull/CMakeLists.txt @@ -0,0 +1,17 @@ +# Licensed to the Apache Software Foundation (ASF) under one or more +# contributor license agreements. See the NOTICE file distributed with +# this work for additional information regarding copyright ownership. +# The ASF licenses this file to You under the Apache License, Version 2.0 +# (the "License"); you may not use this file except in compliance with +# the License. You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +file(GLOB CPP_FILES ${CMAKE_CURRENT_SOURCE_DIR}/*.cpp) +list(APPEND SOURCE_FILES ${CPP_FILES}) +set(SOURCE_FILES ${SOURCE_FILES} PARENT_SCOPE) \ No newline at end of file diff --git a/cpp/rocketmq-client-cpp-tests/cpp4.x/test/pull/PullAckTest.cpp b/cpp/rocketmq-client-cpp-tests/cpp4.x/test/pull/PullAckTest.cpp new file mode 100644 index 0000000..f3674d8 --- /dev/null +++ b/cpp/rocketmq-client-cpp-tests/cpp4.x/test/pull/PullAckTest.cpp @@ -0,0 +1,70 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +#include +#include +#include +#include +#include +#include +#include "gtest/gtest.h" +#include "rocketmq/SendResult.h" +#include "spdlog/logger.h" +#include "enums/MessageType.h" +#include "frame/BaseOperate.h" +#include "resource/Resource.h" +#include "utils/NameUtils.h" +#include "utils/RandomUtils.h" +#include "utils/VerifyUtils.h" +#include "factory/ConsumerFactory.h" +#include "factory/ProducerFactory.h" +#include "factory/MessageFactory.h" + +extern std::shared_ptr multi_logger; +extern std::shared_ptr resource; + +// Send 20 normal messages synchronously and expect consume with receive and ack messages successful +TEST(PullAckTest, testNormal_pull_receive_ack) +{ + int SEND_NUM = 20; + std::string topic = getTopic(MessageType::NORMAL, "testNormal_pull_receive_ack", resource->getBrokerAddr(), resource->getNamesrv(), resource->getCluster()); + std::string group = getGroupId("testNormal_pull_receive_ack"); + std::string tag = NameUtils::getRandomTagName(); + + auto pullConsumer = ConsumerFactory::getPullConsumer(topic, group); + + std::this_thread::sleep_for(std::chrono::seconds(2)); + + ASSERT_TRUE(VerifyUtils::tryReceiveOnce(topic, tag, pullConsumer)); + + auto producer = ProducerFactory::getRMQProducer(group); + + ASSERT_NE(producer, nullptr); + + for (int i = 0; i < SEND_NUM; i++) + { + auto message = MessageFactory::buildMessage(topic, tag, tag + "-" + std::to_string(i)); + producer->send(message); + } + std::this_thread::sleep_for(std::chrono::seconds(2)); + + ASSERT_EQ(SEND_NUM, producer->getEnqueueMessages()->getDataSize()); + + ASSERT_TRUE(VerifyUtils::waitReceiveThenAck(producer, pullConsumer, topic, tag, 1)); + + pullConsumer->shutdown(); + producer->shutdown(); +} \ No newline at end of file diff --git a/cpp/rocketmq-client-cpp-tests/cpp4.x/test/pull/PullOrderParamTest.cpp b/cpp/rocketmq-client-cpp-tests/cpp4.x/test/pull/PullOrderParamTest.cpp new file mode 100644 index 0000000..4a751e4 --- /dev/null +++ b/cpp/rocketmq-client-cpp-tests/cpp4.x/test/pull/PullOrderParamTest.cpp @@ -0,0 +1,103 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +#include +#include +#include +#include +#include +#include +#include "gtest/gtest.h" +#include "rocketmq/SendResult.h" +#include "spdlog/logger.h" +#include "enums/MessageType.h" +#include "frame/BaseOperate.h" +#include "resource/Resource.h" +#include "utils/NameUtils.h" +#include "utils/RandomUtils.h" +#include "utils/VerifyUtils.h" +#include "factory/ConsumerFactory.h" +#include "factory/ProducerFactory.h" +#include "factory/MessageFactory.h" + +extern std::shared_ptr multi_logger; +extern std::shared_ptr resource; + +// When sending 20 sequential messages synchronously using the same queue, PullConsumer normally receives messages, but does not ack messages, and keeps the sequence; the messages are stuck at the first +TEST(PullOrderParamTest, testFIFO_pull_receive_nack) +{ + int SEND_NUM = 20; + std::string topic = getTopic(MessageType::FIFO, "testFIFO_pull_receive_nack", resource->getBrokerAddr(), resource->getNamesrv(), resource->getCluster()); + std::string group = getGroupId("testFIFO_pull_receive_nack"); + std::string tag = NameUtils::getRandomTagName(); + + auto pullConsumer = ConsumerFactory::getPullConsumer(topic, group); + + std::this_thread::sleep_for(std::chrono::seconds(2)); + + ASSERT_TRUE(VerifyUtils::tryReceiveOnce(topic, tag, pullConsumer)); + + auto producer = ProducerFactory::getRMQProducer(group); + + ASSERT_NE(producer, nullptr); + + for (int i = 0; i < SEND_NUM; i++) + { + auto message = MessageFactory::buildMessage(topic, tag, std::to_string(i)); + producer->sendOrderMessage(message, 0); + } + std::this_thread::sleep_for(std::chrono::seconds(1)); + + ASSERT_EQ(SEND_NUM, producer->getEnqueueMessages()->getDataSize()); + + ASSERT_TRUE(VerifyUtils::waitFIFOParamReceiveThenNAck(producer, pullConsumer, topic, tag, 1)); + + pullConsumer->shutdown(); + producer->shutdown(); +} + +// Twenty sequential messages are sent synchronously and receive 3 messages in batch. All pulled messages are ack messages except the last one. It is expected that all messages remain sequential and are consumed again after a certain time +TEST(PullOrderParamTest, testFIFO_pull_receive_multi_nack) +{ + int SEND_NUM = 20; + std::string topic = getTopic(MessageType::FIFO, "testFIFO_pull_receive_multi_nack", resource->getBrokerAddr(), resource->getNamesrv(), resource->getCluster()); + std::string group = getGroupId("testFIFO_pull_receive_multi_nack"); + std::string tag = NameUtils::getRandomTagName(); + + auto pullConsumer = ConsumerFactory::getPullConsumer(topic, group); + + std::this_thread::sleep_for(std::chrono::seconds(2)); + + ASSERT_TRUE(VerifyUtils::tryReceiveOnce(topic, tag, pullConsumer)); + + auto producer = ProducerFactory::getRMQProducer(group); + + ASSERT_NE(producer, nullptr); + + for (int i = 0; i < SEND_NUM; i++) + { + auto message = MessageFactory::buildMessage(topic, tag, std::to_string(i)); + producer->sendOrderMessage(message, 0); + } + std::this_thread::sleep_for(std::chrono::seconds(1)); + + ASSERT_EQ(SEND_NUM, producer->getEnqueueMessages()->getDataSize()); + + ASSERT_TRUE(VerifyUtils::waitFIFOParamReceiveThenAckExceptedLast(producer, pullConsumer, topic, tag, 20)); + + pullConsumer->shutdown(); + producer->shutdown(); +} \ No newline at end of file diff --git a/cpp/rocketmq-client-cpp-tests/cpp4.x/test/pull/PullOrderTest.cpp b/cpp/rocketmq-client-cpp-tests/cpp4.x/test/pull/PullOrderTest.cpp new file mode 100644 index 0000000..cbf56b0 --- /dev/null +++ b/cpp/rocketmq-client-cpp-tests/cpp4.x/test/pull/PullOrderTest.cpp @@ -0,0 +1,71 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +#include +#include +#include +#include +#include +#include +#include "gtest/gtest.h" +#include "rocketmq/SendResult.h" +#include "spdlog/logger.h" +#include "enums/MessageType.h" +#include "frame/BaseOperate.h" +#include "resource/Resource.h" +#include "utils/NameUtils.h" +#include "utils/RandomUtils.h" +#include "utils/VerifyUtils.h" +#include "factory/ConsumerFactory.h" +#include "factory/ProducerFactory.h" +#include "factory/MessageFactory.h" + +extern std::shared_ptr multi_logger; +extern std::shared_ptr resource; + +// Send 20 sequential messages synchronously, and expect PullConsumer to receive and ack messages properly and maintain the sequence +TEST(PullOrderTest, testFIFO_pull_receive_ack) +{ + int SEND_NUM = 20; + std::string topic = getTopic(MessageType::FIFO, "testFIFO_pull_receive_ack", resource->getBrokerAddr(), resource->getNamesrv(), resource->getCluster()); + std::string group = getGroupId("testFIFO_pull_receive_ack"); + std::string tag = NameUtils::getRandomTagName(); + + auto pullConsumer = ConsumerFactory::getPullConsumer(topic, group); + + std::this_thread::sleep_for(std::chrono::seconds(2)); + + ASSERT_TRUE(VerifyUtils::tryReceiveOnce(topic, tag, pullConsumer)); + + auto producer = ProducerFactory::getRMQProducer(group); + + ASSERT_NE(producer, nullptr); + + for (int i = 0; i < SEND_NUM; i++) + { + auto message = MessageFactory::buildMessage(topic, tag, std::to_string(i)); + producer->sendOrderMessage(message, 0); + } + + std::this_thread::sleep_for(std::chrono::seconds(1)); + + ASSERT_EQ(SEND_NUM, producer->getEnqueueMessages()->getDataSize()); + // It can only guarantee that the messages in the pulled queue are in order, but it cannot guarantee that the messages in the entire queue are in order. + ASSERT_TRUE(VerifyUtils::waitFIFOReceiveThenAck(producer, pullConsumer, topic, tag, 5)); + + pullConsumer->shutdown(); + producer->shutdown(); +} \ No newline at end of file diff --git a/cpp/rocketmq-client-cpp-tests/cpp4.x/test/pull/PullParamTest.cpp b/cpp/rocketmq-client-cpp-tests/cpp4.x/test/pull/PullParamTest.cpp new file mode 100644 index 0000000..c45a7bf --- /dev/null +++ b/cpp/rocketmq-client-cpp-tests/cpp4.x/test/pull/PullParamTest.cpp @@ -0,0 +1,130 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +#include +#include +#include +#include +#include +#include +#include "gtest/gtest.h" +#include "rocketmq/SendResult.h" +#include "spdlog/logger.h" +#include "enums/MessageType.h" +#include "frame/BaseOperate.h" +#include "resource/Resource.h" +#include "utils/NameUtils.h" +#include "utils/RandomUtils.h" +#include "utils/VerifyUtils.h" +#include "factory/ConsumerFactory.h" +#include "factory/ProducerFactory.h" +#include "factory/MessageFactory.h" + +extern std::shared_ptr multi_logger; +extern std::shared_ptr resource; + +////TEST(PullParamTest, test_waitAckException_reReceive_ack){ +//// std::string topic = getTopic(MessageType::NORMAL, "test_waitAckException_reReceive_ack", resource->getBrokerAddr(), resource->getNamesrv(),resource->getCluster()); +//// std::string group = getGroupId("test_waitAckException_reReceive_ack"); +//// std::string tag = NameUtils::getRandomTagName(); +//// +//// auto pullConsumer = ConsumerFactory::getPullConsumer(topic,group); +//// +//// std::this_thread::sleep_for(std::chrono::seconds(2)); +//// +//// ASSERT_TRUE(VerifyUtils::tryReceiveOnce(topic,tag,pullConsumer)); +//// +//// auto producer = ProducerFactory::getRMQProducer(group); +//// +//// ASSERT_NE(producer, nullptr); +//// +//// auto message = MessageFactory::buildMessage(topic,tag,RandomUtils::getStringByUUID()); +//// producer->send(message); +//// +//// std::this_thread::sleep_for(std::chrono::seconds(1)); +//// +//// ASSERT_EQ(1,producer->getEnqueueMessages()->getDataSize()); +//// pullConsumer->shutdown(); +//// producer->shutdown(); +////} + +// Send 300 normal messages synchronously, and after using PullConsumer receive(30,10s) messages, ack them after consuming them, expecting each receive to be less than or equal to 32 messages, and never receive the ack messages again +TEST(PullParamTest, testNormal_pull_receive_maxsize_sync) +{ + int SEND_NUM = 300; + std::string topic = getTopic(MessageType::NORMAL, "testNormal_pull_receive_maxsize_sync", resource->getBrokerAddr(), resource->getNamesrv(), resource->getCluster()); + std::string group = getGroupId("testNormal_pull_receive_maxsize_sync"); + std::string tag = NameUtils::getRandomTagName(); + + auto pullConsumer = ConsumerFactory::getPullConsumer(topic, group); + + std::this_thread::sleep_for(std::chrono::seconds(2)); + + ASSERT_TRUE(VerifyUtils::tryReceiveOnce(topic, tag, pullConsumer)); + + auto producer = ProducerFactory::getRMQProducer(group); + + ASSERT_NE(producer, nullptr); + + for (int i = 0; i < SEND_NUM; i++) + { + auto message = MessageFactory::buildMessage(topic, tag, std::to_string(i)); + producer->send(message); + } + + std::this_thread::sleep_for(std::chrono::seconds(1)); + + ASSERT_EQ(SEND_NUM, producer->getEnqueueMessages()->getDataSize()); + + ASSERT_TRUE(VerifyUtils::waitReceiveMaxsizeSync(producer, pullConsumer, topic, tag, 50)); + + pullConsumer->shutdown(); + producer->shutdown(); +} + +// Twenty ordinary messages are sent synchronously, and receive(50) messages are received in batch. All the pulled messages are ack() messages except the last one. expected the ack messages will not be consumed repeatedly +TEST(PullParamTest, testNormal_pull_receive_multi_nack) +{ + int SEND_NUM = 20; + std::string topic = getTopic(MessageType::NORMAL, "testNormal_pull_receive_multi_nack", resource->getBrokerAddr(), resource->getNamesrv(), resource->getCluster()); + std::string group = getGroupId("testNormal_pull_receive_multi_nack"); + std::string tag = NameUtils::getRandomTagName(); + + auto pullConsumer = ConsumerFactory::getPullConsumer(topic, group); + + std::this_thread::sleep_for(std::chrono::seconds(2)); + + ASSERT_TRUE(VerifyUtils::tryReceiveOnce(topic, tag, pullConsumer)); + + auto producer = ProducerFactory::getRMQProducer(group); + + ASSERT_NE(producer, nullptr); + + for (int i = 0; i < SEND_NUM; i++) + { + auto message = MessageFactory::buildMessage(topic, tag, std::to_string(i)); + producer->send(message); + } + + std::this_thread::sleep_for(std::chrono::seconds(1)); + + ASSERT_EQ(SEND_NUM, producer->getEnqueueMessages()->getDataSize()); + + ASSERT_TRUE(VerifyUtils::waitReceiveMultiNack(producer, pullConsumer, topic, tag, 50)); + + pullConsumer->shutdown(); + producer->shutdown(); +} diff --git a/cpp/rocketmq-client-cpp-tests/cpp4.x/test/pull/PullTopicTypeTest.cpp b/cpp/rocketmq-client-cpp-tests/cpp4.x/test/pull/PullTopicTypeTest.cpp new file mode 100644 index 0000000..f5b3870 --- /dev/null +++ b/cpp/rocketmq-client-cpp-tests/cpp4.x/test/pull/PullTopicTypeTest.cpp @@ -0,0 +1,68 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +#include +#include +#include +#include +#include +#include +#include "gtest/gtest.h" +#include "rocketmq/SendResult.h" +#include "spdlog/logger.h" +#include "enums/MessageType.h" +#include "frame/BaseOperate.h" +#include "resource/Resource.h" +#include "utils/NameUtils.h" +#include "utils/RandomUtils.h" +#include "utils/VerifyUtils.h" +#include "factory/ConsumerFactory.h" +#include "factory/ProducerFactory.h" +#include "factory/MessageFactory.h" +#include "common/MQTransactionListener.h" + +extern std::shared_ptr multi_logger; +extern std::shared_ptr resource; + +////TEST(PullTopicTypeTest, testTrans_pull_receive_ackAsync){ +//// int SEND_NUM = 10; +//// std::string topic = getTopic(MessageType::TRANSACTION, "testTrans_pull_receive_ackAsync", resource->getBrokerAddr(), resource->getNamesrv(),resource->getCluster()); +//// std::string group = getGroupId("testTrans_pull_receive_ackAsync"); +//// std::string tag = NameUtils::getRandomTagName(); +//// +//// auto pullConsumer = ConsumerFactory::getPullConsumer(topic,group); +//// +//// std::this_thread::sleep_for(std::chrono::seconds(2)); +//// +//// ASSERT_TRUE(VerifyUtils::tryReceiveOnce(topic,tag,pullConsumer)); +//// +//// rocketmq::TransactionListener* listener = new CommitMQTransactionListener(); +//// auto transProducer = ProducerFactory::getRMQTransProducer(group,listener); +//// +//// ASSERT_NE(transProducer, nullptr); +//// +//// for(int i=0;isendTrans(message,rocketmq::LocalTransactionState::COMMIT_MESSAGE); +//// } +//// +//// std::this_thread::sleep_for(std::chrono::seconds(1)); +//// +//// ASSERT_EQ(SEND_NUM,transProducer->getEnqueueMessages()->getDataSize()); +//// //pull does not support asynchronous confirmation reception +//// pullConsumer->shutdown(); +//// transProducer->shutdownTransaction(); +////} \ No newline at end of file diff --git a/cpp/rocketmq-client-cpp-tests/cpp4.x/test/server/CMakeLists.txt b/cpp/rocketmq-client-cpp-tests/cpp4.x/test/server/CMakeLists.txt new file mode 100644 index 0000000..6231626 --- /dev/null +++ b/cpp/rocketmq-client-cpp-tests/cpp4.x/test/server/CMakeLists.txt @@ -0,0 +1,19 @@ +# Licensed to the Apache Software Foundation (ASF) under one or more +# contributor license agreements. See the NOTICE file distributed with +# this work for additional information regarding copyright ownership. +# The ASF licenses this file to You under the Apache License, Version 2.0 +# (the "License"); you may not use this file except in compliance with +# the License. You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +file(GLOB CPP_FILES ${CMAKE_CURRENT_SOURCE_DIR}/*.cpp) +file(GLOB CPP_FILES ${CMAKE_CURRENT_SOURCE_DIR}/abnormal/*.cpp) +file(GLOB CPP_FILES ${CMAKE_CURRENT_SOURCE_DIR}/batch/*.cpp) +list(APPEND SOURCE_FILES ${CPP_FILES}) +set(SOURCE_FILES ${SOURCE_FILES} PARENT_SCOPE) \ No newline at end of file diff --git a/cpp/rocketmq-client-cpp-tests/cpp4.x/test/server/DelayMessageTest.cpp b/cpp/rocketmq-client-cpp-tests/cpp4.x/test/server/DelayMessageTest.cpp new file mode 100644 index 0000000..d521650 --- /dev/null +++ b/cpp/rocketmq-client-cpp-tests/cpp4.x/test/server/DelayMessageTest.cpp @@ -0,0 +1,116 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +#include +#include +#include +#include "gtest/gtest.h" +#include "spdlog/logger.h" +#include "enums/MessageType.h" +#include "frame/BaseOperate.h" +#include "resource/Resource.h" +#include "utils/NameUtils.h" +#include "utils/RandomUtils.h" +#include "utils/VerifyUtils.h" +#include "factory/ConsumerFactory.h" +#include "factory/ProducerFactory.h" +#include "factory/MessageFactory.h" + +extern std::shared_ptr multi_logger; +extern std::shared_ptr resource; + +//Send 10 messages timed 10 seconds later synchronously, and expect these 10 messages to be consumed by PushConsumer 10 seconds(level 3) later +TEST(DelayMessageTest, testDelay_Send_PushConsume){ + int SEND_NUM = 10; + std::string topic = getTopic(MessageType::DELAY, "testDelay_Send_PushConsume", resource->getBrokerAddr(), resource->getNamesrv(),resource->getCluster()); + std::string group = getGroupId("testDelay_Send_PushConsume"); + std::string tag = NameUtils::getRandomTagName(); + + auto pushConsumer = ConsumerFactory::getRMQPushConsumer(topic,group,tag,std::make_shared()); + + auto producer = ProducerFactory::getRMQProducer(group); + + ASSERT_NE(producer, nullptr); + + for(int i=0;isend(message); + } + + ASSERT_EQ(SEND_NUM,producer->getEnqueueMessages()->getDataSize()); + + ASSERT_TRUE(VerifyUtils::verifyDelayMessage(*(producer->getEnqueueMessages()),*(pushConsumer->getListener()->getDequeueMessages()),3)); + + pushConsumer->shutdown(); + producer->shutdown(); +} + +////TEST(DelayMessageTest, testDelay_SendAsync_PushConsume){ +//// int SEND_NUM = 10; +//// std::string topic = getTopic(MessageType::DELAY, "testDelay_SendAsync_PushConsume", resource->getBrokerAddr(), resource->getNamesrv(),resource->getCluster()); +//// std::string group = getGroupId("testDelay_SendAsync_PushConsume"); +//// std::string tag = NameUtils::getRandomTagName(); +//// +//// auto pushConsumer = ConsumerFactory::getRMQPushConsumer(topic,group,tag,std::make_shared()); +//// +//// auto producer = ProducerFactory::getRMQProducer(group); +//// +//// ASSERT_NE(producer, nullptr); +//// +//// for(int i=0;isendAsync(message); +//// } +//// +//// pushConsumer->shutdown(); +//// producer->shutdown(); +////} + +////TEST(DelayMessageTest, testDelayTime15SecondsAgo){ +//// int SEND_NUM = 10; +//// std::string topic = getTopic(MessageType::DELAY, "testDelayTime15SecondsAgo", resource->getBrokerAddr(), resource->getNamesrv(),resource->getCluster()); +//// std::string group = getGroupId("testDelayTime15SecondsAgo"); +//// std::string tag = NameUtils::getRandomTagName(); +//// +//// auto pushConsumer = ConsumerFactory::getRMQPushConsumer(topic,group,tag,std::make_shared()); +//// +//// auto producer = ProducerFactory::getRMQProducer(group); +//// +//// ASSERT_NE(producer, nullptr); +//// +//// pushConsumer->shutdown(); +//// producer->shutdown(); +////} + +////TEST(DelayMessageTest, testDelayTime24hAfter){ +//// int SEND_NUM = 10; +//// std::string topic = getTopic(MessageType::DELAY, "testDelayTime24hAfter", resource->getBrokerAddr(), resource->getNamesrv(),resource->getCluster()); +//// std::string group = getGroupId("testDelayTime24hAfter"); +//// std::string tag = NameUtils::getRandomTagName(); +//// +//// auto pushConsumer = ConsumerFactory::getRMQPushConsumer(topic,group,tag,std::make_shared()); +//// +//// auto producer = ProducerFactory::getRMQProducer(group); +//// +//// ASSERT_NE(producer, nullptr); +//// +//// auto message = MessageFactory::buildDelayMessage(topic,tag,RandomUtils::getStringByUUID(),19); +//// +//// ASSERT_THROW(producer->send(message), std::exception); +//// +//// pushConsumer->shutdown(); +//// producer->shutdown(); +////} \ No newline at end of file diff --git a/cpp/rocketmq-client-cpp-tests/cpp4.x/test/server/NormalMessageTest.cpp b/cpp/rocketmq-client-cpp-tests/cpp4.x/test/server/NormalMessageTest.cpp new file mode 100644 index 0000000..71c7ec2 --- /dev/null +++ b/cpp/rocketmq-client-cpp-tests/cpp4.x/test/server/NormalMessageTest.cpp @@ -0,0 +1,81 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +#include +#include +#include +#include +#include "gtest/gtest.h" +#include "spdlog/logger.h" +#include "enums/MessageType.h" +#include "frame/BaseOperate.h" +#include "resource/Resource.h" +#include "utils/NameUtils.h" +#include "utils/RandomUtils.h" +#include "utils/VerifyUtils.h" +#include "factory/ConsumerFactory.h" +#include "factory/ProducerFactory.h" +#include "factory/MessageFactory.h" + +extern std::shared_ptr multi_logger; +extern std::shared_ptr resource; + +//Send 10 normal messages synchronously, expecting those 10 messages to be consumed through PushConsumer +TEST(NormalMessageTest, testNormal_Send_PushConsume){ + int SEND_NUM = 10; + std::string topic = getTopic(MessageType::NORMAL, "testNormal_Send_PushConsume", resource->getBrokerAddr(), resource->getNamesrv(),resource->getCluster()); + std::string group = getGroupId("testNormal_Send_PushConsume"); + std::string tag = NameUtils::getRandomTagName(); + + auto pushConsumer = ConsumerFactory::getRMQPushConsumer(topic,group,tag,std::make_shared()); + + auto producer = ProducerFactory::getRMQProducer(group); + + ASSERT_NE(producer, nullptr); + + for(int i=0;isend(message); + } + + ASSERT_EQ(SEND_NUM,producer->getEnqueueMessages()->getDataSize()); + + ASSERT_TRUE(VerifyUtils::verifyNormalMessage(*(producer->getEnqueueMessages()),*(pushConsumer->getListener()->getDequeueMessages()))); + + pushConsumer->shutdown(); + producer->shutdown(); +} + +////TEST(NormalMessageTest, testNormal_SendAsync_PushConsume){ +//// int SEND_NUM = 10; +//// std::string topic = getTopic(MessageType::NORMAL, "testNormal_SendAsync_PushConsume", resource->getBrokerAddr(), resource->getNamesrv(),resource->getCluster()); +//// std::string group = getGroupId("testNormal_SendAsync_PushConsume"); +//// std::string tag = NameUtils::getRandomTagName(); +//// +//// auto pushConsumer = ConsumerFactory::getRMQPushConsumer(topic,group,tag,std::make_shared()); +//// +//// auto producer = ProducerFactory::getRMQProducer(group); +//// +//// ASSERT_NE(producer, nullptr); +//// +//// for(int i=0;isendAsync(message); +//// } +//// +//// pushConsumer->shutdown(); +//// producer->shutdown(); +////} \ No newline at end of file diff --git a/cpp/rocketmq-client-cpp-tests/cpp4.x/test/server/OrderMessageTest.cpp b/cpp/rocketmq-client-cpp-tests/cpp4.x/test/server/OrderMessageTest.cpp new file mode 100644 index 0000000..9aa5cfc --- /dev/null +++ b/cpp/rocketmq-client-cpp-tests/cpp4.x/test/server/OrderMessageTest.cpp @@ -0,0 +1,61 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +#include +#include +#include +#include +#include "gtest/gtest.h" +#include "spdlog/logger.h" +#include "enums/MessageType.h" +#include "frame/BaseOperate.h" +#include "resource/Resource.h" +#include "utils/NameUtils.h" +#include "utils/RandomUtils.h" +#include "utils/VerifyUtils.h" +#include "factory/ConsumerFactory.h" +#include "factory/ProducerFactory.h" +#include "factory/MessageFactory.h" +#include "listener/rmq/RMQOrderListener.h" + +extern std::shared_ptr multi_logger; +extern std::shared_ptr resource; + +//Send 100 sequential messages synchronously, set 2 Messagegroups(message groups are divided by message content), and expect these 100 messages to be sequentially consumed by PushConsumer +TEST(OrderMessageTest, testOrder_Send_PushConsumeOrderly){ + int SEND_NUM = 10; + std::string topic = getTopic(MessageType::FIFO, "testOrder_Send_PushConsumeOrderly", resource->getBrokerAddr(), resource->getNamesrv(),resource->getCluster()); + std::string group = getGroupId("testOrder_Send_PushConsumeOrderly"); + std::string tag = NameUtils::getRandomTagName(); + + auto pushConsumer = ConsumerFactory::getRMQPushConsumer(topic,group,tag,std::make_shared()); + + auto producer = ProducerFactory::getRMQProducer(group); + + ASSERT_NE(producer, nullptr); + + for(int i=0;isendOrderMessage(message,i%2); + } + + ASSERT_EQ(SEND_NUM,producer->getEnqueueMessages()->getDataSize()); + + ASSERT_TRUE(VerifyUtils::verifyOrderMessage(*(producer->getEnqueueMessages()),*(pushConsumer->getOrderListener()->getDequeueMessages()))); + + pushConsumer->shutdown(); + producer->shutdown(); +} \ No newline at end of file diff --git a/cpp/rocketmq-client-cpp-tests/cpp4.x/test/server/TransactionMessageTest.cpp b/cpp/rocketmq-client-cpp-tests/cpp4.x/test/server/TransactionMessageTest.cpp new file mode 100644 index 0000000..8bbf203 --- /dev/null +++ b/cpp/rocketmq-client-cpp-tests/cpp4.x/test/server/TransactionMessageTest.cpp @@ -0,0 +1,207 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +#include +#include +#include +#include +#include +#include +#include "gtest/gtest.h" +#include "rocketmq/SendResult.h" +#include "spdlog/logger.h" +#include "enums/MessageType.h" +#include "frame/BaseOperate.h" +#include "resource/Resource.h" +#include "utils/NameUtils.h" +#include "utils/RandomUtils.h" +#include "utils/VerifyUtils.h" +#include "factory/ConsumerFactory.h" +#include "factory/ProducerFactory.h" +#include "factory/MessageFactory.h" +#include "common/MQTransactionListener.h" + +extern std::shared_ptr multi_logger; +extern std::shared_ptr resource; + +//Send 10 transaction messages and synchronously commit the transaction (Checker performs rollback), expecting those 10 messages to be consumed via PushConsumer +TEST(TransactionMessageTest, testTrans_SendCommit_PushConsume){ + int SEND_NUM = 10; + std::string topic = getTopic(MessageType::TRANSACTION, "testTrans_SendCommit_PushConsume", resource->getBrokerAddr(), resource->getNamesrv(),resource->getCluster()); + std::string group = getGroupId("testTrans_SendCommit_PushConsume"); + std::string tag = NameUtils::getRandomTagName(); + + auto pushConsumer = ConsumerFactory::getRMQPushConsumer(topic,group,tag,std::make_shared()); + + rocketmq::TransactionListener* listener = new CommitMQTransactionListener(); + auto transProducer = ProducerFactory::getRMQTransProducer(group,listener); + + std::this_thread::sleep_for(std::chrono::seconds(2)); + + ASSERT_NE(transProducer, nullptr); + + for(int i=0;isendTrans(message,rocketmq::LocalTransactionState::COMMIT_MESSAGE); + ASSERT_EQ(sendResult.getSendStatus(),rocketmq::SendStatus::SEND_OK); + } + + ASSERT_EQ(SEND_NUM,transProducer->getEnqueueMessages()->getDataSize()); + + ASSERT_TRUE(VerifyUtils::verifyNormalMessage(*(transProducer->getEnqueueMessages()),*(pushConsumer->getListener()->getDequeueMessages()))); + + pushConsumer->shutdown(); + transProducer->shutdownTransaction(); +} + +//Send 10 transaction messages and rollback directly (Checker does commit), expecting that these 10 messages cannot be consumed by PushConsumer +TEST(TransactionMessageTest, testTrans_SendRollback_PushConsume){ + int SEND_NUM = 10; + std::string topic = getTopic(MessageType::TRANSACTION, "testTrans_SendRollback_PushConsume", resource->getBrokerAddr(), resource->getNamesrv(),resource->getCluster()); + std::string group = getGroupId("testTrans_SendRollback_PushConsume"); + std::string tag = NameUtils::getRandomTagName(); + + auto pushConsumer = ConsumerFactory::getRMQPushConsumer(topic,group,tag,std::make_shared()); + + rocketmq::TransactionListener* listener = new CommitMQTransactionListener(); + auto transProducer = ProducerFactory::getRMQTransProducer(group,listener); + + ASSERT_NE(transProducer, nullptr); + + std::this_thread::sleep_for(std::chrono::seconds(2)); + + for(int i=0;isendTrans(message,rocketmq::LocalTransactionState::ROLLBACK_MESSAGE); + ASSERT_EQ(sendResult.getSendStatus(),rocketmq::SendStatus::SEND_OK); + } + + std::this_thread::sleep_for(std::chrono::seconds(30)); + + ASSERT_EQ(SEND_NUM,transProducer->getEnqueueMessages()->getDataSize()); + + ASSERT_EQ(0,pushConsumer->getListener()->getDequeueMessages()->getDataSize()); + + pushConsumer->shutdown(); + transProducer->shutdownTransaction(); +} + +//Send 10 transaction messages and COMMIT the transaction by Checker (perform COMMIT), expecting the 10 messages to be consumed by PushConsumer +TEST(TransactionMessageTest, testTrans_SendCheckerCommit_PushConsume){ + int SEND_NUM = 10; + std::string topic = getTopic(MessageType::TRANSACTION, "testTrans_SendCheckerCommit_PushConsume", resource->getBrokerAddr(), resource->getNamesrv(),resource->getCluster()); + std::string group = getGroupId("testTrans_SendCheckerCommit_PushConsume"); + std::string tag = NameUtils::getRandomTagName(); + + auto pushConsumer = ConsumerFactory::getRMQPushConsumer(topic,group,tag,std::make_shared()); + + rocketmq::TransactionListener* listener = new CommitMQTransactionListener(); + auto transProducer = ProducerFactory::getRMQTransProducer(group,listener); + + ASSERT_NE(transProducer, nullptr); + + std::this_thread::sleep_for(std::chrono::seconds(2)); + + for(int i=0;isendTrans(message,rocketmq::LocalTransactionState::UNKNOWN); + ASSERT_EQ(sendResult.getSendStatus(),rocketmq::SendStatus::SEND_OK); + } + + std::this_thread::sleep_for(std::chrono::seconds(30)); + + ASSERT_EQ(SEND_NUM,transProducer->getEnqueueMessages()->getDataSize()); + + ASSERT_TRUE(VerifyUtils::verifyNormalMessage(*(transProducer->getEnqueueMessages()),*(pushConsumer->getListener()->getDequeueMessages()))); + + pushConsumer->shutdown(); + transProducer->shutdownTransaction(); +} + +//Send 10 transaction messages and roll back the transaction by Checker (performing ROLLBACK), expecting that the 10 messages will not be consumed by PushConsumer +TEST(TransactionMessageTest, testTrans_CheckerRollback){ + int SEND_NUM = 10; + std::string topic = getTopic(MessageType::TRANSACTION, "testTrans_CheckerRollback", resource->getBrokerAddr(), resource->getNamesrv(),resource->getCluster()); + std::string group = getGroupId("testTrans_CheckerRollback"); + std::string tag = NameUtils::getRandomTagName(); + + auto pushConsumer = ConsumerFactory::getRMQPushConsumer(topic,group,tag,std::make_shared()); + + rocketmq::TransactionListener* listener = new RollbackMQTransactionListener(); + auto transProducer = ProducerFactory::getRMQTransProducer(group,listener); + + ASSERT_NE(transProducer, nullptr); + + std::this_thread::sleep_for(std::chrono::seconds(2)); + + for(int i=0;isendTrans(message,rocketmq::LocalTransactionState::UNKNOWN); + ASSERT_EQ(sendResult.getSendStatus(),rocketmq::SendStatus::SEND_OK); + } + + std::this_thread::sleep_for(std::chrono::seconds(30)); + + ASSERT_EQ(SEND_NUM,transProducer->getEnqueueMessages()->getDataSize()); + + ASSERT_EQ(0,pushConsumer->getListener()->getDequeueMessages()->getDataSize()); + + pushConsumer->shutdown(); + transProducer->shutdownTransaction(); +} + +TEST(TransactionMessageTest, testTrans_SendCheckerPartionCommit){ + int SEND_NUM = 10; + std::string topic = getTopic(MessageType::TRANSACTION, "testTrans_SendCheckerPartionCommit", resource->getBrokerAddr(), resource->getNamesrv(),resource->getCluster()); + std::string group = getGroupId("testTrans_SendCheckerPartionCommit"); + std::string tag = NameUtils::getRandomTagName(); + + auto pushConsumer = ConsumerFactory::getRMQPushConsumer(topic,group,tag,std::make_shared()); + + std::atomic commitMsgNum(0); + std::atomic rollbackMsgNum(0); + rocketmq::TransactionListener* listener = new UserdefinedMQTransactionListener(commitMsgNum,rollbackMsgNum); + auto transProducer = ProducerFactory::getRMQTransProducer(group,listener); + + ASSERT_NE(transProducer, nullptr); + + std::this_thread::sleep_for(std::chrono::seconds(2)); + + for(int i=0;isendTrans(message,rocketmq::LocalTransactionState::UNKNOWN); + ASSERT_EQ(sendResult.getSendStatus(),rocketmq::SendStatus::SEND_OK); + } + + int timeoutSeconds = 90; + auto startTime = std::chrono::steady_clock::now(); + auto endTime = startTime + std::chrono::seconds(timeoutSeconds); + while (commitMsgNum.load() != rollbackMsgNum.load() || commitMsgNum.load() != SEND_NUM/2) { + if (std::chrono::steady_clock::now() >= endTime) { + break; + } + std::this_thread::sleep_for(std::chrono::milliseconds(100)); + } + + std::this_thread::sleep_for(std::chrono::seconds(30)); + + ASSERT_EQ(SEND_NUM,transProducer->getEnqueueMessages()->getDataSize()); + + ASSERT_EQ(SEND_NUM/2,pushConsumer->getListener()->getDequeueMessages()->getDataSize()); + + pushConsumer->shutdown(); + transProducer->shutdownTransaction(); +} \ No newline at end of file diff --git a/cpp/rocketmq-client-cpp-tests/cpp4.x/test/server/abnormal/PushConsumerRetryTest.cpp b/cpp/rocketmq-client-cpp-tests/cpp4.x/test/server/abnormal/PushConsumerRetryTest.cpp new file mode 100644 index 0000000..f455648 --- /dev/null +++ b/cpp/rocketmq-client-cpp-tests/cpp4.x/test/server/abnormal/PushConsumerRetryTest.cpp @@ -0,0 +1,307 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +#include +#include +#include +#include +#include "gtest/gtest.h" +#include "spdlog/logger.h" +#include "rocketmq/MQMessageExt.h" +#include "rocketmq/DefaultMQPushConsumer.h" +#include "rocketmq/MQMessageListener.h" +#include "enums/MessageType.h" +#include "frame/BaseOperate.h" +#include "resource/Resource.h" +#include "utils/NameUtils.h" +#include "utils/RandomUtils.h" +#include "utils/VerifyUtils.h" +#include "utils/SimpleConcurrentHashMapUtils.h" +#include "utils/SimpleConcurrentVectorUtils.h" +#include "factory/ConsumerFactory.h" +#include "factory/ProducerFactory.h" +#include "factory/MessageFactory.h" + +extern std::shared_ptr multi_logger; +extern std::shared_ptr resource; + +class ExceptionMsgListener : public rocketmq::MessageListenerConcurrently { +public: + SimpleConcurrentHashMap firstMsgs; + SimpleConcurrentHashMap retryMsgs; + ExceptionMsgListener() {} + virtual ~ExceptionMsgListener() {} + + virtual rocketmq::ConsumeStatus consumeMessage(const std::vector& msgs) { + for (size_t i = 0; i < msgs.size(); ++i) { + auto msg = msgs[i]; + if(msg.getReconsumeTimes() == 1){ + if(!retryMsgs.contains(msg.getMsgId())){ + retryMsgs.insert(msg.getMsgId(), msg); + } + multi_logger->info("consume success: {}",msg.getMsgId()); + }else{ + // Simulate consuming operations + multi_logger->info("{}","Simulate consuming operations fail"); + throw std::runtime_error("Simulate consuming operations fail"); + multi_logger->info("{}","Simulate consuming operations fail end"); + if(!firstMsgs.contains(msg.getMsgId())){ + firstMsgs.insert(msg.getMsgId(), msg); + } + multi_logger->info("recv msg(fail) {} ", msg.getMsgId()); + } + } + return rocketmq::CONSUME_SUCCESS; + } +}; + +// class NullMsgListener : public rocketmq::MessageListenerConcurrently { +// public: +// SimpleConcurrentHashMap firstMsgs; +// SimpleConcurrentHashMap retryMsgs; +// NullMsgListener() {} +// virtual ~NullMsgListener() {} + +// virtual rocketmq::ConsumeStatus consumeMessage(const std::vector& msgs) { +// for (size_t i = 0; i < msgs.size(); ++i) { +// auto msg = msgs[i]; +// if(msg.getReconsumeTimes() == 2){ +// if(!retryMsgs.contains(msg.getMsgId())){ +// retryMsgs.insert(msg.getMsgId(), msg); +// } +// multi_logger->info("consume success: {}",msg.getMsgId()); +// }else{ +// // Simulate consuming operations +// multi_logger->info("{}","Simulate consuming operations return null"); +// if(!firstMsgs.contains(msg.getMsgId())){ +// firstMsgs.insert(msg.getMsgId(), msg); +// } +// multi_logger->info("recv msg(fail) {} ", msg.getMsgId()); +// return nullptr; +// } +// } +// return rocketmq::CONSUME_SUCCESS; +// } +// }; + +class NormalMsgListener : public rocketmq::MessageListenerConcurrently { +public: + SimpleConcurrentHashMap firstMsgs; + SimpleConcurrentHashMap retryMsgs; + NormalMsgListener() {} + virtual ~NormalMsgListener() {} + + virtual rocketmq::ConsumeStatus consumeMessage(const std::vector& msgs) { + for (size_t i = 0; i < msgs.size(); ++i) { + auto msg = msgs[i]; + if(msg.getReconsumeTimes() > 0){ + if(!retryMsgs.contains(msg.getMsgId())){ + retryMsgs.insert(msg.getMsgId(), msg); + } + multi_logger->info("consume success: {}",msg.getMsgId()); + return rocketmq::CONSUME_SUCCESS; + }else{ + if(!firstMsgs.contains(msg.getMsgId())){ + firstMsgs.insert(msg.getMsgId(), msg); + } + multi_logger->info("recv msg(fail) {} ", msg.getMsgId()); + return rocketmq::RECONSUME_LATER; + } + } + return rocketmq::CONSUME_SUCCESS; + } +}; + +class FIFOMsgListener : public rocketmq::MessageListenerOrderly { +public: + SimpleConcurrentVector recvMessages; + FIFOMsgListener() {} + virtual ~FIFOMsgListener() {} + + virtual rocketmq::ConsumeStatus consumeMessage(const std::vector& msgs) { + for (size_t i = 0; i < msgs.size(); ++i) { + auto msg = msgs[i]; + if(msg.getReconsumeTimes() > 0){ + multi_logger->info("consume success: {}",msg.getMsgId()); + recvMessages.push_back(msg); + return rocketmq::CONSUME_SUCCESS; + }else{ + multi_logger->info("recv msg(fail) {} ", msg.getMsgId()); + return rocketmq::RECONSUME_LATER; + } + } + return rocketmq::CONSUME_SUCCESS; + } +}; + +//Simulate pushconsumer consumption fail, expect that the original message was not received, and capture all messages after message retry +TEST(PushConsumerRetryTest, testExceptionConsumption){ + int SEND_NUM = 5; + std::string topic = getTopic(MessageType::NORMAL, "testExceptionConsumption", resource->getBrokerAddr(), resource->getNamesrv(),resource->getCluster()); + std::string group = getGroupId("testExceptionConsumption"); + std::string tag = NameUtils::getRandomTagName(); + + ExceptionMsgListener* listener = new ExceptionMsgListener(); + auto rmqPushConsumer = std::make_shared(group); + rmqPushConsumer->setNamesrvAddr(resource->getNamesrv()); + rmqPushConsumer->setSessionCredentials(resource->getAccessKey(), resource->getSecretKey(), resource->getAccessChannel()); + rmqPushConsumer->setConsumeFromWhere(rocketmq::CONSUME_FROM_LAST_OFFSET); + rmqPushConsumer->setConsumeThreadCount(8); + rmqPushConsumer->subscribe(topic, tag); + rmqPushConsumer->setMessageModel(rocketmq::MessageModel::CLUSTERING); + rmqPushConsumer->setMaxReconsumeTimes(2); + rmqPushConsumer->registerMessageListener(listener); + rmqPushConsumer->start(); + + auto producer = ProducerFactory::getRMQProducer(group); + ASSERT_NE(producer, nullptr); + for(int i=0;isend(message); + } + + ASSERT_EQ(SEND_NUM,producer->getEnqueueMessages()->getDataSize()); + long endTime = std::chrono::duration_cast(std::chrono::system_clock::now().time_since_epoch()).count()+240*1000L; + while(endTime > std::chrono::duration_cast(std::chrono::system_clock::now().time_since_epoch()).count()){ + if(listener->retryMsgs.size() == SEND_NUM){ + break; + } + std::this_thread::sleep_for(std::chrono::seconds(5)); + } + ASSERT_EQ(SEND_NUM,listener->retryMsgs.size()); + ASSERT_EQ(0,listener->firstMsgs.size()); + rmqPushConsumer->shutdown(); + producer->shutdown(); +} + +//Simulate pushconsumer consumption return null, expect that the original message was not received, and capture all messages after message retry +//// TEST(PushConsumerRetryTest, testNullConsumption){ +//// int SEND_NUM = 5; +//// std::string topic = getTopic(MessageType::NORMAL, "testNullConsumption", resource->getBrokerAddr(), resource->getNamesrv(),resource->getCluster()); +//// std::string group = getGroupId("testNullConsumption"); +//// std::string tag = NameUtils::getRandomTagName(); +//// NullMsgListener* listener = new NullMsgListener(); +//// auto rmqPushConsumer = std::make_shared(group); +//// rmqPushConsumer->setNamesrvAddr(resource->getNamesrv()); +//// rmqPushConsumer->setSessionCredentials(resource->getAccessKey(), resource->getSecretKey(), resource->getAccessChannel()); +//// rmqPushConsumer->setConsumeFromWhere(rocketmq::CONSUME_FROM_LAST_OFFSET); +//// rmqPushConsumer->setConsumeThreadCount(4); +//// rmqPushConsumer->subscribe(topic, tag); +//// rmqPushConsumer->setMessageModel(rocketmq::MessageModel::CLUSTERING); +//// rmqPushConsumer->setMaxReconsumeTimes(2); +//// rmqPushConsumer->registerMessageListener(listener); +//// rmqPushConsumer->start(); +//// auto producer = ProducerFactory::getRMQProducer(group); +//// ASSERT_NE(producer, nullptr); +//// for(int i=0;isend(message); +//// } +//// ASSERT_EQ(SEND_NUM,producer->getEnqueueMessages()->getDataSize()); +//// std::this_thread::sleep_for(std::chrono::seconds(5)); +//// ASSERT_EQ(SEND_NUM,listener->retryMsgs.size()); +//// ASSERT_EQ(0,listener->firstMsgs.size()); +//// rmqPushConsumer->shutdown(); +//// producer->shutdown(); +//// } + +//The normal message is sent, and after the PushConsumer retry, the retry message is expected to be consumed +TEST(PushConsumerRetryTest, testNormalTopicPushConsumerRetry){ + int SEND_NUM = 1; + std::string topic = getTopic(MessageType::NORMAL, "testNormalTopicPushConsumerRetry", resource->getBrokerAddr(), resource->getNamesrv(),resource->getCluster()); + std::string group = getGroupId("testNormalTopicPushConsumerRetry"); + std::string tag = NameUtils::getRandomTagName(); + + NormalMsgListener* listener = new NormalMsgListener(); + auto rmqPushConsumer = std::make_shared(group); + rmqPushConsumer->setNamesrvAddr(resource->getNamesrv()); + rmqPushConsumer->setSessionCredentials(resource->getAccessKey(), resource->getSecretKey(), resource->getAccessChannel()); + rmqPushConsumer->setConsumeFromWhere(rocketmq::CONSUME_FROM_LAST_OFFSET); + rmqPushConsumer->setConsumeThreadCount(8); + rmqPushConsumer->subscribe(topic, tag); + rmqPushConsumer->setMessageModel(rocketmq::MessageModel::CLUSTERING); + rmqPushConsumer->setMaxReconsumeTimes(2); + rmqPushConsumer->registerMessageListener(listener); + rmqPushConsumer->start(); + + auto producer = ProducerFactory::getRMQProducer(group); + ASSERT_NE(producer, nullptr); + + for(int i=0;isend(message); + } + + ASSERT_EQ(SEND_NUM,producer->getEnqueueMessages()->getDataSize()); + + long endTime = std::chrono::duration_cast(std::chrono::system_clock::now().time_since_epoch()).count()+240*1000L; + while(endTime > std::chrono::duration_cast(std::chrono::system_clock::now().time_since_epoch()).count()){ + if(listener->retryMsgs.size() == SEND_NUM && listener->firstMsgs.size() == SEND_NUM){ + break; + } + std::this_thread::sleep_for(std::chrono::seconds(5)); + } + + for(auto& msg : producer->getEnqueueMessages()->getAllData()){ + ASSERT_TRUE(listener->firstMsgs.contains(msg) && listener->retryMsgs.contains(msg)); + } + rmqPushConsumer->shutdown(); + producer->shutdown(); +} + + +//The send order message, after the PushConsumer retry, is expected to consume the retry message, and the message consumption order and send order +TEST(PushConsumerRetryTest, testFiFoTopicPushConsumerRetry){ + int SEND_NUM = 5; + std::string topic = getTopic(MessageType::NORMAL, "testFiFoTopicPushConsumerRetry", resource->getBrokerAddr(), resource->getNamesrv(),resource->getCluster()); + std::string group = getGroupId("testFiFoTopicPushConsumerRetry"); + std::string tag = NameUtils::getRandomTagName(); + + FIFOMsgListener* listener = new FIFOMsgListener(); + auto rmqPushConsumer = std::make_shared(group); + rmqPushConsumer->setNamesrvAddr(resource->getNamesrv()); + rmqPushConsumer->setSessionCredentials(resource->getAccessKey(), resource->getSecretKey(), resource->getAccessChannel()); + rmqPushConsumer->setConsumeFromWhere(rocketmq::CONSUME_FROM_LAST_OFFSET); + rmqPushConsumer->setConsumeThreadCount(4); + rmqPushConsumer->subscribe(topic, tag); + rmqPushConsumer->setMessageModel(rocketmq::MessageModel::CLUSTERING); + rmqPushConsumer->setMaxReconsumeTimes(2); + rmqPushConsumer->registerMessageListener(listener); + rmqPushConsumer->start(); + + auto producer = ProducerFactory::getRMQProducer(group); + ASSERT_NE(producer, nullptr); + + for(int i=0;isendOrderMessage(message,0); + } + + ASSERT_EQ(SEND_NUM,producer->getEnqueueMessages()->getDataSize()); + + long endTime = std::chrono::duration_cast(std::chrono::system_clock::now().time_since_epoch()).count()+240*1000L; + while(endTime > std::chrono::duration_cast(std::chrono::system_clock::now().time_since_epoch()).count()){ + if(listener->recvMessages.size() == SEND_NUM){ + break; + } + std::this_thread::sleep_for(std::chrono::seconds(5)); + } + for (int i = 0; i < SEND_NUM; i++) { + ASSERT_EQ(std::to_string(i), listener->recvMessages[i].getBody()); + } + rmqPushConsumer->shutdown(); + producer->shutdown(); +} \ No newline at end of file diff --git a/cpp/rocketmq-client-cpp-tests/cpp4.x/test/server/batch/BatchProducerTest.cpp b/cpp/rocketmq-client-cpp-tests/cpp4.x/test/server/batch/BatchProducerTest.cpp new file mode 100644 index 0000000..6442029 --- /dev/null +++ b/cpp/rocketmq-client-cpp-tests/cpp4.x/test/server/batch/BatchProducerTest.cpp @@ -0,0 +1,121 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +#include +#include +#include +#include +#include "gtest/gtest.h" +#include "spdlog/logger.h" +#include "rocketmq/MQMessageExt.h" +#include "rocketmq/DefaultMQPushConsumer.h" +#include "rocketmq/MQMessageListener.h" +#include "enums/MessageType.h" +#include "frame/BaseOperate.h" +#include "resource/Resource.h" +#include "utils/NameUtils.h" +#include "utils/RandomUtils.h" +#include "utils/VerifyUtils.h" +#include "utils/SimpleConcurrentHashMapUtils.h" +#include "utils/SimpleConcurrentVectorUtils.h" +#include "factory/ConsumerFactory.h" +#include "factory/ProducerFactory.h" +#include "factory/MessageFactory.h" + +extern std::shared_ptr multi_logger; +extern std::shared_ptr resource; + +//Send 10 messages in batch, expect pushconsumer to accept them all +TEST(BatchProducerTest, testBatchProducer){ + int SEND_NUM = 10; + std::string topic = getTopic(MessageType::NORMAL, "testBatchProducer", resource->getBrokerAddr(), resource->getNamesrv(),resource->getCluster()); + std::string group = getGroupId("testBatchProducer"); + std::string tag = NameUtils::getRandomTagName(); + + auto pushConsumer = ConsumerFactory::getRMQPushConsumer(topic,group,tag,std::make_shared()); + + auto producer = ProducerFactory::getRMQProducer(group); + + ASSERT_NE(producer, nullptr); + + std::vector msgs; + for(int i=0;igetProducer()->send(msgs); + ASSERT_EQ(sendResult.getSendStatus(),rocketmq::SEND_OK); + }); + + long endTime = std::chrono::duration_cast(std::chrono::system_clock::now().time_since_epoch()).count()+240*1000L; + while(endTime > std::chrono::duration_cast(std::chrono::system_clock::now().time_since_epoch()).count()){ + if(pushConsumer->getListener()->getDequeueMessages()->getDataSize() == SEND_NUM){ + break; + } + std::this_thread::sleep_for(std::chrono::seconds(5)); + } + + ASSERT_EQ(pushConsumer->getListener()->getDequeueMessages()->getDataSize(),SEND_NUM); + + pushConsumer->shutdown(); + producer->shutdown(); +} + +//std::vector& msgs, const MQMessageQueue& mq +//Send 10 messages to a queue in batch , expect pushconsumer to accept them all +TEST(BatchProducerTest, testBatchProducer_queue){ + int SEND_NUM = 10; + std::string topic = getTopic(MessageType::NORMAL, "testBatchProducer_queue", resource->getBrokerAddr(), resource->getNamesrv(),resource->getCluster()); + std::string group = getGroupId("testBatchProducer_queue"); + std::string tag = NameUtils::getRandomTagName(); + + auto pushConsumer = ConsumerFactory::getRMQPushConsumer(topic,group,tag,std::make_shared()); + + auto pullConsumer = ConsumerFactory::getRMQPullConsumer(topic,group); + std::vector mqs; + pullConsumer->getPullConsumer()->fetchSubscribeMessageQueues(topic, mqs); + pullConsumer->shutdown(); + + auto producer = ProducerFactory::getRMQProducer(group); + + ASSERT_NE(producer, nullptr); + + std::vector msgs; + for(int i=0;igetProducer()->send(msgs,mqs[0]); + ASSERT_EQ(sendResult.getSendStatus(),rocketmq::SEND_OK); + }); + + long endTime = std::chrono::duration_cast(std::chrono::system_clock::now().time_since_epoch()).count()+240*1000L; + while(endTime > std::chrono::duration_cast(std::chrono::system_clock::now().time_since_epoch()).count()){ + if(pushConsumer->getListener()->getDequeueMessages()->getDataSize() == SEND_NUM){ + break; + } + std::this_thread::sleep_for(std::chrono::seconds(5)); + } + + ASSERT_EQ(pushConsumer->getListener()->getDequeueMessages()->getDataSize(),SEND_NUM); + + pushConsumer->shutdown(); + producer->shutdown(); +} \ No newline at end of file