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
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 <future>
+#include <mutex>
+#include <atomic>
+#include <thread>
+#include <unordered_map>
+#include <vector>
+#include <memory>
+#include <string>
+#include <optional>
+#include <chrono>
+
+extern std::shared_ptr<spdlog::logger> multi_logger;
+extern std::shared_ptr<Resource> resource;
+
+class RMQNormalConsumer
+{
+private:
+    std::shared_ptr<rocketmq::DefaultMQPushConsumer> pushConsumer;
+    std::shared_ptr<rocketmq::DefaultMQPullConsumer> pullConsumer;
+    std::shared_ptr<rocketmq::DefaultMQPushConsumer> consumer;
+    std::vector<std::future<void>> executorService;
+    static constexpr int WAIT_RESPONSE_MILLS = 15 * 1000;
+    std::shared_ptr<RMQNormalListener> normalListener = nullptr;
+    std::shared_ptr<RMQOrderListener> orderListener = nullptr;
+    bool needRun = true;
+    int consumerThreadNum = 20;
+    static std::atomic<int> receivedIndex;
+
+public:
+    RMQNormalConsumer(std::shared_ptr<rocketmq::DefaultMQPushConsumer> consumer, std::shared_ptr<RMQNormalListener> listener)
+        : pushConsumer(consumer), normalListener(listener) {}
+
+    RMQNormalConsumer(std::shared_ptr<rocketmq::DefaultMQPushConsumer> consumer, std::shared_ptr<RMQOrderListener> listener)
+        : pushConsumer(consumer), orderListener(listener) {}
+
+    RMQNormalConsumer(std::shared_ptr<rocketmq::DefaultMQPullConsumer> consumer)
+        : pullConsumer(consumer) {}
+
+    // void receiveThenNack(const std::string& topic,int maxMessageNum, std::optional<std::chrono::duration<double>> receiveInvisibleDuration, std::optional<std::chrono::duration<double>> changeInvisibleDuration) {
+
+    // }
+
+    void shutdown()
+    {
+        if (pushConsumer)
+        {
+            pushConsumer->shutdown();
+        }
+        if (pullConsumer)
+        {
+            pullConsumer->shutdown();
+        }
+    }
+
+    std::shared_ptr<rocketmq::DefaultMQPushConsumer> getPushConsumer()
+    {
+        return pushConsumer;
+    }
+
+    void setPushConsumer(std::shared_ptr<rocketmq::DefaultMQPushConsumer> pushConsumer)
+    {
+        this->pushConsumer = pushConsumer;
+    }
+
+    std::shared_ptr<rocketmq::DefaultMQPullConsumer> getPullConsumer()
+    {
+        return pullConsumer;
+    }
+
+    void setSimpleConsumer(std::shared_ptr<rocketmq::DefaultMQPullConsumer> pullConsumer)
+    {
+        this->pullConsumer = pullConsumer;
+    }
+
+    std::shared_ptr<rocketmq::DefaultMQPushConsumer> getConsumer()
+    {
+        return consumer;
+    }
+
+    void setConsumer(std::shared_ptr<rocketmq::DefaultMQPushConsumer> consumer)
+    {
+        this->consumer = consumer;
+    }
+
+    std::shared_ptr<RMQNormalListener> getListener()
+    {
+        return normalListener;
+    }
+
+    std::shared_ptr<RMQOrderListener> getOrderListener()
+    {
+        return orderListener;
+    }
+
+    void setListener(std::shared_ptr<RMQNormalListener> listener)
+    {
+        this->normalListener = listener;
+    }
+
+    void setOrderListener(std::shared_ptr<RMQOrderListener> 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 <future>
+#include <iostream>
+#include <thread>
+#include <memory>
+
+extern std::shared_ptr<spdlog::logger> multi_logger;
+extern std::shared_ptr<Resource> resource;
+
+class RMQNormalProducer : public AbstractMQProducer
+{
+private:
+    std::shared_ptr<rocketmq::DefaultMQProducer> producer;
+    std::shared_ptr<rocketmq::TransactionMQProducer> transactionProducer;
+
+public:
+    RMQNormalProducer(std::shared_ptr<rocketmq::DefaultMQProducer> producer) : producer(producer) {}
+
+    RMQNormalProducer(std::shared_ptr<rocketmq::TransactionMQProducer> producer) : transactionProducer(producer) {}
+
+    std::shared_ptr<rocketmq::DefaultMQProducer> getProducer()
+    {
+        return producer;
+    }
+
+    std::shared_ptr<rocketmq::TransactionMQProducer> 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<MQMessageQueueSelector> selector = std::make_shared<MQMessageQueueSelector>();
+        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 <string>
+
+class AbstractMQConsumer : public MQCollector<std::string>
+{
+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 <string>
+#include "common/MQCollector.h"
+#include "rocketmq/MQMessage.h"
+
+class AbstractMQProducer : public MQCollector<std::string>
+{
+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 <string>
+#include "utils/RandomUtils.h"
+#include "utils/data/collect/DataCollector.h"
+#include "utils/data/collect/DataCollectorManager.h"
+
+template <typename T>
+class MQCollector
+{
+protected:
+    DataCollector<T> *enqueueMessages;
+    DataCollector<T> *enqueueFailedMessages;
+    DataCollector<T> *dequeueMessages;
+    DataCollector<T> *dequeueAllMessages;
+    DataCollector<T> *dequeueUndupMessageBody;
+    DataCollector<T> *msgRTs;
+
+public:
+    MQCollector()
+    {
+        enqueueMessages = &DataCollectorManager<T>::getInstance().fetchDataCollector(RandomUtils::getStringByUUID());
+        enqueueFailedMessages = &DataCollectorManager<T>::getInstance().fetchDataCollector(RandomUtils::getStringByUUID());
+        dequeueMessages = &DataCollectorManager<T>::getInstance().fetchDataCollector(RandomUtils::getStringByUUID());
+        dequeueAllMessages = &DataCollectorManager<T>::getInstance().fetchDataCollector(RandomUtils::getStringByUUID());
+        dequeueUndupMessageBody = &DataCollectorManager<T>::getInstance().fetchDataCollector(RandomUtils::getStringByUUID());
+        msgRTs = &DataCollectorManager<T>::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<T> *getEnqueueMessages()
+    {
+        return enqueueMessages;
+    }
+
+    DataCollector<T> *getEnqueueFailedMessages()
+    {
+        return enqueueFailedMessages;
+    }
+
+    DataCollector<T> *getDequeueMessages()
+    {
+        return dequeueMessages;
+    }
+
+    DataCollector<T> *getDequeueAllMessages()
+    {
+        return dequeueAllMessages;
+    }
+
+    DataCollector<T> *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<rocketmq::MQMessageQueue> &mqs, const rocketmq::MQMessage &msg, void *arg)
+    {
+        int orderId = *static_cast<int *>(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 <atomic>
+#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<spdlog::logger> multi_logger;
+extern std::shared_ptr<Resource> 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<int> &commitMsgNum;
+    std::atomic<int> &rollbackMsgNum;
+
+public:
+    UserdefinedMQTransactionListener(std::atomic<int> &commitMsgNum, std::atomic<int> &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 <string>
+#include <map>
+
+enum class MessageType
+{
+    UNSPECIFIED,
+    NORMAL,
+    FIFO,
+    DELAY,
+    TRANSACTION
+};
+
+extern std::map<MessageType, std::string> 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 <memory>
+
+extern std::shared_ptr<spdlog::logger> multi_logger;
+extern std::shared_ptr<Resource> resource;
+
+class ConsumerFactory
+{
+public:
+    ConsumerFactory() = delete;
+
+    static std::shared_ptr<rocketmq::DefaultMQPushConsumer> getPushConsumer(const std::string &topic, const std::string &group, const std::string &subExpression, std::shared_ptr<MsgListener> msglistener)
+    {
+        auto rmqPushConsumer = std::make_shared<rocketmq::DefaultMQPushConsumer>(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<rocketmq::DefaultMQPushConsumer> getPushConsumer(const std::string &topic, const std::string &group, const std::string &subExpression, std::shared_ptr<RMQNormalListener> listener)
+    {
+        auto rmqPushConsumer = std::make_shared<rocketmq::DefaultMQPushConsumer>(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<rocketmq::DefaultMQPushConsumer> getBroadcastPushConsumer(const std::string &topic, const std::string &group, const std::string &subExpression, std::shared_ptr<RMQNormalListener> listener)
+    {
+        auto rmqPushConsumer = std::make_shared<rocketmq::DefaultMQPushConsumer>(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<RMQNormalConsumer> getRMQPushConsumer(const std::string &topic, const std::string &group, const std::string &subExpression, std::shared_ptr<RMQNormalListener> listener)
+    {
+        auto rmqPushConsumer = std::make_shared<rocketmq::DefaultMQPushConsumer>(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<RMQNormalConsumer>(rmqPushConsumer, listener);
+    }
+
+    static std::shared_ptr<RMQNormalConsumer> getRMQPushConsumer(const std::string &topic, const std::string &group, const std::string &subExpression, std::shared_ptr<RMQOrderListener> listener)
+    {
+        auto rmqPushConsumer = std::make_shared<rocketmq::DefaultMQPushConsumer>(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<RMQNormalConsumer>(rmqPushConsumer, listener);
+    }
+
+    static std::shared_ptr<rocketmq::DefaultMQPullConsumer> getPullConsumer(const std::string &topic, const std::string &group)
+    {
+        auto rmqPullConsumer = std::make_shared<rocketmq::DefaultMQPullConsumer>(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<RMQNormalConsumer> getRMQPullConsumer(const std::string &topic, const std::string &group)
+    {
+        auto rmqPullConsumer = std::make_shared<rocketmq::DefaultMQPullConsumer>(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<RMQNormalConsumer>(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 <memory>
+
+extern std::shared_ptr<spdlog::logger> multi_logger;
+extern std::shared_ptr<Resource> 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<std::string, std::string> &properties);
+
+    static rocketmq::MQMessage buildMessageWithProperty(const std::string &topic, const std::string &messageBody, std::map<std::string, std::string> &properties);
+
+    // static rocketmq::MQMessage buildOrderMessageWithProperty(const std::string& topic, const std::string& messageBody, const std::string& messageGroup, std::map<std::string,std::string>& properties);
+
+    static rocketmq::MQMessage buildMessageWithProperty(const std::string &topic, const std::string &tag, const std::string &body, std::map<std::string, std::string> &properties, const std::vector<std::string> &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 <memory>
+
+extern std::shared_ptr<spdlog::logger> multi_logger;
+extern std::shared_ptr<Resource> resource;
+
+class ProducerFactory
+{
+public:
+    ProducerFactory() = delete;
+
+    static std::shared_ptr<rocketmq::DefaultMQProducer> getProducer(const std::string &group)
+    {
+        auto producer = std::make_shared<rocketmq::DefaultMQProducer>(group);
+        producer->setNamesrvAddr(resource->getNamesrv());
+        producer->setTcpTransportTryLockTimeout(1000);
+        producer->setTcpTransportConnectTimeout(400);
+        producer->start();
+        return producer;
+    }
+
+    static std::shared_ptr<RMQNormalProducer> getRMQProducer(const std::string &group)
+    {
+        auto producer = std::make_shared<rocketmq::DefaultMQProducer>(group);
+        producer->setNamesrvAddr(resource->getNamesrv());
+        producer->setTcpTransportTryLockTimeout(1000);
+        producer->setTcpTransportConnectTimeout(400);
+        producer->start();
+        return std::make_shared<RMQNormalProducer>(producer);
+    }
+
+    static std::shared_ptr<RMQNormalProducer> getRMQTransProducer(const std::string &group, rocketmq::TransactionListener *listener)
+    {
+        // rocketmq::LocalTransactionState& state
+        auto transProducer = std::make_shared<rocketmq::TransactionMQProducer>(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<RMQNormalProducer>(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 <string>
+#include <memory>
+#include <random>
+#include <sstream>
+#include <iomanip>
+#include <iostream>
+#include <cassert>
+#include <chrono>
+#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 <atomic>
+#include <memory>
+#include <vector>
+
+extern std::shared_ptr<spdlog::logger> multi_logger;
+extern std::shared_ptr<Resource> resource;
+
+class MsgListener : public rocketmq::MessageListenerConcurrently
+{
+private:
+    std::atomic<int> count;
+    std::vector<rocketmq::MQMessageExt> messages;
+
+public:
+    MsgListener() { count = 0; };
+
+    void resetMsgCount() { count = 0; }
+
+    int getMsgCount() const { return count; }
+
+    std::vector<rocketmq::MQMessageExt> getMessages() const { return messages; }
+
+    virtual ~MsgListener() {}
+
+    virtual rocketmq::ConsumeStatus consumeMessage(const std::vector<rocketmq::MQMessageExt> &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 <atomic>
+#include <memory>
+#include <vector>
+
+
+extern std::shared_ptr<spdlog::logger> multi_logger;
+extern std::shared_ptr<Resource> resource;
+
+class MsgQueueListener : public rocketmq::MessageQueueListener
+{
+public:
+    virtual ~MsgQueueListener() {}
+
+    virtual void messageQueueChanged(const std::string &topic,
+                                     const std::vector<rocketmq::MQMessageQueue> &mqAll,
+                                     const std::vector<rocketmq::MQMessageQueue> &mqDivided) override
+    {
+        multi_logger->info("Message queue changed for topic: {}", topic);
+    }
+
+    virtual void messageQueueCreated(const std::string &topic,
+                                     const std::vector<rocketmq::MQMessageQueue> &mqAll,
+                                     const std::vector<rocketmq::MQMessageQueue> &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 <memory>
+#include <atomic>
+#include <iostream>
+#include <thread>
+
+extern std::shared_ptr<spdlog::logger> multi_logger;
+extern std::shared_ptr<Resource> resource;
+
+class RMQNormalListener : public MQCollector<MQMsg>, public rocketmq::MessageListenerConcurrently
+{
+private:
+    rocketmq::ConsumeStatus consumeStatus = rocketmq::CONSUME_SUCCESS;
+    std::atomic<int> 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<rocketmq::MQMessageExt> &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 <memory>
+#include <atomic>
+#include <iostream>
+#include <thread>
+
+extern std::shared_ptr<spdlog::logger> multi_logger;
+extern std::shared_ptr<Resource> resource;
+
+class RMQOrderListener : public MQCollector<MQMsg>, public rocketmq::MessageListenerOrderly
+{
+private:
+    rocketmq::ConsumeStatus consumeStatus = rocketmq::CONSUME_SUCCESS;
+    std::atomic<int> 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<rocketmq::MQMessageExt> &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 <string>
+
+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 &regionId) { 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 <string>
+
+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 <memory>
+#include "resource/Resource.h"
+
+void initResource(std::shared_ptr<Resource> 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 <string>
+#include <iostream>
+#include <cstdlib>
+#include <cstdio>
+
+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 <unordered_map>
+#include <random>
+#include <chrono>
+#include <mutex>
+#include "openssl/md5.h"
+
+class NameUtils
+{
+private:
+    static std::unordered_map<std::string, std::string> 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 <random>
+#include <string>
+#include <sstream>
+
+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 <iostream>
+#include <unordered_map>
+#include <shared_mutex>
+#include <functional>
+#include <vector>
+#include <atomic>
+
+template <typename Key, typename Value>
+class SimpleConcurrentHashMap
+{
+private:
+    std::unordered_map<Key, Value> map_;
+    mutable std::shared_mutex mutex_;
+
+public:
+    size_t size() const
+    {
+        std::lock_guard<std::shared_mutex> lock(mutex_);
+        return map_.size();
+    }
+
+    void insert(const Key &key, const Value &value)
+    {
+        std::lock_guard<std::shared_mutex> lock(mutex_);
+        map_[key] = value;
+    }
+
+    bool contains(const Key &key) const
+    {
+        std::shared_lock<std::shared_mutex> lock(mutex_);
+        return (map_.find(key) != map_.end());
+    }
+
+    void update(const Key &key, const Value &value)
+    {
+        std::lock_guard<std::shared_mutex> lock(mutex_);
+        map_[key] = value;
+    }
+
+    std::vector<Value> getAllValues() const
+    {
+        std::shared_lock<std::shared_mutex> lock(mutex_);
+        std::vector<Value> 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<std::shared_mutex> lock(mutex_);
+        return map_[key];
+    }
+};
+
+template <typename Key, typename Value>
+class SimpleConcurrentHashMap<Key, std::atomic<Value>>
+{
+private:
+    std::unordered_map<Key, Value> map_;
+    mutable std::shared_mutex mutex_;
+
+public:
+    size_t size() const
+    {
+        std::lock_guard<std::shared_mutex> lock(mutex_);
+        return map_.size();
+    }
+
+    void insert(const Key &key, const Value &value)
+    {
+        std::lock_guard<std::shared_mutex> lock(mutex_);
+        map_[key] = value;
+    }
+
+    bool contains(const Key &key) const
+    {
+        std::shared_lock<std::shared_mutex> lock(mutex_);
+        return (map_.find(key) != map_.end());
+    }
+
+    void update(const Key &key, const Value &value)
+    {
+        std::lock_guard<std::shared_mutex> lock(mutex_);
+        map_[key] = value;
+    }
+
+    std::vector<Value> getAllValues() const
+    {
+        std::shared_lock<std::shared_mutex> lock(mutex_);
+        std::vector<Value> 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<std::shared_mutex> 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 <iostream>
+#include <unordered_map>
+#include <shared_mutex>
+#include <functional>
+#include <vector>
+#include <atomic>
+
+template <typename Value>
+class SimpleConcurrentVector
+{
+private:
+    std::vector<Value> v_;
+    mutable std::shared_mutex mutex_;
+
+public:
+    size_t size() const
+    {
+        std::lock_guard<std::shared_mutex> lock(mutex_);
+        return v_.size();
+    }
+
+    void push_back(const Value &value)
+    {
+        std::lock_guard<std::shared_mutex> lock(mutex_);
+        v_.push_back(value);
+    }
+
+    void insert(const size_t &index, const Value &value)
+    {
+        std::lock_guard<std::shared_mutex> lock(mutex_);
+        v_.insert(v_.begin() + index, value);
+    }
+
+    void erase(const size_t &index)
+    {
+        std::lock_guard<std::shared_mutex> lock(mutex_);
+        v_.erase(v_.begin() + index);
+    }
+
+    std::vector<Value> getCopy() const
+    {
+        std::shared_lock<std::shared_mutex> lock(mutex_);
+        return v_;
+    }
+
+    Value &operator[](const size_t &index)
+    {
+        std::shared_lock<std::shared_mutex> 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 <memory>
+#include <unordered_map>
+#include <unordered_set>
+#include <sstream>
+
+class VerifyUtils
+{
+private:
+    const static int TIMEOUT = 90;
+    const static int defaultSimpleThreadNums = 4;
+    static std::atomic<int> receivedIndex;
+    static std::unordered_map<std::string, long> checkDelay(DataCollector<MQMsg> &dequeueMessages, int delayLevel);
+    static bool checkOrder(DataCollector<MQMsg> &dequeueMessages);
+    static std::vector<rocketmq::MQMessageExt> msgs;
+    static std::vector<std::string> waitForMessageConsume(DataCollector<std::string> &enqueueMessages, DataCollector<std::string> &dequeueMessages, long long timeoutMills, int consumedTimes);
+    static std::vector<std::string> waitForMessageConsume(DataCollector<std::string> &enqueueMessages, DataCollector<MQMsg> &dequeueMessages, long long timeoutMills, int consumedTimes);
+
+public:
+    VerifyUtils() = delete;
+    static long long getDelayTime(int delayLevel);
+    static bool checkOrderMessage(std::unordered_map<std::string, std::vector<MQMsg>> &receivedMessage);
+    static bool tryReceiveOnce(const std::string &topic, const std::string &subExpression, std::shared_ptr<rocketmq::DefaultMQPullConsumer> pullConsumer);
+    static std::vector<rocketmq::MQMessageExt> fetchMessages(std::shared_ptr<rocketmq::DefaultMQPullConsumer> pullConsumer, const std::string &topic);
+    static bool verifyNormalMessage(DataCollector<std::string> &enqueueMessages, DataCollector<std::string> &dequeueMessages);
+    static bool verifyNormalMessage(DataCollector<std::string> &enqueueMessages, DataCollector<MQMsg> &dequeueMessages);
+    static bool verifyNormalMessage(DataCollector<std::string> &enqueueMessages, DataCollector<std::string> &dequeueMessages, std::unordered_set<std::string> &unconsumedMsgIds);
+    static bool verifyNormalMessageWithUserProperties(DataCollector<std::string> &enqueueMessages, DataCollector<MQMsg> &dequeueMessages, std::map<std::string, std::string> &props, int expectedUnrecvMsgNum);
+    static bool verifyDelayMessage(DataCollector<std::string> &enqueueMessages, DataCollector<MQMsg> &dequeueMessages, int delayLevel);
+    static bool verifyOrderMessage(DataCollector<std::string> &enqueueMessages, DataCollector<MQMsg> &dequeueMessages);
+    static bool waitReceiveThenAck(std::shared_ptr<RMQNormalProducer> producer, std::shared_ptr<rocketmq::DefaultMQPullConsumer> pullConsumer, std::string &topic, std::string &tag, int maxMessageNum);
+    static bool waitFIFOParamReceiveThenNAck(std::shared_ptr<RMQNormalProducer> producer, std::shared_ptr<rocketmq::DefaultMQPullConsumer> pullConsumer, std::string &topic, std::string &tag, int maxMessageNum);
+    static bool waitFIFOParamReceiveThenAckExceptedLast(std::shared_ptr<RMQNormalProducer> producer, std::shared_ptr<rocketmq::DefaultMQPullConsumer> pullConsumer, std::string &topic, std::string &tag, int maxMessageNum = 3);
+    static bool waitFIFOReceiveThenAck(std::shared_ptr<RMQNormalProducer> producer, std::shared_ptr<rocketmq::DefaultMQPullConsumer> pullConsumer, std::string &topic, std::string &tag, int maxMessageNum);
+    static bool waitAckExceptionReReceiveAck(std::shared_ptr<RMQNormalProducer> producer, std::shared_ptr<rocketmq::DefaultMQPullConsumer> pullConsumer, std::string &topic, std::string &tag, int maxMessageNum);
+    static bool waitReceiveMaxsizeSync(std::shared_ptr<RMQNormalProducer> producer, std::shared_ptr<rocketmq::DefaultMQPullConsumer> pullConsumer, std::string &topic, std::string &tag, int maxMessageNum);
+    static bool waitReceiveMultiNack(std::shared_ptr<RMQNormalProducer> producer, std::shared_ptr<rocketmq::DefaultMQPullConsumer> 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 <set>
+#include <vector>
+#include <string>
+
+template <typename T>
+class DataCollector
+{
+public:
+    virtual void resetData() = 0;
+    virtual std::vector<T> getAllData() = 0;
+    virtual std::set<T> 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 <unordered_map>
+#include <mutex>
+#include <memory>
+#include <string>
+#include "utils/data/collect/DataCollector.h"
+#include "utils/data/collect/impl/ListDataCollector.h"
+#include "utils/data/collect/impl/MapDataCollector.h"
+
+template <typename T>
+class DataCollectorManager
+{
+private:
+    static std::unique_ptr<DataCollectorManager<T>> instance;
+    std::unordered_map<std::string, std::unique_ptr<DataCollector<T>>> collectMap;
+    static std::mutex mtx;
+
+    DataCollectorManager() {}
+
+public:
+    static DataCollectorManager &getInstance()
+    {
+        if (instance == nullptr)
+        {
+            std::lock_guard<std::mutex> lock(mtx);
+            if (instance == nullptr)
+                instance = std::unique_ptr<DataCollectorManager<T>>(new DataCollectorManager<T>());
+        }
+        return *instance;
+    }
+
+    DataCollector<T> &fetchDataCollector(const std::string &key)
+    {
+        std::lock_guard<std::mutex> lock(mtx);
+
+        if (collectMap.find(key) == collectMap.end())
+        {
+            collectMap[key] = std::make_unique<MapDataCollectorImpl<T>>();
+        }
+
+        return *collectMap[key];
+    }
+
+    DataCollector<T> &fetchMapDataCollector(const std::string &key)
+    {
+        std::lock_guard<std::mutex> lock(mtx);
+
+        if (collectMap.find(key) == collectMap.end() ||
+            dynamic_cast<MapDataCollectorImpl<T> *>(collectMap[key].get()) == nullptr)
+        {
+            collectMap[key] = std::make_unique<MapDataCollectorImpl<T>>();
+        }
+
+        return *collectMap[key];
+    }
+
+    DataCollector<T> &fetchListDataCollector(const std::string &key)
+    {
+        std::lock_guard<std::mutex> lock(mtx);
+
+        if (collectMap.find(key) == collectMap.end() ||
+            dynamic_cast<ListDataCollectorImpl<T> *>(collectMap[key].get()) == nullptr)
+        {
+            collectMap[key] = std::make_unique<ListDataCollectorImpl<T>>();
+        }
+
+        return *collectMap[key];
+    }
+
+    void resetDataCollect(const std::string &key)
+    {
+        std::lock_guard<std::mutex> lock(mtx);
+
+        if (collectMap.find(key) != collectMap.end())
+        {
+            collectMap[key]->resetData();
+        }
+    }
+
+    void resetAll()
+    {
+        std::lock_guard<std::mutex> lock(mtx);
+
+        for (auto &pair : collectMap)
+        {
+            pair.second->resetData();
+        }
+    }
+
+    void removeDataCollect(const std::string &key)
+    {
+        std::lock_guard<std::mutex> lock(mtx);
+
+        collectMap.erase(key);
+    }
+
+    void removeAll()
+    {
+        std::lock_guard<std::mutex> lock(mtx);
+
+        collectMap.clear();
+    }
+};
+
+template <typename T>
+std::mutex DataCollectorManager<T>::mtx;
+
+template <typename T>
+std::unique_ptr<DataCollectorManager<T>> DataCollectorManager<T>::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 <algorithm>
+#include <set>
+#include <vector>
+#include <mutex>
+#include <string>
+#include "utils/data/collect/DataCollector.h"
+
+template <typename T>
+class ListDataCollectorImpl : public DataCollector<T>
+{
+private:
+    std::vector<T> data;
+    bool lock = false;
+    std::mutex mtx;
+
+public:
+    ListDataCollectorImpl() {}
+
+    ListDataCollectorImpl(const std::vector<T> &initial_data)
+    {
+        for (const std::string &item : initial_data)
+        {
+            addData(item);
+        }
+    }
+
+    void resetData() override
+    {
+        std::lock_guard<std::mutex> lock_this(mtx);
+        data.clear();
+        unlockIncrement();
+    }
+
+    std::vector<T> getAllData() override
+    {
+        std::lock_guard<std::mutex> lock_this(mtx);
+        return std::vector<T>(data.begin(), data.end());
+    }
+
+    std::set<T> getAllDataWithoutDuplicate() override
+    {
+        std::lock_guard<std::mutex> lock_this(mtx);
+        return std::set<T>(data.begin(), data.end());
+    }
+
+    void addData(const T &new_data) override
+    {
+        std::lock_guard<std::mutex> lock_this(mtx);
+        if (!lock)
+        {
+            data.push_back(new_data);
+        }
+    }
+
+    size_t getDataSizeWithoutDuplicate() override
+    {
+        std::lock_guard<std::mutex> lock_thislock(mtx);
+        return getAllDataWithoutDuplicate().size();
+    }
+
+    size_t getDataSize() override
+    {
+        std::lock_guard<std::mutex> lock_this(mtx);
+        return data.size();
+    }
+
+    bool isRepeatedData(const T &data) override
+    {
+        std::lock_guard<std::mutex> lock_this(mtx);
+        return std::count(this->data.begin(), this->data.end(), data) > 1;
+    }
+
+    int getRepeatedTimeForData(const T &data) override
+    {
+        std::lock_guard<std::mutex> lock_this(mtx);
+        return std::count(this->data.begin(), this->data.end(), data);
+    }
+
+    void removeData(const T &data) override
+    {
+        std::lock_guard<std::mutex> 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<std::mutex> 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 <atomic>
+#include <memory>
+#include <mutex>
+#include <map>
+#include <set>
+#include <vector>
+#include <string>
+#include "utils/data/collect/DataCollector.h"
+
+template <typename T>
+class MapDataCollectorImpl : public DataCollector<T>
+{
+    std::map<T, std::atomic<int>> data;
+    bool lock = false;
+    mutable std::mutex mtx;
+
+public:
+    MapDataCollectorImpl() {}
+
+    MapDataCollectorImpl(const std::vector<T> &initial_data)
+    {
+        for (const std::string &item : initial_data)
+        {
+            addData(item);
+        }
+    }
+
+    void resetData() override
+    {
+        std::lock_guard<std::mutex> lock_this(mtx);
+        data.clear();
+        unlockIncrement();
+    }
+
+    std::vector<T> getAllData() override
+    {
+        std::lock_guard<std::mutex> lock_this(mtx);
+        std::vector<T> 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<std::mutex> lock_this(mtx);
+        return data.size();
+    }
+
+    void addData(const T &new_data) override
+    {
+        std::lock_guard<std::mutex> 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<std::mutex> 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<std::mutex> lock_this(mtx);
+        return this->data[data] > 1;
+    }
+
+    std::set<T> getAllDataWithoutDuplicate() override
+    {
+        std::lock_guard<std::mutex> lock_this(mtx);
+        std::set<T> result;
+        for (const auto &pair : data)
+        {
+            result.insert(pair.first);
+        }
+        return result;
+    }
+
+    int getRepeatedTimeForData(const T &data) override
+    {
+        std::lock_guard<std::mutex> lock_this(mtx);
+        return this->data[data];
+    }
+
+    void removeData(const T &data) override
+    {
+        std::lock_guard<std::mutex> 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 <iostream>
+#include <gtest/gtest.h>
+#include <memory>
+#include <string>
+#include <sys/socket.h>
+#include <arpa/inet.h>
+#include <netdb.h>
+#include <unistd.h>
+#include <spdlog/spdlog.h>
+#include <spdlog/sinks/stdout_color_sinks.h>
+#include <spdlog/sinks/basic_file_sink.h>
+#include "resource/Resource.h"
+#include "utils/InitResourceUtils.h"
+
+std::shared_ptr<spdlog::logger> multi_logger;
+std::shared_ptr<Resource> resource;
+
+int main(int argc, char *argv[])
+{
+    // Register console and file log output
+    auto console_sink = std::make_shared<spdlog::sinks::stdout_color_sink_mt>();
+    auto file_sink = std::make_shared<spdlog::sinks::basic_file_sink_mt>("logs/output.log", true);
+    spdlog::sinks_init_list sink_list = {console_sink, file_sink};
+    multi_logger = std::make_shared<spdlog::logger>("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<Resource>();
+    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<MessageType, std::string> 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 <string>
+
+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<std::string, std::string> &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<std::string, std::string> &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<std::string,std::string>& properties);
+
+rocketmq::MQMessage MessageFactory::buildMessageWithProperty(const std::string &topic, const std::string &tag, const std::string &body, std::map<std::string, std::string> &properties, const std::vector<std::string> &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<spdlog::logger> 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<spdlog::logger> multi_logger;
+extern std::shared_ptr<Resource> resource;
+
+std::mutex NameUtils::mtx;
+
+std::unordered_map<std::string, std::string> NameUtils::alreadyUsed;
+
+std::atomic<int> RMQNormalConsumer::receivedIndex(0);
+
+std::vector<rocketmq::MQMessageExt> VerifyUtils::msgs;
+
+void initResource(std::shared_ptr<Resource> 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<std::string>("rocketmq.namesrv"));
+    resource->setBrokerAddr(pt.get<std::string>("rocketmq.brokerAddr"));
+    resource->setCluster(pt.get<std::string>("rocketmq.cluster"));
+    resource->setAccessKey(pt.get<std::string>("rocketmq.accessKey"));
+    resource->setSecretKey(pt.get<std::string>("rocketmq.secretKey"));
+    resource->setAccessChannel(pt.get<std::string>("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<spdlog::logger> 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 <unordered_map>
+#include <random>
+#include <chrono>
+#include <iomanip>
+#include <sstream>
+#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<const unsigned char *>(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<int>(hash[i]);
+    }
+
+    return ss.str();
+}
+
+std::string NameUtils::getTopicName()
+{
+    while (true)
+    {
+        std::string topic = "topic-server-" + generateRandomAlphanumeric(20);
+        std::lock_guard<std::mutex> 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<std::mutex> 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<std::mutex> 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<std::mutex> 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<std::mutex> 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<std::mutex> 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<std::mutex> 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<std::mutex> 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<std::mutex> 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<std::mutex> 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 <algorithm>
+#include <random>
+#include <string>
+#include <sstream>
+#include <array>
+
+#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<int> 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<int> 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<char>(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<char, 4> bytes;
+
+  for (size_t i = 0; i < hexStr.size(); ++i)
+  {
+    bytes[i] = static_cast<char>(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<char>(value);
+    utf8Bytes[1] = '\0';
+    utf8Bytes[2] = '\0';
+    utf8Bytes[3] = '\0';
+  }
+  else if (value <= 0x7FF)
+  {
+    utf8Bytes[0] = static_cast<char>((value >> 6) | 0xC0);
+    utf8Bytes[1] = static_cast<char>((value & 0x3F) | 0x80);
+    utf8Bytes[2] = '\0';
+    utf8Bytes[3] = '\0';
+  }
+  else
+  {
+    utf8Bytes[0] = static_cast<char>((value >> 12) | 0xE0);
+    utf8Bytes[1] = static_cast<char>(((value >> 6) & 0x3F) | 0x80);
+    utf8Bytes[2] = static_cast<char>((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 <map>
+#include <memory>
+#include <string>
+#include <vector>
+#include <future>
+#include <chrono>
+#include <thread>
+#include <atomic>
+
+extern std::shared_ptr<spdlog::logger> multi_logger;
+extern std::shared_ptr<Resource> resource;
+
+std::atomic<int> 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<std::string, long> VerifyUtils::checkDelay(DataCollector<MQMsg> &dequeueMessages, int delayLevel)
+{
+    std::unordered_map<std::string, long> map;
+    std::vector<MQMsg> receivedMessages = dequeueMessages.getAllData();
+    std::chrono::system_clock::time_point now = std::chrono::system_clock::now();
+    // 将时间点转换为时间戳(以毫秒为单位)
+    std::chrono::seconds duration = std::chrono::duration_cast<std::chrono::seconds>(now.time_since_epoch());
+    long consumeTime = duration.count() * 1000L;
+    // std::cout<<getDelayTime(delayLevel)<<std::endl;
+    for (auto &msg : receivedMessages)
+    {
+        long bornTimestamp = msg.getBornTimestamp();
+        // std::cout<<consumeTime<<" "<<bornTimestamp<<std::endl;
+        if (std::abs((consumeTime - bornTimestamp) - getDelayTime(delayLevel)) > 5000)
+        {
+            map.insert(std::make_pair(msg.getMsgId(), consumeTime - bornTimestamp));
+        }
+    }
+    return map;
+}
+
+bool VerifyUtils::checkOrder(DataCollector<MQMsg> &dequeueMessages)
+{
+    std::vector<MQMsg> receivedMessages = dequeueMessages.getAllData();
+    std::unordered_map<std::string, std::vector<MQMsg>> map;
+    for (const auto &receivedMessage : receivedMessages)
+    {
+        const std::string &shardingKey = std::to_string(std::stoi(receivedMessage.getBody()) % 2);
+        std::vector<MQMsg> 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<rocketmq::DefaultMQPullConsumer> pullConsumer)
+{
+    std::vector<rocketmq::MQMessageQueue> 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<rocketmq::DefaultMQPullConsumer> pullConsumer)
+{
+    // async_function(topic, pullConsumer);
+    std::future<bool> future1 = std::async(std::launch::async, [topic, subExpression, pullConsumer]()
+                                           { return async_function(topic, subExpression, pullConsumer); });
+    // std::future<bool> future2 = std::async(std::launch::async, [topic, pullConsumer](){ return async_function(topic, pullConsumer); });
+    // std::future<bool> future3 = std::async(std::launch::async, [topic, pullConsumer](){ return async_function(topic, pullConsumer); });
+    // std::future<bool> future4 = std::async(std::launch::async, [topic, pullConsumer](){ return async_function(topic, pullConsumer); });
+    // std::future<bool> 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<rocketmq::MQMessageExt> VerifyUtils::fetchMessages(std::shared_ptr<rocketmq::DefaultMQPullConsumer> pullConsumer, const std::string &topic)
+{
+    std::vector<rocketmq::MQMessageQueue> 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<std::string> VerifyUtils::waitForMessageConsume(DataCollector<std::string> &enqueueMessages, DataCollector<std::string> &dequeueMessages, long long timeoutMills, int consumedTimes)
+{
+    multi_logger->info("Set timeout: {}ms", timeoutMills);
+
+    std::vector<std::string> sendMessages = enqueueMessages.getAllData();
+
+    auto currentTime = std::chrono::steady_clock::now();
+
+    while (!sendMessages.empty())
+    {
+        std::vector<std::string> 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::milliseconds>(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<std::string> VerifyUtils::waitForMessageConsume(DataCollector<std::string> &enqueueMessages, DataCollector<MQMsg> &dequeueMessages, long long timeoutMills, int consumedTimes)
+{
+    multi_logger->info("Set timeout: {}ms", timeoutMills);
+
+    std::vector<std::string> sendMessages = enqueueMessages.getAllData();
+
+    auto currentTime = std::chrono::steady_clock::now();
+
+    while (!sendMessages.empty())
+    {
+        std::vector<MQMsg> 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::milliseconds>(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<std::string> &enqueueMessages, DataCollector<std::string> &dequeueMessages)
+{
+    std::vector<std::string> 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<std::string> &enqueueMessages, DataCollector<std::string> &dequeueMessages, std::unordered_set<std::string> &unconsumedMsgIds)
+{
+    std::vector<std::string> 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<std::string> &enqueueMessages, DataCollector<MQMsg> &dequeueMessages)
+{
+    std::vector<std::string> 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<std::string> &enqueueMessages, DataCollector<MQMsg> &dequeueMessages, std::map<std::string, std::string> &props, int expectedUnrecvMsgNum)
+{
+    std::vector<std::string> unConsumedMessages = waitForMessageConsume(enqueueMessages, dequeueMessages, TIMEOUT * 1000L, 1);
+    std::vector<MQMsg> 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<std::string> &enqueueMessages, DataCollector<MQMsg> &dequeueMessages, int delayLevel)
+{
+    std::vector<std::string> 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<std::string, long> 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<std::string> &enqueueMessages, DataCollector<MQMsg> &dequeueMessages)
+{
+    std::vector<std::string> 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<std::string, std::vector<MQMsg>> &receivedMessage)
+{
+    for (auto &pair : receivedMessage)
+    {
+        std::ostringstream oss;
+        int preNode = -1;
+        std::string key = pair.first;
+        std::vector<MQMsg> 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<std::string> &msgs, std::mutex &mtx, std::atomic<int> &recvCount)
+{
+    std::lock_guard<std::mutex> 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<RMQNormalProducer> producer, std::shared_ptr<rocketmq::DefaultMQPullConsumer> pullConsumer, std::string &topic, std::string &tag, int maxMessageNum)
+{
+    long endTime = std::chrono::duration_cast<std::chrono::milliseconds>(std::chrono::system_clock::now().time_since_epoch()).count() + TIMEOUT * 1000L;
+
+    std::vector<rocketmq::MQMessageQueue> mqs;
+    pullConsumer->fetchSubscribeMessageQueues(topic, mqs);
+
+    std::vector<std::string> sendMsgs = producer->getEnqueueMessages()->getAllData();
+    std::atomic<int> recvCount(sendMsgs.size());
+
+    while (endTime > std::chrono::duration_cast<std::chrono::milliseconds>(std::chrono::system_clock::now().time_since_epoch()).count())
+    {
+        std::vector<std::function<void()>> 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<std::future<void>> 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<RMQNormalProducer> producer, std::shared_ptr<rocketmq::DefaultMQPullConsumer> pullConsumer, std::string &topic, std::string &tag, int maxMessageNum)
+{
+    long endTime = std::chrono::duration_cast<std::chrono::milliseconds>(std::chrono::system_clock::now().time_since_epoch()).count() + 30 * 1000L;
+
+    std::vector<rocketmq::MQMessageQueue> mqs;
+    pullConsumer->fetchSubscribeMessageQueues(topic, mqs);
+
+    SimpleConcurrentHashMap<std::string, rocketmq::MQMessageExt> receivedMap;
+
+    while (endTime > std::chrono::duration_cast<std::chrono::milliseconds>(std::chrono::system_clock::now().time_since_epoch()).count())
+    {
+        std::vector<std::function<void()>> 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<std::future<void>> 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<RMQNormalProducer> producer, std::shared_ptr<rocketmq::DefaultMQPullConsumer> pullConsumer, std::string &topic, std::string &tag, int maxMessageNum)
+{
+    long endTime = std::chrono::duration_cast<std::chrono::milliseconds>(std::chrono::system_clock::now().time_since_epoch()).count() + 30 * 1000L;
+
+    std::vector<rocketmq::MQMessageQueue> mqs;
+    pullConsumer->fetchSubscribeMessageQueues(topic, mqs);
+
+    std::vector<rocketmq::MQMessageExt> receivedMessage;
+    SimpleConcurrentHashMap<std::string, std::atomic<int>> map;
+
+    while (endTime > std::chrono::duration_cast<std::chrono::milliseconds>(std::chrono::system_clock::now().time_since_epoch()).count())
+    {
+        std::vector<std::function<bool()>> 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;j<pullResult.msgFoundList.size();j++){
+                            std::string msgId = pullResult.msgFoundList[j].getMsgId();
+                            int id = std::stoi(pullResult.msgFoundList[j].getBody());
+                            if(id != 19 && count!=id) {
+                                multi_logger->error("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<int> 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<std::future<bool>> 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<RMQNormalProducer> producer, std::shared_ptr<rocketmq::DefaultMQPullConsumer> pullConsumer, std::string &topic, std::string &tag, int maxMessageNum)
+{
+    long endTime = std::chrono::duration_cast<std::chrono::milliseconds>(std::chrono::system_clock::now().time_since_epoch()).count() + TIMEOUT * 1000L;
+
+    std::vector<std::string> sendCollection = producer->getEnqueueMessages()->getAllData();
+
+    try
+    {
+        while (endTime > std::chrono::duration_cast<std::chrono::milliseconds>(std::chrono::system_clock::now().time_since_epoch()).count())
+        {
+            std::vector<rocketmq::MQMessageQueue> mqs;
+            pullConsumer->fetchSubscribeMessageQueues(topic, mqs);
+            for (auto &mq : mqs)
+            {
+                std::unordered_map<std::string, std::vector<MQMsg>> 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<MQMsg> 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<RMQNormalProducer> producer, std::shared_ptr<rocketmq::DefaultMQPullConsumer> pullConsumer, std::string &topic, std::string &tag, int maxMessageNum)
+{
+    long endTime = std::chrono::duration_cast<std::chrono::milliseconds>(std::chrono::system_clock::now().time_since_epoch()).count() + 60 * 1000L;
+
+    try
+    {
+        while (endTime > std::chrono::duration_cast<std::chrono::milliseconds>(std::chrono::system_clock::now().time_since_epoch()).count())
+        {
+            std::vector<rocketmq::MQMessageQueue> 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<RMQNormalProducer> producer, std::shared_ptr<rocketmq::DefaultMQPullConsumer> pullConsumer, std::string &topic, std::string &tag, int maxMessageNum)
+{
+    long endTime = std::chrono::duration_cast<std::chrono::milliseconds>(std::chrono::system_clock::now().time_since_epoch()).count() + TIMEOUT * 1000L;
+
+    SimpleConcurrentHashMap<std::string, rocketmq::MQMessageExt> map;
+
+    try
+    {
+        while (endTime > std::chrono::duration_cast<std::chrono::milliseconds>(std::chrono::system_clock::now().time_since_epoch()).count())
+        {
+            std::vector<rocketmq::MQMessageQueue> 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<std::string> &dequeueMessages = DataCollectorManager<std::string>::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<RMQNormalProducer> producer, std::shared_ptr<rocketmq::DefaultMQPullConsumer> pullConsumer, std::string &topic, std::string &tag, int maxMessageNum)
+{
+    long endTime = std::chrono::duration_cast<std::chrono::milliseconds>(std::chrono::system_clock::now().time_since_epoch()).count() + 30 * 1000L;
+
+    std::vector<std::function<bool()>> runnables;
+    SimpleConcurrentHashMap<std::string, rocketmq::MQMessageExt> recvMsgs;
+    bool flag{true};
+    std::unordered_set<std::string> unconsumedMsgIds;
+
+    for (int i = 0; i < 4; i++)
+    {
+        while (endTime > std::chrono::duration_cast<std::chrono::milliseconds>(std::chrono::system_clock::now().time_since_epoch()).count())
+        {
+            try
+            {
+                std::vector<rocketmq::MQMessageQueue> 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<std::string> &dequeueMessages = DataCollectorManager<std::string>::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 <stdlib.h>
+#include <chrono>
+#include <iomanip>
+#include <iostream>
+#include <map>
+#include <vector>
+#include <string>
+#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<spdlog::logger> multi_logger;
+extern std::shared_ptr<Resource> 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<rocketmq::MQMessageQueue> 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 <string>
+#include <vector>
+#include <mutex>
+#include <thread>
+#include "gtest/gtest.h"
+#include "rocketmq/DefaultMQPullConsumer.h"
+#include "rocketmq/MQMessage.h"
+#include "resource/Resource.h"
+#include "frame/BaseOperate.h"
+
+extern std::shared_ptr<spdlog::logger> multi_logger;
+extern std::shared_ptr<Resource> 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 <string>
+#include <vector>
+#include <mutex>
+#include <thread>
+#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<spdlog::logger> multi_logger;
+extern std::shared_ptr<Resource> 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 <string>
+#include <mutex>
+#include <chrono>
+#include "gtest/gtest.h"
+#include "spdlog/spdlog.h"
+#include "rocketmq/MQMessage.h"
+#include "frame/BaseOperate.h"
+#include "resource/Resource.h"
+
+extern std::shared_ptr<spdlog::logger> multi_logger;
+extern std::shared_ptr<Resource> 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 <iostream>
+#include <string>
+#include <mutex>
+#include <chrono>
+#include <thread>
+#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<spdlog::logger> multi_logger;
+extern std::shared_ptr<Resource> 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> msglistener = std::make_shared<MsgListener>();
+        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<rocketmq::MQMessageExt> 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> msglistener = std::make_shared<MsgListener>();
+        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> msglistener = std::make_shared<MsgListener>();
+        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 <string>
+#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<spdlog::logger> multi_logger;
+extern std::shared_ptr<Resource> 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 <string>
+#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<spdlog::logger> multi_logger;
+extern std::shared_ptr<Resource> 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 <string>
+#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<spdlog::logger> multi_logger;
+extern std::shared_ptr<Resource> 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 <string>
+#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<spdlog::logger> multi_logger;
+extern std::shared_ptr<Resource> 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 <string>
+#include <thread>
+#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<spdlog::logger> multi_logger;
+extern std::shared_ptr<Resource> 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 <iostream>
+#include <memory>
+#include <string>
+#include <mutex>
+#include <chrono>
+#include <thread>
+#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<spdlog::logger> multi_logger;
+extern std::shared_ptr<Resource> 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<RMQNormalListener> listener1 = std::make_shared<RMQNormalListener>("Listener1");
+    std::shared_ptr<RMQNormalListener> listener2 = std::make_shared<RMQNormalListener>("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::milliseconds>(std::chrono::system_clock::now().time_since_epoch()).count() + 240 * 1000L;
+    while (endTime > std::chrono::duration_cast<std::chrono::milliseconds>(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<RMQNormalListener> listener1 = std::make_shared<RMQNormalListener>("Listener1");
+        std::shared_ptr<RMQNormalListener> listener2 = std::make_shared<RMQNormalListener>("Listener2");
+        std::shared_ptr<RMQNormalListener> listener3 = std::make_shared<RMQNormalListener>("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 <iostream>
+#include <memory>
+#include <string>
+#include <mutex>
+#include <chrono>
+#include <thread>
+#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<spdlog::logger> multi_logger;
+extern std::shared_ptr<Resource> 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<RMQNormalListener> listener1 = std::make_shared<RMQNormalListener>("Listener1");
+//     std::shared_ptr<RMQNormalListener> listener2 = std::make_shared<RMQNormalListener>("Listener2");
+//     std::shared_ptr<RMQNormalListener> listener3 = std::make_shared<RMQNormalListener>("Listener3");
+//     std::shared_ptr<RMQNormalListener> listener4 = std::make_shared<RMQNormalListener>("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;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::milliseconds>(std::chrono::system_clock::now().time_since_epoch()).count()+240*1000L;
+//     while(endTime > std::chrono::duration_cast<std::chrono::milliseconds>(std::chrono::system_clock::now().time_since_epoch()).count()){
+//         std::cout<<listener1->getDequeueMessages()->getDataSize() +
+//            listener2->getDequeueMessages()->getDataSize() +
+//            listener3->getDequeueMessages()->getDataSize() +
+//            listener4->getDequeueMessages()->getDataSize() <<std::endl;
+//         if(listener1->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 <iostream>
+#include <memory>
+#include <string>
+#include <mutex>
+#include <chrono>
+#include <thread>
+#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<spdlog::logger> multi_logger;
+extern std::shared_ptr<Resource> 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<std::string, std::string> 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<RMQNormalListener>());
+////         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; i<SEND_NUM; i++) {
+////             rocketmq::MQMessage msg(topic, RandomUtils::getStringByUUID());
+////             msg.setProperties(properties);
+////             producer->send(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<std::string, std::string> properties1;
+////     properties1["price"] = "10";
+////     std::map<std::string, std::string> properties2;
+////     properties2["price"] = "30";
+////     std::string subExpression = "*";
+////     ASSERT_NO_THROW({
+////         auto pushConsumer = ConsumerFactory::getRMQPushConsumer(topic,group,subExpression,std::make_shared<RMQNormalListener>());
+////         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; i<SEND_NUM; i++) {
+////             rocketmq::MQMessage msg(topic, RandomUtils::getStringByUUID());
+////             msg.setProperties(properties1);
+////             producer->send(msg);
+////         }
+////         for (int i=0; i<SEND_NUM; i++) {
+////             rocketmq::MQMessage msg(topic, RandomUtils::getStringByUUID());
+////             msg.setProperties(properties2);
+////             producer->send(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 <iostream>
+#include <memory>
+#include <string>
+#include <mutex>
+#include <chrono>
+#include <thread>
+#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<spdlog::logger> multi_logger;
+extern std::shared_ptr<Resource> 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<RMQNormalListener>());
+
+        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<RMQNormalListener>());
+
+        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<RMQNormalListener>());
+
+        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<RMQNormalListener>());
+
+        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<RMQNormalListener>());
+
+        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<RMQNormalListener>());
+
+        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<RMQNormalListener>());
+
+        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<RMQNormalListener>());
+
+        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<RMQNormalListener>());
+
+        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<RMQNormalListener>());
+
+        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<RMQNormalListener>());
+
+        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 <chrono>
+#include <iostream>
+#include <cassert>
+#include <string>
+#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<spdlog::logger> multi_logger;
+extern std::shared_ptr<Resource> 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<RMQNormalListener> listener1 = std::make_shared<RMQNormalListener>("Listener1");
+    std::shared_ptr<RMQNormalListener> listener2 = std::make_shared<RMQNormalListener>("Listener2");
+
+    auto rmqPushConsumer1 = std::make_shared<rocketmq::DefaultMQPushConsumer>(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::milliseconds>(std::chrono::system_clock::now().time_since_epoch()).count() + 240 * 1000L;
+    while (endTime > std::chrono::duration_cast<std::chrono::milliseconds>(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<rocketmq::DefaultMQPushConsumer>(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::milliseconds>(std::chrono::system_clock::now().time_since_epoch()).count() + 240 * 1000L;
+    while (endTime2 > std::chrono::duration_cast<std::chrono::milliseconds>(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<RMQNormalListener> listener = std::make_shared<RMQNormalListener>("Listener");
+
+    auto rmqPushConsumer = std::make_shared<rocketmq::DefaultMQPushConsumer>(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::milliseconds>(std::chrono::system_clock::now().time_since_epoch()).count() + 240 * 1000L;
+    while (endTime > std::chrono::duration_cast<std::chrono::milliseconds>(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<rocketmq::MQMessageQueue> mqs;
+    pullConsumer->fetchSubscribeMessageQueues(topic, mqs);
+
+    std::vector<rocketmq::MQMessageExt> receivedMessage;
+    SimpleConcurrentHashMap<std::string, std::atomic<int>> map;
+
+    long endTime = std::chrono::duration_cast<std::chrono::milliseconds>(std::chrono::system_clock::now().time_since_epoch()).count() + 30 * 1000L;
+    while (endTime > std::chrono::duration_cast<std::chrono::milliseconds>(std::chrono::system_clock::now().time_since_epoch()).count())
+    {
+        std::vector<std::function<void()>> 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<pullResult.msgFoundList.size();j++){
+                            std::string msgId = pullResult.msgFoundList[j].getMsgId();
+                            if(map.contains(msgId)){
+                                map[msgId]++;
+                            }else{
+                                std::atomic<int> 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<std::future<void>> 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 <atomic>
+#include <chrono>
+#include <string>
+#include <cstddef>
+#include <iostream>
+#include <cassert>
+#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<spdlog::logger> multi_logger;
+extern std::shared_ptr<Resource> 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 <atomic>
+#include <chrono>
+#include <cstddef>
+#include <iostream>
+#include <cassert>
+#include <string>
+#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<spdlog::logger> multi_logger;
+extern std::shared_ptr<Resource> 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 <atomic>
+#include <chrono>
+#include <cstddef>
+#include <iostream>
+#include <cassert>
+#include <string>
+#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<spdlog::logger> multi_logger;
+extern std::shared_ptr<Resource> 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 <atomic>
+#include <chrono>
+#include <cstddef>
+#include <iostream>
+#include <cassert>
+#include <string>
+#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<spdlog::logger> multi_logger;
+extern std::shared_ptr<Resource> 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 <atomic>
+#include <chrono>
+#include <cstddef>
+#include <iostream>
+#include <cassert>
+#include <string>
+#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<spdlog::logger> multi_logger;
+extern std::shared_ptr<Resource> 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;i<SEND_NUM;i++){
+////        auto message = MessageFactory::buildMessage(topic,tag,std::to_string(i));
+////        transProducer->sendTrans(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 <chrono>
+#include <iostream>
+#include <cassert>
+#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<spdlog::logger> multi_logger;
+extern std::shared_ptr<Resource> 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<RMQNormalListener>());
+
+    auto producer = ProducerFactory::getRMQProducer(group);
+
+    ASSERT_NE(producer, nullptr);
+
+    for(int i=0;i<SEND_NUM;i++){
+        auto message = MessageFactory::buildDelayMessage(topic,tag,RandomUtils::getStringByUUID(),3);
+        producer->send(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<RMQNormalListener>());
+////
+////    auto producer = ProducerFactory::getRMQProducer(group);
+////
+////    ASSERT_NE(producer, nullptr);
+////
+////    for(int i=0;i<SEND_NUM;i++){
+////        auto message = MessageFactory::buildDelayMessage(topic,tag,RandomUtils::getStringByUUID(),3);
+////        producer->sendAsync(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<RMQNormalListener>());
+////
+////    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<RMQNormalListener>());
+////
+////    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 <chrono>
+#include <iostream>
+#include <cassert>
+#include <string>
+#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<spdlog::logger> multi_logger;
+extern std::shared_ptr<Resource> 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<RMQNormalListener>());
+
+    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);
+    }
+
+    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<RMQNormalListener>());
+////
+////    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->sendAsync(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 <chrono>
+#include <iostream>
+#include <cassert>
+#include <string>
+#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<spdlog::logger> multi_logger;
+extern std::shared_ptr<Resource> 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<RMQOrderListener>());
+
+    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,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 <atomic>
+#include <chrono>
+#include <cstddef>
+#include <iostream>
+#include <cassert>
+#include <string>
+#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<spdlog::logger> multi_logger;
+extern std::shared_ptr<Resource> 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<RMQNormalListener>());
+
+    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;i<SEND_NUM;i++){
+        auto message = MessageFactory::buildMessage(topic,tag,RandomUtils::getStringByUUID());
+        rocketmq::SendResult sendResult = transProducer->sendTrans(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<RMQNormalListener>());
+
+    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;i<SEND_NUM;i++){
+        auto message = MessageFactory::buildMessage(topic,tag,RandomUtils::getStringByUUID());
+        rocketmq::SendResult sendResult = transProducer->sendTrans(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<RMQNormalListener>());
+
+    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;i<SEND_NUM;i++){
+        auto message = MessageFactory::buildMessage(topic,tag,RandomUtils::getStringByUUID());
+        rocketmq::SendResult sendResult = transProducer->sendTrans(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<RMQNormalListener>());
+
+    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;i<SEND_NUM;i++){
+        auto message = MessageFactory::buildMessage(topic,tag,RandomUtils::getStringByUUID());
+        rocketmq::SendResult sendResult = transProducer->sendTrans(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<RMQNormalListener>());
+
+    std::atomic<int> commitMsgNum(0);
+    std::atomic<int> 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;i<SEND_NUM;i++){
+        auto message = MessageFactory::buildMessage(topic,tag,std::to_string(i));
+        rocketmq::SendResult sendResult = transProducer->sendTrans(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 <chrono>
+#include <iostream>
+#include <cassert>
+#include <string>
+#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<spdlog::logger> multi_logger;
+extern std::shared_ptr<Resource> resource;
+
+class ExceptionMsgListener : public rocketmq::MessageListenerConcurrently {
+public:
+    SimpleConcurrentHashMap<std::string, rocketmq::MQMessageExt> firstMsgs;
+    SimpleConcurrentHashMap<std::string, rocketmq::MQMessageExt> retryMsgs;
+    ExceptionMsgListener() {}
+    virtual ~ExceptionMsgListener() {}
+
+  virtual rocketmq::ConsumeStatus consumeMessage(const std::vector<rocketmq::MQMessageExt>& 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<std::string, rocketmq::MQMessageExt> firstMsgs;
+//     SimpleConcurrentHashMap<std::string, rocketmq::MQMessageExt> retryMsgs;
+//     NullMsgListener() {}
+//     virtual ~NullMsgListener() {}
+
+//   virtual rocketmq::ConsumeStatus consumeMessage(const std::vector<rocketmq::MQMessageExt>& 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<std::string, rocketmq::MQMessageExt> firstMsgs;
+    SimpleConcurrentHashMap<std::string, rocketmq::MQMessageExt> retryMsgs;
+    NormalMsgListener() {}
+    virtual ~NormalMsgListener() {}
+
+  virtual rocketmq::ConsumeStatus consumeMessage(const std::vector<rocketmq::MQMessageExt>& 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<rocketmq::MQMessageExt> recvMessages;
+    FIFOMsgListener() {}
+    virtual ~FIFOMsgListener() {}
+
+  virtual rocketmq::ConsumeStatus consumeMessage(const std::vector<rocketmq::MQMessageExt>& 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<rocketmq::DefaultMQPushConsumer>(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;i<SEND_NUM;i++){
+        auto message = MessageFactory::buildMessage(topic,tag,std::to_string(i));
+        producer->send(message);
+    }
+
+    ASSERT_EQ(SEND_NUM,producer->getEnqueueMessages()->getDataSize());
+    long endTime = std::chrono::duration_cast<std::chrono::milliseconds>(std::chrono::system_clock::now().time_since_epoch()).count()+240*1000L;
+    while(endTime > std::chrono::duration_cast<std::chrono::milliseconds>(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<rocketmq::DefaultMQPushConsumer>(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;i<SEND_NUM;i++){
+////         auto message = MessageFactory::buildMessage(topic,tag,std::to_string(i));
+////         producer->send(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<rocketmq::DefaultMQPushConsumer>(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;i<SEND_NUM;i++){
+        auto message = MessageFactory::buildMessage(topic,tag,std::to_string(i));
+        producer->send(message);
+    }
+
+    ASSERT_EQ(SEND_NUM,producer->getEnqueueMessages()->getDataSize());
+
+    long endTime = std::chrono::duration_cast<std::chrono::milliseconds>(std::chrono::system_clock::now().time_since_epoch()).count()+240*1000L;
+    while(endTime > std::chrono::duration_cast<std::chrono::milliseconds>(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<rocketmq::DefaultMQPushConsumer>(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;i<SEND_NUM;i++){
+        auto message = MessageFactory::buildMessage(topic,tag,std::to_string(i));
+        producer->sendOrderMessage(message,0);
+    }
+
+    ASSERT_EQ(SEND_NUM,producer->getEnqueueMessages()->getDataSize());
+
+    long endTime = std::chrono::duration_cast<std::chrono::milliseconds>(std::chrono::system_clock::now().time_since_epoch()).count()+240*1000L;
+    while(endTime > std::chrono::duration_cast<std::chrono::milliseconds>(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 <chrono>
+#include <iostream>
+#include <cassert>
+#include <string>
+#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<spdlog::logger> multi_logger;
+extern std::shared_ptr<Resource> 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<RMQNormalListener>());
+
+    auto producer = ProducerFactory::getRMQProducer(group);
+
+    ASSERT_NE(producer, nullptr);
+    
+    std::vector<rocketmq::MQMessage> msgs;
+    for(int i=0;i<SEND_NUM;i++){
+        auto message = MessageFactory::buildMessage(topic,tag,std::to_string(i));
+        msgs.push_back(message);
+    }
+
+    ASSERT_NO_THROW({
+        rocketmq::SendResult sendResult= producer->getProducer()->send(msgs);
+        ASSERT_EQ(sendResult.getSendStatus(),rocketmq::SEND_OK);
+    });
+
+    long endTime = std::chrono::duration_cast<std::chrono::milliseconds>(std::chrono::system_clock::now().time_since_epoch()).count()+240*1000L;
+    while(endTime > std::chrono::duration_cast<std::chrono::milliseconds>(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<MQMessage>& 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<RMQNormalListener>());
+
+    auto pullConsumer = ConsumerFactory::getRMQPullConsumer(topic,group);
+    std::vector<rocketmq::MQMessageQueue> mqs;
+    pullConsumer->getPullConsumer()->fetchSubscribeMessageQueues(topic, mqs);
+    pullConsumer->shutdown();
+
+    auto producer = ProducerFactory::getRMQProducer(group);
+
+    ASSERT_NE(producer, nullptr);
+    
+    std::vector<rocketmq::MQMessage> msgs;
+    for(int i=0;i<SEND_NUM;i++){
+        auto message = MessageFactory::buildMessage(topic,tag,std::to_string(i));
+        msgs.push_back(message);
+    }
+
+    ASSERT_NO_THROW({
+        rocketmq::SendResult sendResult= producer->getProducer()->send(msgs,mqs[0]);
+        ASSERT_EQ(sendResult.getSendStatus(),rocketmq::SEND_OK);
+    });
+
+    long endTime = std::chrono::duration_cast<std::chrono::milliseconds>(std::chrono::system_clock::now().time_since_epoch()).count()+240*1000L;
+    while(endTime > std::chrono::duration_cast<std::chrono::milliseconds>(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